def test_non_match(self, regex, sequence): m = Matcher(regex) for i, symbol in enumerate(sequence): if i == len(sequence) - 1: assert m.match_symbol(symbol) is False else: assert m.match_symbol(symbol) is True
def test_incomplete_match(self): m = Matcher("foo bar+") assert not m.is_complete() assert m.match_symbol("foo") assert not m.is_complete() assert m.match_symbol("bar") assert m.is_complete() assert m.match_symbol("bar") assert m.is_complete()
def test_bad_parse_code(self): state = bytes_to_state( serialise_to_bytes(bitstream.ParseInfo(parse_code=0x11))) state["_generic_sequence_matcher"] = Matcher(".*") with pytest.raises(decoder.BadParseCode) as exc_info: decoder.parse_info(state) assert exc_info.value.parse_code == 0x11
def parse_parameters(state): """(11.2.1)""" state["major_version"] = read_uint(state) # (11.2.2) Check the major_version is at least 1. (It may need to be # higher depending on the features used on the sequence, but this will be # checked as the stream is processed. Later, at the end of the stream we'll # also check the major_version was not too high for the set of features # actually used.) ## Begin not in spec if state["major_version"] < MINIMUM_MAJOR_VERSION: raise MajorVersionTooLow(state["major_version"]) ## End not in spec state["minor_version"] = read_uint(state) # (11.2.2) Check the minor_version is 0. ## Begin not in spec if state["minor_version"] != 0: raise MinorVersionNotZero(state["minor_version"]) ## End not in spec state["profile"] = read_uint(state) # (C.2) Profile must be a supported value assert_in_enum(state["profile"], Profiles, BadProfile) ## Not in spec # (11.2.2) Profile must be supported by current version ## Begin not in spec minimum_required_version = profile_version_implication(state["profile"]) if state["major_version"] < minimum_required_version: raise ProfileNotSupportedByVersion(state["profile"], state["major_version"]) log_version_lower_bound(state, minimum_required_version) ## End not in spec state["level"] = read_uint(state) # (C.3) Level must be a supported value assert_in_enum(state["level"], Levels, BadLevel) ## Not in spec # (C.3) Levels may constrain the order and choice of data units in a # sequence. See the various level standards documents (e.g. ST 2042-2) for # details. ## Begin not in spec if "_level_sequence_matcher" not in state: state["_level_sequence_matcher"] = Matcher( LEVEL_SEQUENCE_RESTRICTIONS[state["level"]].sequence_restriction_regex ) # If we're at this point we're currently reading the first sequence # header (in the first data unit) of a sequence. Advance the state # machine accordingly. assert state["_level_sequence_matcher"].match_symbol("sequence_header") ## End not in spec # (C.3) Levels may constrain the allowed profiles and versions ## Begin not in spec assert_level_constraint(state, "level", state["level"]) assert_level_constraint(state, "profile", state["profile"]) assert_level_constraint(state, "major_version", state["major_version"]) assert_level_constraint(state, "minor_version", state["minor_version"])
def test_level_restricts_sequence(self): state = bytes_to_state( serialise_to_bytes( bitstream.ParseInfo( parse_code=tables.ParseCodes.end_of_sequence, ))) state["_generic_sequence_matcher"] = Matcher(".*") state["_level_sequence_matcher"] = Matcher("sequence_header") state["level"] = tables.Levels.unconstrained with pytest.raises(decoder.LevelInvalidSequence) as exc_info: decoder.parse_info(state) assert exc_info.value.parse_code is tables.ParseCodes.end_of_sequence assert exc_info.value.expected_parse_codes == [ tables.ParseCodes.sequence_header ] assert exc_info.value.expected_end is False assert exc_info.value.level == tables.Levels.unconstrained
def test_allowed_zero_next_parse_offset_for_pictures(self, parse_code): state = bytes_to_state( serialise_to_bytes( bitstream.ParseInfo( parse_code=parse_code, next_parse_offset=0, ))) state["major_version"] = 3 state["_generic_sequence_matcher"] = Matcher(".*") decoder.parse_info(state)
def test_never_allowed_invalid_offset(self, parse_code, next_parse_offset): state = bytes_to_state( serialise_to_bytes( bitstream.ParseInfo( parse_code=parse_code, next_parse_offset=next_parse_offset, ))) state["_generic_sequence_matcher"] = Matcher(".*") with pytest.raises(decoder.InvalidNextParseOffset) as exc_info: decoder.parse_info(state) assert exc_info.value.next_parse_offset == next_parse_offset
def test_not_allowed_zero_next_parse_offset_for_non_pictures( self, parse_code): state = bytes_to_state( serialise_to_bytes( bitstream.ParseInfo( parse_code=parse_code, next_parse_offset=0, ))) state["_generic_sequence_matcher"] = Matcher(".*") with pytest.raises(decoder.MissingNextParseOffset) as exc_info: decoder.parse_info(state) assert exc_info.value.parse_code == parse_code
def test_non_zero_previous_parse_offset_for_start_of_sequence(self): state = bytes_to_state( serialise_to_bytes( bitstream.ParseInfo( parse_code=tables.ParseCodes.end_of_sequence, previous_parse_offset=1, ))) state["_generic_sequence_matcher"] = Matcher(".*") with pytest.raises(decoder.NonZeroPreviousParseOffsetAtStartOfSequence ) as exc_info: decoder.parse_info(state) assert exc_info.value.previous_parse_offset == 1
def test_invalid_generic_sequence(self): state = bytes_to_state( serialise_to_bytes( bitstream.ParseInfo( parse_code=tables.ParseCodes.end_of_sequence, ))) state["_generic_sequence_matcher"] = Matcher("sequence_header") with pytest.raises(decoder.GenericInvalidSequence) as exc_info: decoder.parse_info(state) assert exc_info.value.parse_code is tables.ParseCodes.end_of_sequence assert exc_info.value.expected_parse_codes == [ tables.ParseCodes.sequence_header ] assert exc_info.value.expected_end is False
def test_parse_code_in_sequence(regex, expected_parse_codes, expected_end): m = Matcher(regex) class CustomException(Exception): def __init__( self, parse_code, actual_expected_parse_codes, actual_expected_end, foo ): assert parse_code is ParseCodes.auxiliary_data assert set(actual_expected_parse_codes) == expected_parse_codes assert actual_expected_end == expected_end assert foo == "bar" with pytest.raises(CustomException): assert_parse_code_in_sequence( ParseCodes.auxiliary_data, m, CustomException, "bar" )
def test_parse_code_sequence_ended(regex, expected_parse_codes): m = Matcher(regex) class CustomException(Exception): def __init__( self, parse_code, actual_expected_parse_codes, actual_expected_end, foo ): assert parse_code is None if expected_parse_codes is None: assert actual_expected_parse_codes is None else: assert set(actual_expected_parse_codes) == expected_parse_codes assert actual_expected_end is False assert foo == "bar" with pytest.raises(CustomException): assert_parse_code_sequence_ended(m, CustomException, "bar")
def test_profile_restricts_allowed_parse_codes(self, parse_code, allowed): state = bytes_to_state( serialise_to_bytes( bitstream.ParseInfo( parse_code=parse_code, next_parse_offset=tables.PARSE_INFO_HEADER_BYTES, ))) state["_generic_sequence_matcher"] = Matcher(".*") state["profile"] = tables.Profiles.high_quality if allowed: decoder.parse_info(state) else: with pytest.raises( decoder.ParseCodeNotAllowedInProfile) as exc_info: decoder.parse_info(state) assert exc_info.value.parse_code == tables.ParseCodes.low_delay_picture assert exc_info.value.profile == tables.Profiles.high_quality
def test_inconsistent_next_parse_offset(self): state = bytes_to_state( serialise_to_bytes( bitstream.ParseInfo( parse_code=tables.ParseCodes.padding_data, next_parse_offset=tables.PARSE_INFO_HEADER_BYTES + 10, )) + b"\x00" * 9 + serialise_to_bytes( bitstream.ParseInfo( parse_code=tables.ParseCodes.end_of_sequence, previous_parse_offset=tables.PARSE_INFO_HEADER_BYTES + 9, ))) state["_generic_sequence_matcher"] = Matcher(".*") decoder.parse_info(state) decoder.read_uint_lit(state, 9) with pytest.raises(decoder.InconsistentNextParseOffset) as exc_info: decoder.parse_info(state) assert exc_info.value.parse_info_offset == 0 assert exc_info.value.next_parse_offset == tables.PARSE_INFO_HEADER_BYTES + 10 assert exc_info.value.true_parse_offset == tables.PARSE_INFO_HEADER_BYTES + 9
def parse_sequence(state): """ (10.4.1) Parse a complete VC-2 sequence. """ reset_state(state) # (10.4.1) Check that the sequence starts with a sequence_header and ends # with and end_of_sequence. ## Begin not in spec state["_generic_sequence_matcher"] = Matcher("sequence_header .* end_of_sequence") ## End not in spec state["_num_pictures_in_sequence"] = 0 ## Not in spec state["_fragment_slices_remaining"] = 0 ## Not in spec parse_info(state) while not is_end_of_sequence(state): if is_seq_header(state): state["video_parameters"] = sequence_header(state) elif is_picture(state): # Errata: fragments under-specified in spec # # (14.2) Picture data units may not be interleaved with in-progress # fragmented pictures. ## Begin not in spec if state["_fragment_slices_remaining"] != 0: raise PictureInterleavedWithFragmentedPicture( state["_picture_initial_fragment_offset"], tell(state), state["fragment_slices_received"], state["_fragment_slices_remaining"], ) ## End not in spec picture_parse(state) picture_decode(state) elif is_fragment(state): fragment_parse(state) if state["fragmented_picture_done"]: picture_decode(state) elif is_auxiliary_data(state): auxiliary_data(state) elif is_padding_data(state): padding(state) parse_info(state) # (10.4.1) and (C.3) Check sequence structure allowed to end at this point ## Begin not in spec assert_parse_code_sequence_ended( state["_generic_sequence_matcher"], GenericInvalidSequence ) if "_level_sequence_matcher" in state: assert_parse_code_sequence_ended( state["_level_sequence_matcher"], LevelInvalidSequence, state["level"], ) ## End not in spec # Errata: fragments under-specified in spec # # (14.2) Ensure that any fragmented picture has been received completely by # the end of the stream. ## Begin not in spec if state["_fragment_slices_remaining"] != 0: raise SequenceContainsIncompleteFragmentedPicture( state["_picture_initial_fragment_offset"], state["fragment_slices_received"], state["_fragment_slices_remaining"], ) ## End not in spec # (10.4.3) When pictures are fields, a sequence should contain a whole # number of frames ## Begin not in spec if state["picture_coding_mode"] == PictureCodingModes.pictures_are_fields: if state["_num_pictures_in_sequence"] % 2 != 0: raise OddNumberOfFieldsInSequence(state["_num_pictures_in_sequence"]) ## End not in spec # (11.2.2) Check that the major_version number used was the lowest which # would be permissible for the features actually used assert_major_version_is_minimal(state) ## Not in spec
def test_valid_next_symbols(self, regex, next_symbols): m = Matcher(regex) assert m.valid_next_symbols() == next_symbols
def test_complete_matches(self, regex, sequence): m = Matcher(regex) for symbol in sequence: assert m.match_symbol(symbol) is True assert m.is_complete() is True