def get(self, file_name, attachment_name): # TODO(Kenadia): Implement the history item handler. The implementation # depends on the format used to store test records on disk. fn = os.path.join(self.history_path, file_name) if not os.path.isfile(fn): self.write("Not found") self.set_status(404) return with open(fn, mode="rb") as f: me = mfg_event_pb2.MfgEvent() me.ParseFromString(f.read()) # TODO - could use sha1 here to check? desired_real = [ a for a in me.attachment if a.name == attachment_name ] if len(desired_real) > 0: try: mimetype = next( k for k, v in test_runs_converter.MIMETYPE_MAP.items() if v == desired_real[0].type) except StopIteration: mimetype = "application/octet-stream" self.set_header("Content-Type", mimetype) self.write(desired_real[0].value_binary) self.set_status(200) else: self.write("some, but no match?!") self.set_status(404)
def test_copy_attachments_from_phase(self): first_attachment_name = 'first_attachment_name' first_attachment = _create_hacked_massive_attachment() expected_first_attachment_proto = mfg_event_pb2.EventAttachment( name=first_attachment_name, value_binary=first_attachment.data, type=test_runs_pb2.TEXT_UTF8) other_attachment_name = 'mock-attachment-name1' other_attachment = _create_hacked_massive_attachment() expected_other_attachment_proto = mfg_event_pb2.EventAttachment( name=other_attachment_name, value_binary=other_attachment.data, type=test_runs_pb2.TEXT_UTF8) phase = test_record.PhaseRecord( name='mock-phase-name', descriptor_id=1, codeinfo=self.create_codeinfo(), attachments={ first_attachment_name: first_attachment, other_attachment_name: other_attachment, }, ) mfg_event = mfg_event_pb2.MfgEvent() # Leave attachment_cache as None so attachment sizes are irrelevant as # partial uploads are considered unavailable. copier = mfg_event_converter.PhaseCopier([phase]) copier.copy_attachments(mfg_event) self.assertCountEqual( tuple(mfg_event.attachment), (expected_first_attachment_proto, expected_other_attachment_proto))
def test_copy_measurements_from_phase(self): measurement = (self._create_and_set_measurement( 'mock-measurement-name', 5).doc('mock measurement docstring').with_units( units.Unit('radian')).in_range(1, 10)) # We 'incorrectly' create a measurement with a unicode character as # a python2 string. We don't want mfg_event_converter to guess at it's # encoding but we also don't want to fail after a test on conversion so it # replaces errors with a unicode question mark character. measurement_text = self._create_and_set_measurement('text', b'\xfd') measurement_unicode = self._create_and_set_measurement( 'unicode', u'\ufffa') phase = test_record.PhaseRecord( name='mock-phase-name', descriptor_id=1, codeinfo=self.create_codeinfo(), measurements={ 'mock-measurement-name': measurement, 'text': measurement_text, 'unicode': measurement_unicode, }, ) mfg_event = mfg_event_pb2.MfgEvent() copier = mfg_event_converter.PhaseCopier([phase]) copier.copy_measurements(mfg_event) # Names. created_measurements = sorted(mfg_event.measurement, key=lambda m: m.name) mock_measurement = created_measurements[0] text_measurement = created_measurements[1] unicode_measurement = created_measurements[2] self.assertEqual(mock_measurement.name, u'mock-measurement-name') self.assertEqual(text_measurement.name, u'text') self.assertEqual(unicode_measurement.name, u'unicode') # Basic measurement fields. self.assertEqual(mock_measurement.status, test_runs_pb2.PASS) self.assertEqual(mock_measurement.description, 'mock measurement docstring') self.assertEqual(mock_measurement.parameter_tag[0], 'mock-phase-name') self.assertEqual(mock_measurement.unit_code, test_runs_pb2.Units.UnitCode.Value('RADIAN')) # Measurement value. self.assertEqual(mock_measurement.numeric_value, 5.0) # FFFD is unicode's '?'. This occurs when we can't easily convert a python2 # string to unicode. self.assertEqual(text_measurement.text_value, u'\ufffd') self.assertEqual(unicode_measurement.text_value, u'\ufffa') # Measurement validators. self.assertEqual(mock_measurement.numeric_minimum, 1.0) self.assertEqual(mock_measurement.numeric_maximum, 10.0) self.assertEqual(mock_measurement.name, u'mock-measurement-name') self.assertEqual(mock_measurement.name, u'mock-measurement-name')
def test_attach_record_as_json(self): record = test_record.TestRecord('mock-dut-id', 'mock-station-id') mfg_event = mfg_event_pb2.MfgEvent() mfg_event_converter._attach_record_as_json(mfg_event, record) self.assertEqual(mfg_event.attachment[0].name, 'OpenHTF_record.json') self.assertTrue(mfg_event.attachment[0].value_binary) # Assert truthy. self.assertEqual(mfg_event.attachment[0].type, test_runs_pb2.TEXT_UTF8)
def test_attach_config(self): record = test_record.TestRecord('mock-dut-id', 'mock-station-id', metadata={'config': {'key': 'value'}}) mfg_event = mfg_event_pb2.MfgEvent() mfg_event_converter._attach_config(mfg_event, record) self.assertEqual(mfg_event.attachment[0].name, 'config') self.assertTrue(mfg_event.attachment[0].value_binary) # Assert truthy. self.assertEqual(mfg_event.attachment[0].type, test_runs_pb2.TEXT_UTF8)
def mfg_event_from_test_record( record: htf_test_record.TestRecord, attachment_cache: Optional[AttachmentCacheT] = None, ) -> mfg_event_pb2.MfgEvent: """Convert an OpenHTF TestRecord to an MfgEvent proto. Most fields are copied over directly and some are pulled out of metadata (listed below). Multi-dimensional measurements are stored only in the JSON dump of the record. Important Note: This function mutates the test_record so any output callbacks called after this callback will operate on the mutated record. Metadata fields: test_name: The name field from the test's TestOptions. config: The OpenHTF config, as a dictionary. assembly_events: List of AssemblyEvent protos. (see proto/assembly_event.proto). operator_name: Name of the test operator. Args: record: An OpenHTF TestRecord. attachment_cache: Provides a lookup to get EventAttachment protos for already uploaded (or converted) attachments. Returns: An MfgEvent proto representing the given test record. """ mfg_event = mfg_event_pb2.MfgEvent() _populate_basic_data(mfg_event, record) _attach_record_as_json(mfg_event, record) _attach_argv(mfg_event) _attach_config(mfg_event, record) # Only include assembly events if the test passed. if ('assembly_events' in record.metadata and mfg_event.test_status == test_runs_pb2.PASS): for assembly_event in record.metadata['assembly_events']: mfg_event.assembly_events.add().CopyFrom(assembly_event) convert_multidim_measurements(record.phases) phase_copier = PhaseCopier(phase_uniquizer(record.phases), attachment_cache) phase_copier.copy_measurements(mfg_event) if not phase_copier.copy_attachments(mfg_event): mfg_event.test_run_type = mfg_event_pb2.TEST_RUN_PARTIAL return mfg_event
def testCopyAttachmentsFromPhase(self): attachment = test_record.Attachment('mock-data', 'text/plain') phase = test_record.PhaseRecord( name='mock-phase-name', descriptor_id=1, codeinfo=self.create_codeinfo(), attachments={'mock-attachment-name': attachment}, ) mfg_event = mfg_event_pb2.MfgEvent() copier = mfg_event_converter.PhaseCopier([phase]) copier.copy_attachments(mfg_event) self.assertEqual(mfg_event.attachment[0].name, 'mock-attachment-name') self.assertEqual(mfg_event.attachment[0].value_binary, b'mock-data') self.assertEqual(mfg_event.attachment[0].type, test_runs_pb2.TEXT_UTF8)
def test_copy_attachments_skips_if_too_much_data_and_returns_false(self): attachment_names = ('mock-attachment-name0', 'mock-attachment-name1') phase = test_record.PhaseRecord( name='mock-phase-name', descriptor_id=1, codeinfo=self.create_codeinfo(), attachments={ name: _create_hacked_massive_attachment() for name in attachment_names }, ) mfg_event = mfg_event_pb2.MfgEvent() copier = mfg_event_converter.PhaseCopier( [phase], attachment_cache={}, # Indicates partial uploads are available. ) self.assertFalse(copier.copy_attachments(mfg_event)) self.assertEqual(len(mfg_event.attachment), 1)
def test_copy_attachments_uses_attachment_cache_and_overcomes_size_limits( self): cached_attachment_name = 'cached_attachment_name' cached_attachment = _create_hacked_massive_attachment() cached_attachment_proto = mfg_event_pb2.EventAttachment( name='incorrect_name_to_ensure_cache_is_used', existing_blobref=mfg_event_pb2.EventAttachment.ExistingBlobRef( blob_id=b'dummy_id', size=cached_attachment.size)) other_attachment_name = 'mock-attachment-name1' other_attachment = _create_hacked_massive_attachment() expected_other_attachment_proto = mfg_event_pb2.EventAttachment( name=other_attachment_name, value_binary=other_attachment.data, type=test_runs_pb2.TEXT_UTF8) phase = test_record.PhaseRecord( name='mock-phase-name', descriptor_id=1, codeinfo=self.create_codeinfo(), attachments={ cached_attachment_name: cached_attachment, other_attachment_name: other_attachment, }, ) mfg_event = mfg_event_pb2.MfgEvent() copier = mfg_event_converter.PhaseCopier( [phase], attachment_cache={ mfg_event_converter.AttachmentCacheKey(name=cached_attachment_name, size=cached_attachment.size): cached_attachment_proto }) self.assertTrue(copier.copy_attachments(mfg_event)) self.assertCountEqual( tuple(mfg_event.attachment), (cached_attachment_proto, expected_other_attachment_proto))
def get(self, file_name): # TODO(Kenadia): Implement the history item handler. The implementation # depends on the format used to store test records on disk. # (Out of box disk format is fixed, only subclasses/alternative # implementations would need to concern themselves with that) fn = os.path.join(self.history_path, file_name) if not os.path.isfile(fn): self.write("Not found") self.set_status(404) return with open(fn, mode="rb") as f: me = mfg_event_pb2.MfgEvent() me.ParseFromString(f.read()) from openhtf.core.test_record import TestRecord tr: TestRecord = mfg_event_converter.test_record_from_mfg_event(me) test_record_dict = data.convert_to_base_types(tr) test_state_dict = _test_state_from_record( test_record_dict, StationPubSub._last_execution_uid) self.set_status(200) self.write(test_state_dict)
def test_populate_basic_data(self): outcome_details = test_record.OutcomeDetails( code='mock-code', description='mock-description', ) phase = test_record.PhaseRecord( name='mock-phase-name', descriptor_id=1, codeinfo=self.create_codeinfo(), start_time_millis=200, end_time_millis=400, ) log_record = test_logs.LogRecord( level=logging.INFO, logger_name='mock-logger-name', source='mock-source', lineno=123, timestamp_millis=300, message='mock-message', ) record = test_record.TestRecord( dut_id='mock-dut-id', station_id='mock-station-id', start_time_millis=100, end_time_millis=500, outcome=test_record.Outcome.PASS, outcome_details=[outcome_details], metadata={ 'test_name': 'mock-test-name', 'operator_name': 'mock-operator-name', 'test_version': 1.0, 'test_description': 'mock-test-description', }, phases=[phase], log_records=[log_record], ) mfg_event = mfg_event_pb2.MfgEvent() mfg_event_converter._populate_basic_data(mfg_event, record) self.assertEqual(mfg_event.dut_serial, 'mock-dut-id') self.assertEqual(mfg_event.start_time_ms, 100) self.assertEqual(mfg_event.end_time_ms, 500) self.assertEqual(mfg_event.tester_name, 'mock-station-id') self.assertEqual(mfg_event.test_name, 'mock-test-name') self.assertEqual(mfg_event.test_version, '1.0') self.assertEqual(mfg_event.test_description, 'mock-test-description') self.assertEqual(mfg_event.test_status, test_runs_pb2.PASS) # Phases. self.assertEqual(mfg_event.phases[0].name, 'mock-phase-name') self.assertEqual(mfg_event.phases[0].description, 'mock-sourcecode') self.assertEqual(mfg_event.phases[0].timing.start_time_millis, 200) self.assertEqual(mfg_event.phases[0].timing.end_time_millis, 400) # Failure codes. self.assertEqual(mfg_event.failure_codes[0].code, 'mock-code') self.assertEqual(mfg_event.failure_codes[0].details, 'mock-description') # Test logs. self.assertEqual(mfg_event.test_logs[0].timestamp_millis, 300) self.assertEqual(mfg_event.test_logs[0].log_message, 'mock-message') self.assertEqual(mfg_event.test_logs[0].logger_name, 'mock-logger-name') self.assertEqual(mfg_event.test_logs[0].levelno, logging.INFO) self.assertEqual(mfg_event.test_logs[0].level, test_runs_pb2.TestRunLogMessage.INFO) self.assertEqual(mfg_event.test_logs[0].log_source, 'mock-source') self.assertEqual(mfg_event.test_logs[0].lineno, 123)