Esempio n. 1
0
 def test_delitem(self):
     res = Resource()
     with self.assertRaises(KeyError):
         del res["attr"]
     res = Resource({'attr': 'value'})
     del res['attr']
     self.assertRaises(KeyError, lambda res: res['attr'], res)
Esempio n. 2
0
 def _populate_session_state(self, job, state):
     io_log = [
         IOLogRecord(count, 'stdout', line.encode('utf-8'))
         for count, line in enumerate(
             job.get_record_value('io_log').splitlines(keepends=True))
     ]
     result = MemoryJobResult({
         'outcome':
         job.get_record_value('outcome', job.get_record_value('status')),
         'comments':
         job.get_record_value('comments'),
         'execution_duration':
         job.get_record_value('duration'),
         'io_log':
         io_log,
     })
     state.update_job_result(job, result)
     if job.plugin == 'resource':
         new_resource_list = []
         for record in gen_rfc822_records_from_io_log(job, result):
             resource = Resource(record.data)
             new_resource_list.append(resource)
         if not new_resource_list:
             new_resource_list = [Resource({})]
         state.set_resource_list(job.id, new_resource_list)
     job_state = state.job_state_map[job.id]
     job_state.effective_category_id = job.get_record_value(
         'category_id', 'com.canonical.plainbox::uncategorised')
     job_state.effective_certification_status = job.get_record_value(
         'certification_status', 'unspecified')
Esempio n. 3
0
 def test_delattr(self):
     res = Resource()
     self.assertRaises(AttributeError, delattr, res, "_attr")
     res = Resource({'attr': 'value'})
     del res.attr
     self.assertRaises(AttributeError, getattr, res, "attr")
     self.assertRaises(AttributeError, lambda res: res.attr, res)
Esempio n. 4
0
 def test_resource_job_result_overwrites_old_resources(self):
     # This function checks what happens when a JobResult for job R is
     # presented to a session that has some resources from that job already.
     result_R_old = JobResult({
         'job':
         self.job_R,
         'io_log':
         make_io_log(((0, 'stdout', b"attr: old value\n"), ),
                     self.scratch_dir)
     })
     self.session.update_job_result(self.job_R, result_R_old)
     # So here the old result is stored into a new 'R' resource
     expected_before = {'R': [Resource({'attr': 'old value'})]}
     self.assertEqual(self.session._resource_map, expected_before)
     # Now we present the second result for the same job
     result_R_new = JobResult({
         'job':
         self.job_R,
         'io_log':
         make_io_log(((0, 'stdout', b"attr: new value\n"), ),
                     self.scratch_dir)
     })
     self.session.update_job_result(self.job_R, result_R_new)
     # What should happen here is that the R resource is entirely replaced
     # by the data from the new result. The data should not be merged or
     # appended in any way.
     expected_after = {'R': [Resource({'attr': 'new value'})]}
     self.assertEqual(self.session._resource_map, expected_after)
Esempio n. 5
0
 def test_evaluate_success(self):
     resource_map = {
         'package':
         [Resource({'name': 'plainbox'}),
          Resource({'name': 'fwts'})],
         'platform': [Resource({'arch': 'i386'})]
     }
     self.assertTrue(self.prog.evaluate_or_raise(resource_map))
Esempio n. 6
0
 def test_should_instantiate__no_filter(self):
     template = TemplateUnit({
         'template-resource': 'resource',
     })
     self.assertTrue(
         template.should_instantiate(Resource({'attr': 'value'})))
     self.assertTrue(
         template.should_instantiate(Resource({'attr': 'other value'})))
     self.assertTrue(template.should_instantiate(Resource()))
Esempio n. 7
0
 def test_compound_expression_or_passing(self):
     resource_map = {
         'a': [Resource({'foo': 1})],
         'b': [Resource({'bar': 3})]
     }
     expr = ResourceExpression("a.foo == 1 or b.bar == 2")
     self.assertTrue(
         expr.evaluate(resource_map['a'],
                       resource_map['b'],
                       resource_map=resource_map))
