[Avocado-devel] miniRFC: Call `tearDown` after `setUp` failure

Lukáš Doktor ldoktor at redhat.com
Thu Mar 23 06:10:23 UTC 2017


Dne 22.3.2017 v 20:00 Ademar Reis napsal(a):
> On Wed, Mar 22, 2017 at 07:05:48PM +0100, Lukáš Doktor wrote:
>> Hello guys,
>>
>> I remember early in development we decided to abort the execution when
>> `setUp` fails without executing `tearDown`, but over the time I keep
>> thinking about it and I don't think it's optimal. My main reason is to
>> simplify `setUp` code, let me demonstrate it on an example.
>
> So you're basically proposing that setUp() and tearDown() are
> always executed "together". In other words, if setUp() is called,
> then tearDown() will also be called, even if setUp() resulted in
> error, exception or cancellation.
>
> I think it makes perfect sense.
>
> The only problem I see is in the case of test interruption
> (ctrl+c or timeout). I think this will have to be the only
> exception. In case of interruption, we can't wait to run
> tearDown().
>
> In summary, the behavior would be:
>
>  normal execution
>    - test status = PASS | FAIL | WARN
>    - setUp() is run
>    - tearDown() is run
yep

>
>  skip (test runner decision)
this one is tricky, but we can refine it when we have dep solver...

>  skipIf() (decorator)
>    - Test is completely skipped by the test runner
>    - Test status = SKIP
>    - Neither setUp() nor tearDown() are run.
yep

>
>  Uncaught error/exception
>    - test status = ERROR
>    - tearDown() is run
This is the same behavior as PASS | FAIL | WARN, right?

>
>  self.cancel()
>    - Can be called during any stage of the test execution.
>      Results in test status = CANCEL.
>    - tearDown() is run.
Again, the same behavior as PASS | FAIL | WARN.

>
>  Test is interrupted (ctrl+C or timeout)
>    - test status = INTERRUPT
>    - tearDown() is not run
On timeout I agree, on single ctrl+c we wait till the test is over. I 
think it'd make sense to let the tearDown finish as well unless the 
double ctrl+c is pressed, what do you think?

Lukáš

>
> Thanks.
>    - Ademar
>
>>
>> Currently when you (safely) want to get a few remote servers, nfs share and
>> local service, you have to:
>>
>>     class MyTest(test):
>>         def setUp(self):
>>             self.nfs_share = None
>>             self.remote_servers = []
>>             self.service = None
>>             try:
>>                 for addr in self.params.get("remote_servers"):
>>                     self.remote_servers.append(login(addr))
>>                 self.nfs_share = get_nfs_share()
>>                 self.service = get_service()
>>             except:
>>                 for server in self.remote_servers:
>>                     server.close()
>>                 if self.nfs_share:
>>                     self.nfs_share.close()
>>                 if self.service:
>>                     self.service.stop()
>>                 raise
>>
>>         def tearDown(self):
>>             if self.nfs_share:
>>                 self.nfs_share.close()
>>             for server in self.remote_servers:
>>                 server.close()
>>             if self.service:
>>                 self.service.stop()
>>
>>
>> But if the tearDown was also executed, you'd simply write:
>>
>>     class MyTest(test):
>>         def setUp(self):
>>             self.nfs_share = None
>>             self.remote_servers = []
>>             self.service = None
>>
>>             for addr in self.params.get("remote_servers"):
>>                 self.remote_servers.append(login(addr))
>>             self.nfs_share = get_nfs_share()
>>             self.service = get_service()
>>
>>         def tearDown(self):
>>             if self.nfs_share:
>>                 self.nfs_share.close()
>>             for server in self.remote_servers:
>>                 server.close()
>>             if self.service:
>>                 self.service.stop()
>>
>> As you can see the current solution requires catching exceptions and
>> basically writing the tearDown twice. Yes, properly written tearDown could
>> be manually executed from the `setUp`, but that looks a bit odd and my
>> experience is that people usually just write:
>>
>>     class MyTest(test):
>>         def setUp(self):
>>             self.remote_servers = []
>>             for addr in self.params.get("remote_servers"):
>>                 self.remote_servers.append(login(addr))
>>             self.nfs_share = get_nfs_share()
>>             self.service = get_service()
>>
>>         def tearDown(self):
>>             self.nfs_share.close()
>>             for server in self.remote_servers:
>>                 server.close()
>>             self.service.stop()
>>
>> which usually works but when the `get_nfs_share` fails, the remote_servers
>> are not cleaned, which might spoil the following tests (as they might be
>> persistent, eg. via aexpect).
>>
>> Kind regards,
>> Lukáš
>>
>> PS: Yes, the tearDown is unsafe as when `nfs_share.close()` fails the rest
>> is not cleaned up. This is just a demonstration and the proper tearDown and
>> a proper setUp for the current behavior would be way more complex.
>>
>
>
>
>

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 502 bytes
Desc: OpenPGP digital signature
URL: <http://listman.redhat.com/archives/avocado-devel/attachments/20170323/f0179a09/attachment.sig>


More information about the Avocado-devel mailing list