1#
2# diffoscope: in-depth comparison of files, archives, and directories
3#
4# Copyright © 2015 Jérémy Bobbio <lunar@debian.org>
5# Copyright © 2016-2017, 2019-2020 Chris Lamb <lamby@debian.org>
6#
7# diffoscope is free software: you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation, either version 3 of the License, or
10# (at your option) any later version.
11#
12# diffoscope is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with diffoscope.  If not, see <https://www.gnu.org/licenses/>.
19
20import struct
21import pytest
22import re
23import subprocess
24
25from diffoscope.comparators.cbfs import CbfsFile
26from diffoscope.comparators.binary import FilesystemFile
27from diffoscope.comparators.utils.specialize import specialize
28
29from ..utils.data import data, get_data
30from ..utils.tools import skip_unless_tools_exist
31from ..utils.nonexisting import assert_non_existing
32
33
34TEST_FILE1_PATH = data("text_ascii1")
35TEST_FILE2_PATH = data("text_ascii2")
36
37
38@pytest.fixture
39def rom1(tmpdir):
40    path = str(tmpdir.join("coreboot1"))
41
42    subprocess.check_call(
43        ("cbfstool", path, "create", "-m", "x86", "-s", "32768")
44    )
45
46    subprocess.check_call(
47        (
48            "cbfstool",
49            path,
50            "add",
51            "-f",
52            TEST_FILE1_PATH,
53            "-n",
54            "test file",
55            "-t",
56            "raw",
57        ),
58    )
59
60    return specialize(FilesystemFile(path))
61
62
63@pytest.fixture
64def rom2(tmpdir):
65    size = 32768
66    path = str(tmpdir.join("coreboot2.rom"))
67
68    subprocess.check_call(
69        ("cbfstool", path, "create", "-m", "x86", "-s", "%s" % size),
70    )
71
72    subprocess.check_call(
73        (
74            "cbfstool",
75            path,
76            "add",
77            "-f",
78            TEST_FILE2_PATH,
79            "-n",
80            "test file",
81            "-t",
82            "raw",
83        ),
84        shell=False,
85    )
86
87    # Remove the last 4 bytes to exercise the full header search
88    buf = bytearray(size)
89    with open(path, "rb") as f:
90        f.readinto(buf)
91
92    with open(path, "wb") as f:
93        size = struct.unpack_from("!I", buf, offset=len(buf) - 4 - 32 + 8)[0]
94        struct.pack_into("!I", buf, len(buf) - 4 - 32 + 8, size - 4)
95        f.write(buf[:-4])
96
97    return specialize(FilesystemFile(path))
98
99
100@skip_unless_tools_exist("cbfstool")
101def test_identification_using_offset(rom1):
102    assert isinstance(rom1, CbfsFile)
103
104
105@skip_unless_tools_exist("cbfstool")
106def test_identification_without_offset(rom2):
107    assert isinstance(rom2, CbfsFile)
108
109
110@skip_unless_tools_exist("cbfstool")
111def test_no_differences(rom1):
112    difference = rom1.compare(rom1)
113    assert difference is None
114
115
116@pytest.fixture
117def differences(rom1, rom2):
118    difference = rom1.compare(rom2)
119    return difference.details
120
121
122@skip_unless_tools_exist("cbfstool")
123def test_listing(differences):
124    # Compares outputs of: "cbfstool $tmpdir/coreboot*.rom print"
125    #
126    # As the output of this command keeps changing slightly (see
127    # https://salsa.debian.org/reproducible-builds/diffoscope/merge_requests/38/
128    # and the git log of this file), perform only these basic coherence check.
129
130    assert differences[0].source1.startswith("cbfstool")
131    assert re.search(r"\+test file\s.*\sraw\s", differences[0].unified_diff)
132
133
134@skip_unless_tools_exist("cbfstool")
135def test_content(differences):
136    assert differences[1].source1 == "test file"
137    assert differences[1].source2 == "test file"
138    expected_diff = get_data("text_ascii_expected_diff")
139    assert differences[1].unified_diff == expected_diff
140
141
142@skip_unless_tools_exist("cbfstool")
143def test_compare_non_existing(monkeypatch, rom1):
144    assert_non_existing(monkeypatch, rom1)
145