Esempio n. 8
0
 def test_compound_expression_or_failing(self):
     resource_map = {
         'a': [Resource({'foo': 2})],
         'b': [Resource({'bar': 3})]
     }
     expr = ResourceExpression("a.foo == 1 and b.bar == 2")
     self.assertFalse(
         expr.evaluate(resource_map['a'],
                       resource_map['b'],
                       resource_map=resource_map))
Esempio n. 9
0
 def test_evaluate_failure_not_true(self):
     resource_map = {
         'package': [
             Resource({'name': 'plainbox'}),
         ],
         'platform': [Resource({'arch': 'i386'})]
     }
     with self.assertRaises(ExpressionFailedError) as call:
         self.prog.evaluate_or_raise(resource_map)
     self.assertEqual(call.exception.expression.text,
                      "package.name == 'fwts'")
Esempio n. 10
0
 def test_evaluate(self):
     resources = {
         'package':
         [Resource({'name': 'plainbox'}),
          Resource({'name': 'fwts'})],
         'platform': [Resource({'arch': 'i386'})]
     }
     self.assertIn('package', resources)
     self.assertIn('platform', resources)
     for name in self.prog.required_resources:
         self.assertIn(name, resources)
     self.assertTrue(self.prog.evaluate(resources))
Esempio n. 11
0
 def test_compound_many_subexpressions_failing(self):
     resource_map = {
         'a': [Resource({'foo': 1}),
               Resource({'foo': 2})],
         'b': [Resource({'bar': 3}),
               Resource({'bar': 4})]
     }
     expr = ResourceExpression(
         "a.foo == 3 or b.bar == 3 and a.foo == 2 and b.bar == 1")
     self.assertFalse(
         expr.evaluate(resource_map['a'],
                       resource_map['b'],
                       resource_map=resource_map))
Esempio n. 12
0
 def setUp(self):
     self.exporter_unit = self._get_all_exporter_units()[
         '2013.com.canonical.plainbox::html']
     self.resource_map = {
         '2013.com.canonical.certification::lsb': [
             Resource({'description': 'Ubuntu 14.04 LTS'})],
         '2013.com.canonical.certification::package': [
             Resource({'name': 'plainbox', 'version': '1.0'}),
             Resource({'name': 'fwts', 'version': '0.15.2'})],
     }
     self.job1 = JobDefinition({'id': 'job_id1', '_summary': 'job 1'})
     self.job2 = JobDefinition({'id': 'job_id2', '_summary': 'job 2'})
     self.job3 = JobDefinition({'id': 'job_id3', '_summary': 'job 3'})
     self.result_fail = MemoryJobResult({
         'outcome': IJobResult.OUTCOME_FAIL, 'return_code': 1,
         'io_log': [(0, 'stderr', b'FATAL ERROR\n')],
     })
     self.result_pass = MemoryJobResult({
         'outcome': IJobResult.OUTCOME_PASS, 'return_code': 0,
         'io_log': [(0, 'stdout', b'foo\n')],
         'comments': 'blah blah'
     })
     self.result_skip = MemoryJobResult({
         'outcome': IJobResult.OUTCOME_SKIP,
         'comments': 'No such device'
     })
     self.attachment = JobDefinition({
         'id': 'dmesg_attachment',
         'plugin': 'attachment'})
     self.attachment_result = MemoryJobResult({
         'outcome': IJobResult.OUTCOME_PASS,
         'io_log': [(0, 'stdout', b'bar\n')],
         'return_code': 0
     })
     self.session_manager = SessionManager.create()
     self.session_manager.add_local_device_context()
     self.session_state = self.session_manager.default_device_context.state
     session_state = self.session_state
     session_state.add_unit(self.job1)
     session_state.add_unit(self.job2)
     session_state.add_unit(self.job3)
     session_state.add_unit(self.attachment)
     session_state.update_job_result(self.job1, self.result_fail)
     session_state.update_job_result(self.job2, self.result_pass)
     session_state.update_job_result(self.job3, self.result_skip)
     session_state.update_job_result(
         self.attachment, self.attachment_result)
     for resource_id, resource_list in self.resource_map.items():
         session_state.set_resource_list(resource_id, resource_list)
