def test_receive_data_duplicate_sequence_number(self):
        '''Test receiving data with duplicate sequence number

        Only one message (arbitrary) should be processed.
        '''
        run = agnostic.Dse2Runtime('engine')
        run.always_snapshot = False
        run.create_policy('datasource1')

        # send three updates with the same seqnum
        run.receive_data_sequenced(
            publisher='datasource1', table='p',
            data=[[[1]], []], seqnum=1, is_snapshot=False)
        run.receive_data_sequenced(
            publisher='datasource1', table='p',
            data=[[[2]], []], seqnum=1, is_snapshot=False)
        run.receive_data_sequenced(
            publisher='datasource1', table='p',
            data=[[[3]], []], seqnum=1, is_snapshot=False)

        # start with empty data
        run.receive_data_sequenced(
            publisher='datasource1', table='p',
            data=[], seqnum=0, is_snapshot=True)

        # exactly one of the three updates should be applied
        actual = run.select('p(x)')
        correct1 = 'p(1)'
        correct2 = 'p(2)'
        correct3 = 'p(3)'
        self.assertTrue(
            helper.db_equal(actual, correct1) or
            helper.db_equal(actual, correct2) or
            helper.db_equal(actual, correct3))
    def test_poll_subscribe(self):
        """Test polling before subscribing."""
        cage = self.info['cage']
        policy = cage.service_object('policy')
        neutron = cage.service_object('neutron')
        datalog1 = self.info['datalog1']
        datalog2 = self.info['datalog2']
        fake_networks = self.info['fake_networks']

        # add garbage to policy
        for formula in fake_networks:
            policy.insert(formula)

        # poll 1 and then subscribe; should still see first result
        neutron.poll()
        helper.pause()  # so that data is sent
        policy.subscribe('neutron', 'networks', callback=policy.receive_data)
        helper.pause()  # so that data updates are processed
        e = helper.db_equal(policy.select('p(x)'), datalog1)
        self.assertTrue(e, 'Neutron insertion 1')

        # poll 2
        neutron.poll()
        helper.pause()
        e = helper.db_equal(policy.select('p(x)'), datalog2)
        self.assertTrue(e, 'Neutron insertion 2')
    def test_communication(self):
        """Test the module's ability to be loaded into the DSE
        by checking its ability to communicate on the message bus.
        """
        cage = congress.dse.d6cage.d6Cage()
        # so that we exit once test finishes; all other threads are forced
        #    to be daemons
        cage.daemon = True
        cage.start()

        # Create modules.
        # Turn off polling so we don't need to deal with real data.
        args = helper.datasource_openstack_args()
        args['poll_time'] = 0
        cage.loadModule("NovaDriver",
                        helper.data_module_path("nova_driver.py"))
        cage.loadModule("PolicyDriver", helper.policy_module_path())
        cage.createservice(name="policy", moduleName="PolicyDriver",
                           args={'d6cage': cage,
                                 'rootdir': helper.data_module_path('')})
        cage.createservice(name="nova", moduleName="NovaDriver", args=args)

        # Check that data gets sent from nova to policy as expected
        nova = cage.service_object('nova')
        policy = cage.service_object('policy')
        policy.subscribe('nova', 'server',
                         callback=policy.receive_data)

        # publishing is slightly convoluted b/c deltas are computed
        #  automatically.  (Not just convenient--useful so that DSE
        #  properly handles the initial state problem.)
        # Need to set nova.state and nova.prior_state and then publish
        #  anything.

        # publish server(1), server(2), server(3)
        helper.pause()
        nova.prior_state = {}
        nova.state['server'] = set([(1,), (2,), (3,)])
        nova.publish('server', None)
        helper.pause()
        e = helper.db_equal(
            policy.select('nova:server(x)'),
            'nova:server(1) nova:server(2) nova:server(3)')
        self.assertTrue(e, 'Nova insertion 1')

        # publish server(1), server(4), server(5)
        helper.pause()
        nova.prior_state['server'] = nova.state['server']
        nova.state['server'] = set([(1,), (4,), (5,)])
        nova.publish('server', None)
        helper.pause()
        e = helper.db_equal(
            policy.select('nova:server(x)'),
            'nova:server(1) nova:server(4) nova:server(5)')
        self.assertTrue(e, 'Nova insertion 2')
    def test_receive_data_no_sequence_num(self):
        '''Test receiving data without sequence numbers'''
        run = agnostic.DseRuntime(api_base.ENGINE_SERVICE_ID)
        run.always_snapshot = False
        run.create_policy('datasource1')

        # initialize with full table
        run.receive_data_sequenced(publisher='datasource1',
                                   table='p',
                                   data=[[1], [2]],
                                   seqnum=None,
                                   is_snapshot=True)
        actual = run.select('p(x)')
        correct = 'p(1) p(2)'
        self.assertTrue(helper.db_equal(actual, correct))

        # add data
        run.receive_data_sequenced(publisher='datasource1',
                                   table='p',
                                   data=[[[3], [4]], []],
                                   seqnum=None,
                                   is_snapshot=False)
        actual = run.select('p(x)')
        correct = 'p(1) p(2) p(3) p(4)'
        self.assertTrue(helper.db_equal(actual, correct))

        # remove data
        run.receive_data_sequenced(publisher='datasource1',
                                   table='p',
                                   data=[[], [[2], [4]]],
                                   seqnum=None,
                                   is_snapshot=False)
        actual = run.select('p(x)')
        correct = 'p(1) p(3)'
        self.assertTrue(helper.db_equal(actual, correct))

        # add & remove data
        run.receive_data_sequenced(publisher='datasource1',
                                   table='p',
                                   data=[[[4]], [[3]]],
                                   seqnum=None,
                                   is_snapshot=False)
        actual = run.select('p(x)')
        correct = 'p(1) p(4)'
        self.assertTrue(helper.db_equal(actual, correct))

        # re-initialize with full table
        run.receive_data_sequenced(publisher='datasource1',
                                   table='p',
                                   data=[[1], [2]],
                                   seqnum=None,
                                   is_snapshot=True)
        actual = run.select('p(x)')
        correct = 'p(1) p(2)'
        self.assertTrue(helper.db_equal(actual, correct))
    def test_receive_data_out_of_order(self):
        '''Test receiving data with sequence numbers, out of order'''
        run = agnostic.DseRuntime(api_base.ENGINE_SERVICE_ID)
        run.always_snapshot = False
        run.create_policy('datasource1')

        # update with lower seqnum than init snapshot is ignored
        run.receive_data_sequenced(publisher='datasource1',
                                   table='p',
                                   data=[[[10]], []],
                                   seqnum=3,
                                   is_snapshot=False)

        # add & remove data
        run.receive_data_sequenced(publisher='datasource1',
                                   table='p',
                                   data=[[[4]], [[3]]],
                                   seqnum=7,
                                   is_snapshot=False)
        actual = run.select('p(x)')
        correct = ''
        self.assertTrue(helper.db_equal(actual, correct))

        # remove data
        run.receive_data_sequenced(publisher='datasource1',
                                   table='p',
                                   data=[[], [[2], [4]]],
                                   seqnum=6,
                                   is_snapshot=False)
        actual = run.select('p(x)')
        correct = ''
        self.assertTrue(helper.db_equal(actual, correct))

        # add data
        run.receive_data_sequenced(publisher='datasource1',
                                   table='p',
                                   data=[[[3], [4]], []],
                                   seqnum=5,
                                   is_snapshot=False)
        actual = run.select('p(x)')
        correct = ''
        self.assertTrue(helper.db_equal(actual, correct))

        # initialize with full table
        run.receive_data_sequenced(publisher='datasource1',
                                   table='p',
                                   data=[[1], [2]],
                                   seqnum=4,
                                   is_snapshot=True)
        actual = run.select('p(x)')
        correct = 'p(1) p(4)'
        self.assertTrue(helper.db_equal(actual, correct))
