def test_report_eof(self, capsys, truncated_bitstream_fname):
        v = BitstreamViewer(truncated_bitstream_fname, verbose=1)
        assert v.run() == 3

        out, err = capsys.readouterr()
        assert out == (
            "                                                  +- sequences:\n"
            "                                                  | +- 0:\n"
            "                                                  | | +- data_units:\n"
            "                                                  | | | +- 0:\n"
            "                                                  | | | | +- parse_info:\n"
            "000000000000:                                     | | | | | +- padding: 0b\n"
            "000000000000: 01000010010000100100001101000100    | | | | | +- parse_info_prefix: Correct (0x42424344)\n"
            "000000000032: 00010000                            | | | | | +- parse_code: end_of_sequence (0x10)\n"
        )
        assert (
            re.match(
                r"000000000040: 0000000000000000                    <next 16 bits>\n"
                r".*: offset: 56\n"
                r".*: target: sequences: 0: data_units: 0: parse_info\n"
                r".*: error: reached the end of the file while parsing parse_info \(10.5.1\)\n",
                err,
            )
            is not None
        )
    def test_print_omitted_bits(self, capsys):
        v = BitstreamViewer(None)

        v._last_displayed_tell = (1, 7)

        v._print_omitted_bits((2, 3))
        assert capsys.readouterr().out == (
            "000000000008: <12 bits omitted>                   ...\n"
        )
 def test_clear_previous_data_units(self, padding_sequence_bitstream_fname):
     v = BitstreamViewer(
         padding_sequence_bitstream_fname,
         shown_pseudocode_names=["padding"],
     )
     assert v.run() == 0
     assert len(v._serdes.context["sequences"][0]["data_units"]) == 2
     assert v._serdes.context["sequences"][0]["data_units"][0] is None
     assert v._serdes.context["sequences"][0]["data_units"][1] is not None
    def test_report_parse_errors(self, capsys, missing_sequence_header_bitstream_fname):
        v = BitstreamViewer(
            missing_sequence_header_bitstream_fname,
        )
        assert v.run() == 4

        out, err = capsys.readouterr()
        assert err.endswith(
            ": error: transform_parameters (12.4.1) failed to parse bitstream (KeyError: 'major_version') (missing sequence_header, fragment or earlier out of range value?)\n"
        )
    def test_print_internal_state(self, capsys):
        v = BitstreamViewer(None)

        v._state = State(major_version=3, minor_version=0)

        v._print_internal_state()
        assert capsys.readouterr().out == (
            "----------------------------------------------\n"
            "State:\n"
            "  major_version: 3\n"
            "  minor_version: 0\n"
            "----------------------------------------------\n"
        )
    def test_from_to_display(self, capsys, minimal_sequence_bitstream_fname, kwargs):
        v = BitstreamViewer(minimal_sequence_bitstream_fname, **kwargs)
        assert v.run() == 0

        # Should include whole data structure and end as expected.
        assert capsys.readouterr().out == (
            "000000000000: <32 bits omitted>                   ...\n"
            "                                                  +- sequences:\n"
            "                                                  | +- 0:\n"
            "                                                  | | +- data_units:\n"
            "                                                  | | | +- 0:\n"
            "                                                  | | | | +- parse_info:\n"
            "000000000032: 00010000                            | | | | | +- parse_code: end_of_sequence (0x10)\n"
            "000000000040: 00000000000000000000000000000000    | | | | | +- next_parse_offset: 0\n"
        )
    def test_filtered_display_hide(self, capsys, padding_sequence_bitstream_fname):
        v = BitstreamViewer(
            padding_sequence_bitstream_fname,
            hidden_pseudocode_names=["parse_info"],
        )
        assert v.run() == 0

        assert capsys.readouterr().out == (
            "000000000000: <104 bits omitted>                  ...\n"
            "                                                  +- sequences:\n"
            "                                                  | +- 0:\n"
            "                                                  | | +- data_units:\n"
            "                                                  | | | +- 0:\n"
            "                                                  | | | | +- padding:\n"
            "000000000104: 1010101011111111                    | | | | | +- bytes: 0xAA_FF\n"
            "000000000120: <104 bits omitted>                  ...\n"
        )
    def test_normal_display(self, capsys, minimal_sequence_bitstream_fname, kwargs):
        v = BitstreamViewer(minimal_sequence_bitstream_fname, **kwargs)
        assert v.run() == 0

        # Should include whole data structure and end as expected.
        assert capsys.readouterr().out == (
            "                                                  +- sequences:\n"
            "                                                  | +- 0:\n"
            "                                                  | | +- data_units:\n"
            "                                                  | | | +- 0:\n"
            "                                                  | | | | +- parse_info:\n"
            "000000000000:                                     | | | | | +- padding: 0b\n"
            "000000000000: 01000010010000100100001101000100    | | | | | +- parse_info_prefix: Correct (0x42424344)\n"
            "000000000032: 00010000                            | | | | | +- parse_code: end_of_sequence (0x10)\n"
            "000000000040: 00000000000000000000000000000000    | | | | | +- next_parse_offset: 0\n"
            "000000000072: 00000000000000000000000000000000    | | | | | +- previous_parse_offset: 0\n"
        )
    def test_show_internal_state(self, capsys, padding_sequence_bitstream_fname):
        v = BitstreamViewer(padding_sequence_bitstream_fname, show_internal_state=True)
        assert v.run() == 0

        assert capsys.readouterr().out == (
            "                                                  +- sequences:\n"
            "                                                  | +- 0:\n"
            "                                                  | | +- data_units:\n"
            "                                                  | | | +- 0:\n"
            "                                                  | | | | +- parse_info:\n"
            "000000000000:                                     | | | | | +- padding: 0b\n"
            "000000000000: 01000010010000100100001101000100    | | | | | +- parse_info_prefix: Correct (0x42424344)\n"
            "000000000032: 00110000                            | | | | | +- parse_code: padding_data (0x30)\n"
            "000000000040: 00000000000000000000000000001111    | | | | | +- next_parse_offset: 15\n"
            "000000000072: 00000000000000000000000000000000    | | | | | +- previous_parse_offset: 0\n"
            "                                                  | | | | +- padding:\n"
            "000000000104: 1010101011111111                    | | | | | +- bytes: 0xAA_FF\n"
            "----------------------------------------------\n"
            "State:\n"
            "  parse_code: padding_data (0x30)\n"
            "  next_parse_offset: 15\n"
            "  previous_parse_offset: 0\n"
            "----------------------------------------------\n"
            "                                                  | | | +- 1:\n"
            "                                                  | | | | +- parse_info:\n"
            "000000000120:                                     | | | | | +- padding: 0b\n"
            "000000000120: 01000010010000100100001101000100    | | | | | +- parse_info_prefix: Correct (0x42424344)\n"
            "000000000152: 00010000                            | | | | | +- parse_code: end_of_sequence (0x10)\n"
            "000000000160: 00000000000000000000000000000000    | | | | | +- next_parse_offset: 0\n"
            "000000000192: 00000000000000000000000000000000    | | | | | +- previous_parse_offset: 0\n"
            "----------------------------------------------\n"
            "State:\n"
            "  parse_code: end_of_sequence (0x10)\n"
            "  next_parse_offset: 0\n"
            "  previous_parse_offset: 0\n"
            "----------------------------------------------\n"
        )
    def print_error_with_mock_state(self, verbosity, message):
        v = BitstreamViewer(None, verbose=verbosity)

        v._last_tell = (1, 7)

        v._reader = bitstream.BitstreamReader(BytesIO(b"\xFF" + b"\xAA" * 16 + b"\x00"))
        v._reader.seek(1, 3)

        v._serdes = Mock(path=Mock(return_value=["foo", "bar"]))

        try:
            raise KeyError("missing")
        except KeyError:
            v._print_error(message)
    def test_show_hide_sets(self, shown, hidden, exp_shown, exp_hidden):
        v = BitstreamViewer(
            None,
            shown_pseudocode_names=shown,
            hidden_pseudocode_names=hidden,
        )

        if exp_shown is None:
            assert v._shown_types is None
        else:
            assert v._shown_types == exp_shown

        if exp_hidden is None:
            assert v._hidden_types is None
        else:
            assert v._hidden_types == exp_hidden
    def test_check_parse_info_prefix(
        self, capsys, bad_parse_info_prefix_bitstream_fname
    ):
        # Checking enabled: should stop on bad prefix
        v = BitstreamViewer(
            bad_parse_info_prefix_bitstream_fname, check_parse_info_prefix=True
        )
        assert v.run() == 2

        out, err = capsys.readouterr()
        assert out == (
            "                                                  +- sequences:\n"
            "                                                  | +- 0:\n"
            "                                                  | | +- data_units:\n"
            "                                                  | | | +- 0:\n"
            "                                                  | | | | +- parse_info:\n"
            "000000000000:                                     | | | | | +- padding: 0b\n"
            "000000000000: 11011110101011011011111011101111    | | | | | +- parse_info_prefix: INCORRECT (0xDEADBEEF)\n"
        )
        assert err.endswith(": error: invalid parse_info prefix (0xDEADBEEF)\n")

        # Disable checking should result in non-crashing run
        v = BitstreamViewer(
            bad_parse_info_prefix_bitstream_fname, check_parse_info_prefix=False
        )
        assert v.run() == 0
        out, err = capsys.readouterr()
        assert out == (
            "                                                  +- sequences:\n"
            "                                                  | +- 0:\n"
            "                                                  | | +- data_units:\n"
            "                                                  | | | +- 0:\n"
            "                                                  | | | | +- parse_info:\n"
            "000000000000:                                     | | | | | +- padding: 0b\n"
            "000000000000: 11011110101011011011111011101111    | | | | | +- parse_info_prefix: INCORRECT (0xDEADBEEF)\n"
            "000000000032: 00010000                            | | | | | +- parse_code: end_of_sequence (0x10)\n"
            "000000000040: 00000000000000000000000000000000    | | | | | +- next_parse_offset: 0\n"
            "000000000072: 00000000000000000000000000000000    | | | | | +- previous_parse_offset: 0\n"
        )
        assert err == ""
    def test_print_value(self, capsys):
        v = BitstreamViewer(None)

        v._reader = Mock()
        v._reader.bits_remaining = None

        v._serdes = Mock()
        v._serdes.path.return_value = ["foo", "parse_info", "parse_code"]
        v._serdes.cur_context = bitstream.ParseInfo(
            parse_info_prefix=tables.PARSE_INFO_PREFIX,
            parse_code=0x10,
            next_parse_offset=123,
            previous_parse_offset=0,
        )

        # Test that:
        # * First time, the whole path should be printed
        # * Formatted value should be shown
        v._print_value(123, bitarray("1010"), "parse_code", 0x10)
        assert capsys.readouterr().out == (
            "                                                  +- foo:\n"
            "                                                  | +- parse_info:\n"
            "000000000123: 1010                                | | +- parse_code: end_of_sequence (0x10)\n"
        )

        # Test that when only the tail of the the path changes, no headers are
        # repeated.
        v._serdes.path.return_value = ["foo", "parse_info", "next_parse_offset"]
        v._print_value(123, bitarray("1010"), "next_parse_offset", 123)
        assert capsys.readouterr().out == (
            "000000000123: 1010                                | | +- next_parse_offset: 123\n"
        )

        # Test that:
        # * When the path changes only the minimum set of changes are shown
        #   ordinary dict values can be printed
        # * Ordinary dict values are printed using 'str'
        v._serdes.path.return_value = ["foo", "bar", "baz", "qux"]
        v._serdes.cur_context = {"qux": 321}  # An ordinary dict
        v._print_value(123, bitarray("1010"), "qux", 321)
        assert capsys.readouterr().out == (
            "                                                  | +- bar:\n"
            "                                                  | | +- baz:\n"
            "000000000123: 1010                                | | | +- qux: 321\n"
        )

        # Test that when changing only an intermediate path entry, the whole
        # subtree is shown
        v._serdes.path.return_value = ["foo", "BAR", "baz", "qux"]
        v._print_value(123, bitarray("1010"), "qux", 321)
        assert capsys.readouterr().out == (
            "                                                  | +- BAR:\n"
            "                                                  | | +- baz:\n"
            "000000000123: 1010                                | | | +- qux: 321\n"
        )

        # Test that:
        # * Array values are formatted appropriately
        # * Paths can become less deeply nested
        v._serdes.path.return_value = ["slice", "y_transform", 1]
        v._serdes.cur_context = bitstream.LDSlice(y_transform=[0, 123])
        v._print_value(123, bitarray("1010"), "y_transform", 123)
        assert capsys.readouterr().out == (
            "                                                  +- slice:\n"
            "                                                  | +- y_transform:\n"
            "000000000123: 1010                                | | +- 1: 123\n"
        )
 def test_print_error_no_context(self, verbosity, capsys):
     # Check that error messages can be printed at all levels of verbosity
     # even when nothing is opened and no traceback has ocurred
     v = BitstreamViewer(None, verbose=verbosity)
     v._print_error("foobar")
     assert capsys.readouterr().err.endswith(": error: foobar\n")