Esempio n. 13
0
 def test_resource_job_result_updates_resource_and_job_states(self):
     # This function checks what happens when a JobResult for job R (which
     # is a resource job via the resource plugin) is presented to the
     # session.
     result_R = MemoryJobResult({
         'outcome': IJobResult.OUTCOME_PASS,
         'io_log': [(0, 'stdout', b"attr: value\n")],
     })
     self.session.update_job_result(self.job_R, result_R)
     # The most obvious thing that can happen, is that the result is simply
     # stored in the associated job state object.
     self.assertIs(self.job_state('R').result, result_R)
     # Initially the _resource_map was empty. SessionState parses the io_log
     # of results of resource jobs and creates appropriate resource objects.
     self.assertIn("R", self.session._resource_map)
     expected = {'R': [Resource({'attr': 'value'})]}
     self.assertEqual(self.session._resource_map, expected)
     # As job results are presented to the session the readiness of other
     # jobs is changed. Since A depends on R via a resource expression and
     # the particular resource that were produced by R in this test should
     # allow the expression to match the readiness inhibitor from A should
     # have been removed. Since this test does not use
     # update_desired_job_list() a will still have the UNDESIRED inhibitor
     # but it will no longer have the PENDING_RESOURCE inhibitor,
     self.assertEqual(
         self.job_inhibitor('A', 0).cause, JobReadinessInhibitor.UNDESIRED)
     # Now if we put A on the desired list this should clear the UNDESIRED
     # inhibitor and make A runnable.
     self.session.update_desired_job_list([self.job_A])
     self.assertTrue(self.job_state('A').can_start())
Esempio n. 14
0
 def _plugin_resource(self, job):
     proc = self._run_command(job)
     if proc.returncode == 127:
         logging.warning("Unable to find command: %s", job.command)
         return
     line_list = []
     for byte_line in proc.stdout.splitlines():
         try:
             line = byte_line.decode("UTF-8")
         except UnicodeDecodeError as exc:
             logger.warning("resource script %s returned invalid UTF-8 data"
                            " %r: %s", job, byte_line, exc)
         else:
             line_list.append(line)
     with StringIO("\n".join(line_list)) as stream:
         try:
             record_list = load_rfc822_records(stream)
         except RFC822SyntaxError as exc:
             logger.warning("resource script %s returned invalid RFC822"
                            " data: %s", job, exc)
         else:
             for record in record_list:
                 logger.info("Storing resource record %s: %s",
                             job.name, record)
                 resource = Resource(record)
                 self._context.add_resource(job.name, resource)
Esempio n. 15
0
    def test_resource_job_with_broken_output(self, mock_logger):
        # This function checks how SessionState parses partially broken
        # resource jobs.  A JobResult with broken output is constructed below.
        # The output will describe one proper record, one broken record and
        # another proper record in that order.
        result_R = MemoryJobResult({
            'outcome':
            IJobResult.OUTCOME_PASS,
            'io_log': [(0, 'stdout', b"attr: value-1\n"), (1, 'stdout', b"\n"),
                       (1, 'stdout', b"I-sound-like-a-broken-record\n"),
                       (1, 'stdout', b"\n"),
                       (1, 'stdout', b"attr: value-2\n")],
        })
        # Since we cannot control the output of scripts and people indeed make
        # mistakes a warning is issued but no exception is raised to the
        # caller.
        self.session.update_job_result(self.job_R, result_R)
        # The observation here is that the parser is not handling the exception
        # in away which would allow for recovery. Out of all the output only
        # the first record is created and stored properly. The third, proper
        # record is entirely ignored.
        expected = {'R': [Resource({'attr': 'value-1'})]}
        self.assertEqual(self.session._resource_map, expected)

        # Make sure the right warning was logged
        mock_logger.warning.assert_called_once_with(
            "local script %s returned invalid RFC822 data: %s", self.job_R.id,
            RFC822SyntaxError(
                None, 3, "Unexpected non-empty line: "
                "'I-sound-like-a-broken-record\\n'"))