Exemple #6
0
    def test_rule_api_model(self):
        """Test the rule api model.  Same as test_multiple except
        we use the api interface instead of the DSE interface.
        """
        api = self.api
        cage = self.cage
        engine = self.engine

        # Insert formula (which creates neutron services)
        net_formula = create_networkXnetwork_group('p')
        LOG.debug("Sending formula: {}".format(str(net_formula)))
        engine.debug_mode()
        context = {'policy_id': engine.DEFAULT_THEORY}
        (id1, rule) = api['rule'].add_item(
            {'rule': str(net_formula)}, {}, context=context)
        # Poll
        neutron = cage.service_object('neutron')
        neutron2 = cage.service_object('neutron2')
        neutron.poll()
        neutron2.poll()
        helper.pause()
        # Insert a second formula
        other_formula = compile.parse1('q(x,y) :- p(x,y)')
        (id2, rule) = api['rule'].add_item(
            {'rule': str(other_formula)}, {}, context=context)
        helper.pause()  # give time for messages/creation of services
        ans1 = ('p("240ff9df-df35-43ae-9df5-27fae87f2492",  '
                '  "240ff9df-df35-43ae-9df5-27fae87f2492") ')
        ans2 = ('q("240ff9df-df35-43ae-9df5-27fae87f2492",  '
                '  "240ff9df-df35-43ae-9df5-27fae87f2492") ')
        e = helper.db_equal(engine.select('p(x,y)'), ans1)
        self.assertTrue(e, "Insert rule-api 1")
        e = helper.db_equal(engine.select('q(x,y)'), ans2)
        self.assertTrue(e, "Insert rule-api 2")
        # Get formula
        ruleobj = api['rule'].get_item(id1, {}, context=context)
        self.assertTrue(e, net_formula == compile.parse1(ruleobj['rule']))
        # Get all formulas
        ds = api['rule'].get_items({}, context=context)['results']
        self.assertEqual(len(ds), 2)
        ids = set([x['id'] for x in ds])
        rules = set([compile.parse1(x['rule']) for x in ds])
        self.assertEqual(ids, set([id1, id2]))
        self.assertEqual(rules, set([net_formula, other_formula]))
        # Delete formula
        api['rule'].delete_item(id1, {}, context=context)
        # Get all formulas
        ds = api['rule'].get_items({}, context=context)['results']
        self.assertEqual(len(ds), 1)
        ids = sorted([x['id'] for x in ds])
        self.assertEqual(ids, sorted([id2]))
    def test_receive_data_sequence_number_max_int(self):
        '''Test receiving data when sequence number goes over max int'''
        run = agnostic.DseRuntime(api_base.ENGINE_SERVICE_ID)
        run.always_snapshot = False
        run.create_policy('datasource1')

        run.receive_data_sequenced(publisher='datasource1',
                                   table='p',
                                   data=[[1], [2]],
                                   seqnum=sys.maxsize,
                                   is_snapshot=True)
        actual = run.select('p(x)')
        correct = 'p(1) p(2)'
        self.assertTrue(helper.db_equal(actual, correct))

        run.receive_data_sequenced(publisher='datasource1',
                                   table='p',
                                   data=[[], [[2]]],
                                   seqnum=sys.maxsize + 1,
                                   is_snapshot=False)
        actual = run.select('p(x)')
        correct = 'p(1)'
        self.assertTrue(helper.db_equal(actual, correct))

        # test out-of-sequence update ignored
        run.receive_data_sequenced(publisher='datasource1',
                                   table='p',
                                   data=[[[2]], []],
                                   seqnum=sys.maxsize,
                                   is_snapshot=False)
        actual = run.select('p(x)')
        correct = 'p(1)'
        self.assertTrue(helper.db_equal(actual, correct))

        run.receive_data_sequenced(publisher='datasource1',
                                   table='p',
                                   data=[[[4]], []],
                                   seqnum=sys.maxsize + 3,
                                   is_snapshot=False)
        actual = run.select('p(x)')
        correct = 'p(1)'
        self.assertTrue(helper.db_equal(actual, correct))

        run.receive_data_sequenced(publisher='datasource1',
                                   table='p',
                                   data=[[[3]], []],
                                   seqnum=sys.maxsize + 2,
                                   is_snapshot=False)
        actual = run.select('p(x)')
        correct = 'p(1) p(3) p(4)'
        self.assertTrue(helper.db_equal(actual, correct))
