def test_attachment_memory_safety(self): empty_attachment = test_record.Attachment('', 'text') expected_obj_size = _get_obj_size(empty_attachment) large_data = b'test attachment data' * 1000 attachment = test_record.Attachment(large_data, 'text') obj_size = _get_obj_size(attachment) self.assertEqual(obj_size, expected_obj_size)
def test_attachment_memory_safety(self): small_data = b' ' # Use non-empty so Attachment.size (ints) are equal size. empty_attachment = test_record.Attachment(small_data, 'text') expected_obj_size = _get_obj_size(empty_attachment) large_data = b'test attachment data' * 1000 attachment = test_record.Attachment(large_data, 'text') obj_size = _get_obj_size(attachment) self.assertEqual(obj_size, expected_obj_size)
def test_mfg_event_from_test_record(self): """Test for the full conversion flow.""" record = test_record.TestRecord( dut_id='dut_serial', start_time_millis=1, end_time_millis=1, station_id='localhost', outcome=test_record.Outcome.PASS, ) record.outcome = test_record.Outcome.PASS record.metadata = { 'assembly_events': [assembly_event_pb2.AssemblyEvent()] * 2, 'config': {'mock-config-key': 'mock-config-value'}, 'operator_name': 'mock-operator-name', } record.phases = [ test_record.PhaseRecord( name='phase-%d' % idx, descriptor_id=idx, codeinfo=test_record.CodeInfo.uncaptured(), result=None, attachments={}, start_time_millis=1, end_time_millis=1 ) for idx in range(1, 5) ] for phase in record.phases: phase.measurements = { 'meas-1': measurements.Measurement('meas-1'), 'meas-2': measurements.Measurement('meas-2'), 'meas-3': measurements.Measurement('meas-3').with_dimensions('V'), } phase.attachments = { 'attach-1': test_record.Attachment(data='data-1', mimetype=''), 'attach-2': test_record.Attachment(data='data-2', mimetype=''), } mfg_event = mfg_event_converter.mfg_event_from_test_record(record) self.assertEqual(mfg_event.dut_serial, record.dut_id) self.assertEqual(len(mfg_event.assembly_events), 2) self.assertEqual(len(mfg_event.measurement), 8) self.assertEqual(sorted(m.name for m in mfg_event.measurement), ['meas-1_0', 'meas-1_1', 'meas-1_2', 'meas-1_3', 'meas-2_0', 'meas-2_1', 'meas-2_2', 'meas-2_3']) self.assertEqual(len(mfg_event.attachment), 15) self.assertEqual(sorted(str(m.name) for m in mfg_event.attachment), ['OpenHTF_record.json', 'argv', 'attach-1_0', 'attach-1_1', 'attach-1_2', 'attach-1_3', 'attach-2_0', 'attach-2_1', 'attach-2_2', 'attach-2_3', 'config', 'multidim_meas-3_0', 'multidim_meas-3_1', 'multidim_meas-3_2', 'multidim_meas-3_3'])
def attach(self, name, data, mimetype=INFER_MIMETYPE): """Store the given data as an attachment with the given name. Args: name: Attachment name under which to store this data. data: Data to attach. mimetype: One of the following: INFER_MIMETYPE: The type will be guessed from the attachment name. None: The type will be left unspecified. A string: The type will be set to the specified value. Raises: DuplicateAttachmentError: Raised if there is already an attachment with the given name. ValueError: Raised if the name contains a period """ if '.' in name: raise ValueError('Attachment names cannot contain periods.') if name in self.phase_record.attachments: raise DuplicateAttachmentError('Duplicate attachment for %s' % name) if mimetype is INFER_MIMETYPE: mimetype = mimetypes.guess_type(name)[0] elif mimetype is not None and not mimetypes.guess_extension(mimetype): _LOG.debug('Unrecognized MIME type: "%s" for attachment "%s"', mimetype, name) self.phase_record.attachments[name] = test_record.Attachment( data, mimetype)
def attach(self, name: Text, binary_data: Union[Text, bytes], mimetype: MimetypeT = INFER_MIMETYPE) -> None: """Store the given binary_data as an attachment with the given name. Args: name: Attachment name under which to store this binary_data. binary_data: Data to attach. mimetype: One of the following: INFER_MIMETYPE - The type will be guessed from the attachment name. None - The type will be left unspecified. A string - The type will be set to the specified value. Raises: DuplicateAttachmentError: Raised if there is already an attachment with the given name. """ if name in self.phase_record.attachments: raise DuplicateAttachmentError('Duplicate attachment for %s' % name) if mimetype is INFER_MIMETYPE: mimetype = mimetypes.guess_type(name)[0] elif mimetype is not None and not mimetypes.guess_extension(mimetype): self.logger.warning( 'Unrecognized MIME type: "%s" for attachment "%s"', mimetype, name) attach_record = test_record.Attachment(binary_data, mimetype) self.phase_record.attachments[name] = attach_record self._cached['attachments'][name] = attach_record._asdict()
def multidim_measurement_to_attachment(name, measurement): """Convert a multi-dim measurement to an `openhtf.test_record.Attachment`.""" dimensions = list(measurement.dimensions) if measurement.units: dimensions.append( measurements.Dimension.from_unit_descriptor(measurement.units)) dims = [] for d in dimensions: if d.suffix is None: suffix = u'' else: suffix = six.ensure_text(d.suffix) dims.append({ 'uom_suffix': suffix, 'uom_code': d.code, 'name': d.name, }) # Refer to the module docstring for the expected schema. dimensioned_measured_value = measurement.measured_value value = (sorted(dimensioned_measured_value.value, key=lambda x: x[0]) if dimensioned_measured_value.is_value_set else None) outcome_str = _measurement_outcome_to_test_run_status_name( measurement.outcome, measurement.marginal) data = _convert_object_to_json({ 'outcome': outcome_str, 'name': name, 'dimensions': dims, 'value': value, }) attachment = htf_test_record.Attachment(data, test_runs_pb2.MULTIDIM_JSON) return attachment
def test_attachment_to_multidim_measurement(self): expected = self.create_multi_dim_measurement() attachment = test_record.Attachment(TEST_MULTIDIM_JSON, test_runs_pb2.MULTIDIM_JSON) measurement = mfg_event_converter.attachment_to_multidim_measurement( attachment) self.assertEqual(expected.measured_value.value, measurement.measured_value.value) for exp, act in zip(expected.dimensions, measurement.dimensions): self.assertEqual(exp, act)
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 attach(self, name, data, mimetype=None): """Store the given data as an attachment with the given name. Args: name: Attachment name under which to store this data. data: Data to attach. mimetype: If provided, will be saved in the attachment. Raises: DuplicateAttachmentError: Raised if there is already an attachment with the given name. """ if name in self.phase_record.attachments: raise DuplicateAttachmentError('Duplicate attachment for %s' % name) if mimetype and not mimetypes.guess_extension(mimetype): _LOG.warning('Unrecognized MIME type: "%s" for attachment "%s"', mimetype, name) self.phase_record.attachments[name] = test_record.Attachment(data, mimetype)
def test_reversibleish_leagcy_status_int(self): """Verfiy multidim -> attachment is reversible even on leagacy data. Older implementations would cast the outcome to an int instead of a string. We verify we can cast the saved int into correct outcome. """ mdim = self.create_multi_dim_measurement() attachment = mfg_event_converter.multidim_measurement_to_attachment( name='test_measurement_multidim', measurement=mdim) # Re-parse the data, edit the outcome field to a int, then reserialize. data_dict = json.loads(attachment.data) data_dict['outcome'] = test_runs_pb2.Status.Value(data_dict['outcome']) attachment = test_record.Attachment(json.dumps(data_dict), test_runs_pb2.MULTIDIM_JSON) reversed_mdim = mfg_event_converter.attachment_to_multidim_measurement( attachment) self.assert_same_mdim(mdim, reversed_mdim)
def multidim_measurement_to_attachment(name, measurement): """Convert a multi-dim measurement to an `openhtf.test_record.Attachment`.""" dimensions = list(measurement.dimensions) if measurement.units: dimensions.append( measurements.Dimension.from_unit_descriptor(measurement.units)) dims = [] for d in dimensions: if d.suffix is None: suffix = u'' # Ensure that the suffix is unicode. It's typically str/bytes because # units.py looks them up against str/bytes. elif isinstance(d.suffix, unicode): suffix = d.suffix else: suffix = d.suffix.decode('utf8') dims.append({ 'uom_suffix': suffix, 'uom_code': d.code, 'name': d.name, }) # Refer to the module docstring for the expected schema. dimensioned_measured_value = measurement.measured_value value = (sorted(dimensioned_measured_value.value, key=lambda x: x[0]) if dimensioned_measured_value.is_value_set else None) outcome_str = MEASUREMENT_OUTCOME_TO_TEST_RUN_STATUS_NAME[ measurement.outcome] data = _convert_object_to_json({ 'outcome': outcome_str, 'name': name, 'dimensions': dims, 'value': value, }) attachment = htf_test_record.Attachment(data, test_runs_pb2.MULTIDIM_JSON) return attachment
def test_attachment_size(self): expected_size = 10 data = b't' * expected_size attachment = test_record.Attachment(data, 'text') self.assertEqual(attachment.size, expected_size)
def test_attachment_data(self): expected_data = b'test attachment data' attachment = test_record.Attachment(expected_data, 'text') data = attachment.data self.assertEqual(data, expected_data)
def testStringFromAttachment_SuccessfullyConvertsPassMeasurement(self): attachment = test_record.Attachment('content', 'text/plain') self.assertEqual( text.StringFromAttachment(attachment, 'attachment_a.txt'), '| attachment: attachment_a.txt (mimetype=text/plain)')
def _create_hacked_massive_attachment() -> test_record.Attachment: """Returns an attachment that seems massive by size.""" attachment = test_record.Attachment(b'dummy', 'text/plain') attachment.size = mfg_event_converter.MAX_TOTAL_ATTACHMENT_BYTES return attachment