Esempio n. 16
0
 def test_resource_job_with_broken_output(self):
     # This function checks how SessionState parses partially broken
     # resource jobs.  A JobResult with broken output is constructed below.
     # The output will describe one proper record, one broken record and
     # another proper record in that order.
     result_R = JobResult({
         'job':
         self.job_R,
         'io_log':
         make_io_log(
             ((0, 'stdout', b"attr: value-1\n"), (1, 'stdout', b"\n"),
              (1, 'stdout', b"I-sound-like-a-broken-record\n"),
              (1, 'stdout', b"\n"), (1, 'stdout', b"attr: value-2\n")),
             self.scratch_dir)
     })
     # Since we cannot control the output of scripts and people indeed make
     # mistakes a warning is issued but no exception is raised to the
     # caller.
     self.session.update_job_result(self.job_R, result_R)
     # The observation here is that the parser is not handling the exception
     # in away which would allow for recovery. Out of all the output only
     # the first record is created and stored properly. The third, proper
     # record is entirely ignored.
     expected = {'R': [Resource({'attr': 'value-1'})]}
     self.assertEqual(self.session._resource_map, expected)
Esempio n. 17
0
 def test_trim_does_remove_resources(self):
     """
     verify that trim_job_list() removes resources for removed jobs
     """
     self.session.set_resource_list("a", [Resource({'attr': 'value'})])
     self.assertIn("a", self.session.resource_map)
     self.session.trim_job_list(JobIdQualifier("a"))
     self.assertNotIn("a", self.session.resource_map)
Esempio n. 18
0
 def test_set_resource_list(self):
     # Define an empty session
     session = SessionState([])
     # Define a resource
     old_res = Resource({'attr': 'old value'})
     # Set the resource list with the old resource
     # So here the old result is stored into a new 'R' resource
     session.set_resource_list('R', [old_res])
     # Ensure that it worked
     self.assertEqual(session._resource_map, {'R': [old_res]})
     # Define another resource
     new_res = Resource({'attr': 'new value'})
     # Now we present the second result for the same job
     session.set_resource_list('R', [new_res])
     # What should happen here is that the R resource is entirely replaced
     # by the data from the new result. The data should not be merged or
     # appended in any way.
     self.assertEqual(session._resource_map, {'R': [new_res]})
Esempio n. 19
0
 def test_observe_result__resource(self):
     job = mock.Mock(spec=JobDefinition, plugin='resource')
     result = mock.Mock(spec=IJobResult, outcome=IJobResult.OUTCOME_PASS)
     result.get_io_log.return_value = [(0, 'stdout', b'attr: value1\n'),
                                       (0, 'stdout', b'\n'),
                                       (0, 'stdout', b'attr: value2\n')]
     session_state = mock.MagicMock(spec=SessionState)
     self.ctrl.observe_result(session_state, job, result)
     # Ensure that result got stored
     self.assertIs(session_state.job_state_map[job.id].result, result)
     # Ensure that signals got fired
     session_state.on_job_state_map_changed.assert_called_once_with()
     session_state.on_job_result_changed.assert_called_once_with(
         job, result)
     # Ensure that new resource was defined
     session_state.set_resource_list.assert_called_once_with(
         job.id,
         [Resource({'attr': 'value1'}),
          Resource({'attr': 'value2'})])
Esempio n. 20
0
 def test_get_inhibitor_list_good_resource(self):
     # verify that jobs that require a resource that has been invoked and
     # produced resources for which the expression evaluates to True don't
     # have any inhibitors
     j1 = JobDefinition({'id': 'j1', 'requires': 'j2.attr == "ok"'})
     j2 = JobDefinition({'id': 'j2'})
     session_state = mock.MagicMock(spec=SessionState)
     session_state.resource_map = {'j2': [Resource({'attr': 'ok'})]}
     session_state.job_state_map['j2'].job = j2
     self.assertEqual(self.ctrl.get_inhibitor_list(session_state, j1), [])
Esempio n. 21
0
 def _process_resource_result(self, result):
     new_resource_list = []
     for record in self._gen_rfc822_records_from_io_log(result):
         # XXX: Consider forwarding the origin object here.  I guess we
         # should have from_frc822_record as with JobDefinition
         resource = Resource(record.data)
         logger.info("Storing resource record %r: %s", result.job.name,
                     resource)
         new_resource_list.append(resource)
     # Replace any old resources with the new resource list
     self._resource_map[result.job.name] = new_resource_list