Exemple #8
0
 def test_policy_tables(self):
     """Test basic DSE functionality with policy engine and the API."""
     cage = congress.dse.d6cage.d6Cage()
     cage.loadModule(
         "TestDriver",
         helper.data_module_path("../tests/datasources/test_driver.py"))
     cage.loadModule("TestPolicy", helper.policy_module_path())
     cage.createservice(name="data",
                        moduleName="TestDriver",
                        args=helper.datasource_openstack_args())
     # using regular testdriver as API for now
     cage.createservice(name="api",
                        moduleName="TestDriver",
                        args=helper.datasource_openstack_args())
     cage.createservice(name="policy",
                        moduleName="TestPolicy",
                        args={
                            'd6cage': cage,
                            'rootdir': ''
                        })
     data = cage.services['data']['object']
     api = cage.services['api']['object']
     policy = cage.services['policy']['object']
     policy.create_policy('data')
     policy.set_schema('data', compile.Schema({'q': (1, )}))
     policy.subscribe('api',
                      'policy-update',
                      callback=policy.receive_policy_update)
     # simulate API call for insertion of policy statements
     formula = policy.parse1('p(x) :- data:q(x)')
     api.publish('policy-update', [runtime.Event(formula)])
     helper.retry_check_nonempty_last_policy_change(policy)
     # simulate data source publishing to q
     formula = policy.parse1('q(1)')
     data.publish('q', [runtime.Event(formula)])
     helper.retry_check_db_equal(policy, 'data:q(x)', 'data:q(1)')
     # check that policy did the right thing with data
     e = helper.db_equal(policy.select('p(x)'), 'p(1)')
     self.assertTrue(e, 'Policy insert')
     # check that publishing into 'p' does not work
     formula = policy.parse1('p(3)')
     data.publish('p', [runtime.Event(formula)])
     # can't actually check that the update for p does not arrive
     # so instead wait a bit and check
     helper.pause()
     e = helper.db_equal(policy.select('p(x)'), 'p(1)')
     self.assertTrue(e, 'Policy non-insert')
    def test_receive_data_no_sequence_num(self):
        '''Test receiving data without sequence numbers'''
        run = agnostic.Dse2Runtime('engine')
        run.always_snapshot = False
        run.create_policy('datasource1')

        # initialize with full table
        run.receive_data_sequenced(
            publisher='datasource1', table='p',
            data=[[1], [2]], seqnum=None, is_snapshot=True)
        actual = run.select('p(x)')
        correct = 'p(1) p(2)'
        self.assertTrue(helper.db_equal(actual, correct))

        # add data
        run.receive_data_sequenced(
            publisher='datasource1', table='p',
            data=[[[3], [4]], []], seqnum=None, is_snapshot=False)
        actual = run.select('p(x)')
        correct = 'p(1) p(2) p(3) p(4)'
        self.assertTrue(helper.db_equal(actual, correct))

        # remove data
        run.receive_data_sequenced(
            publisher='datasource1', table='p',
            data=[[], [[2], [4]]], seqnum=None, is_snapshot=False)
        actual = run.select('p(x)')
        correct = 'p(1) p(3)'
        self.assertTrue(helper.db_equal(actual, correct))

        # add & remove data
        run.receive_data_sequenced(
            publisher='datasource1', table='p',
            data=[[[4]], [[3]]], seqnum=None, is_snapshot=False)
        actual = run.select('p(x)')
        correct = 'p(1) p(4)'
        self.assertTrue(helper.db_equal(actual, correct))

        # re-initialize with full table
        run.receive_data_sequenced(
            publisher='datasource1', table='p',
            data=[[1], [2]], seqnum=None, is_snapshot=True)
        actual = run.select('p(x)')
        correct = 'p(1) p(2)'
        self.assertTrue(helper.db_equal(actual, correct))
    def test_receive_data_in_order(self):
        '''Test receiving data with sequence numbers, in order'''
        run = agnostic.DseRuntime(api_base.ENGINE_SERVICE_ID)
        run.create_policy('datasource1')

        # initialize with full table
        run.receive_data_sequenced(
            publisher='datasource1', table='p',
            data=[[1], [2]], seqnum=0, is_snapshot=True)
        actual = run.select('p(x)')
        correct = 'p(1) p(2)'
        self.assertTrue(helper.db_equal(actual, correct))

        # add data
        run.receive_data_sequenced(
            publisher='datasource1', table='p',
            data=[[[3], [4]], []], seqnum=1, is_snapshot=False)
        actual = run.select('p(x)')
        correct = 'p(1) p(2) p(3) p(4)'
        self.assertTrue(helper.db_equal(actual, correct))

        # remove data
        run.receive_data_sequenced(
            publisher='datasource1', table='p',
            data=[[], [[2], [4]]], seqnum=2, is_snapshot=False)
        actual = run.select('p(x)')
        correct = 'p(1) p(3)'
        self.assertTrue(helper.db_equal(actual, correct))

        # add & remove data
        run.receive_data_sequenced(
            publisher='datasource1', table='p',
            data=[[[4]], [[3]]], seqnum=3, is_snapshot=False)
        actual = run.select('p(x)')
        correct = 'p(1) p(4)'
        self.assertTrue(helper.db_equal(actual, correct))

        # re-initialize with full table
        run.receive_data_sequenced(
            publisher='datasource1', table='p',
            data=[[1], [2]], seqnum=4, is_snapshot=True)
        actual = run.select('p(x)')
        correct = 'p(1) p(2)'
        self.assertTrue(helper.db_equal(actual, correct))
 def check(self, run, action_sequence, query, correct, msg, delta=False):
     original_db = str(run.theory[self.DEFAULT_THEORY])
     actual = run.simulate(
         query, self.DEFAULT_THEORY, action_sequence,
         self.ACTION_THEORY, delta=delta)
     e = helper.datalog_equal(actual, correct)
     self.assertTrue(e, msg + " (Query results not correct)")
     e = helper.db_equal(
         str(run.theory[self.DEFAULT_THEORY]), original_db)
     self.assertTrue(e, msg + " (Rollback failed)")
 def test_receive_data_arbitrary_start(self):
     '''Test receiving data with arbitrary starting sequence number'''
     run = agnostic.DseRuntime(api_base.ENGINE_SERVICE_ID)
     run.create_policy('datasource1')
     run.receive_data_sequenced(
         publisher='datasource1', table='p',
         data=[[1], [2]], seqnum=1234, is_snapshot=True)
     actual = run.select('p(x)')
     correct = 'p(1) p(2)'
     self.assertTrue(helper.db_equal(actual, correct))
    def test_receive_data_out_of_order(self):
        '''Test receiving data with sequence numbers, out of order'''
        run = agnostic.Dse2Runtime('engine')
        run.always_snapshot = False
        run.create_policy('datasource1')

        # update with lower seqnum than init snapshot is ignored
        run.receive_data_sequenced(
            publisher='datasource1', table='p',
            data=[[[10]], []], seqnum=3, is_snapshot=False)

        # add & remove data
        run.receive_data_sequenced(
            publisher='datasource1', table='p',
            data=[[[4]], [[3]]], seqnum=7, is_snapshot=False)
        actual = run.select('p(x)')
        correct = ''
        self.assertTrue(helper.db_equal(actual, correct))

        # remove data
        run.receive_data_sequenced(
            publisher='datasource1', table='p',
            data=[[], [[2], [4]]], seqnum=6, is_snapshot=False)
        actual = run.select('p(x)')
        correct = ''
        self.assertTrue(helper.db_equal(actual, correct))

        # add data
        run.receive_data_sequenced(
            publisher='datasource1', table='p',
            data=[[[3], [4]], []], seqnum=5, is_snapshot=False)
        actual = run.select('p(x)')
        correct = ''
        self.assertTrue(helper.db_equal(actual, correct))

        # initialize with full table
        run.receive_data_sequenced(
            publisher='datasource1', table='p',
            data=[[1], [2]], seqnum=4, is_snapshot=True)
        actual = run.select('p(x)')
        correct = 'p(1) p(4)'
        self.assertTrue(helper.db_equal(actual, correct))
    def test_receive_data_duplicate_sequence_number(self):
        '''Test receiving data with duplicate sequence number

        Only one message (arbitrary) should be processed.
        '''
        run = agnostic.DseRuntime(api_base.ENGINE_SERVICE_ID)
        run.always_snapshot = False
        run.create_policy('datasource1')

        # send three updates with the same seqnum
        run.receive_data_sequenced(publisher='datasource1',
                                   table='p',
                                   data=[[[1]], []],
                                   seqnum=1,
                                   is_snapshot=False)
        run.receive_data_sequenced(publisher='datasource1',
                                   table='p',
                                   data=[[[2]], []],
                                   seqnum=1,
                                   is_snapshot=False)
        run.receive_data_sequenced(publisher='datasource1',
                                   table='p',
                                   data=[[[3]], []],
                                   seqnum=1,
                                   is_snapshot=False)

        # start with empty data
        run.receive_data_sequenced(publisher='datasource1',
                                   table='p',
                                   data=[],
                                   seqnum=0,
                                   is_snapshot=True)

        # exactly one of the three updates should be applied
        actual = run.select('p(x)')
        correct1 = 'p(1)'
        correct2 = 'p(2)'
        correct3 = 'p(3)'
        self.assertTrue(
            helper.db_equal(actual, correct1)
            or helper.db_equal(actual, correct2)
            or helper.db_equal(actual, correct3))
