def setUp(self): self.A = make_job('a', name='A') self.B = make_job('b', name='B', plugin='local', description='foo') self.C = make_job('c', name='C') self.D = self.B.create_child_job_from_record( RFC822Record(data={ 'id': 'd', 'name': 'D', 'plugin': 'shell' }, origin=Origin(source=JobOutputTextSource(self.B), line_start=1, line_end=1))) self.E = self.B.create_child_job_from_record( RFC822Record(data={ 'id': 'e', 'name': 'E', 'plugin': 'shell' }, origin=Origin(source=JobOutputTextSource(self.B), line_start=1, line_end=1))) self.F = make_job('f', name='F', plugin='resource', description='baz') self.tree = SelectableJobTreeNode.create_tree( [self.A, self.B, self.C, self.D, self.E, self.F], legacy_mode=True)
def setUp(self): A = make_job('A') B = make_job('B', plugin='local', description='foo') C = make_job('C') D = B.create_child_job_from_record( RFC822Record(data={ 'id': 'D', 'plugin': 'shell' }, origin=Origin(source=JobOutputTextSource(B), line_start=1, line_end=1))) E = B.create_child_job_from_record( RFC822Record(data={ 'id': 'E', 'plugin': 'local', 'description': 'bar' }, origin=Origin(source=JobOutputTextSource(B), line_start=1, line_end=1))) F = E.create_child_job_from_record( RFC822Record(data={ 'id': 'F', 'plugin': 'shell' }, origin=Origin(source=JobOutputTextSource(E), line_start=1, line_end=1))) G = make_job('G', plugin='local', description='baz') R = make_job('R', plugin='resource') Z = make_job('Z', plugin='local', description='zaz') self.tree = JobTreeNode.create_tree([R, B, C, D, E, F, G, A, Z], legacy_mode=True)
def test_relative_to(self): """ verify how Origin.relative_to() works in various situations """ # if the source does not have relative_to method, nothing is changed origin = Origin(UnknownTextSource(), 1, 2) self.assertIs(origin.relative_to("/some/path"), origin) # otherwise the source is replaced and a new origin is returned self.assertEqual( Origin(FileTextSource("/some/path/file.txt"), 1, 2).relative_to("/some/path"), Origin(FileTextSource("file.txt"), 1, 2))
def test_relative_to(self): """ verify how Origin.relative_to() works in various situations """ # if the source does not have relative_to method, nothing is changed origin = Origin(UnknownTextSource(), 1, 2) self.assertIs(origin.relative_to("/some/path"), origin) # otherwise the source is replaced and a new origin is returned self.assertEqual( Origin( FileTextSource("/some/path/file.txt"), 1, 2 ).relative_to("/some/path"), Origin(FileTextSource("file.txt"), 1, 2))
def test_origin_caller(self): """ verify that Origin.get_caller_origin() uses PythonFileTextSource as the origin.source attribute. """ self.assertIsInstance(Origin.get_caller_origin().source, PythonFileTextSource)
def test_origin_caller(self): """ verify that Origin.get_caller_origin() uses PythonFileTextSource as the origin.source attribute. """ self.assertIsInstance( Origin.get_caller_origin().source, PythonFileTextSource)
def test_via_does_not_change_checksum(self): """ verify that the 'via' attribute in no way influences job checksum """ # Create a 'parent' job parent = JobDefinition({'id': 'parent', 'plugin': 'local'}) # Create a 'child' job, using create_child_job_from_record() should # time the two so that child.via should be parent.checksum. # # The elaborate record that gets passed has all the meta-data that # traces back to the 'parent' job (as well as some imaginary line_start # and line_end values for the purpose of the test). child = parent.create_child_job_from_record( RFC822Record(data={ 'id': 'test', 'plugin': 'shell' }, origin=Origin(source=JobOutputTextSource(parent), line_start=1, line_end=1))) # Now 'child.via' should be the same as 'parent.checksum' self.assertEqual(child.via, parent.checksum) # Create an unrelated job 'helper' with the definition identical as # 'child' but without any ties to the 'parent' job helper = JobDefinition({'id': 'test', 'plugin': 'shell'}) # And again, child.checksum should be the same as helper.checksum self.assertEqual(child.checksum, helper.checksum)
def from_string(cls, text, *, filename=None, name=None, origin=None, implicit_namespace=None): """ Load and initialize the WhiteList object from the specified string. :param text: full text of the whitelist :param filename: (optional, keyword-only) filename from which text was read from. This simulates a call to :meth:`from_file()` which properly computes the name and origin of the whitelist. :param name: (optional) name of the whitelist, only used if filename is not specified. :param origin: (optional) origin of the whitelist, only used if a filename is not specified. If omitted a default origin value will be constructed out of UnknownTextSource instance :param implicit_namespace: (optional) implicit namespace for jobs that are using partial identifiers (all jobs) :returns: a fresh WhiteList object The optional filename or a pair of name and origin arguments may be provided in order to have additional meta-data. This is typically needed when the :meth:`from_file()` method cannot be used as the caller already has the full text of the intended file available. """ _logger.debug("Loaded whitelist from %r", filename) pattern_list, max_lineno = cls._parse_patterns(text) # generate name and origin if filename is provided if filename is not None: name = WhiteList.name_from_filename(filename) origin = Origin(FileTextSource(filename), 1, max_lineno) else: # otherwise generate origin if it's not specified if origin is None: origin = Origin(UnknownTextSource(), 1, max_lineno) return cls(pattern_list, name, origin, implicit_namespace)
def test_origin_source_filename_is_correct(self): """ verify that make_job() can properly trace the filename of the python module that called make_job() """ # Pass -1 to get_caller_origin() to have filename point at this file # instead of at whatever ends up calling the test method self.assertEqual( os.path.basename(Origin.get_caller_origin(-1).source.filename), "test_rfc822.py")
def test_parse_typical(self): """ verify typical operation without any parsing errors """ # Setup a mock job and result, give some io log to the result job = mock.Mock(spec=JobDefinition) result = mock.Mock(spec=IJobResult) result.get_io_log.return_value = [(0, 'stdout', b'attr: value1\n'), (0, 'stdout', b'\n'), (0, 'stdout', b'attr: value2\n')] # Parse the IO log records records = list(gen_rfc822_records_from_io_log(job, result)) # Ensure that we saw both records self.assertEqual(records, [ RFC822Record({'attr': 'value1'}, Origin(JobOutputTextSource(job), 1, 1)), RFC822Record({'attr': 'value2'}, Origin(JobOutputTextSource(job), 3, 3)), ])
def test_from_string__with_name_and_origin(self): """ verify that WhiteList.from_string() works when passing name and origin """ # construct a whitelist with some dummy data, the names, pathnames and # line ranges are arbitrary whitelist = WhiteList.from_string("\n".join(self._content), name="somefile", origin=Origin( FileTextSource("somefile.txt"), 1, 3)) # verify that the patterns are okay self.assertEqual(repr(whitelist.qualifier_list[0]), "RegExpJobQualifier('^foo$', inclusive=True)") # verify that whitelist name is copied self.assertEqual(whitelist.name, "somefile") # verify that the origin is copied self.assertEqual(whitelist.origin, Origin(FileTextSource("somefile.txt"), 1, 3))
def test_job_data(self): """ verify the contents of the loaded JobDefinition object """ job = self.plugin.plugin_object[0] self.assertEqual(job.partial_id, "test/job") self.assertEqual(job.id, "2013.com.canonical.plainbox::test/job") self.assertEqual(job.plugin, "shell") self.assertEqual(job.command, "true") self.assertEqual(job.origin, Origin(FileTextSource("/path/to/jobs.txt"), 1, 3))
def test_origin_from_filename_is_filename(self): # If the test's origin has a filename, we need a valid origin # with proper data. # We're faking the name by using a StringIO subclass with a # name property, which is how rfc822 gets that data. expected_origin = Origin(FileTextSource("file.txt"), 1, 1) with NamedStringIO("key:value", fake_filename="file.txt") as stream: records = type(self).loader(stream) self.assertEqual(len(records), 1) self.assertEqual(records[0].data, {'key': 'value'}) self.assertEqual(records[0].origin, expected_origin)
def test_gt(self): """ verify that Origin instances are ordered by their constituting components """ self.assertTrue( Origin(FileTextSource('file.txt'), 1, 1) < Origin( FileTextSource('file.txt'), 1, 2) < Origin( FileTextSource('file.txt'), 1, 3)) self.assertTrue( Origin(FileTextSource('file.txt'), 1, 10) < Origin( FileTextSource('file.txt'), 2, 10) < Origin( FileTextSource('file.txt'), 3, 10)) self.assertTrue( Origin(FileTextSource('file1.txt'), 1, 10) < Origin( FileTextSource('file2.txt'), 1, 10) < Origin( FileTextSource('file3.txt'), 1, 10))
def test_whitelist_data(self): """ verify the contents of the loaded whitelist object """ self.assertEqual( self.plugin.plugin_object.qualifier_list[0].pattern_text, "^foo$") self.assertEqual( self.plugin.plugin_object.qualifier_list[1].pattern_text, "^bar$") self.assertEqual(self.plugin.plugin_object.name, 'some') self.assertEqual( self.plugin.plugin_object.origin, Origin(FileTextSource('/path/to/some.whitelist'), 1, 2))
def test_from_string(self): """ verify that WhiteList.from_string() works """ whitelist = WhiteList.from_string("\n".join(self._content)) # verify that the patterns are okay self.assertEqual(repr(whitelist.qualifier_list[0]), "RegExpJobQualifier('^foo$', inclusive=True)") # verify that whitelist name is the empty default self.assertEqual(whitelist.name, None) # verify that the origin got set to the default constructed value self.assertEqual(whitelist.origin, Origin(UnknownTextSource(), 1, 3))
def __init__(self, data, origin=None, provider=None, controller=None): super(JobDefinition, self).__init__(data) if origin is None: origin = Origin.get_caller_origin() if controller is None: # XXX: moved here because of cyclic imports from plainbox.impl.ctrl import checkbox_session_state_ctrl controller = checkbox_session_state_ctrl self._resource_program = None self._origin = origin self._provider = provider self._controller = controller
def test_origin_from_stream_is_Unknown(self): """ verify that gen_rfc822_records() uses origin instances with source equal to UnknownTextSource, when no explicit source is provided and the stream has no name to infer a FileTextSource() from. """ expected_origin = Origin(UnknownTextSource(), 1, 1) with StringIO("key:value") as stream: records = type(self).loader(stream) self.assertEqual(len(records), 1) self.assertEqual(records[0].data, {'key': 'value'}) self.assertEqual(records[0].origin, expected_origin)
def test_from_file(self): """ verify that WhiteList.from_file() works """ with self.mocked_file(self._name, self._content): whitelist = WhiteList.from_file(self._name) # verify that the patterns are okay self.assertEqual(repr(whitelist.qualifier_list[0]), "RegExpJobQualifier('^foo$', inclusive=True)") # verify that whitelist name got set self.assertEqual(whitelist.name, "whitelist") # verify that the origin got set self.assertEqual(whitelist.origin, Origin(FileTextSource("whitelist.txt"), 1, 3))
def test_eq(self): """ verify instances of Origin are all equal to other instances with the same instance attributes but not equal to instances with different attributes """ origin1 = Origin(self.origin.source, self.origin.line_start, self.origin.line_end) origin2 = Origin(self.origin.source, self.origin.line_start, self.origin.line_end) self.assertTrue(origin1 == origin2) origin_other1 = Origin(self.origin.source, self.origin.line_start + 1, self.origin.line_end) self.assertTrue(origin1 != origin_other1) self.assertFalse(origin1 == origin_other1) origin_other2 = Origin(self.origin.source, self.origin.line_start, self.origin.line_end + 1) self.assertTrue(origin1 != origin_other2) self.assertFalse(origin1 == origin_other2) origin_other3 = Origin(FileTextSource("unrelated"), self.origin.line_start, self.origin.line_end) self.assertTrue(origin1 != origin_other3) self.assertFalse(origin1 == origin_other3)
def make_job(id, plugin="dummy", requires=None, depends=None, **kwargs): """ Make and return a dummy JobDefinition instance """ data = {'id': id} if plugin is not None: data['plugin'] = plugin if requires is not None: data['requires'] = requires if depends is not None: data['depends'] = depends # Add any custom key-value properties data.update(kwargs) return JobDefinition(data, Origin.get_caller_origin())
def setUp(self): self._full_record = RFC822Record( { 'plugin': 'plugin', 'id': 'id', 'summary': 'summary', 'requires': 'requires', 'command': 'command', 'description': 'description' }, Origin(FileTextSource('file.txt'), 1, 5)) self._full_gettext_record = RFC822Record( { '_plugin': 'plugin', '_id': 'id', '_summary': 'summary', '_requires': 'requires', '_command': 'command', '_description': 'description' }, Origin(FileTextSource('file.txt.in'), 1, 5)) self._min_record = RFC822Record({ 'plugin': 'plugin', 'id': 'id', }, Origin(FileTextSource('file.txt'), 1, 2))
def from_file(cls, pathname, implicit_namespace=None): """ Load and initialize the WhiteList object from the specified file. :param pathname: file to load :param implicit_namespace: (optional) implicit namespace for jobs that are using partial identifiers (all jobs) :returns: a fresh WhiteList object """ pattern_list, max_lineno = cls._load_patterns(pathname) name = os.path.splitext(os.path.basename(pathname))[0] origin = Origin(FileTextSource(pathname), 1, max_lineno) return cls(pattern_list, name, origin, implicit_namespace)
def test_parse_error(self, mock_logger): # Setup a mock job and result, give some io log to the result job = mock.Mock(spec=JobDefinition) result = mock.Mock(spec=IJobResult) result.get_io_log.return_value = [(0, 'stdout', b'attr: value1\n'), (0, 'stdout', b'\n'), (0, 'stdout', b'error\n'), (0, 'stdout', b'\n'), (0, 'stdout', b'attr: value2\n')] # Parse the IO log records records = list(gen_rfc822_records_from_io_log(job, result)) # Ensure that only the first record was generated self.assertEqual(records, [ RFC822Record({'attr': 'value1'}, Origin(JobOutputTextSource(job), 1, 1)), ]) # Ensure that a warning was logged mock_logger.warning.assert_called_once_with( "local script %s returned invalid RFC822 data: %s", job, RFC822SyntaxError(None, 3, "Unexpected non-empty line: 'error\\n'"))
def __init__(self, data, origin=None, provider=None, controller=None, raw_data=None): """ Initialize a new JobDefinition instance. :param data: Normalized data that makes up this job definition :param origin: An (optional) Origin object. If omitted a fake origin object is created. Normally the origin object should be obtained from the RFC822Record object. :param provider: An (optional) Provider1 object. If omitted it defaults to None but the actual job definition is not suitable for execution. All job definitions are expected to have a provider. :param controller: An (optional) session state controller. If omitted a checkbox session state controller is implicitly used. The controller defines how this job influences the session it executes in. :param raw_data: An (optional) raw version of data, without whitespace normalization. If omitted then raw_data is assumed to be data. .. note:: You should almost always use :meth:`from_rfc822_record()` instead. """ super(JobDefinition, self).__init__(data, raw_data) if origin is None: origin = Origin.get_caller_origin() if controller is None: # XXX: moved here because of cyclic imports from plainbox.impl.ctrl import checkbox_session_state_ctrl controller = checkbox_session_state_ctrl self._resource_program = None self._origin = origin self._provider = provider self._controller = controller
def setUp(self): self.origin = Origin(FileTextSource("file.txt"), 10, 12)
def setUp(self): self.raw_data = {'key': ' value'} self.data = {'key': 'value'} self.origin = Origin(FileTextSource('file.txt'), 1, 1) self.record = RFC822Record(self.data, self.origin, self.raw_data)