Skip to content

Conversation

@stinos
Copy link

@stinos stinos commented Sep 6, 2022

I finally came around to getting my micropython-lib fork in sync, turns out it broke all my tests :) Strange no-one noticed this.

@stinos stinos force-pushed the unittest-regression-fixes branch 2 times, most recently from 7f36020 to 705f32f Compare September 6, 2022 14:49
@andrewleech
Copy link
Contributor

Thanks for the updates - when I originally tried to run "all tests in the repo" with the discover updates there were so many failures from a mixture of:

  • dependency hell
  • non-unittest based test scripts
  • tests that have clearly been broken for a long time
    I basically stopped bothering trying to work out which tests should/shouldn't pass and made it work with the test usages I had / could find locally.

I'm curious about the first commit here:

Fix regression introduced by a7b2f63 which broke the typical case of
running a test script which has

if name == "main":
unittest.main()

This usage seems to work fine for me as-is on current master?

STEP: ~/micropython-lib/unix-ffi/unittest $ micropython ./test_unittest.py 
test_NotChangedByOtherTest (test_unittest_isolated.TestUnittestIsolated) ... ok

----------------------------------------------------------------------
Ran 1 tests

OK
testFail (test_unittest.TestUnittestAssertions) ... ok
testEqual (test_unittest.TestUnittestAssertions) ... ok
test_AlmostEqual (test_unittest.TestUnittestAssertions) ... ok
test_AlmostEqualWithDelta (test_unittest.TestUnittestAssertions) ... ok
testNotEqual (test_unittest.TestUnittestAssertions) ... ok
testIs (test_unittest.TestUnittestAssertions) ... ok
testIsNot (test_unittest.TestUnittestAssertions) ... ok
testIsNone (test_unittest.TestUnittestAssertions) ... ok
testIsNotNone (test_unittest.TestUnittestAssertions) ... ok
testTrue (test_unittest.TestUnittestAssertions) ... ok
testFalse (test_unittest.TestUnittestAssertions) ... ok
testIn (test_unittest.TestUnittestAssertions) ... ok
testIsInstance (test_unittest.TestUnittestAssertions) ... ok
testRaises (test_unittest.TestUnittestAssertions) ... ok
testSkip (test_unittest.TestUnittestAssertions) ... skipped: test of skipping
testAssert (test_unittest.TestUnittestAssertions) ... ok
testExpectedFailure (test_unittest.TestUnittestAssertions) ... ok
testExpectedFailureNot (test_unittest.TestUnittestAssertions) ... ok
test_NotChangedByOtherTest (test_unittest.TestUnittestAssertions) ... FAIL
test_subtest_even (test_unittest.TestUnittestAssertions) ... ok
testSetUpTearDownClass_1 (test_unittest.TestUnittestSetup) ... ok
testSetUpTearDownClass_2 (test_unittest.TestUnittestSetup) ... ok

======================================================================
FAIL: test_NotChangedByOtherTest <class 'TestUnittestAssertions'>
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/anl/micropython-lib/unix-ffi/unittest/unittest.py", line 393, in run_one
  File "<string>", line 1, in <module>
  File "./test_unittest.py", line 148, in test_NotChangedByOtherTest
AssertionError: 

----------------------------------------------------------------------
Ran 22 tests

FAILED (failures=1, errors=0)

Can you suggest a reproduction of the issue you ran into?
Your change looks clean, I'm just not following the usage of it.

if isinstance(c, object) and isinstance(c, type) and issubclass(c, TestCase):
yield c
elif tn.startswith("test_") and callable(c):
elif tn.startswith("test") and callable(c):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for breaking some tests with this change... I don't remember clearly now but I think I found some existing test scripts in the repo where there were testFoo functions being run that were not intended to be unittest functions, so thought the _ made more sense. I believe this is non-standard functionality that cpython doesn't support anyway (finding & running bare test* functions)?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is non-standard functionality that cpython doesn't support anyway (finding & running bare test* functions)?

I think so yes, at least I didn't find anything which does that. I'd personally refrain from doing that because it's non-standard, and because if you have a file like that then decide it runs under CPython as well you might think its tests are all fine but they are just not being ran. CPython does support a single runTest function though like

class Test(unittest.TestCase):
  @staticmethod
  def runTest():
    test = unittest.TestCase()
    test.assertEqual(1, 1)