Exemple #15
0
 def test_policy_creation_after_ref(self):
     """Test ability to write rules that span multiple policies."""
     # Local table used
     run = agnostic.Runtime()
     run.create_policy('test1')
     run.insert('p(x) :- test2:q(x)', 'test1')
     run.create_policy('test2')
     run.insert('q(1)', 'test2')
     actual = run.select('p(x)', 'test1')
     e = helper.db_equal(actual, 'p(1)')
     self.assertTrue(e, "Creation after reference")
Exemple #16
0
 def check(self, run, action_sequence, query, correct, msg, delta=False):
     original_db = str(run.theory[self.DEFAULT_THEORY])
     actual = run.simulate(query,
                           self.DEFAULT_THEORY,
                           action_sequence,
                           self.ACTION_THEORY,
                           delta=delta)
     e = helper.datalog_equal(actual, correct)
     self.assertTrue(e, msg + " (Query results not correct)")
     e = helper.db_equal(str(run.theory[self.DEFAULT_THEORY]), original_db)
     self.assertTrue(e, msg + " (Rollback failed)")
 def test_policy_creation_after_ref(self):
     """Test ability to write rules that span multiple policies."""
     # Local table used
     run = agnostic.Runtime()
     run.create_policy('test1')
     run.insert('p(x) :- test2:q(x)', 'test1')
     run.create_policy('test2')
     run.insert('q(1)', 'test2')
     actual = run.select('p(x)', 'test1')
     e = helper.db_equal(actual, 'p(1)')
     self.assertTrue(e, "Creation after reference")
Exemple #18
0
 def test_policy_tables(self):
     """Test basic DSE functionality with policy engine and the API."""
     cage = congress.dse.d6cage.d6Cage()
     cage.loadModule("TestDriver",
                     helper.data_module_path(
                         "../tests/datasources/test_driver.py"))
     cage.loadModule("TestPolicy", helper.policy_module_path())
     cage.createservice(name="data", moduleName="TestDriver",
                        args=helper.datasource_openstack_args())
     # using regular testdriver as API for now
     cage.createservice(name="api", moduleName="TestDriver",
                        args=helper.datasource_openstack_args())
     cage.createservice(name="policy", moduleName="TestPolicy",
                        args={'d6cage': cage, 'rootdir': '',
                              'log_actions_only': True})
     data = cage.services['data']['object']
     api = cage.services['api']['object']
     policy = cage.services['policy']['object']
     policy.create_policy('data')
     policy.set_schema('data', compile.Schema({'q': (1,)}))
     policy.subscribe('api', 'policy-update',
                      callback=policy.receive_policy_update)
     # simulate API call for insertion of policy statements
     formula = policy.parse1('p(x) :- data:q(x)')
     api.publish('policy-update', [compile.Event(formula)])
     helper.retry_check_nonempty_last_policy_change(policy)
     # simulate data source publishing to q
     formula = policy.parse1('q(1)')
     data.publish('q', [compile.Event(formula)])
     helper.retry_check_db_equal(policy, 'data:q(x)', 'data:q(1)')
     # check that policy did the right thing with data
     e = helper.db_equal(policy.select('p(x)'), 'p(1)')
     self.assertTrue(e, 'Policy insert')
     # check that publishing into 'p' does not work
     formula = policy.parse1('p(3)')
     data.publish('p', [compile.Event(formula)])
     # can't actually check that the update for p does not arrive
     # so instead wait a bit and check
     helper.pause()
     e = helper.db_equal(policy.select('p(x)'), 'p(1)')
     self.assertTrue(e, 'Policy non-insert')