Esempio n. 22
0
 def _parse_and_store_resource(self, session_state, job, result):
     # NOTE: https://bugs.launchpad.net/checkbox/+bug/1297928
     # If we are resuming from a session that had a resource job that
     # never ran, we will see an empty MemoryJobResult object.
     # Processing empty I/O log would create an empty resource list
     # and that state is different from the state the session started
     # before it was suspended, so don't
     if result.outcome is IJobResult.OUTCOME_NONE:
         return
     new_resource_list = []
     for record in gen_rfc822_records_from_io_log(job, result):
         # XXX: Consider forwarding the origin object here.  I guess we
         # should have from_frc822_record as with JobDefinition
         resource = Resource(record.data)
         logger.info(_("Storing resource record %r: %s"), job.id, resource)
         new_resource_list.append(resource)
     # Create an empty resource object to properly fail __getattr__ calls
     if not new_resource_list:
         new_resource_list = [Resource({})]
     # Replace any old resources with the new resource list
     session_state.set_resource_list(job.id, new_resource_list)
Esempio n. 23
0
 def _make_cert_resources(self):
     # Create some specific resources that this exporter relies on. The
     # corresponding jobs are _not_ loaded but this is irrelevant.
     state = self.manager.default_device_context.state
     ns = CERTIFICATION_NS
     state.set_resource_list(
         ns + 'cpuinfo',
         [
             Resource({
                 'PROP-1': 'VALUE-1',
                 'PROP-2': 'VALUE-2',
                 'count': '2',  # NOTE: this has to be a number :/
             })
         ])
     state.set_resource_list(
         ns + 'dpkg', [Resource({
             'architecture': 'dpkg.ARCHITECTURE',
         })])
     state.set_resource_list(ns + 'lsb', [
         Resource({
             'codename': 'lsb.CODENAME',
             'description': 'lsb.DESCRIPTION',
             'release': 'lsb.RELEASE',
             'distributor_id': 'lsb.DISTRIBUTOR_ID',
         })
     ])
     state.set_resource_list(ns + 'uname',
                             [Resource({
                                 'release': 'uname.RELEASE',
                             })])
     state.set_resource_list(ns + 'package', [
         Resource({
             'name': 'package.0.NAME',
             'version': 'package.0.VERSION',
         }),
         Resource({
             'name': 'package.1.NAME',
             'version': 'package.1.VERSION',
         })
     ])
     state.set_resource_list(ns + 'requirements', [
         Resource({
             'name': 'requirement.0.NAME',
             'link': 'requirement.0.LINK',
         }),
         Resource({
             'name': 'requirement.1.NAME',
             'link': 'requirement.1.LINK',
         })
     ])
Esempio n. 24
0
 def test_get_inhibitor_list_FAILED_RESOURCE(self):
     # verify that jobs that require a resource that has been
     # invoked and produced resources but the expression dones't
     # evaluate to True produce the FAILED_RESOURCE inhibitor
     j1 = JobDefinition({'id': 'j1', 'requires': 'j2.attr == "ok"'})
     j2 = JobDefinition({'id': 'j2'})
     session_state = mock.MagicMock(spec=SessionState)
     session_state.job_state_map['j2'].job = j2
     session_state.resource_map = {'j2': [Resource({'attr': 'not-ok'})]}
     self.assertEqual(self.ctrl.get_inhibitor_list(session_state, j1), [
         JobReadinessInhibitor(JobReadinessInhibitor.FAILED_RESOURCE, j2,
                               ResourceExpression('j2.attr == "ok"'))
     ])
Esempio n. 25
0
 def _process_resource_result(self,
                              session_state,
                              job,
                              result,
                              fake_resources=False):
     """
     Analyze a result of a CheckBox "resource" job and generate
     or replace resource records.
     """
     self._parse_and_store_resource(session_state, job, result)
     if session_state.resource_map[job.id] != [Resource({})]:
         self._instantiate_templates(session_state, job, result,
                                     fake_resources)
