def setup(): log_handler, log_buffer = setupLogging() status = monitor.status.Status() # Create our global shared status. Sort of a hard coded file adapter. config_file = os.path.join(BASE_DIR, 'server.json') with open(config_file, 'r') as f: status.set('status://server', json.load(f)) # Setup the normal adapters. setupAdapters(status) # Create the manager for performing actions. action_manager = monitor.actions.ActionManager(status) # Instantiating the engine sets up the deferreds needed to keep it running. monitor.rules_engine.RulesEngine(status, action_manager) # Assemble the factory for our web server. # Serve the standard static web content, overlaid with our dynamic content root = File("./static") root.putChild("button", monitor.web_resources.Button(status)) root.putChild("log", monitor.web_resources.Log(log_handler, log_buffer)) root.putChild("restart", monitor.web_resources.Restart(status)) root.putChild("status", monitor.web_resources.Status(status)) reactor.listenTCP(status.get('status://server/port', 8080), Site(root))
def test_url_not_updated(self): status = self._create_status({"foo": 1, "bar": 2}) # Ask for a specialized notification. d = status.deferred(revision=1, url="status://bar") status.set("status://int", 3) self.assertFalse(d.called)
def test_noop_change(self): """Ensure the deferred does not fire on a non-meaningful change.""" status = self._create_status({'int': 2}) d = status.deferred(revision=1) status.set('status://int', 2) self.assertFalse(d.called)
def test_watch_rules_fired(self): """Setup and fire two watch rules in the rules_engine.""" status, engine = self._setup_status_engine({ 'watch_test1': { 'behavior': 'watch', 'value': 'status://values/one', 'action': 'take_action1' }, 'watch_test2': { 'behavior': 'watch', 'value': 'status://values/two', 'action': 'take_action2' } }) expected_actions = [ 'status://config/rule/watch_test1/action', 'status://config/rule/watch_test2/action' ] d = self._test_actions_fired(engine, expected_actions) status.set('status://values/one', 2) status.set('status://values/two', 2) return d
def test_noop_change(self): """Ensure the deferred does not fire on a non-meaningful change.""" status = self._create_status({"int": 2}) d = status.deferred(revision=1) status.set("status://int", 2) self.assertFalse(d.called)
def test_set_revision(self): """Test Status.set() if revision is passed in.""" # Make Sure initial revisions match expectations. status = self._create_status() # Set a new value, update with correct specified revision. status.set("status://int", 10, revision=1) self.assertEqual(status.revision(), 2) self.assertEqual(status.revision("status://int"), 2) # Update an existing value with correct specified revision. status.set("status://int", 20, revision=2) self.assertEqual(status.revision(), 3) self.assertEqual(status.revision("status://int"), 3) # Set a new value, update with incorrect specified revision. # Should raise exception, and modify nothing. self.assertRaises(monitor.status.RevisionMismatch, status.set, "status://int", 30, revision=1) self.assertEqual(status.get("status://int"), 20) self.assertEqual(status.revision(), 3) self.assertEqual(status.revision("status://int"), 3) # Set a new value, update with a future version. self.assertRaises(monitor.status.RevisionMismatch, status.set, "status://int", 30, revision=100) self.assertEqual(status.get("status://int"), 20) self.assertEqual(status.revision(), 3) self.assertEqual(status.revision("status://int"), 3)
def test_url_not_updated(self): status = self._create_status({'foo': 1, 'bar': 2}) # Ask for a specialized notification. d = status.deferred(revision=1, url='status://bar') status.set('status://int', 3) self.assertFalse(d.called)
def test_single_change_no_url(self): status = self._create_status({'int': 2}) # Test that the expected notification fires after we make a change. url = 'status://int' d = status.deferred() status.set(url, 3) d.addCallback(self.assertEquals, ['status://']) self.assertTrue(d.called)
def test_double_change_no_url(self): """Ensure the deferred does not fire on a non-meaningful change.""" status = self._create_status({'int': 2}) d = status.deferred(revision=1) status.set('status://int', 3) status.set('status://int', 4) d.addCallback(self.assertEquals, ['status://']) self.assertTrue(d.called)
def test_double_change_no_url(self): """Ensure the deferred does not fire on a non-meaningful change.""" status = self._create_status({"int": 2}) d = status.deferred(revision=1) status.set("status://int", 3) status.set("status://int", 4) d.addCallback(self.assertEquals, ["status://"]) self.assertTrue(d.called)
def test_single_change_no_url(self): status = self._create_status({"int": 2}) # Test that the expected notification fires after we make a change. url = "status://int" d = status.deferred() status.set(url, 3) d.addCallback(self.assertEquals, ["status://"]) self.assertTrue(d.called)
def test_non_existent_url(self): status = self._create_status() # Ask for a specialized notification. url = "status://foo" d = status.deferred(url=url) status.set(url, 3) d.addCallback(self.assertEquals, [url]) self.assertTrue(d.called)
def test_non_existent_url(self): status = self._create_status() # Ask for a specialized notification. url = 'status://foo' d = status.deferred(url=url) status.set(url, 3) d.addCallback(self.assertEquals, [url]) self.assertTrue(d.called)
def test_url_double_updated(self): status = self._create_status() # Ask for a specialized notification. url = "status://int" d = status.deferred(revision=1, url=url) d.addCallback(self.assertEquals, [url]) status.set(url, 3) status.set(url, 4) self.assertTrue(d.called)
def test_url_double_updated(self): status = self._create_status() # Ask for a specialized notification. url = 'status://int' d = status.deferred(revision=1, url=url) d.addCallback(self.assertEquals, [url]) status.set(url, 3) status.set(url, 4) self.assertTrue(d.called)
def test_url_parent_different_revision(self): status = self._create_status({"foo": 1, "bar": 2}) status.set("status://foo", 3) self.assertEqual(status.revision(), 2) self.assertEqual(status.revision("status://bar"), 1) # Ask for a specialized notification with the parents revision, not # the revision of the url. d = status.deferred(revision=1, url="status://bar") status.set("status://int", 3) self.assertFalse(d.called)
def test_watch_rule_fired(self): """Setup and fire a single watch rule in the rules_engine.""" status, engine = self._setup_status_engine( {"watch_test": {"behavior": "watch", "value": "status://values/one", "action": "take_action"}} ) expected_actions = ["status://config/rule/watch_test/action"] d = self._test_actions_fired(engine, expected_actions) status.set("status://values/one", 2) return d
def test_url_parent_different_revision(self): status = self._create_status({'foo': 1, 'bar': 2}) status.set('status://foo', 3) self.assertEqual(status.revision(), 2) self.assertEqual(status.revision('status://bar'), 1) # Ask for a specialized notification with the parents revision, not # the revision of the url. d = status.deferred(revision=1, url='status://bar') status.set('status://int', 3) self.assertFalse(d.called)
def test_url_nested_updates(self): status = self._create_status() url = 'status://int' def callback(value, new_int): status.set(url, new_int) return value d = status.deferred(url=url) d.addCallback(callback, 4) d.addCallback(callback, 5) status.set(url, 3) self.assertTrue(d.called)
def test_watch_rule_value_cleared(self): """Setup and clear value a watch rule watches.""" status, engine = self._setup_status_engine( {"watch_test": {"behavior": "watch", "value": "status://values/one", "action": "take_action"}} ) # If a value is cleared, we shouldn't fire at all. expected_actions = [] d = self._test_actions_fired(engine, expected_actions) status.set("status://values", {}) return d
def test_url_nested_updates(self): status = self._create_status() url = "status://int" def callback(value, new_int): status.set(url, new_int) return value d = status.deferred(url=url) d.addCallback(callback, 4) d.addCallback(callback, 5) status.set(url, 3) self.assertTrue(d.called)
def test_watch_rules_fired(self): """Setup and fire two watch rules in the rules_engine.""" status, engine = self._setup_status_engine( { "watch_test1": {"behavior": "watch", "value": "status://values/one", "action": "take_action1"}, "watch_test2": {"behavior": "watch", "value": "status://values/two", "action": "take_action2"}, } ) expected_actions = ["status://config/rule/watch_test1/action", "status://config/rule/watch_test2/action"] d = self._test_actions_fired(engine, expected_actions) status.set("status://values/one", 2) status.set("status://values/two", 2) return d
def test_watch_rule_value_cleared(self): """Setup and clear value a watch rule watches.""" status, engine = self._setup_status_engine({ 'watch_test': { 'behavior': 'watch', 'value': 'status://values/one', 'action': 'take_action' } }) # If a value is cleared, we shouldn't fire at all. expected_actions = [] d = self._test_actions_fired(engine, expected_actions) status.set('status://values', {}) return d
def test_watch_rule_fired_twice(self): """Setup and fire a single watch rule in the rules_engine twice.""" status, engine = self._setup_status_engine({ 'watch_test': { 'behavior': 'watch', 'value': 'status://values/one', 'action': 'take_action' } }) expected_actions = [ 'status://config/rule/watch_test/action', 'status://config/rule/watch_test/action' ] d = self._test_actions_fired(engine, expected_actions) status.set('status://values/one', 2) status.set('status://values/one', 3) return d
def test_url_circular_deferreds(self): status = self._create_status({"foo": 1, "bar": 1}) url_foo = "status://foo" url_bar = "status://bar" d_foo = status.deferred(url=url_foo) d_bar = status.deferred(url=url_bar) def callback(value, new_url, new_int): status.set(new_url, new_int) return value d_foo.addCallback(callback, url_bar, 3) d_bar.addCallback(callback, url_foo, 3) status.set(url_foo, 2) status.set(url_bar, 2) self.assertTrue(d_foo.called) self.assertTrue(d_bar.called)
def test_url_circular_deferreds(self): status = self._create_status({'foo': 1, 'bar': 1}) url_foo = 'status://foo' url_bar = 'status://bar' d_foo = status.deferred(url=url_foo) d_bar = status.deferred(url=url_bar) def callback(value, new_url, new_int): status.set(new_url, new_int) return value d_foo.addCallback(callback, url_bar, 3) d_bar.addCallback(callback, url_foo, 3) status.set(url_foo, 2) status.set(url_bar, 2) self.assertTrue(d_foo.called) self.assertTrue(d_bar.called)
def test_set_revision(self): """Test Status.set() if revision is passed in.""" # Make Sure initial revisions match expectations. status = self._create_status() # Set a new value, update with correct specified revision. status.set('status://int', 10, revision=1) self.assertEqual(status.revision(), 2) self.assertEqual(status.revision('status://int'), 2) # Update an existing value with correct specified revision. status.set('status://int', 20, revision=2) self.assertEqual(status.revision(), 3) self.assertEqual(status.revision('status://int'), 3) # Set a new value, update with incorrect specified revision. # Should raise exception, and modify nothing. self.assertRaises(monitor.status.RevisionMismatch, status.set, 'status://int', 30, revision=1) self.assertEqual(status.get('status://int'), 20) self.assertEqual(status.revision(), 3) self.assertEqual(status.revision('status://int'), 3) # Set a new value, update with a future version. self.assertRaises(monitor.status.RevisionMismatch, status.set, 'status://int', 30, revision=100) self.assertEqual(status.get('status://int'), 20) self.assertEqual(status.revision(), 3) self.assertEqual(status.revision('status://int'), 3)
def test_set(self): status = self._create_status() self.assertEqual(status.revision(), 1) self.assertEqual(status.get("status://int"), 2) self.assertEqual(status.get("status://list"), []) self.assertEqual(status.get("status://dict"), {"sub1": 3, "sub2": 4}) # Revision didn't change with the gets. self.assertEqual(status.revision(), 1) # Set an integer status.set("status://int", 10) self.assertEqual(status.get("status://int"), 10) self.assertEqual(status.revision(), 2) # Set a complex structure, and ensure it is copied, not referenced. l = [] status.set("status://list2", l) self.assertEqual(status.get("status://list2"), []) l.append(1) self.assertEqual(status.get("status://list2"), []) self.assertEqual(status.revision(), 3) # Ensure that setting to an unchanged value does not increment revision. status.set("status://int", 10) self.assertEqual(status.get("status://int"), 10) self.assertEqual(status.revision(), 3) # Set a nested value. status.set("status://dict/sub1", 5) self.assertEqual(status.get("status://dict/sub1"), 5) self.assertEqual(status.revision(), 4) # Set a nested value with new intermediate paths. status.set("status://nest1/nest2/nest3", "foo") self.assertEqual(status.get("status://nest1"), {"nest2": {"nest3": "foo"}}) self.assertEqual(status.revision(), 5) # Clear an existing value. status.set("status://dict/sub1", None) self.assertEqual(status.get("status://dict"), {"sub1": None, "sub2": 4}) self.assertEqual(status.revision(), 6) # Clear a sub-tree status.set("status://nest1", None) self.assertEqual( status.get("status://"), {"dict": {"sub1": None, "sub2": 4}, "int": 10, "list": [], "list2": [], "nest1": None}, ) self.assertEqual(status.revision(), 7) # Clear a nonexitent value status.set("status://nonexistent", None) self.assertEqual( status.get("status://"), {"dict": {"sub1": None, "sub2": 4}, "int": 10, "list": [], "list2": [], "nest1": None}, ) self.assertEqual(status.revision(), 7)
def callback(value, new_url, new_int): status.set(new_url, new_int) return value
def test_nested_revisions(self): """Test Status.revision() handles nested revision numbers.""" # Make Sure initial revisions match expectations. status = self._create_status( { "int": 1, "string": "foo", "list": [], "deep1": {"foo": 2}, "deep2": {"sub_deep1": {"foo": 3}, "sub_deep2": {"foo": 4}}, } ) self.assertEqual(status.revision(), 1) self.assertEqual(status.revision("status://int"), 1) self.assertEqual(status.revision("status://string"), 1) self.assertEqual(status.revision("status://list"), 1) self.assertEqual(status.revision("status://deep1"), 1) self.assertEqual(status.revision("status://deep1/foo"), 1) self.assertEqual(status.revision("status://deep2"), 1) self.assertEqual(status.revision("status://deep2/sub_deep1"), 1) self.assertEqual(status.revision("status://deep2/sub_deep1/foo"), 1) self.assertEqual(status.revision("status://deep2/sub_deep2"), 1) self.assertEqual(status.revision("status://deep2/sub_deep2/foo"), 1) self.assertRaises(monitor.status.UnknownUrl, status.revision, "status://nonexistent") # Make a simple update. status.set("status://int", 10) self.assertEqual(status.revision(), 2) self.assertEqual(status.revision("status://int"), 2) self.assertEqual(status.revision("status://string"), 1) self.assertEqual(status.revision("status://list"), 1) self.assertEqual(status.revision("status://deep1"), 1) self.assertEqual(status.revision("status://deep1/foo"), 1) self.assertEqual(status.revision("status://deep2"), 1) self.assertEqual(status.revision("status://deep2/sub_deep1"), 1) self.assertEqual(status.revision("status://deep2/sub_deep1/foo"), 1) self.assertEqual(status.revision("status://deep2/sub_deep2"), 1) self.assertEqual(status.revision("status://deep2/sub_deep2/foo"), 1) # Make a nested update. status.set("status://deep1/foo", 20) self.assertEqual(status.revision(), 3) self.assertEqual(status.revision("status://int"), 2) self.assertEqual(status.revision("status://string"), 1) self.assertEqual(status.revision("status://list"), 1) self.assertEqual(status.revision("status://deep1"), 3) self.assertEqual(status.revision("status://deep1/foo"), 3) self.assertEqual(status.revision("status://deep2"), 1) self.assertEqual(status.revision("status://deep2/sub_deep1"), 1) self.assertEqual(status.revision("status://deep2/sub_deep1/foo"), 1) self.assertEqual(status.revision("status://deep2/sub_deep2"), 1) self.assertEqual(status.revision("status://deep2/sub_deep2/foo"), 1) # Make a deeper nested update. status.set("status://deep2/sub_deep1/foo", 30) self.assertEqual(status.revision(), 4) self.assertEqual(status.revision("status://int"), 2) self.assertEqual(status.revision("status://string"), 1) self.assertEqual(status.revision("status://list"), 1) self.assertEqual(status.revision("status://deep1"), 3) self.assertEqual(status.revision("status://deep1/foo"), 3) self.assertEqual(status.revision("status://deep2"), 4) self.assertEqual(status.revision("status://deep2/sub_deep1"), 4) self.assertEqual(status.revision("status://deep2/sub_deep1/foo"), 4) self.assertEqual(status.revision("status://deep2/sub_deep2"), 1) self.assertEqual(status.revision("status://deep2/sub_deep2/foo"), 1) # Replace a sub-tree status.set("status://deep2/sub_deep1", {"foo": 40, "bar": 50}) self.assertEqual(status.revision(), 5) self.assertEqual(status.revision("status://int"), 2) self.assertEqual(status.revision("status://string"), 1) self.assertEqual(status.revision("status://list"), 1) self.assertEqual(status.revision("status://deep1"), 3) self.assertEqual(status.revision("status://deep1/foo"), 3) self.assertEqual(status.revision("status://deep2"), 5) self.assertEqual(status.revision("status://deep2/sub_deep1"), 5) self.assertEqual(status.revision("status://deep2/sub_deep1/foo"), 5) self.assertEqual(status.revision("status://deep2/sub_deep1/bar"), 5) self.assertEqual(status.revision("status://deep2/sub_deep2"), 1) self.assertEqual(status.revision("status://deep2/sub_deep2/foo"), 1) # Replace a sub-tree, unchanged. status.set("status://deep2/sub_deep1", {"foo": 40, "bar": 50}) self.assertEqual(status.revision(), 5) self.assertEqual(status.revision("status://int"), 2) self.assertEqual(status.revision("status://string"), 1) self.assertEqual(status.revision("status://list"), 1) self.assertEqual(status.revision("status://deep1"), 3) self.assertEqual(status.revision("status://deep1/foo"), 3) self.assertEqual(status.revision("status://deep2"), 5) self.assertEqual(status.revision("status://deep2/sub_deep1"), 5) self.assertEqual(status.revision("status://deep2/sub_deep1/foo"), 5) self.assertEqual(status.revision("status://deep2/sub_deep1/bar"), 5) self.assertEqual(status.revision("status://deep2/sub_deep2"), 1) self.assertEqual(status.revision("status://deep2/sub_deep2/foo"), 1)
def test_set_revision_nested(self): """Test Status.set() if revision is passed in.""" # Make Sure initial revisions match expectations. status = self._create_status({"int": 1, "deep": {"foo": 2}}) # Update one section. status.set("status://int", 10, revision=1) self.assertEqual(status.revision(), 2) self.assertEqual(status.revision("status://int"), 2) self.assertEqual(status.revision("status://deep"), 1) self.assertEqual(status.revision("status://deep/foo"), 1) # Ensure old section can still update using original revision. status.set("status://deep/foo", 20, revision=1) self.assertEqual(status.revision(), 3) self.assertEqual(status.revision("status://int"), 2) self.assertEqual(status.revision("status://deep"), 3) self.assertEqual(status.revision("status://deep/foo"), 3) # Ensure new creation works with nested revisions. status.set("status://deep/bar", 30, revision=3) self.assertEqual(status.revision(), 4) self.assertEqual(status.revision("status://int"), 2) self.assertEqual(status.revision("status://deep"), 4) self.assertEqual(status.revision("status://deep/foo"), 3) self.assertEqual(status.revision("status://deep/bar"), 4) # Ensure value can be updated using a parents revision. status.set("status://deep/foo", 21, revision=4) self.assertEqual(status.revision(), 5) self.assertEqual(status.revision("status://int"), 2) self.assertEqual(status.revision("status://deep"), 5) self.assertEqual(status.revision("status://deep/foo"), 5) self.assertEqual(status.revision("status://deep/bar"), 4) # Ensure new creation works with updated revisions. status.set("status://deep/bar", 31, revision=4) self.assertEqual(status.revision(), 6) self.assertEqual(status.revision("status://int"), 2) self.assertEqual(status.revision("status://deep"), 6) self.assertEqual(status.revision("status://deep/foo"), 5) self.assertEqual(status.revision("status://deep/bar"), 6) # Ensure value can be updated using a parents revision. status.set("status://int", 11, revision=6) status.set("status://int", 12, revision=7) self.assertEqual(status.revision(), 8) self.assertEqual(status.revision("status://int"), 8) self.assertEqual(status.revision("status://deep"), 6) self.assertEqual(status.revision("status://deep/foo"), 5) self.assertEqual(status.revision("status://deep/bar"), 6) # Ensure value can NOT be updated using an outdated parent version. self.assertRaises(monitor.status.RevisionMismatch, status.set, "status://deep/foo", 21, revision=7) self.assertEqual(status.revision(), 8) self.assertEqual(status.revision("status://int"), 8) self.assertEqual(status.revision("status://deep"), 6) self.assertEqual(status.revision("status://deep/foo"), 5) self.assertEqual(status.revision("status://deep/bar"), 6)
def test_set(self): status = self._create_status() self.assertEqual(status.revision(), 1) self.assertEqual(status.get('status://int'), 2) self.assertEqual(status.get('status://list'), []) self.assertEqual(status.get('status://dict'), {'sub1': 3, 'sub2': 4}) # Revision didn't change with the gets. self.assertEqual(status.revision(), 1) # Set an integer status.set('status://int', 10) self.assertEqual(status.get('status://int'), 10) self.assertEqual(status.revision(), 2) # Set a complex structure, and ensure it is copied, not referenced. l = [] status.set('status://list2', l) self.assertEqual(status.get('status://list2'), []) l.append(1) self.assertEqual(status.get('status://list2'), []) self.assertEqual(status.revision(), 3) # Ensure that setting to an unchanged value does not increment revision. status.set('status://int', 10) self.assertEqual(status.get('status://int'), 10) self.assertEqual(status.revision(), 3) # Set a nested value. status.set('status://dict/sub1', 5) self.assertEqual(status.get('status://dict/sub1'), 5) self.assertEqual(status.revision(), 4) # Set a nested value with new intermediate paths. status.set('status://nest1/nest2/nest3', 'foo') self.assertEqual(status.get('status://nest1'), {'nest2': { 'nest3': 'foo' }}) self.assertEqual(status.revision(), 5) # Clear an existing value. status.set('status://dict/sub1', None) self.assertEqual(status.get('status://dict'), { 'sub1': None, 'sub2': 4 }) self.assertEqual(status.revision(), 6) # Clear a sub-tree status.set('status://nest1', None) self.assertEqual( status.get('status://'), { 'dict': { 'sub1': None, 'sub2': 4 }, 'int': 10, 'list': [], 'list2': [], 'nest1': None }) self.assertEqual(status.revision(), 7) # Clear a nonexitent value status.set('status://nonexistent', None) self.assertEqual( status.get('status://'), { 'dict': { 'sub1': None, 'sub2': 4 }, 'int': 10, 'list': [], 'list2': [], 'nest1': None }) self.assertEqual(status.revision(), 7)
def test_set_revision_nested(self): """Test Status.set() if revision is passed in.""" # Make Sure initial revisions match expectations. status = self._create_status({ 'int': 1, 'deep': { 'foo': 2 }, }) # Update one section. status.set('status://int', 10, revision=1) self.assertEqual(status.revision(), 2) self.assertEqual(status.revision('status://int'), 2) self.assertEqual(status.revision('status://deep'), 1) self.assertEqual(status.revision('status://deep/foo'), 1) # Ensure old section can still update using original revision. status.set('status://deep/foo', 20, revision=1) self.assertEqual(status.revision(), 3) self.assertEqual(status.revision('status://int'), 2) self.assertEqual(status.revision('status://deep'), 3) self.assertEqual(status.revision('status://deep/foo'), 3) # Ensure new creation works with nested revisions. status.set('status://deep/bar', 30, revision=3) self.assertEqual(status.revision(), 4) self.assertEqual(status.revision('status://int'), 2) self.assertEqual(status.revision('status://deep'), 4) self.assertEqual(status.revision('status://deep/foo'), 3) self.assertEqual(status.revision('status://deep/bar'), 4) # Ensure value can be updated using a parents revision. status.set('status://deep/foo', 21, revision=4) self.assertEqual(status.revision(), 5) self.assertEqual(status.revision('status://int'), 2) self.assertEqual(status.revision('status://deep'), 5) self.assertEqual(status.revision('status://deep/foo'), 5) self.assertEqual(status.revision('status://deep/bar'), 4) # Ensure new creation works with updated revisions. status.set('status://deep/bar', 31, revision=4) self.assertEqual(status.revision(), 6) self.assertEqual(status.revision('status://int'), 2) self.assertEqual(status.revision('status://deep'), 6) self.assertEqual(status.revision('status://deep/foo'), 5) self.assertEqual(status.revision('status://deep/bar'), 6) # Ensure value can be updated using a parents revision. status.set('status://int', 11, revision=6) status.set('status://int', 12, revision=7) self.assertEqual(status.revision(), 8) self.assertEqual(status.revision('status://int'), 8) self.assertEqual(status.revision('status://deep'), 6) self.assertEqual(status.revision('status://deep/foo'), 5) self.assertEqual(status.revision('status://deep/bar'), 6) # Ensure value can NOT be updated using an outdated parent version. self.assertRaises(monitor.status.RevisionMismatch, status.set, 'status://deep/foo', 21, revision=7) self.assertEqual(status.revision(), 8) self.assertEqual(status.revision('status://int'), 8) self.assertEqual(status.revision('status://deep'), 6) self.assertEqual(status.revision('status://deep/foo'), 5) self.assertEqual(status.revision('status://deep/bar'), 6)
def test_nested_revisions(self): """Test Status.revision() handles nested revision numbers.""" # Make Sure initial revisions match expectations. status = self._create_status({ 'int': 1, 'string': 'foo', 'list': [], 'deep1': { 'foo': 2 }, 'deep2': { 'sub_deep1': { 'foo': 3 }, 'sub_deep2': { 'foo': 4 } }, }) self.assertEqual(status.revision(), 1) self.assertEqual(status.revision('status://int'), 1) self.assertEqual(status.revision('status://string'), 1) self.assertEqual(status.revision('status://list'), 1) self.assertEqual(status.revision('status://deep1'), 1) self.assertEqual(status.revision('status://deep1/foo'), 1) self.assertEqual(status.revision('status://deep2'), 1) self.assertEqual(status.revision('status://deep2/sub_deep1'), 1) self.assertEqual(status.revision('status://deep2/sub_deep1/foo'), 1) self.assertEqual(status.revision('status://deep2/sub_deep2'), 1) self.assertEqual(status.revision('status://deep2/sub_deep2/foo'), 1) self.assertRaises(monitor.status.UnknownUrl, status.revision, 'status://nonexistent') # Make a simple update. status.set('status://int', 10) self.assertEqual(status.revision(), 2) self.assertEqual(status.revision('status://int'), 2) self.assertEqual(status.revision('status://string'), 1) self.assertEqual(status.revision('status://list'), 1) self.assertEqual(status.revision('status://deep1'), 1) self.assertEqual(status.revision('status://deep1/foo'), 1) self.assertEqual(status.revision('status://deep2'), 1) self.assertEqual(status.revision('status://deep2/sub_deep1'), 1) self.assertEqual(status.revision('status://deep2/sub_deep1/foo'), 1) self.assertEqual(status.revision('status://deep2/sub_deep2'), 1) self.assertEqual(status.revision('status://deep2/sub_deep2/foo'), 1) # Make a nested update. status.set('status://deep1/foo', 20) self.assertEqual(status.revision(), 3) self.assertEqual(status.revision('status://int'), 2) self.assertEqual(status.revision('status://string'), 1) self.assertEqual(status.revision('status://list'), 1) self.assertEqual(status.revision('status://deep1'), 3) self.assertEqual(status.revision('status://deep1/foo'), 3) self.assertEqual(status.revision('status://deep2'), 1) self.assertEqual(status.revision('status://deep2/sub_deep1'), 1) self.assertEqual(status.revision('status://deep2/sub_deep1/foo'), 1) self.assertEqual(status.revision('status://deep2/sub_deep2'), 1) self.assertEqual(status.revision('status://deep2/sub_deep2/foo'), 1) # Make a deeper nested update. status.set('status://deep2/sub_deep1/foo', 30) self.assertEqual(status.revision(), 4) self.assertEqual(status.revision('status://int'), 2) self.assertEqual(status.revision('status://string'), 1) self.assertEqual(status.revision('status://list'), 1) self.assertEqual(status.revision('status://deep1'), 3) self.assertEqual(status.revision('status://deep1/foo'), 3) self.assertEqual(status.revision('status://deep2'), 4) self.assertEqual(status.revision('status://deep2/sub_deep1'), 4) self.assertEqual(status.revision('status://deep2/sub_deep1/foo'), 4) self.assertEqual(status.revision('status://deep2/sub_deep2'), 1) self.assertEqual(status.revision('status://deep2/sub_deep2/foo'), 1) # Replace a sub-tree status.set('status://deep2/sub_deep1', {'foo': 40, 'bar': 50}) self.assertEqual(status.revision(), 5) self.assertEqual(status.revision('status://int'), 2) self.assertEqual(status.revision('status://string'), 1) self.assertEqual(status.revision('status://list'), 1) self.assertEqual(status.revision('status://deep1'), 3) self.assertEqual(status.revision('status://deep1/foo'), 3) self.assertEqual(status.revision('status://deep2'), 5) self.assertEqual(status.revision('status://deep2/sub_deep1'), 5) self.assertEqual(status.revision('status://deep2/sub_deep1/foo'), 5) self.assertEqual(status.revision('status://deep2/sub_deep1/bar'), 5) self.assertEqual(status.revision('status://deep2/sub_deep2'), 1) self.assertEqual(status.revision('status://deep2/sub_deep2/foo'), 1) # Replace a sub-tree, unchanged. status.set('status://deep2/sub_deep1', {'foo': 40, 'bar': 50}) self.assertEqual(status.revision(), 5) self.assertEqual(status.revision('status://int'), 2) self.assertEqual(status.revision('status://string'), 1) self.assertEqual(status.revision('status://list'), 1) self.assertEqual(status.revision('status://deep1'), 3) self.assertEqual(status.revision('status://deep1/foo'), 3) self.assertEqual(status.revision('status://deep2'), 5) self.assertEqual(status.revision('status://deep2/sub_deep1'), 5) self.assertEqual(status.revision('status://deep2/sub_deep1/foo'), 5) self.assertEqual(status.revision('status://deep2/sub_deep1/bar'), 5) self.assertEqual(status.revision('status://deep2/sub_deep2'), 1) self.assertEqual(status.revision('status://deep2/sub_deep2/foo'), 1)