Exemple #19
0
 def test_policy_tables(self):
     """Test basic DSE functionality with policy engine and the API."""
     cage = congress.dse.d6cage.d6Cage()
     # so that we exit once test finishes; all other threads are forced
     #    to be daemons
     cage.daemon = True
     cage.start()
     cage.loadModule("TestDriver",
                     helper.data_module_path("test_driver.py"))
     cage.loadModule("TestPolicy", helper.policy_module_path())
     cage.createservice(name="data", moduleName="TestDriver",
                        args=helper.datasource_openstack_args())
     # using regular testdriver as API for now
     cage.createservice(name="api", moduleName="TestDriver",
                        args=helper.datasource_openstack_args())
     cage.createservice(name="policy", moduleName="TestPolicy",
                        args={'d6cage': cage, 'rootdir': ''})
     data = cage.services['data']['object']
     api = cage.services['api']['object']
     policy = cage.services['policy']['object']
     policy.subscribe('api', 'policy-update',
                      callback=policy.receive_policy_update)
     # simulate API call for insertion of policy statements
     formula = compile.parse1('p(x) :- data:q(x)')
     api.publish('policy-update', [runtime.Event(formula)])
     helper.pause()
     # simulate data source publishing to q
     formula = compile.parse1('q(1)')
     data.publish('q', [runtime.Event(formula)])
     helper.pause()  # give other threads chance to run
     # check that policy did the right thing with data
     e = helper.db_equal(policy.select('data:q(x)'), 'data:q(1)')
     self.assertTrue(e, 'Policy insert 1')
     e = helper.db_equal(policy.select('p(x)'), 'p(1)')
     self.assertTrue(e, 'Policy insert 2')
     #check that publishing into 'p' does not work
     formula = compile.parse1('p(3)')
     data.publish('p', [runtime.Event(formula)])
     helper.pause()
     e = helper.db_equal(policy.select('p(x)'), 'p(1)')
     self.assertTrue(e, 'Policy noninsert')
    def test_receive_data_sequence_number_max_int(self):
        '''Test receiving data when sequence number goes over max int'''
        run = agnostic.Dse2Runtime('engine')
        run.always_snapshot = False
        run.create_policy('datasource1')

        run.receive_data_sequenced(
            publisher='datasource1', table='p',
            data=[[1], [2]], seqnum=sys.maxsize, is_snapshot=True)
        actual = run.select('p(x)')
        correct = 'p(1) p(2)'
        self.assertTrue(helper.db_equal(actual, correct))

        run.receive_data_sequenced(
            publisher='datasource1', table='p',
            data=[[], [[2]]], seqnum=sys.maxsize + 1, is_snapshot=False)
        actual = run.select('p(x)')
        correct = 'p(1)'
        self.assertTrue(helper.db_equal(actual, correct))

        # test out-of-sequence update ignored
        run.receive_data_sequenced(
            publisher='datasource1', table='p',
            data=[[[2]], []], seqnum=sys.maxsize, is_snapshot=False)
        actual = run.select('p(x)')
        correct = 'p(1)'
        self.assertTrue(helper.db_equal(actual, correct))

        run.receive_data_sequenced(
            publisher='datasource1', table='p',
            data=[[[4]], []], seqnum=sys.maxsize + 3, is_snapshot=False)
        actual = run.select('p(x)')
        correct = 'p(1)'
        self.assertTrue(helper.db_equal(actual, correct))

        run.receive_data_sequenced(
            publisher='datasource1', table='p',
            data=[[[3]], []], seqnum=sys.maxsize + 2, is_snapshot=False)
        actual = run.select('p(x)')
        correct = 'p(1) p(3) p(4)'
        self.assertTrue(helper.db_equal(actual, correct))
Exemple #21
0
 def test_external(self):
     """Test ability to write rules that span multiple policies."""
     # External theory
     run = runtime.Runtime()
     run.create_policy('test1')
     run.insert('q(1)', target='test1')
     run.insert('q(2)', target='test1')
     run.create_policy('test2')
     run.insert('p(x) :- test1:q(x)', target='test2')
     actual = run.select('p(x)', target='test2')
     e = helper.db_equal(actual, 'p(1) p(2)')
     self.assertTrue(e, "Basic")
 def test_receive_data_arbitrary_start(self):
     '''Test receiving data with arbitrary starting sequence number'''
     run = agnostic.DseRuntime(api_base.ENGINE_SERVICE_ID)
     run.create_policy('datasource1')
     run.receive_data_sequenced(publisher='datasource1',
                                table='p',
                                data=[[1], [2]],
                                seqnum=1234,
                                is_snapshot=True)
     actual = run.select('p(x)')
     correct = 'p(1) p(2)'
     self.assertTrue(helper.db_equal(actual, correct))
