-
Notifications
You must be signed in to change notification settings - Fork 352
Fix multiple associations and removals of networks #573
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
7e82e28
7eb8fbe
d02d5ee
f9d477a
38d3836
e4ac0ce
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,113 @@ | ||||||
| """Unit tests for the RemoteNode and LocalNode classes.""" | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not really fond of having comments just for the sake of having a comment. None of our test files so far have a module-level docstring. There is no generated API documentation for them. And In essence, don't feel obliged to write docstrings that add no value. Or try to come up with a really good one that does so. What IS the common denominator for stuff that will be grouped in this test file? So far it is only network association, what else do you have in mind?
Suggested change
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Eventually we'll have more unit tests of the methods in these two classes, no?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe, that's why I'm asking if you have any concrete ideas what will be added? |
||||||
| import unittest | ||||||
|
|
||||||
| import canopen | ||||||
| import canopen.network | ||||||
| import canopen.objectdictionary | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These seem to be not needed.
Suggested change
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I understand. How come the code works without them though? |
||||||
|
|
||||||
|
|
||||||
| def count_subscribers(network: canopen.Network) -> int: | ||||||
| """Count the number of subscribers in the network.""" | ||||||
| return sum( | ||||||
| len(n) for n in network.subscribers.values() | ||||||
| ) | ||||||
|
sveinse marked this conversation as resolved.
Outdated
acolomb marked this conversation as resolved.
Outdated
|
||||||
|
|
||||||
|
|
||||||
| class TestLocalNode(unittest.TestCase): | ||||||
| """Unit tests for the LocalNode class.""" | ||||||
|
acolomb marked this conversation as resolved.
Outdated
|
||||||
|
|
||||||
| @classmethod | ||||||
| def setUpClass(cls): | ||||||
| cls.network = canopen.Network() | ||||||
| cls.network.NOTIFIER_SHUTDOWN_TIMEOUT = 0.0 | ||||||
| cls.network.connect(interface="virtual") | ||||||
|
|
||||||
| cls.node = canopen.LocalNode(2, canopen.objectdictionary.ObjectDictionary()) | ||||||
|
|
||||||
| @classmethod | ||||||
| def tearDownClass(cls): | ||||||
| cls.network.disconnect() | ||||||
|
|
||||||
| def test_associate_network(self): | ||||||
|
|
||||||
|
acolomb marked this conversation as resolved.
Outdated
|
||||||
| # Need to store the number of subscribers before associating because the | ||||||
| # network implementation automatically adds subscribers to the list | ||||||
| n_subscribers = count_subscribers(self.network) | ||||||
|
|
||||||
| # Associating the network with the local node | ||||||
| self.node.associate_network(self.network) | ||||||
| self.assertIs(self.node.network, self.network) | ||||||
| self.assertIs(self.node.sdo.network, self.network) | ||||||
| self.assertIs(self.node.tpdo.network, self.network) | ||||||
| self.assertIs(self.node.rpdo.network, self.network) | ||||||
| self.assertIs(self.node.nmt.network, self.network) | ||||||
| self.assertIs(self.node.emcy.network, self.network) | ||||||
|
|
||||||
| # Test that its not possible to associate the network multiple times | ||||||
| with self.assertRaises(RuntimeError) as cm: | ||||||
| self.node.associate_network(self.network) | ||||||
| self.assertIn("already associated with a network", str(cm.exception)) | ||||||
|
|
||||||
| # Test removal of the network. The count of subscribers should | ||||||
| # be the same as before the association | ||||||
| self.node.remove_network() | ||||||
| uninitalized = canopen.network._UNINITIALIZED_NETWORK | ||||||
| self.assertIs(self.node.network, uninitalized) | ||||||
| self.assertIs(self.node.sdo.network, uninitalized) | ||||||
| self.assertIs(self.node.tpdo.network, uninitalized) | ||||||
| self.assertIs(self.node.rpdo.network, uninitalized) | ||||||
| self.assertIs(self.node.nmt.network, uninitalized) | ||||||
| self.assertIs(self.node.emcy.network, uninitalized) | ||||||
| self.assertEqual(count_subscribers(self.network), n_subscribers) | ||||||
|
|
||||||
| # Test that its possible to deassociate the network multiple times | ||||||
| self.node.remove_network() | ||||||
|
|
||||||
|
|
||||||
| class TestRemoteNode(unittest.TestCase): | ||||||
| """Unittests for the RemoteNode class.""" | ||||||
|
acolomb marked this conversation as resolved.
Outdated
|
||||||
|
|
||||||
| @classmethod | ||||||
| def setUpClass(cls): | ||||||
| cls.network = canopen.Network() | ||||||
| cls.network.NOTIFIER_SHUTDOWN_TIMEOUT = 0.0 | ||||||
| cls.network.connect(interface="virtual") | ||||||
|
|
||||||
| cls.node = canopen.RemoteNode(2, canopen.objectdictionary.ObjectDictionary()) | ||||||
|
|
||||||
| @classmethod | ||||||
| def tearDownClass(cls): | ||||||
| cls.network.disconnect() | ||||||
|
|
||||||
| def test_associate_network(self): | ||||||
|
|
||||||
|
acolomb marked this conversation as resolved.
Outdated
|
||||||
| # Need to store the number of subscribers before associating because the | ||||||
| # network implementation automatically adds subscribers to the list | ||||||
| n_subscribers = count_subscribers(self.network) | ||||||
|
|
||||||
| # Associating the network with the local node | ||||||
| self.node.associate_network(self.network) | ||||||
| self.assertIs(self.node.network, self.network) | ||||||
| self.assertIs(self.node.sdo.network, self.network) | ||||||
| self.assertIs(self.node.tpdo.network, self.network) | ||||||
| self.assertIs(self.node.rpdo.network, self.network) | ||||||
| self.assertIs(self.node.nmt.network, self.network) | ||||||
|
|
||||||
| # Test that its not possible to associate the network multiple times | ||||||
| with self.assertRaises(RuntimeError) as cm: | ||||||
| self.node.associate_network(self.network) | ||||||
| self.assertIn("already associated with a network", str(cm.exception)) | ||||||
|
|
||||||
| # Test removal of the network. The count of subscribers should | ||||||
| # be the same as before the association | ||||||
| self.node.remove_network() | ||||||
| uninitalized = canopen.network._UNINITIALIZED_NETWORK | ||||||
| self.assertIs(self.node.network, uninitalized) | ||||||
| self.assertIs(self.node.sdo.network, uninitalized) | ||||||
| self.assertIs(self.node.tpdo.network, uninitalized) | ||||||
| self.assertIs(self.node.rpdo.network, uninitalized) | ||||||
| self.assertIs(self.node.nmt.network, uninitalized) | ||||||
| self.assertEqual(count_subscribers(self.network), n_subscribers) | ||||||
|
|
||||||
| # Test that its possible to deassociate the network multiple times | ||||||
| self.node.remove_network() | ||||||

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant something like this instead, with a single
delcode-path. I find that easier to reason about:There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, two lines shorter.