Esempio n. 26
0
 def test_instantiate_all(self):
     template = TemplateUnit({
         'template-resource': 'resource',
         'template-filter': 'resource.attr == "value"',
         'id': 'check-device-{dev_name}',
         'summary': 'Test {name} ({sys_path})',
         'plugin': 'shell',
     })
     unit_list = template.instantiate_all([
         Resource({
             'attr': 'value',
             'dev_name': 'sda1',
             'name': 'some device',
             'sys_path': '/sys/something',
         }),
         Resource({
             'attr': 'bad value',
             'dev_name': 'sda2',
             'name': 'some other device',
             'sys_path': '/sys/something-else',
         })
     ])
     self.assertEqual(len(unit_list), 1)
     self.assertEqual(unit_list[0].partial_id, 'check-device-sda1')
Esempio n. 27
0
 def test_evaluate_normal(self):
     # NOTE: the actual expr.resource_name is irrelevant for this test
     expr = ResourceExpression("obj.a == 2")
     self.assertTrue(expr.evaluate([Resource({'a': 1}),
                                    Resource({'a': 2})]))
     self.assertTrue(expr.evaluate([Resource({'a': 2}),
                                    Resource({'a': 1})]))
     self.assertFalse(
         expr.evaluate([Resource({'a': 1}),
                        Resource({'a': 3})]))
Esempio n. 28
0
 def test_instantiate_one(self):
     template = TemplateUnit({
         'template-resource': 'resource',
         'id': 'check-device-{dev_name}',
         'summary': 'Test {name} ({sys_path})',
         'plugin': 'shell',
     })
     job = template.instantiate_one(Resource({
         'dev_name': 'sda1',
         'name': 'some device',
         'sys_path': '/sys/something',
     }))
     self.assertIsInstance(job, JobDefinition)
     self.assertEqual(job.partial_id, 'check-device-sda1')
     self.assertEqual(job.summary, 'Test some device (/sys/something)')
     self.assertEqual(job.plugin, 'shell')
Esempio n. 29
0
    def run_generator_job(self, checksum, env):
        """
        Run a job with and process the stdout to get a job definition.

        :param checksum:
            The checksum of the job to execute
        :param env:
            Environment to execute the job in.
        :returns:
            A list of job definitions that were processed from the output.
        :raises LookupError:
            If the checksum does not match any known job
        """
        job = self.find_job(checksum)
        cmd = [job.shell, '-c', job.command]
        output = subprocess.check_output(
            cmd,
            universal_newlines=True,
            env=self.modify_execution_environment(env))
        job_list = []
        source = JobOutputTextSource(job)
        try:
            record_list = load_rfc822_records(output, source=source)
        except RFC822SyntaxError as exc:
            logging.error(_("Syntax error in record generated from %s: %s"),
                          job, exc)
        else:
            if job.plugin == 'local':
                for record in record_list:
                    job = JobDefinition.from_rfc822_record(record)
                    job_list.append(job)
            elif job.plugin == 'resource':
                resource_list = []
                for record in record_list:
                    resource = Resource(record.data)
                    resource_list.append(resource)
                for plugin in all_providers.get_all_plugins():
                    for u in plugin.plugin_object.unit_list:
                        if (isinstance(u, TemplateUnit)
                                and u.resource_id == job.id):
                            logging.info(_("Instantiating unit: %s"), u)
                            for new_unit in u.instantiate_all(resource_list):
                                job_list.append(new_unit)
        return job_list
Esempio n. 30
0
 def test_instantiate_missing_parameter(self):
     """
     Ensure that a MissingParam exeception is raised when attempting to
     instantiate a template unit that contains a paremeter not present in
     the associated resource.
     """
     template = TemplateUnit({
         'template-resource': 'resource',
         'id': 'check-device-{missing}',
         'plugin': 'shell',
     })
     job = template.instantiate_one(Resource({
         'dev_name': 'sda1',
         'name': 'some device',
         'sys_path': '/sys/something',
     }))
     self.assertIsInstance(job, JobDefinition)
     with self.assertRaises(MissingParam):
         self.assertEqual(job.partial_id, 'check-device-sda1')
Esempio n. 31
0
 def test_setattr(self):
     res = Resource()
     res.attr = 'value'
     self.assertEqual(res.attr, 'value')
     res.attr = 'other value'
     self.assertEqual(res.attr, 'other value')