Exemple #23
0
    def test_rule_api_model(self):
        """Test the rule api model.

        Same as test_multiple except we use the api interface
        instead of the DSE interface.
        """
        api = self.api
        cage = self.cage
        engine = self.engine
        policy = engine.DEFAULT_THEORY

        # Insert formula
        net_formula = test_neutron.create_networkXnetwork_group('p')
        LOG.debug("Sending formula: %s", net_formula)
        engine.debug_mode()
        context = {'policy_id': engine.DEFAULT_THEORY}
        (id1, _) = api['rule'].add_item(
            {'rule': str(net_formula)}, {}, context=context)
        # Poll
        neutron = cage.service_object('neutron')
        neutron2 = cage.service_object('neutron2')
        neutron.poll()
        neutron2.poll()
        # Insert a second formula
        other_formula = engine.parse1('q(x,y) :- p(x,y)')
        (id2, _) = api['rule'].add_item(
            {'rule': str(other_formula)}, {}, context=context)
        ans1 = ('p("240ff9df-df35-43ae-9df5-27fae87f2492",  '
                '  "240ff9df-df35-43ae-9df5-27fae87f2492") ')
        ans2 = ('q("240ff9df-df35-43ae-9df5-27fae87f2492",  '
                '  "240ff9df-df35-43ae-9df5-27fae87f2492") ')
        # Wait for first query so messages can be delivered.
        #    But once the p table has its data, no need to wait anymore.
        helper.retry_check_db_equal(engine, 'p(x,y)', ans1, target=policy)
        e = helper.db_equal(engine.select('q(x,y)', target=policy), ans2)
        self.assertTrue(e, "Insert rule-api 2")
        # Get formula
        ruleobj = api['rule'].get_item(id1, {}, context=context)
        self.assertTrue(e, net_formula == engine.parse1(ruleobj['rule']))
        # Get all formulas
        ds = api['rule'].get_items({}, context=context)['results']
        self.assertEqual(len(ds), 2)
        ids = set([x['id'] for x in ds])
        rules = set([engine.parse1(x['rule']) for x in ds])
        self.assertEqual(ids, set([id1, id2]))
        self.assertEqual(rules, set([net_formula, other_formula]))
        # Delete formula
        api['rule'].delete_item(id1, {}, context=context)
        # Get all formulas
        ds = api['rule'].get_items({}, context=context)['results']
        self.assertEqual(len(ds), 1)
        ids = sorted([x['id'] for x in ds])
        self.assertEqual(ids, sorted([id2]))
    def test_rule_api_model(self):
        """Test the rule api model.

        Same as test_multiple except we use the api interface
        instead of the DSE interface.
        """
        api = self.api
        cage = self.cage
        engine = self.engine
        policy = engine.DEFAULT_THEORY

        # Insert formula
        net_formula = test_neutron.create_networkXnetwork_group('p')
        LOG.debug("Sending formula: %s", net_formula)
        engine.debug_mode()
        context = {'policy_id': engine.DEFAULT_THEORY}
        (id1, rule) = api['rule'].add_item({'rule': str(net_formula)}, {},
                                           context=context)
        # Poll
        neutron = cage.service_object('neutron')
        neutron2 = cage.service_object('neutron2')
        neutron.poll()
        neutron2.poll()
        # Insert a second formula
        other_formula = engine.parse1('q(x,y) :- p(x,y)')
        (id2, rule) = api['rule'].add_item({'rule': str(other_formula)}, {},
                                           context=context)
        ans1 = ('p("240ff9df-df35-43ae-9df5-27fae87f2492",  '
                '  "240ff9df-df35-43ae-9df5-27fae87f2492") ')
        ans2 = ('q("240ff9df-df35-43ae-9df5-27fae87f2492",  '
                '  "240ff9df-df35-43ae-9df5-27fae87f2492") ')
        # Wait for first query so messages can be delivered.
        #    But once the p table has its data, no need to wait anymore.
        helper.retry_check_db_equal(engine, 'p(x,y)', ans1, target=policy)
        e = helper.db_equal(engine.select('q(x,y)', target=policy), ans2)
        self.assertTrue(e, "Insert rule-api 2")
        # Get formula
        ruleobj = api['rule'].get_item(id1, {}, context=context)
        self.assertTrue(e, net_formula == engine.parse1(ruleobj['rule']))
        # Get all formulas
        ds = api['rule'].get_items({}, context=context)['results']
        self.assertEqual(len(ds), 2)
        ids = set([x['id'] for x in ds])
        rules = set([engine.parse1(x['rule']) for x in ds])
        self.assertEqual(ids, set([id1, id2]))
        self.assertEqual(rules, set([net_formula, other_formula]))
        # Delete formula
        api['rule'].delete_item(id1, {}, context=context)
        # Get all formulas
        ds = api['rule'].get_items({}, context=context)['results']
        self.assertEqual(len(ds), 1)
        ids = sorted([x['id'] for x in ds])
        self.assertEqual(ids, sorted([id2]))
 def test_external(self):
     """Test ability to write rules that span multiple policies."""
     # External theory
     run = agnostic.Runtime()
     run.create_policy('test1')
     run.insert('q(1)', target='test1')
     run.insert('q(2)', target='test1')
     run.create_policy('test2')
     run.insert('p(x) :- test1:q(x)', target='test2')
     actual = run.select('p(x)', target='test2')
     e = helper.db_equal(actual, 'p(1) p(2)')
     self.assertTrue(e, "Basic")
 def test_multipolicy_head(self):
     """Test SELECT with different policy in the head."""
     run = agnostic.Runtime()
     run.debug_mode()
     run.create_policy('test1', kind='action')
     run.create_policy('test2', kind='action')
     (permitted, errors) = run.insert('test2:p+(x) :- q(x)', 'test1')
     self.assertTrue(permitted, "modals with policy names must be allowed")
     run.insert('q(1)', 'test1')
     run.insert('p(2)', 'test2')
     actual = run.select('test2:p+(x)', 'test1')
     e = helper.db_equal(actual, 'test2:p+(1)')
     self.assertTrue(e, "Policy name in the head")
Exemple #27
0
 def test_local(self):
     """Test ability to write rules that span multiple policies."""
     # Local table used
     run = runtime.Runtime()
     run.create_policy('test1')
     run.insert('q(1)', target='test1')
     run.insert('q(2)', target='test1')
     run.create_policy('test2')
     run.insert('p(x) :- test1:q(x), q(x)', target='test2')
     run.insert('q(2)', 'test2')
     actual = run.select('p(x)', target='test2')
     e = helper.db_equal(actual, 'p(2)')
     self.assertTrue(e, "Local table used")
 def test_local(self):
     """Test ability to write rules that span multiple policies."""
     # Local table used
     run = agnostic.Runtime()
     run.create_policy('test1')
     run.insert('q(1)', target='test1')
     run.insert('q(2)', target='test1')
     run.create_policy('test2')
     run.insert('p(x) :- test1:q(x), q(x)', target='test2')
     run.insert('q(2)', 'test2')
     actual = run.select('p(x)', target='test2')
     e = helper.db_equal(actual, 'p(2)')
     self.assertTrue(e, "Local table used")
Exemple #29
0
 def test_multipolicy_head(self):
     """Test SELECT with different policy in the head."""
     run = runtime.Runtime()
     run.debug_mode()
     run.create_policy('test1', kind='action')
     run.create_policy('test2', kind='action')
     (permitted, errors) = run.insert('test2:p+(x) :- q(x)', 'test1')
     self.assertTrue(permitted, "modals with policy names must be allowed")
     run.insert('q(1)', 'test1')
     run.insert('p(2)', 'test2')
     actual = run.select('test2:p+(x)', 'test1')
     e = helper.db_equal(actual, 'test2:p+(1)')
     self.assertTrue(e, "Policy name in the head")
 def test_multi_external(self):
     """Test multiple rules that span multiple policies."""
     run = agnostic.Runtime()
     run.debug_mode()
     run.create_policy('test1')
     run.create_policy('test2')
     run.create_policy('test3')
     run.insert('p(x) :- test2:p(x)', target='test1')
     run.insert('p(x) :- test3:p(x)', target='test1')
     run.insert('p(1)', target='test2')
     run.insert('p(2)', target='test3')
     actual = run.select('p(x)', target='test1')
     e = helper.db_equal(actual, 'p(1) p(2)')
     self.assertTrue(e, "Multiple external rules with multiple policies")
    def test_subscribe_poll(self):
        """Test subscribing before polling.  The common case."""
        cage = self.info['cage']
        policy = cage.service_object('policy')
        neutron = cage.service_object('neutron')
        datalog1 = self.info['datalog1']
        datalog2 = self.info['datalog2']

        # subscribe
        policy.subscribe('neutron', 'networks', callback=policy.receive_data)
        helper.pause()  # so that subscription messages are processed

        # poll 1
        neutron.poll()
        helper.pause(3)  # so that data updates are processed
        e = helper.db_equal(policy.select('p(x)'), datalog1)
        self.assertTrue(e, 'Neutron insertion 1: ' + str(policy.content()))

        # poll 2
        neutron.poll()
        helper.pause()
        e = helper.db_equal(policy.select('p(x)'), datalog2)
        self.assertTrue(e, 'Neutron insertion 2')
    def test_policy_recovery(self):
        """Test policy crashing and recovering (sort of)."""
        cage = self.info['cage']
        policy = cage.service_object('policy')
        neutron = cage.service_object('neutron')
        datalog1 = self.info['datalog1']

        # get initial data
        policy.subscribe('neutron', 'networks', callback=policy.receive_data)
        helper.pause()
        neutron.poll()
        helper.pause()
        e = helper.db_equal(policy.select('p(x)'), datalog1)
        self.assertTrue(e, 'Neutron insertion 1')

        # clear out policy's neutron:networks data (to simulate crashing)
        policy.initialize(['neutron:networks'], [])
        # subscribe again (without unsubscribing)
        policy.subscribe('neutron', 'networks', callback=policy.receive_data)
        helper.pause()
        # should get same data
        e = helper.db_equal(policy.select('p(x)'), datalog1)
        self.assertTrue(e, 'Neutron insertion 1')