But anyway test is the standard prefix; e.g. By default these are the method names beginning with test and testMethodPrefix = 'test' in unittest/loader.py.

result = discover(runner)
# If there's one argument assume it's the path to the script which called main(), so run its tests.
elif argn == 1:
result = run_module(runner, module, split_path(sys.argv[0])[0], None)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

split_path(sys.argv[0])
This is to split out just the filename isn't it... I would think this will fail if you're running a unittest from a different folder?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that's basically os.path.dirname . Not sure why that would fail, the point of the path argument is to add the script directory to sys.path right? So python /path/to/test.py is going to add path/to. I just realize this is not actually needed though, the directory of the executing script gets added to sys.path anyway. Moreover, as the comment says this is an assumption but thinking about it: this is just wrong and I'm going to remove it. If anything argv[0] could be used as prog name for argparse but that's it.

@stinos
Copy link
Author

stinos commented Sep 7, 2022

Can you suggest a reproduction of the issue you ran into?

The output from your test actually shows the problem: it runs not just test_unittest, but both test_unittest and test_unittest_isolation, because it's running test discovery in the current directory. E.g. running micropython ./unittest.py will also run both tests (instead of nothing). Whereas going one directory up and running micropython ./unittest/test_unittest.py will discover all tests in unix_ffi. Or still another way: create a file

import unittest
import test_unittest

unittest.main(test_unittest)

in the unittest directory and it will simply ignore the argument to main() and instead run all tests in the directory.

Btw any idea why test_NotChangedByOtherTest fails on your machine?

Also: ping for my other comment

Fix regression introduced by a7b2f63 which broke the typical case of
running a test script which has

if __name__ == "__main__":
    unittest.main()
CPython's unittest.main() function uses a different argument order
than what we have now, so to avoid future problems with unclear error
messages should we add arguments in the future, force callers to
specify exact arguments.
@stinos stinos force-pushed the unittest-regression-fixes branch from 705f32f to 094f3ef Compare September 7, 2022 10:33
@stinos
Copy link
Author

stinos commented Sep 7, 2022

I went over the code again and fixed some things:

  • basically if main is called with a module as argument it shouldn't look at argv at all, just run the module, so make it like that
  • passing a module to run_module would fail because the module was passed to TestSuit but that expects a string
  • force using keywords with main (not sure if that one is really needed)

Note the code still has the equivalent of if len(sys.argv) == 0: discover() because the original had that. but I'm not sure what it would mean for sys.argv to be empty?

@andrewleech
Copy link
Contributor

The output from your test actually shows the problem: it runs not just test_unittest, but both test_unittest and test_unittest_isolation, because it's running test discovery in the current directory.

Ah I see now. That'll be why I hadn't noticed the bug - at first glance it looks like it's still working. But it's not doing the right thing certainly, and pulls in the dependency when it wasn't intended to. Nice fix, thanks!

@andrewleech
Copy link
Contributor

Btw any idea why test_NotChangedByOtherTest fails on your machine?

That I'm not sure about, didn't get a chance to investigate when I ran that this morning.

runner = TestRunner()

if len(sys.argv) <= 1:
result = discover(runner)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note the code still has the equivalent of if len(sys.argv) == 0: discover() because the original had that. but I'm not sure what it would mean for sys.argv to be empty?

Do you mean this original? This was intended to catch the len==1 mostly, main case of this is micropython -m unittest where sys.argv == ['unittest'].

It's true I don't know if there's ever a case where len(sys.argv) == 0 unless you're manipulating sys.argv manually in a wrapper script, in which case all bets are off really. Not sure if I had a specific reason to make this <= 1 rather than just == 1 other than maybe not wanting to leave an undefined case statement.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean this original?

Yes exactly.

But without the check it would be unconditionally indexing sys.argv which is also not very nice so I'll just leave it as-is.

These must be class methods decorated, and tearDownClass must be
called after runTest() as well.
@jimmo
Copy link
Member

jimmo commented Sep 9, 2022

Thanks @stinos . I think all of this should be addressed by #527 (comment)

Please let me know if not.

@dpgeorge
Copy link
Member

Should be fixed in 796a598

@dpgeorge dpgeorge closed this Sep 13, 2022
@stinos stinos deleted the unittest-regression-fixes branch September 13, 2022 09:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants