Skip to content

Refactor of BitcoinTestFramework main function.#26

Closed
jachiang wants to merge 7 commits into
ChaincodeResidency:masterfrom
jachiang:master-testframework-main-refactor
Closed

Refactor of BitcoinTestFramework main function.#26
jachiang wants to merge 7 commits into
ChaincodeResidency:masterfrom
jachiang:master-testframework-main-refactor

Conversation

@jachiang
Copy link
Copy Markdown

@jachiang jachiang commented Sep 2, 2019

In order to wrap the BitcoinTestFramework class in a user-defined class, which can be started and shutdown, the code in BitcoinTestFramework main has been encapsulated in respective functions. Additionally, test-run specific state in temp-directory and NetworkThread is now reset after shutdown.

Behavioural changes:
Potential exceptions raised from any code in setup section of the code are now handled. Previously, only exceptions raised by code beginning here was handled.

TODO's:

  • Refactor NetworkThread class in mininode.py, so each BitcoinTestFramework object initialises a different network thread.
  • Documentation (markdown file) describing how to run an interactive test with TestWrapper.

@jachiang jachiang requested review from jnewbery and maflcko September 2, 2019 23:58
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nit: the convention seems to be that comments usually go the line above in the python test code

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in b1f803d03fec4cf4f66cb142ddde705b96aa8205.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The second parameter (exit) isn't used in this repo. Can you remove it by having self.shutdown return the exit_code and then main() calls sys.exit().

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in e163c98169e0405255a2e6acdbb679494b5fa42d.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I think it makes more logical sense to have the exception handling code in the main() function, and then just start shutdown() at the if self.success == TestStatus.FAILED and ... line

Copy link
Copy Markdown
Author

@jachiang jachiang Oct 5, 2019

Choose a reason for hiding this comment

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

I am not sure I fully understand your suggestion. If we move error handling into main, this is code that needs to be replicated in TestWrapper.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

why not:

        try:
            self.setup()
            self.run_test()
        except BaseException as e:
            handle_exception(e)
        finally:
            exit_code = self.shutdown()
            sys.exit(exit_code)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

my preference would be to not have this as a separate function from main()

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Addressed in e163c98169e0405255a2e6acdbb679494b5fa42d.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This isn't quite right. Since logging.shutdown() is called in TestFramework.shutdown(), no more logging should take place (see https://docs.python.org/3/library/logging.html#logging.shutdown).

A better way to do this would be to replace the logging.shutdown() call with:

handlers = self.log.handlers
for h in handlers:
    h.flush()
    h.flush()
    self.log.removeHandler(h)

That way, when setup is called again, the old handlers will have been removed and the new handlers will be added to the Logging object.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Thanks for this suggestion. Added to e163c98169e0405255a2e6acdbb679494b5fa42.

@jnewbery
Copy link
Copy Markdown

jnewbery commented Sep 3, 2019

You should also reword the second commit log to remove reference to TestWrapper if you want to get this merged to Bitcoin Core.

@jnewbery
Copy link
Copy Markdown

jnewbery commented Sep 6, 2019

You should also reword the second commit log to remove reference to TestWrapper if you want to get this merged to Bitcoin Core.

Thinking about this some more, perhaps a good way to get this merged to Bitcoin Core would be to include the TestWrapper and add some documentation about how to run an interactive test. That's quite a compelling rationale for making these changes.

I'd hold off on that while we're still iterating on TestWrapper, but after the workshops let's open a PR against https://github.com/bitcoin/bitcoin.

@jachiang
Copy link
Copy Markdown
Author

jachiang commented Sep 13, 2019

I am adding another TODO for this PR.

  • Refactor NetworkThread class in mininode.py, so each BitcoinTestFramework object initialises a different network thread.

@jnewbery
Copy link
Copy Markdown

Refactor NetworkThread class in mininode.py, so each BitcoinTestFramework object initialises a different network thread.

I don't think we want more than one instance of BitcoinTestFramework to exist at a time. @jachiang - do you have a reason for wanting this?

@jachiang jachiang force-pushed the master-testframework-main-refactor branch 2 times, most recently from 3f5baf0 to 774ecb1 Compare October 5, 2019 16:57
Copy link
Copy Markdown
Author

@jachiang jachiang left a comment

Choose a reason for hiding this comment

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

Refactor NetworkThread class in mininode.py, so each BitcoinTestFramework object initialises a different network thread.

I don't think we want more than one instance of BitcoinTestFramework to exist at a time. @jachiang - do you have a reason for wanting this?

I will omit this from this PR, because it requires limiting one NetworkThread per BitcoinTestFramework, which I am unsure of how to best implement with minimal code changes.

@jachiang jachiang requested a review from jnewbery October 6, 2019 02:57
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Do these need to be flushed before being removed?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

why not:

        try:
            self.setup()
            self.run_test()
        except BaseException as e:
            handle_exception(e)
        finally:
            exit_code = self.shutdown()
            sys.exit(exit_code)

Copy link
Copy Markdown

@jnewbery jnewbery left a comment

Choose a reason for hiding this comment

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

A few comments inline. I also think the first commit comment ("Removal of residual NetworkThread state.") could be expanded a bit to explain why you're making that change.

Once the documentation is added, I think this is ready to open as a PR against bitcoin/bitcoin

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Is this new traceback functionality necessary? Can it be separated into a new commit so you're not moving and changing code in the same commit?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Not Ideal, I agree. It seems the logger will only capture the traceback if error logging is called directly inside except (see https://stackoverflow.com/a/5191885 and https://docs.python.org/3/library/logging.html#logging.error). Since we are invoking error/exception logging elsewhere, we reconstruct the traceback string from the exception object.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

pep8 import ordering please.

More generally, can you run flake8 over this new file and resolve any style issues?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

is this required? num_nodes defaults to 3 in the setup() method.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I suggest you default this to 1

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Have you tried changing the method signature to catch *kwargs and then set the defaults below, to avoid this huge function signature?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yes, it is much cleaner as you suggested. Thank you!

@jachiang jachiang force-pushed the master-testframework-main-refactor branch 4 times, most recently from 25ebea1 to 5f01275 Compare October 25, 2019 13:15
The asyncio.new_event_loop() instance is now removed from the NetworkThread class during shutdown. This enables a NetworkThread instance to be restarted after being closed. The current NetworkThread class guards against an existing new_event_loop during initialization.
Setup and shutdown code now moved into dedicated methods. Test "success" is
added as a BitcoinTestFramework member, which can be accessed outside of
main.
In order for BitcoinTestFramework to correctly restart after shutdown,
the previous logging handlers need to be removed, or else logging will
continue in the previous temp directory. "Flush" ensures buffers are
emptied, and "close" ensures file handler close logging file.
TestNode objects need to be removed during shutdown, as setup_nodes
does not remove previous TestNode objects from previous test runs during
setup.
This allows a BitcoinTestFramework child class to set test parameters
in an overridden setup() rather than in an overridden set_test_params().
A BitcoinTestFramework child class which can be imported by an
external user or project. TestWrapper.setup() initiates an underlying
BitcoinTestFramework object with bitcoind subprocesses, rpc interfaces
and test logging. TestWrapper.shutdown() safely tears down the BitcoinTestFramework object.
@jachiang jachiang force-pushed the master-testframework-main-refactor branch from 5f01275 to bf4fc41 Compare October 26, 2019 18:49
@jachiang
Copy link
Copy Markdown
Author

@jnewbery Sorry this took a while, but I've addressed all feedback, reworked commits for easier review and added a documentation file for the TestWrapper class.

I will review again tomorrow and then open a PR against master.
A very big thank you for your time spent reviewing and improving this!

@jnewbery
Copy link
Copy Markdown

PR opened against bitcoin/bitcoin here: bitcoin#17288

@jnewbery jnewbery closed this Oct 29, 2019
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.

2 participants