Exemple #33
0
 def test_external_current(self):
     """Test ability to write rules that span multiple policies."""
     # External theory plus current theory
     run = agnostic.Runtime()
     run.create_policy('test1')
     run.insert('q(1)', target='test1')
     run.insert('q(2)', target='test1')
     run.create_policy('test2')
     run.insert('p(x) :- test1:q(x), r(x)', target='test2')
     run.insert('r(1)', target='test2')
     run.insert('r(2)', target='test2')
     actual = run.select('p(x)', target='test2')
     e = helper.db_equal(actual, 'p(1) p(2)')
     self.assertTrue(e, "Mixing external theories with current theory")
Exemple #34
0
 def test_multi_external(self):
     """Test multiple rules that span multiple policies."""
     run = runtime.Runtime()
     run.debug_mode()
     run.create_policy('test1')
     run.create_policy('test2')
     run.create_policy('test3')
     run.insert('p(x) :- test2:p(x)', target='test1')
     run.insert('p(x) :- test3:p(x)', target='test1')
     run.insert('p(1)', target='test2')
     run.insert('p(2)', target='test3')
     actual = run.select('p(x)', target='test1')
     e = helper.db_equal(actual, 'p(1) p(2)')
     self.assertTrue(e, "Multiple external rules with multiple policies")
    def test_double_poll_subscribe(self):
        """Test double polling before subscribing."""
        cage = self.info['cage']
        policy = cage.service_object('policy')
        neutron = cage.service_object('neutron')
        datalog2 = self.info['datalog2']

        # poll twice and then subscribe: should see 2nd result
        neutron.poll()
        helper.pause()
        neutron.poll()
        helper.pause()
        policy.subscribe('neutron', 'networks', callback=policy.receive_data)
        helper.pause()  # so that messages are processed
        e = helper.db_equal(policy.select('p(x)'), datalog2)
        self.assertTrue(e, 'Neutron insertion 2')
Exemple #36
0
 def test_multiple_external(self):
     """Test ability to write rules that span multiple policies."""
     # Multiple external theories
     run = agnostic.Runtime()
     run.create_policy('test1')
     run.insert('q(1)', target='test1')
     run.insert('q(2)', target='test1')
     run.insert('q(3)', target='test1')
     run.create_policy('test2')
     run.insert('q(1)', target='test2')
     run.insert('q(2)', target='test2')
     run.insert('q(4)', target='test2')
     run.create_policy('test3')
     run.insert('p(x) :- test1:q(x), test2:q(x)', target='test3')
     actual = run.select('p(x)', target='test3')
     e = helper.db_equal(actual, 'p(1) p(2)')
     self.assertTrue(e, "Multiple external theories")
    def test_receive_data_multiple_tables(self):
        '''Test receiving data with sequence numbers, multiple tables'''
        run = agnostic.Dse2Runtime('engine')
        run.always_snapshot = False
        run.create_policy('datasource1')

        # initialize p with full table
        run.receive_data_sequenced(
            publisher='datasource1', table='p',
            data=[[1]], seqnum=0, is_snapshot=True)
        actual = run.select('p(x)')
        correct = 'p(1)'
        self.assertTrue(helper.db_equal(actual, correct))

        # add data to p
        run.receive_data_sequenced(
            publisher='datasource1', table='p',
            data=[[[2]], []], seqnum=1, is_snapshot=False)
        actual = run.select('p(x)')
        correct = 'p(1) p(2)'
        self.assertTrue(helper.db_equal(actual, correct))

        # add data to q
        run.receive_data_sequenced(
            publisher='datasource1', table='q',
            data=[[[2]], []], seqnum=1, is_snapshot=False)
        actual = run.select('q(x)')
        correct = ''  # does not apply until initialize
        self.assertTrue(helper.db_equal(actual, correct))

        # initialize q with full table
        run.receive_data_sequenced(
            publisher='datasource1', table='q',
            data=[[1]], seqnum=0, is_snapshot=True)
        actual = run.select('q(x)')
        correct = 'q(1) q(2)'  # both initial data and preceding update applied
        self.assertTrue(helper.db_equal(actual, correct))

        # add data to q
        run.receive_data_sequenced(
            publisher='datasource1', table='q',
            data=[[[3]], []], seqnum=2, is_snapshot=False)
        actual = run.select('q(x)')
        correct = 'q(1) q(2) q(3)'
        self.assertTrue(helper.db_equal(actual, correct))

        # add data to p
        run.receive_data_sequenced(
            publisher='datasource1', table='p',
            data=[[[3]], []], seqnum=2, is_snapshot=False)
        actual = run.select('p(x)')
        correct = 'p(1) p(2) p(3)'
        self.assertTrue(helper.db_equal(actual, correct))
Exemple #38
0
 def test_neutron(self):
     """Test polling and publishing of neutron updates."""
     engine = self.engine
     api = self.api
     cage = self.cage
     helper.pause()
     # Send formula
     formula = test_neutron.create_network_group('p')
     LOG.debug("Sending formula: {}".format(str(formula)))
     api['rule'].publish('policy-update', [runtime.Event(formula)])
     helper.pause()  # give time for messages/creation of services
     LOG.debug("All services: " + str(cage.services.keys()))
     neutron = cage.service_object('neutron')
     neutron.poll()
     helper.pause()
     ans = ('p("240ff9df-df35-43ae-9df5-27fae87f2492") ')
     e = helper.db_equal(engine.select('p(x)'), ans)
     self.assertTrue(e, "Neutron datasource")
Exemple #39
0
 def test_multiple_levels_external(self):
     """Test ability to write rules that span multiple policies."""
     # Multiple levels of external theories
     run = runtime.Runtime()
     run.debug_mode()
     run.create_policy('test1')
     run.insert('p(x) :- test2:q(x), test3:q(x)', target='test1')
     run.insert('s(3) s(1) s(2) s(4)', target='test1')
     run.create_policy('test2')
     run.insert('q(x) :- test4:r(x)', target='test2')
     run.create_policy('test3')
     run.insert('q(x) :- test1:s(x)', target='test3')
     run.create_policy('test4')
     run.insert('r(1)', target='test4')
     run.insert('r(2)', target='test4')
     run.insert('r(5)', target='test4')
     actual = run.select('p(x)', target='test1')
     e = helper.db_equal(actual, 'p(1) p(2)')
     self.assertTrue(e, "Multiple levels of external theories")
 def test_multiple_levels_external(self):
     """Test ability to write rules that span multiple policies."""
     # Multiple levels of external theories
     run = agnostic.Runtime()
     run.debug_mode()
     run.create_policy('test1')
     run.insert('p(x) :- test2:q(x), test3:q(x)', target='test1')
     run.insert('s(3) s(1) s(2) s(4)', target='test1')
     run.create_policy('test2')
     run.insert('q(x) :- test4:r(x)', target='test2')
     run.create_policy('test3')
     run.insert('q(x) :- test1:s(x)', target='test3')
     run.create_policy('test4')
     run.insert('r(1)', target='test4')
     run.insert('r(2)', target='test4')
     run.insert('r(5)', target='test4')
     actual = run.select('p(x)', target='test1')
     e = helper.db_equal(actual, 'p(1) p(2)')
     self.assertTrue(e, "Multiple levels of external theories")
Exemple #41
0
    def test_multiple(self):
        """Test polling and publishing of multiple neutron instances."""
        api = self.api
        cage = self.cage
        engine = self.engine

        # Send formula
        formula = create_networkXnetwork_group('p')
        api['rule'].publish('policy-update', [runtime.Event(formula)])
        helper.pause()  # give time for messages/creation of services
        # poll datasources
        neutron = cage.service_object('neutron')
        neutron2 = cage.service_object('neutron2')
        neutron.poll()
        neutron2.poll()
        helper.pause()
        # check answer
        ans = ('p("240ff9df-df35-43ae-9df5-27fae87f2492",  '
               '  "240ff9df-df35-43ae-9df5-27fae87f2492") ')
        e = helper.db_equal(engine.select('p(x,y)'), ans)
        self.assertTrue(e, "Multiple neutron datasources")
    def test_policy_initialization(self):
        """Test subscribing before polling.  The common case."""
        cage = self.info['cage']
        policy = cage.service_object('policy')
        neutron = cage.service_object('neutron')
        datalog1 = self.info['datalog1']
        fake_networks = self.info['fake_networks']

        # add garbage to policy
        for formula in fake_networks:
            policy.insert(formula)

        # subscribe
        policy.subscribe('neutron', 'networks', callback=policy.receive_data)
        helper.pause()  # so that subscription messages are processed

        # poll 1
        neutron.poll()
        helper.pause()  # so that data updates are processed
        e = helper.db_equal(policy.select('p(x)'), datalog1)
        self.assertTrue(e, 'Neutron insertion 1')
Exemple #43
0
 def test_policy_data(self):
     """Test policy properly inserts data and processes it normally."""
     cage = congress.dse.d6cage.d6Cage()
     # so that we exit once test finishes; all other threads are forced
     #    to be daemons
     cage.daemon = True
     cage.start()
     cage.loadModule("TestDriver",
                     helper.data_module_path("test_driver.py"))
     cage.loadModule("TestPolicy", helper.policy_module_path())
     cage.createservice(name="data", moduleName="TestDriver")
     cage.createservice(name="policy", moduleName="TestPolicy")
     data = cage.services['data']['object']
     policy = cage.services['policy']['object']
     policy.subscribe('data', 'p', callback=policy.receive_data)
     formula = compile.parse1('p(1)')
     # sending a single Insert.  (Default for Event is Insert.)
     data.publish('p', [runtime.Event(formula)])
     helper.pause()  # give other threads chance to run
     e = helper.db_equal(policy.select('data:p(x)'), 'data:p(1)')
     self.assertTrue(e, 'Single insert')
    def test_receive_data_multiple_tables(self):
        '''Test receiving data with sequence numbers, multiple tables'''
        run = agnostic.DseRuntime(api_base.ENGINE_SERVICE_ID)
        run.create_policy('datasource1')

        # initialize p with full table
        run.receive_data_sequenced(publisher='datasource1',
                                   table='p',
                                   data=[[1]],
                                   seqnum=0,
                                   is_snapshot=True)
        actual = run.select('p(x)')
        correct = 'p(1)'
        self.assertTrue(helper.db_equal(actual, correct))

        # add data to p
        run.receive_data_sequenced(publisher='datasource1',
                                   table='p',
                                   data=[[[2]], []],
                                   seqnum=1,
                                   is_snapshot=False)
        actual = run.select('p(x)')
        correct = 'p(1) p(2)'
        self.assertTrue(helper.db_equal(actual, correct))

        # add data to q
        run.receive_data_sequenced(publisher='datasource1',
                                   table='q',
                                   data=[[[2]], []],
                                   seqnum=1,
                                   is_snapshot=False)
        actual = run.select('q(x)')
        correct = ''  # does not apply until initialize
        self.assertTrue(helper.db_equal(actual, correct))

        # initialize q with full table
        run.receive_data_sequenced(publisher='datasource1',
                                   table='q',
                                   data=[[1]],
                                   seqnum=0,
                                   is_snapshot=True)
        actual = run.select('q(x)')
        correct = 'q(1) q(2)'  # both initial data and preceding update applied
        self.assertTrue(helper.db_equal(actual, correct))

        # add data to q
        run.receive_data_sequenced(publisher='datasource1',
                                   table='q',
                                   data=[[[3]], []],
                                   seqnum=2,
                                   is_snapshot=False)
        actual = run.select('q(x)')
        correct = 'q(1) q(2) q(3)'
        self.assertTrue(helper.db_equal(actual, correct))

        # add data to p
        run.receive_data_sequenced(publisher='datasource1',
                                   table='p',
                                   data=[[[3]], []],
                                   seqnum=2,
                                   is_snapshot=False)
        actual = run.select('p(x)')
        correct = 'p(1) p(2) p(3)'
        self.assertTrue(helper.db_equal(actual, correct))