xref: /aosp_15_r20/external/pigweed/pw_tokenizer/py/database_test.py (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*61c4878aSAndroid Build Coastguard Worker# Copyright 2020 The Pigweed Authors
3*61c4878aSAndroid Build Coastguard Worker#
4*61c4878aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); you may not
5*61c4878aSAndroid Build Coastguard Worker# use this file except in compliance with the License. You may obtain a copy of
6*61c4878aSAndroid Build Coastguard Worker# the License at
7*61c4878aSAndroid Build Coastguard Worker#
8*61c4878aSAndroid Build Coastguard Worker#     https://www.apache.org/licenses/LICENSE-2.0
9*61c4878aSAndroid Build Coastguard Worker#
10*61c4878aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
11*61c4878aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12*61c4878aSAndroid Build Coastguard Worker# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13*61c4878aSAndroid Build Coastguard Worker# License for the specific language governing permissions and limitations under
14*61c4878aSAndroid Build Coastguard Worker# the License.
15*61c4878aSAndroid Build Coastguard Worker"""Tests for the database module."""
16*61c4878aSAndroid Build Coastguard Worker
17*61c4878aSAndroid Build Coastguard Workerimport json
18*61c4878aSAndroid Build Coastguard Workerimport io
19*61c4878aSAndroid Build Coastguard Workerimport os
20*61c4878aSAndroid Build Coastguard Workerfrom pathlib import Path
21*61c4878aSAndroid Build Coastguard Workerimport shutil
22*61c4878aSAndroid Build Coastguard Workerimport stat
23*61c4878aSAndroid Build Coastguard Workerimport subprocess
24*61c4878aSAndroid Build Coastguard Workerimport sys
25*61c4878aSAndroid Build Coastguard Workerimport tempfile
26*61c4878aSAndroid Build Coastguard Workerimport unittest
27*61c4878aSAndroid Build Coastguard Workerfrom unittest import mock
28*61c4878aSAndroid Build Coastguard Worker
29*61c4878aSAndroid Build Coastguard Workerfrom pw_tokenizer import database
30*61c4878aSAndroid Build Coastguard Worker
31*61c4878aSAndroid Build Coastguard Worker# This is an ELF file with only the pw_tokenizer sections. It was created
32*61c4878aSAndroid Build Coastguard Worker# from a tokenize_test binary built for the STM32F429i Discovery board. The
33*61c4878aSAndroid Build Coastguard Worker# pw_tokenizer sections were extracted with this command:
34*61c4878aSAndroid Build Coastguard Worker#
35*61c4878aSAndroid Build Coastguard Worker#   arm-none-eabi-objcopy -S --only-section ".pw_tokenize*" <ELF> <OUTPUT>
36*61c4878aSAndroid Build Coastguard Worker#
37*61c4878aSAndroid Build Coastguard WorkerTOKENIZED_ENTRIES_ELF = Path(__file__).with_name(
38*61c4878aSAndroid Build Coastguard Worker    'example_binary_with_tokenized_strings.elf'
39*61c4878aSAndroid Build Coastguard Worker)
40*61c4878aSAndroid Build Coastguard Worker
41*61c4878aSAndroid Build Coastguard WorkerCSV_DEFAULT_DOMAIN = '''\
42*61c4878aSAndroid Build Coastguard Worker00000000,          ,"",""
43*61c4878aSAndroid Build Coastguard Worker141c35d5,          ,"","The answer: ""%s"""
44*61c4878aSAndroid Build Coastguard Worker29aef586,          ,"","1234"
45*61c4878aSAndroid Build Coastguard Worker2b78825f,          ,"","[:-)"
46*61c4878aSAndroid Build Coastguard Worker2e668cd6,          ,"","Jello, world!"
47*61c4878aSAndroid Build Coastguard Worker31631781,          ,"","%d"
48*61c4878aSAndroid Build Coastguard Worker61fd1e26,          ,"","%ld"
49*61c4878aSAndroid Build Coastguard Worker68ab92da,          ,"","%s there are %x (%.2f) of them%c"
50*61c4878aSAndroid Build Coastguard Worker7b940e2a,          ,"","Hello %s! %hd %e"
51*61c4878aSAndroid Build Coastguard Worker7da55d52,          ,"",">:-[]"
52*61c4878aSAndroid Build Coastguard Worker7f35a9a5,          ,"","TestName"
53*61c4878aSAndroid Build Coastguard Worker851beeb6,          ,"","%u %d"
54*61c4878aSAndroid Build Coastguard Worker881436a0,          ,"","The answer is: %s"
55*61c4878aSAndroid Build Coastguard Worker88808930,          ,"","%u%d%02x%X%hu%hhd%d%ld%lu%lld%llu%c%c%c"
56*61c4878aSAndroid Build Coastguard Worker92723f44,          ,"","???"
57*61c4878aSAndroid Build Coastguard Workera09d6698,          ,"","won-won-won-wonderful"
58*61c4878aSAndroid Build Coastguard Workeraa9ffa66,          ,"","void pw::tokenizer::{anonymous}::TestName()"
59*61c4878aSAndroid Build Coastguard Workerad002c97,          ,"","%llx"
60*61c4878aSAndroid Build Coastguard Workerb3653e13,          ,"","Jello!"
61*61c4878aSAndroid Build Coastguard Workercc6d3131,          ,"","Jello?"
62*61c4878aSAndroid Build Coastguard Workere13b0f94,          ,"","%llu"
63*61c4878aSAndroid Build Coastguard Workere65aefef,          ,"","Won't fit : %s%d"
64*61c4878aSAndroid Build Coastguard Worker'''
65*61c4878aSAndroid Build Coastguard Worker
66*61c4878aSAndroid Build Coastguard WorkerCSV_TEST_DOMAIN = """\
67*61c4878aSAndroid Build Coastguard Worker17fa86d3,          ,"TEST_DOMAIN","hello"
68*61c4878aSAndroid Build Coastguard Worker18c5017c,          ,"TEST_DOMAIN","yes"
69*61c4878aSAndroid Build Coastguard Worker59b2701c,          ,"TEST_DOMAIN","The answer was: %s"
70*61c4878aSAndroid Build Coastguard Worker881436a0,          ,"TEST_DOMAIN","The answer is: %s"
71*61c4878aSAndroid Build Coastguard Workerd18ada0f,          ,"TEST_DOMAIN","something"
72*61c4878aSAndroid Build Coastguard Worker"""
73*61c4878aSAndroid Build Coastguard Worker
74*61c4878aSAndroid Build Coastguard WorkerCSV_ALL_DOMAINS = '''\
75*61c4878aSAndroid Build Coastguard Worker00000000,          ,"",""
76*61c4878aSAndroid Build Coastguard Worker141c35d5,          ,"","The answer: ""%s"""
77*61c4878aSAndroid Build Coastguard Worker29aef586,          ,"","1234"
78*61c4878aSAndroid Build Coastguard Worker2b78825f,          ,"","[:-)"
79*61c4878aSAndroid Build Coastguard Worker2e668cd6,          ,"","Jello, world!"
80*61c4878aSAndroid Build Coastguard Worker31631781,          ,"","%d"
81*61c4878aSAndroid Build Coastguard Worker61fd1e26,          ,"","%ld"
82*61c4878aSAndroid Build Coastguard Worker68ab92da,          ,"","%s there are %x (%.2f) of them%c"
83*61c4878aSAndroid Build Coastguard Worker7b940e2a,          ,"","Hello %s! %hd %e"
84*61c4878aSAndroid Build Coastguard Worker7da55d52,          ,"",">:-[]"
85*61c4878aSAndroid Build Coastguard Worker7f35a9a5,          ,"","TestName"
86*61c4878aSAndroid Build Coastguard Worker851beeb6,          ,"","%u %d"
87*61c4878aSAndroid Build Coastguard Worker881436a0,          ,"","The answer is: %s"
88*61c4878aSAndroid Build Coastguard Worker88808930,          ,"","%u%d%02x%X%hu%hhd%d%ld%lu%lld%llu%c%c%c"
89*61c4878aSAndroid Build Coastguard Worker92723f44,          ,"","???"
90*61c4878aSAndroid Build Coastguard Workera09d6698,          ,"","won-won-won-wonderful"
91*61c4878aSAndroid Build Coastguard Workeraa9ffa66,          ,"","void pw::tokenizer::{anonymous}::TestName()"
92*61c4878aSAndroid Build Coastguard Workerad002c97,          ,"","%llx"
93*61c4878aSAndroid Build Coastguard Workerb3653e13,          ,"","Jello!"
94*61c4878aSAndroid Build Coastguard Workercc6d3131,          ,"","Jello?"
95*61c4878aSAndroid Build Coastguard Workere13b0f94,          ,"","%llu"
96*61c4878aSAndroid Build Coastguard Workere65aefef,          ,"","Won't fit : %s%d"
97*61c4878aSAndroid Build Coastguard Worker17fa86d3,          ,"TEST_DOMAIN","hello"
98*61c4878aSAndroid Build Coastguard Worker18c5017c,          ,"TEST_DOMAIN","yes"
99*61c4878aSAndroid Build Coastguard Worker59b2701c,          ,"TEST_DOMAIN","The answer was: %s"
100*61c4878aSAndroid Build Coastguard Worker881436a0,          ,"TEST_DOMAIN","The answer is: %s"
101*61c4878aSAndroid Build Coastguard Workerd18ada0f,          ,"TEST_DOMAIN","something"
102*61c4878aSAndroid Build Coastguard Worker'''
103*61c4878aSAndroid Build Coastguard Worker
104*61c4878aSAndroid Build Coastguard WorkerJSON_SOURCE_STRINGS = '''\
105*61c4878aSAndroid Build Coastguard Worker[
106*61c4878aSAndroid Build Coastguard Worker  "pigweed/pw_polyfill/standard_library_public/pw_polyfill/standard_library/assert.h",
107*61c4878aSAndroid Build Coastguard Worker  "protocol_buffer/gen/pigweed/pw_protobuf/common_protos.proto_library/nanopb/pw_protobuf_protos/status.pb.h",
108*61c4878aSAndroid Build Coastguard Worker  "pigweed/pw_rpc/client_server.cc",
109*61c4878aSAndroid Build Coastguard Worker  "pigweed/pw_rpc/public/pw_rpc/client_server.h",
110*61c4878aSAndroid Build Coastguard Worker  "This is a very long string that will produce two tokens; one for C++ and one for C. This is because this string exceeds the default C hash length."
111*61c4878aSAndroid Build Coastguard Worker]
112*61c4878aSAndroid Build Coastguard Worker'''
113*61c4878aSAndroid Build Coastguard Worker
114*61c4878aSAndroid Build Coastguard WorkerCSV_STRINGS = '''\
115*61c4878aSAndroid Build Coastguard Worker2cbf627a,          ,"","pigweed/pw_rpc/client_server.cc"
116*61c4878aSAndroid Build Coastguard Worker666562a1,          ,"","protocol_buffer/gen/pigweed/pw_protobuf/common_protos.proto_library/nanopb/pw_protobuf_protos/status.pb.h"
117*61c4878aSAndroid Build Coastguard Worker6c1e6eb3,          ,"","pigweed/pw_rpc/public/pw_rpc/client_server.h"
118*61c4878aSAndroid Build Coastguard Workerb25a9932,          ,"","This is a very long string that will produce two tokens; one for C++ and one for C. This is because this string exceeds the default C hash length."
119*61c4878aSAndroid Build Coastguard Workereadf017f,          ,"","pigweed/pw_polyfill/standard_library_public/pw_polyfill/standard_library/assert.h"
120*61c4878aSAndroid Build Coastguard Workerf815dc5c,          ,"","This is a very long string that will produce two tokens; one for C++ and one for C. This is because this string exceeds the default C hash length."
121*61c4878aSAndroid Build Coastguard Worker'''
122*61c4878aSAndroid Build Coastguard Worker
123*61c4878aSAndroid Build Coastguard WorkerEXPECTED_REPORT = {
124*61c4878aSAndroid Build Coastguard Worker    str(TOKENIZED_ENTRIES_ELF): {
125*61c4878aSAndroid Build Coastguard Worker        '': {
126*61c4878aSAndroid Build Coastguard Worker            'present_entries': 22,
127*61c4878aSAndroid Build Coastguard Worker            'present_size_bytes': 289,
128*61c4878aSAndroid Build Coastguard Worker            'total_entries': 22,
129*61c4878aSAndroid Build Coastguard Worker            'total_size_bytes': 289,
130*61c4878aSAndroid Build Coastguard Worker            'collisions': {},
131*61c4878aSAndroid Build Coastguard Worker        },
132*61c4878aSAndroid Build Coastguard Worker        'TEST_DOMAIN': {
133*61c4878aSAndroid Build Coastguard Worker            'present_entries': 5,
134*61c4878aSAndroid Build Coastguard Worker            'present_size_bytes': 57,
135*61c4878aSAndroid Build Coastguard Worker            'total_entries': 5,
136*61c4878aSAndroid Build Coastguard Worker            'total_size_bytes': 57,
137*61c4878aSAndroid Build Coastguard Worker            'collisions': {},
138*61c4878aSAndroid Build Coastguard Worker        },
139*61c4878aSAndroid Build Coastguard Worker    }
140*61c4878aSAndroid Build Coastguard Worker}
141*61c4878aSAndroid Build Coastguard Worker
142*61c4878aSAndroid Build Coastguard Worker
143*61c4878aSAndroid Build Coastguard Workerdef run_cli(*args) -> None:
144*61c4878aSAndroid Build Coastguard Worker    original_argv = sys.argv
145*61c4878aSAndroid Build Coastguard Worker    sys.argv = ['database.py', *(str(a) for a in args)]
146*61c4878aSAndroid Build Coastguard Worker    # pylint: disable=protected-access
147*61c4878aSAndroid Build Coastguard Worker    try:
148*61c4878aSAndroid Build Coastguard Worker        database._main(*database._parse_args())
149*61c4878aSAndroid Build Coastguard Worker    finally:
150*61c4878aSAndroid Build Coastguard Worker        # Remove the log handler added by _main to avoid duplicate logs.
151*61c4878aSAndroid Build Coastguard Worker        if database._LOG.handlers:
152*61c4878aSAndroid Build Coastguard Worker            database._LOG.handlers.pop()
153*61c4878aSAndroid Build Coastguard Worker        # pylint: enable=protected-access
154*61c4878aSAndroid Build Coastguard Worker
155*61c4878aSAndroid Build Coastguard Worker        sys.argv = original_argv
156*61c4878aSAndroid Build Coastguard Worker
157*61c4878aSAndroid Build Coastguard Worker
158*61c4878aSAndroid Build Coastguard Workerdef _mock_output() -> io.TextIOWrapper:
159*61c4878aSAndroid Build Coastguard Worker    output = io.BytesIO()
160*61c4878aSAndroid Build Coastguard Worker    output.name = '<fake stdout>'
161*61c4878aSAndroid Build Coastguard Worker    return io.TextIOWrapper(output, write_through=True)
162*61c4878aSAndroid Build Coastguard Worker
163*61c4878aSAndroid Build Coastguard Worker
164*61c4878aSAndroid Build Coastguard Workerdef _remove_readonly(  # pylint: disable=unused-argument
165*61c4878aSAndroid Build Coastguard Worker    func, path, excinfo
166*61c4878aSAndroid Build Coastguard Worker) -> None:
167*61c4878aSAndroid Build Coastguard Worker    """Changes file permission and recalls the calling function."""
168*61c4878aSAndroid Build Coastguard Worker    print('Path attempted to be deleted:', path)
169*61c4878aSAndroid Build Coastguard Worker    if not os.access(path, os.W_OK):
170*61c4878aSAndroid Build Coastguard Worker        # Change file permissions.
171*61c4878aSAndroid Build Coastguard Worker        os.chmod(path, stat.S_IWUSR)
172*61c4878aSAndroid Build Coastguard Worker        # Call the calling function again.
173*61c4878aSAndroid Build Coastguard Worker        func(path)
174*61c4878aSAndroid Build Coastguard Worker
175*61c4878aSAndroid Build Coastguard Worker
176*61c4878aSAndroid Build Coastguard Workerclass DatabaseCommandLineTest(unittest.TestCase):
177*61c4878aSAndroid Build Coastguard Worker    """Tests the database.py command line interface."""
178*61c4878aSAndroid Build Coastguard Worker
179*61c4878aSAndroid Build Coastguard Worker    def setUp(self) -> None:
180*61c4878aSAndroid Build Coastguard Worker        self._dir = Path(tempfile.mkdtemp('_pw_tokenizer_test'))
181*61c4878aSAndroid Build Coastguard Worker        self._csv = self._dir / 'db.csv'
182*61c4878aSAndroid Build Coastguard Worker        self._elf = TOKENIZED_ENTRIES_ELF
183*61c4878aSAndroid Build Coastguard Worker
184*61c4878aSAndroid Build Coastguard Worker    def tearDown(self) -> None:
185*61c4878aSAndroid Build Coastguard Worker        shutil.rmtree(self._dir)
186*61c4878aSAndroid Build Coastguard Worker
187*61c4878aSAndroid Build Coastguard Worker    def test_create_csv(self) -> None:
188*61c4878aSAndroid Build Coastguard Worker        run_cli('create', '--database', self._csv, self._elf)
189*61c4878aSAndroid Build Coastguard Worker
190*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(
191*61c4878aSAndroid Build Coastguard Worker            CSV_ALL_DOMAINS.splitlines(), self._csv.read_text().splitlines()
192*61c4878aSAndroid Build Coastguard Worker        )
193*61c4878aSAndroid Build Coastguard Worker
194*61c4878aSAndroid Build Coastguard Worker    def test_create_csv_from_three_column_csv(self) -> None:
195*61c4878aSAndroid Build Coastguard Worker        three_col_db = self._dir.joinpath('legacy.csv')
196*61c4878aSAndroid Build Coastguard Worker        # Remove the domain column
197*61c4878aSAndroid Build Coastguard Worker        three_col_db.write_text(CSV_TEST_DOMAIN.replace('"TEST_DOMAIN",', ''))
198*61c4878aSAndroid Build Coastguard Worker        run_cli('create', '--database', self._csv, three_col_db)
199*61c4878aSAndroid Build Coastguard Worker
200*61c4878aSAndroid Build Coastguard Worker        tokens_in_default = CSV_TEST_DOMAIN.replace('"TEST_DOMAIN",', '"",')
201*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(
202*61c4878aSAndroid Build Coastguard Worker            tokens_in_default.splitlines(), self._csv.read_text().splitlines()
203*61c4878aSAndroid Build Coastguard Worker        )
204*61c4878aSAndroid Build Coastguard Worker
205*61c4878aSAndroid Build Coastguard Worker    def test_create_csv_test_domain(self) -> None:
206*61c4878aSAndroid Build Coastguard Worker        run_cli('create', '--database', self._csv, f'{self._elf}#TEST_DOMAIN')
207*61c4878aSAndroid Build Coastguard Worker
208*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(
209*61c4878aSAndroid Build Coastguard Worker            CSV_TEST_DOMAIN.splitlines(),
210*61c4878aSAndroid Build Coastguard Worker            self._csv.read_text().splitlines(),
211*61c4878aSAndroid Build Coastguard Worker        )
212*61c4878aSAndroid Build Coastguard Worker
213*61c4878aSAndroid Build Coastguard Worker    def test_create_csv_all_domains(self) -> None:
214*61c4878aSAndroid Build Coastguard Worker        run_cli('create', '--database', self._csv, self._elf)
215*61c4878aSAndroid Build Coastguard Worker
216*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(
217*61c4878aSAndroid Build Coastguard Worker            CSV_ALL_DOMAINS.splitlines(), self._csv.read_text().splitlines()
218*61c4878aSAndroid Build Coastguard Worker        )
219*61c4878aSAndroid Build Coastguard Worker
220*61c4878aSAndroid Build Coastguard Worker    def test_create_csv_all_domains_regex(self) -> None:
221*61c4878aSAndroid Build Coastguard Worker        run_cli('create', '--database', self._csv, f'{self._elf}#.*')
222*61c4878aSAndroid Build Coastguard Worker
223*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(
224*61c4878aSAndroid Build Coastguard Worker            CSV_ALL_DOMAINS.splitlines(), self._csv.read_text().splitlines()
225*61c4878aSAndroid Build Coastguard Worker        )
226*61c4878aSAndroid Build Coastguard Worker
227*61c4878aSAndroid Build Coastguard Worker    def test_invalid_domain_pattern(self) -> None:
228*61c4878aSAndroid Build Coastguard Worker        with self.assertRaises(SystemExit):
229*61c4878aSAndroid Build Coastguard Worker            run_cli('create', '--database', self._csv, f'{self._elf}#.*#')
230*61c4878aSAndroid Build Coastguard Worker
231*61c4878aSAndroid Build Coastguard Worker    def test_invalid_glob(self) -> None:
232*61c4878aSAndroid Build Coastguard Worker        with self.assertRaises(SystemExit):
233*61c4878aSAndroid Build Coastguard Worker            run_cli('create', '--database', self._csv, 'INVALID PATH')
234*61c4878aSAndroid Build Coastguard Worker
235*61c4878aSAndroid Build Coastguard Worker    def test_create_force(self) -> None:
236*61c4878aSAndroid Build Coastguard Worker        self._csv.write_text(CSV_ALL_DOMAINS)
237*61c4878aSAndroid Build Coastguard Worker
238*61c4878aSAndroid Build Coastguard Worker        with self.assertRaises(FileExistsError):
239*61c4878aSAndroid Build Coastguard Worker            run_cli('create', '--database', self._csv, self._elf)
240*61c4878aSAndroid Build Coastguard Worker
241*61c4878aSAndroid Build Coastguard Worker        run_cli('create', '--force', '--database', self._csv, self._elf)
242*61c4878aSAndroid Build Coastguard Worker
243*61c4878aSAndroid Build Coastguard Worker    def test_create_binary(self) -> None:
244*61c4878aSAndroid Build Coastguard Worker        binary = self._dir / 'db.bin'
245*61c4878aSAndroid Build Coastguard Worker        run_cli(
246*61c4878aSAndroid Build Coastguard Worker            'create',
247*61c4878aSAndroid Build Coastguard Worker            '--type',
248*61c4878aSAndroid Build Coastguard Worker            'binary',
249*61c4878aSAndroid Build Coastguard Worker            '--database',
250*61c4878aSAndroid Build Coastguard Worker            binary,
251*61c4878aSAndroid Build Coastguard Worker            f'{self._elf}#',  # Only default domain since v1 DB excludes domain.
252*61c4878aSAndroid Build Coastguard Worker        )
253*61c4878aSAndroid Build Coastguard Worker
254*61c4878aSAndroid Build Coastguard Worker        # Write the binary database as CSV to verify its contents.
255*61c4878aSAndroid Build Coastguard Worker        run_cli('create', '--database', self._csv, binary)
256*61c4878aSAndroid Build Coastguard Worker
257*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(
258*61c4878aSAndroid Build Coastguard Worker            CSV_DEFAULT_DOMAIN.splitlines(), self._csv.read_text().splitlines()
259*61c4878aSAndroid Build Coastguard Worker        )
260*61c4878aSAndroid Build Coastguard Worker
261*61c4878aSAndroid Build Coastguard Worker    def test_add_does_not_recalculate_tokens(self) -> None:
262*61c4878aSAndroid Build Coastguard Worker        db_with_custom_token = '01234567,          ,"","hello"'
263*61c4878aSAndroid Build Coastguard Worker
264*61c4878aSAndroid Build Coastguard Worker        to_add = self._dir / 'add_this.csv'
265*61c4878aSAndroid Build Coastguard Worker        to_add.write_text(db_with_custom_token + '\n')
266*61c4878aSAndroid Build Coastguard Worker        self._csv.touch()
267*61c4878aSAndroid Build Coastguard Worker
268*61c4878aSAndroid Build Coastguard Worker        run_cli('add', '--database', self._csv, to_add)
269*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(
270*61c4878aSAndroid Build Coastguard Worker            db_with_custom_token.splitlines(),
271*61c4878aSAndroid Build Coastguard Worker            self._csv.read_text().splitlines(),
272*61c4878aSAndroid Build Coastguard Worker        )
273*61c4878aSAndroid Build Coastguard Worker
274*61c4878aSAndroid Build Coastguard Worker    def test_mark_removed(self) -> None:
275*61c4878aSAndroid Build Coastguard Worker        """Tests adding a removal date to tokens in a CSV database."""
276*61c4878aSAndroid Build Coastguard Worker        self._csv.write_text(CSV_ALL_DOMAINS)
277*61c4878aSAndroid Build Coastguard Worker
278*61c4878aSAndroid Build Coastguard Worker        run_cli(
279*61c4878aSAndroid Build Coastguard Worker            'mark_removed',
280*61c4878aSAndroid Build Coastguard Worker            '--database',
281*61c4878aSAndroid Build Coastguard Worker            self._csv,
282*61c4878aSAndroid Build Coastguard Worker            '--date',
283*61c4878aSAndroid Build Coastguard Worker            '1998-09-04',
284*61c4878aSAndroid Build Coastguard Worker            f'{self._elf}#',  # Only load the default domain for this test.
285*61c4878aSAndroid Build Coastguard Worker        )
286*61c4878aSAndroid Build Coastguard Worker
287*61c4878aSAndroid Build Coastguard Worker        # Add the removal date to the tokens not in the default domain
288*61c4878aSAndroid Build Coastguard Worker        new_csv = CSV_ALL_DOMAINS
289*61c4878aSAndroid Build Coastguard Worker        new_csv = new_csv.replace(
290*61c4878aSAndroid Build Coastguard Worker            '17fa86d3,          ,"TEST_DOMAIN","hello"',
291*61c4878aSAndroid Build Coastguard Worker            '17fa86d3,1998-09-04,"TEST_DOMAIN","hello"',
292*61c4878aSAndroid Build Coastguard Worker        )
293*61c4878aSAndroid Build Coastguard Worker        new_csv = new_csv.replace(
294*61c4878aSAndroid Build Coastguard Worker            '18c5017c,          ,"TEST_DOMAIN","yes"',
295*61c4878aSAndroid Build Coastguard Worker            '18c5017c,1998-09-04,"TEST_DOMAIN","yes"',
296*61c4878aSAndroid Build Coastguard Worker        )
297*61c4878aSAndroid Build Coastguard Worker        new_csv = new_csv.replace(
298*61c4878aSAndroid Build Coastguard Worker            '59b2701c,          ,"TEST_DOMAIN","The answer was: %s"',
299*61c4878aSAndroid Build Coastguard Worker            '59b2701c,1998-09-04,"TEST_DOMAIN","The answer was: %s"',
300*61c4878aSAndroid Build Coastguard Worker        )
301*61c4878aSAndroid Build Coastguard Worker        new_csv = new_csv.replace(
302*61c4878aSAndroid Build Coastguard Worker            'd18ada0f,          ,"TEST_DOMAIN","something"',
303*61c4878aSAndroid Build Coastguard Worker            'd18ada0f,1998-09-04,"TEST_DOMAIN","something"',
304*61c4878aSAndroid Build Coastguard Worker        )
305*61c4878aSAndroid Build Coastguard Worker        new_csv = new_csv.replace(
306*61c4878aSAndroid Build Coastguard Worker            '881436a0,          ,"TEST_DOMAIN","The answer is: %s"',
307*61c4878aSAndroid Build Coastguard Worker            '881436a0,1998-09-04,"TEST_DOMAIN","The answer is: %s"',
308*61c4878aSAndroid Build Coastguard Worker        )
309*61c4878aSAndroid Build Coastguard Worker        self.assertNotEqual(CSV_ALL_DOMAINS, new_csv)
310*61c4878aSAndroid Build Coastguard Worker
311*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(
312*61c4878aSAndroid Build Coastguard Worker            new_csv.splitlines(), self._csv.read_text().splitlines()
313*61c4878aSAndroid Build Coastguard Worker        )
314*61c4878aSAndroid Build Coastguard Worker
315*61c4878aSAndroid Build Coastguard Worker    def test_purge(self) -> None:
316*61c4878aSAndroid Build Coastguard Worker        self._csv.write_text(CSV_DEFAULT_DOMAIN)
317*61c4878aSAndroid Build Coastguard Worker
318*61c4878aSAndroid Build Coastguard Worker        first_5_csv = self._dir / 'first_5.csv'
319*61c4878aSAndroid Build Coastguard Worker        first_5_csv.write_text(
320*61c4878aSAndroid Build Coastguard Worker            ''.join(CSV_DEFAULT_DOMAIN.splitlines(keepends=True)[:5])
321*61c4878aSAndroid Build Coastguard Worker        )
322*61c4878aSAndroid Build Coastguard Worker
323*61c4878aSAndroid Build Coastguard Worker        self._csv.write_text(CSV_DEFAULT_DOMAIN)
324*61c4878aSAndroid Build Coastguard Worker
325*61c4878aSAndroid Build Coastguard Worker        # Mark everything except for the first 5 entries as removed.
326*61c4878aSAndroid Build Coastguard Worker        run_cli('mark_removed', '--database', self._csv, first_5_csv)
327*61c4878aSAndroid Build Coastguard Worker
328*61c4878aSAndroid Build Coastguard Worker        # Delete all entries except those in TEST_DOMAIN.
329*61c4878aSAndroid Build Coastguard Worker        run_cli('purge', '--database', self._csv)
330*61c4878aSAndroid Build Coastguard Worker
331*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(
332*61c4878aSAndroid Build Coastguard Worker            first_5_csv.read_text().splitlines(),
333*61c4878aSAndroid Build Coastguard Worker            self._csv.read_text().splitlines(),
334*61c4878aSAndroid Build Coastguard Worker        )
335*61c4878aSAndroid Build Coastguard Worker
336*61c4878aSAndroid Build Coastguard Worker    @mock.patch('sys.stdout', new_callable=_mock_output)
337*61c4878aSAndroid Build Coastguard Worker    def test_report(self, mock_stdout) -> None:
338*61c4878aSAndroid Build Coastguard Worker        run_cli('report', self._elf)
339*61c4878aSAndroid Build Coastguard Worker
340*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(
341*61c4878aSAndroid Build Coastguard Worker            json.loads(mock_stdout.buffer.getvalue()), EXPECTED_REPORT
342*61c4878aSAndroid Build Coastguard Worker        )
343*61c4878aSAndroid Build Coastguard Worker
344*61c4878aSAndroid Build Coastguard Worker    def test_replace(self) -> None:
345*61c4878aSAndroid Build Coastguard Worker        sub = 'replace/ment'
346*61c4878aSAndroid Build Coastguard Worker        run_cli(
347*61c4878aSAndroid Build Coastguard Worker            'create',
348*61c4878aSAndroid Build Coastguard Worker            '--database',
349*61c4878aSAndroid Build Coastguard Worker            self._csv,
350*61c4878aSAndroid Build Coastguard Worker            f'{self._elf}#',  # Only load the default domain for this test.
351*61c4878aSAndroid Build Coastguard Worker            '--replace',
352*61c4878aSAndroid Build Coastguard Worker            r'(?i)\b[jh]ello\b/' + sub,
353*61c4878aSAndroid Build Coastguard Worker        )
354*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(
355*61c4878aSAndroid Build Coastguard Worker            CSV_DEFAULT_DOMAIN.replace('Jello', sub).replace('Hello', sub),
356*61c4878aSAndroid Build Coastguard Worker            self._csv.read_text(),
357*61c4878aSAndroid Build Coastguard Worker        )
358*61c4878aSAndroid Build Coastguard Worker
359*61c4878aSAndroid Build Coastguard Worker    def test_json_strings(self) -> None:
360*61c4878aSAndroid Build Coastguard Worker        strings_file = self._dir / "strings.json"
361*61c4878aSAndroid Build Coastguard Worker
362*61c4878aSAndroid Build Coastguard Worker        with open(strings_file, 'w') as file:
363*61c4878aSAndroid Build Coastguard Worker            file.write(JSON_SOURCE_STRINGS)
364*61c4878aSAndroid Build Coastguard Worker
365*61c4878aSAndroid Build Coastguard Worker        run_cli('create', '--force', '--database', self._csv, strings_file)
366*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(
367*61c4878aSAndroid Build Coastguard Worker            CSV_STRINGS.splitlines(), self._csv.read_text().splitlines()
368*61c4878aSAndroid Build Coastguard Worker        )
369*61c4878aSAndroid Build Coastguard Worker
370*61c4878aSAndroid Build Coastguard Worker
371*61c4878aSAndroid Build Coastguard Workerclass DirectoryDatabaseCommandLineTest(unittest.TestCase):
372*61c4878aSAndroid Build Coastguard Worker    """Tests the directory database command line interface."""
373*61c4878aSAndroid Build Coastguard Worker
374*61c4878aSAndroid Build Coastguard Worker    def setUp(self) -> None:
375*61c4878aSAndroid Build Coastguard Worker        self._dir = Path(tempfile.mkdtemp('_pw_tokenizer_test'))
376*61c4878aSAndroid Build Coastguard Worker        self._db_dir = self._dir / '_dir_database_test'
377*61c4878aSAndroid Build Coastguard Worker        self._db_dir.mkdir(exist_ok=True)
378*61c4878aSAndroid Build Coastguard Worker        self._db_csv = self._db_dir / '8123913.pw_tokenizer.csv'
379*61c4878aSAndroid Build Coastguard Worker        self._elf = TOKENIZED_ENTRIES_ELF
380*61c4878aSAndroid Build Coastguard Worker
381*61c4878aSAndroid Build Coastguard Worker    def _git(self, *command: str) -> None:
382*61c4878aSAndroid Build Coastguard Worker        """Runs git in self._dir with forced user name and email values.
383*61c4878aSAndroid Build Coastguard Worker
384*61c4878aSAndroid Build Coastguard Worker        Prevents accidentally running git in the wrong directory and avoids
385*61c4878aSAndroid Build Coastguard Worker        errors if the name and email are not configured.
386*61c4878aSAndroid Build Coastguard Worker        """
387*61c4878aSAndroid Build Coastguard Worker        subprocess.run(
388*61c4878aSAndroid Build Coastguard Worker            [
389*61c4878aSAndroid Build Coastguard Worker                'git',
390*61c4878aSAndroid Build Coastguard Worker                '-c',
391*61c4878aSAndroid Build Coastguard Worker                'user.name=pw_tokenizer tests',
392*61c4878aSAndroid Build Coastguard Worker                '-c',
393*61c4878aSAndroid Build Coastguard Worker                '[email protected]',
394*61c4878aSAndroid Build Coastguard Worker                *command,
395*61c4878aSAndroid Build Coastguard Worker            ],
396*61c4878aSAndroid Build Coastguard Worker            cwd=self._dir,
397*61c4878aSAndroid Build Coastguard Worker            check=True,
398*61c4878aSAndroid Build Coastguard Worker        )
399*61c4878aSAndroid Build Coastguard Worker
400*61c4878aSAndroid Build Coastguard Worker    def tearDown(self) -> None:
401*61c4878aSAndroid Build Coastguard Worker        shutil.rmtree(self._dir, onerror=_remove_readonly)
402*61c4878aSAndroid Build Coastguard Worker
403*61c4878aSAndroid Build Coastguard Worker    def test_add_csv_to_dir(self) -> None:
404*61c4878aSAndroid Build Coastguard Worker        """Tests a CSV can be created within the database."""
405*61c4878aSAndroid Build Coastguard Worker        run_cli('add', '--database', self._db_dir, f'{self._elf}#TEST_DOMAIN')
406*61c4878aSAndroid Build Coastguard Worker        directory = list(self._db_dir.iterdir())
407*61c4878aSAndroid Build Coastguard Worker
408*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(1, len(directory))
409*61c4878aSAndroid Build Coastguard Worker
410*61c4878aSAndroid Build Coastguard Worker        self._db_csv = directory.pop()
411*61c4878aSAndroid Build Coastguard Worker
412*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(
413*61c4878aSAndroid Build Coastguard Worker            CSV_TEST_DOMAIN.splitlines(),
414*61c4878aSAndroid Build Coastguard Worker            self._db_csv.read_text().splitlines(),
415*61c4878aSAndroid Build Coastguard Worker        )
416*61c4878aSAndroid Build Coastguard Worker
417*61c4878aSAndroid Build Coastguard Worker    def test_add_all_domains_to_dir(self) -> None:
418*61c4878aSAndroid Build Coastguard Worker        """Tests a CSV with all domains can be added to the database."""
419*61c4878aSAndroid Build Coastguard Worker        run_cli('add', '--database', self._db_dir, f'{self._elf}#.*')
420*61c4878aSAndroid Build Coastguard Worker        directory = list(self._db_dir.iterdir())
421*61c4878aSAndroid Build Coastguard Worker
422*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(1, len(directory))
423*61c4878aSAndroid Build Coastguard Worker
424*61c4878aSAndroid Build Coastguard Worker        self._db_csv = directory.pop()
425*61c4878aSAndroid Build Coastguard Worker
426*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(
427*61c4878aSAndroid Build Coastguard Worker            CSV_ALL_DOMAINS.splitlines(), self._db_csv.read_text().splitlines()
428*61c4878aSAndroid Build Coastguard Worker        )
429*61c4878aSAndroid Build Coastguard Worker
430*61c4878aSAndroid Build Coastguard Worker    def test_not_adding_existing_tokens(self) -> None:
431*61c4878aSAndroid Build Coastguard Worker        """Tests duplicate tokens are not added to the database."""
432*61c4878aSAndroid Build Coastguard Worker        run_cli('add', '--database', self._db_dir, self._elf)
433*61c4878aSAndroid Build Coastguard Worker        run_cli('add', '--database', self._db_dir, self._elf)
434*61c4878aSAndroid Build Coastguard Worker        directory = list(self._db_dir.iterdir())
435*61c4878aSAndroid Build Coastguard Worker
436*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(1, len(directory))
437*61c4878aSAndroid Build Coastguard Worker
438*61c4878aSAndroid Build Coastguard Worker        self._db_csv = directory.pop()
439*61c4878aSAndroid Build Coastguard Worker
440*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(
441*61c4878aSAndroid Build Coastguard Worker            CSV_ALL_DOMAINS.splitlines(),
442*61c4878aSAndroid Build Coastguard Worker            self._db_csv.read_text().splitlines(),
443*61c4878aSAndroid Build Coastguard Worker        )
444*61c4878aSAndroid Build Coastguard Worker
445*61c4878aSAndroid Build Coastguard Worker    def test_adding_tokens_without_git_repo(self):
446*61c4878aSAndroid Build Coastguard Worker        """Tests creating new files with new entries when no repo exists."""
447*61c4878aSAndroid Build Coastguard Worker        # Add a subset of entries to a new CSV in the directory database.
448*61c4878aSAndroid Build Coastguard Worker        entry_subset = self._dir / 'entry_subset.csv'
449*61c4878aSAndroid Build Coastguard Worker        entry_subset.write_text(CSV_TEST_DOMAIN)
450*61c4878aSAndroid Build Coastguard Worker
451*61c4878aSAndroid Build Coastguard Worker        run_cli('add', '--database', self._db_dir, entry_subset)
452*61c4878aSAndroid Build Coastguard Worker        directory = list(self._db_dir.iterdir())
453*61c4878aSAndroid Build Coastguard Worker
454*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(1, len(directory))
455*61c4878aSAndroid Build Coastguard Worker
456*61c4878aSAndroid Build Coastguard Worker        first_csv_in_db = directory.pop()
457*61c4878aSAndroid Build Coastguard Worker
458*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(
459*61c4878aSAndroid Build Coastguard Worker            CSV_TEST_DOMAIN.splitlines(),
460*61c4878aSAndroid Build Coastguard Worker            first_csv_in_db.read_text().splitlines(),
461*61c4878aSAndroid Build Coastguard Worker        )
462*61c4878aSAndroid Build Coastguard Worker        # Add a superset of entries to a new CSV in the directory database.
463*61c4878aSAndroid Build Coastguard Worker        entry_superset = self._dir / 'entry_superset.csv'
464*61c4878aSAndroid Build Coastguard Worker        entry_superset.write_text(CSV_ALL_DOMAINS)
465*61c4878aSAndroid Build Coastguard Worker
466*61c4878aSAndroid Build Coastguard Worker        run_cli('add', '--database', self._db_dir, entry_superset)
467*61c4878aSAndroid Build Coastguard Worker        directory = list(self._db_dir.iterdir())
468*61c4878aSAndroid Build Coastguard Worker        # Assert two different CSVs were created to store new tokens.
469*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(2, len(directory))
470*61c4878aSAndroid Build Coastguard Worker        # Retrieve the other CSV in the directory.
471*61c4878aSAndroid Build Coastguard Worker        second_csv_in_db = (
472*61c4878aSAndroid Build Coastguard Worker            directory[0] if directory[0] != first_csv_in_db else directory[1]
473*61c4878aSAndroid Build Coastguard Worker        )
474*61c4878aSAndroid Build Coastguard Worker
475*61c4878aSAndroid Build Coastguard Worker        self.assertNotEqual(first_csv_in_db, second_csv_in_db)
476*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(
477*61c4878aSAndroid Build Coastguard Worker            CSV_TEST_DOMAIN.splitlines(),
478*61c4878aSAndroid Build Coastguard Worker            first_csv_in_db.read_text().splitlines(),
479*61c4878aSAndroid Build Coastguard Worker        )
480*61c4878aSAndroid Build Coastguard Worker
481*61c4878aSAndroid Build Coastguard Worker        # Retrieve entries exclusively in the superset, not the subset.
482*61c4878aSAndroid Build Coastguard Worker        entries_exclusively_in_superset = set(
483*61c4878aSAndroid Build Coastguard Worker            CSV_ALL_DOMAINS.splitlines()
484*61c4878aSAndroid Build Coastguard Worker        ) - set(CSV_TEST_DOMAIN.splitlines())
485*61c4878aSAndroid Build Coastguard Worker        # Ensure only new tokens not in the subset were added to the second CSV
486*61c4878aSAndroid Build Coastguard Worker        # added to the directory database.
487*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(
488*61c4878aSAndroid Build Coastguard Worker            entries_exclusively_in_superset,
489*61c4878aSAndroid Build Coastguard Worker            set(second_csv_in_db.read_text().splitlines()),
490*61c4878aSAndroid Build Coastguard Worker        )
491*61c4878aSAndroid Build Coastguard Worker
492*61c4878aSAndroid Build Coastguard Worker    def test_untracked_files_in_dir(self):
493*61c4878aSAndroid Build Coastguard Worker        """Tests untracked CSVs are reused by the database."""
494*61c4878aSAndroid Build Coastguard Worker        self._git('init')
495*61c4878aSAndroid Build Coastguard Worker        # Add CSV_TEST_DOMAIN to a new CSV in the directory database.
496*61c4878aSAndroid Build Coastguard Worker        run_cli(
497*61c4878aSAndroid Build Coastguard Worker            'add',
498*61c4878aSAndroid Build Coastguard Worker            '--database',
499*61c4878aSAndroid Build Coastguard Worker            self._db_dir,
500*61c4878aSAndroid Build Coastguard Worker            '--discard-temporary',
501*61c4878aSAndroid Build Coastguard Worker            'HEAD',
502*61c4878aSAndroid Build Coastguard Worker            f'{self._elf}#TEST_DOMAIN',
503*61c4878aSAndroid Build Coastguard Worker        )
504*61c4878aSAndroid Build Coastguard Worker        directory = list(self._db_dir.iterdir())
505*61c4878aSAndroid Build Coastguard Worker
506*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(1, len(directory))
507*61c4878aSAndroid Build Coastguard Worker
508*61c4878aSAndroid Build Coastguard Worker        first_path_in_db = directory.pop()
509*61c4878aSAndroid Build Coastguard Worker
510*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(
511*61c4878aSAndroid Build Coastguard Worker            CSV_TEST_DOMAIN.splitlines(),
512*61c4878aSAndroid Build Coastguard Worker            first_path_in_db.read_text().splitlines(),
513*61c4878aSAndroid Build Coastguard Worker        )
514*61c4878aSAndroid Build Coastguard Worker        # Retrieve the untracked CSV in the Git repository and discard
515*61c4878aSAndroid Build Coastguard Worker        # tokens that do not exist in CSV_DEFAULT_DOMAIN.
516*61c4878aSAndroid Build Coastguard Worker        run_cli(
517*61c4878aSAndroid Build Coastguard Worker            'add',
518*61c4878aSAndroid Build Coastguard Worker            '--database',
519*61c4878aSAndroid Build Coastguard Worker            self._db_dir,
520*61c4878aSAndroid Build Coastguard Worker            '--discard-temporary',
521*61c4878aSAndroid Build Coastguard Worker            'HEAD',
522*61c4878aSAndroid Build Coastguard Worker            self._elf,
523*61c4878aSAndroid Build Coastguard Worker        )
524*61c4878aSAndroid Build Coastguard Worker        directory = list(self._db_dir.iterdir())
525*61c4878aSAndroid Build Coastguard Worker
526*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(1, len(directory))
527*61c4878aSAndroid Build Coastguard Worker
528*61c4878aSAndroid Build Coastguard Worker        reused_path_in_db = directory.pop()
529*61c4878aSAndroid Build Coastguard Worker        # Ensure the first path created is the same being reused. Also,
530*61c4878aSAndroid Build Coastguard Worker        # the CSV content is the same as CSV_DEFAULT_DOMAIN.
531*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(first_path_in_db, reused_path_in_db)
532*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(
533*61c4878aSAndroid Build Coastguard Worker            CSV_ALL_DOMAINS.splitlines(),
534*61c4878aSAndroid Build Coastguard Worker            reused_path_in_db.read_text().splitlines(),
535*61c4878aSAndroid Build Coastguard Worker        )
536*61c4878aSAndroid Build Coastguard Worker
537*61c4878aSAndroid Build Coastguard Worker    def test_adding_multiple_elf_files(self) -> None:
538*61c4878aSAndroid Build Coastguard Worker        """Tests adding multiple elf files to a file in the database."""
539*61c4878aSAndroid Build Coastguard Worker        # Add CSV_TEST_DOMAIN to a new CSV in the directory database.
540*61c4878aSAndroid Build Coastguard Worker        run_cli(
541*61c4878aSAndroid Build Coastguard Worker            'add',
542*61c4878aSAndroid Build Coastguard Worker            '--database',
543*61c4878aSAndroid Build Coastguard Worker            self._db_dir,
544*61c4878aSAndroid Build Coastguard Worker            f'{self._elf}#TEST_DOMAIN',
545*61c4878aSAndroid Build Coastguard Worker            self._elf,
546*61c4878aSAndroid Build Coastguard Worker        )
547*61c4878aSAndroid Build Coastguard Worker        directory = list(self._db_dir.iterdir())
548*61c4878aSAndroid Build Coastguard Worker
549*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(1, len(directory))
550*61c4878aSAndroid Build Coastguard Worker        # Combines CSV_DEFAULT_DOMAIN and TEST_DOMAIN into a unique set
551*61c4878aSAndroid Build Coastguard Worker        # of token entries.
552*61c4878aSAndroid Build Coastguard Worker        entries_from_default_and_test_domain = set(
553*61c4878aSAndroid Build Coastguard Worker            CSV_DEFAULT_DOMAIN.splitlines()
554*61c4878aSAndroid Build Coastguard Worker        ).union(set(CSV_TEST_DOMAIN.splitlines()))
555*61c4878aSAndroid Build Coastguard Worker        # Multiple ELF files were added at once to a single CSV.
556*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(
557*61c4878aSAndroid Build Coastguard Worker            entries_from_default_and_test_domain,
558*61c4878aSAndroid Build Coastguard Worker            set(directory.pop().read_text().splitlines()),
559*61c4878aSAndroid Build Coastguard Worker        )
560*61c4878aSAndroid Build Coastguard Worker
561*61c4878aSAndroid Build Coastguard Worker    def test_discarding_old_entries(self) -> None:
562*61c4878aSAndroid Build Coastguard Worker        """Tests discarding old entries for new entries when re-adding."""
563*61c4878aSAndroid Build Coastguard Worker        self._git('init')
564*61c4878aSAndroid Build Coastguard Worker        # Add CSV_ALL_DOMAINS to a new CSV in the directory database.
565*61c4878aSAndroid Build Coastguard Worker        run_cli(
566*61c4878aSAndroid Build Coastguard Worker            'add',
567*61c4878aSAndroid Build Coastguard Worker            '--database',
568*61c4878aSAndroid Build Coastguard Worker            self._db_dir,
569*61c4878aSAndroid Build Coastguard Worker            '--discard-temporary',
570*61c4878aSAndroid Build Coastguard Worker            'HEAD',
571*61c4878aSAndroid Build Coastguard Worker            f'{self._elf}#.*',
572*61c4878aSAndroid Build Coastguard Worker        )
573*61c4878aSAndroid Build Coastguard Worker        directory = list(self._db_dir.iterdir())
574*61c4878aSAndroid Build Coastguard Worker
575*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(1, len(directory))
576*61c4878aSAndroid Build Coastguard Worker
577*61c4878aSAndroid Build Coastguard Worker        untracked_path_in_db = directory.pop()
578*61c4878aSAndroid Build Coastguard Worker
579*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(
580*61c4878aSAndroid Build Coastguard Worker            CSV_ALL_DOMAINS.splitlines(),
581*61c4878aSAndroid Build Coastguard Worker            untracked_path_in_db.read_text().splitlines(),
582*61c4878aSAndroid Build Coastguard Worker        )
583*61c4878aSAndroid Build Coastguard Worker        # Add CSV_DEFAULT_DOMAIN and CSV_TEST_DOMAIN to a CSV in the
584*61c4878aSAndroid Build Coastguard Worker        # directory database, while replacing entries in CSV_ALL_DOMAINS
585*61c4878aSAndroid Build Coastguard Worker        # that no longer exist.
586*61c4878aSAndroid Build Coastguard Worker        run_cli(
587*61c4878aSAndroid Build Coastguard Worker            'add',
588*61c4878aSAndroid Build Coastguard Worker            '--database',
589*61c4878aSAndroid Build Coastguard Worker            self._db_dir,
590*61c4878aSAndroid Build Coastguard Worker            '--discard-temporary',
591*61c4878aSAndroid Build Coastguard Worker            'HEAD',
592*61c4878aSAndroid Build Coastguard Worker            f'{self._elf}#TEST_DOMAIN',
593*61c4878aSAndroid Build Coastguard Worker            self._elf,
594*61c4878aSAndroid Build Coastguard Worker        )
595*61c4878aSAndroid Build Coastguard Worker        directory = list(self._db_dir.iterdir())
596*61c4878aSAndroid Build Coastguard Worker
597*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(1, len(directory))
598*61c4878aSAndroid Build Coastguard Worker
599*61c4878aSAndroid Build Coastguard Worker        reused_path_in_db = directory.pop()
600*61c4878aSAndroid Build Coastguard Worker        # Combines CSV_DEFAULT_DOMAIN and TEST_DOMAIN.
601*61c4878aSAndroid Build Coastguard Worker        entries_from_default_and_test_domain = set(
602*61c4878aSAndroid Build Coastguard Worker            CSV_DEFAULT_DOMAIN.splitlines()
603*61c4878aSAndroid Build Coastguard Worker        ).union(set(CSV_TEST_DOMAIN.splitlines()))
604*61c4878aSAndroid Build Coastguard Worker
605*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(untracked_path_in_db, reused_path_in_db)
606*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(
607*61c4878aSAndroid Build Coastguard Worker            entries_from_default_and_test_domain,
608*61c4878aSAndroid Build Coastguard Worker            set(reused_path_in_db.read_text().splitlines()),
609*61c4878aSAndroid Build Coastguard Worker        )
610*61c4878aSAndroid Build Coastguard Worker
611*61c4878aSAndroid Build Coastguard Worker    def test_retrieving_csv_from_commit(self) -> None:
612*61c4878aSAndroid Build Coastguard Worker        """Tests retrieving a CSV from a commit and removing temp tokens."""
613*61c4878aSAndroid Build Coastguard Worker        self._git('init')
614*61c4878aSAndroid Build Coastguard Worker        self._git('commit', '--allow-empty', '-m', 'First Commit')
615*61c4878aSAndroid Build Coastguard Worker        # Add CSV_ALL_DOMAINS to a new CSV in the directory database.
616*61c4878aSAndroid Build Coastguard Worker        run_cli('add', '--database', self._db_dir, f'{self._elf}#.*')
617*61c4878aSAndroid Build Coastguard Worker        directory = list(self._db_dir.iterdir())
618*61c4878aSAndroid Build Coastguard Worker
619*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(1, len(directory))
620*61c4878aSAndroid Build Coastguard Worker
621*61c4878aSAndroid Build Coastguard Worker        tracked_path_in_db = directory.pop()
622*61c4878aSAndroid Build Coastguard Worker
623*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(
624*61c4878aSAndroid Build Coastguard Worker            CSV_ALL_DOMAINS.splitlines(),
625*61c4878aSAndroid Build Coastguard Worker            tracked_path_in_db.read_text().splitlines(),
626*61c4878aSAndroid Build Coastguard Worker        )
627*61c4878aSAndroid Build Coastguard Worker        # Commit the CSV to avoid retrieving the CSV with the checks
628*61c4878aSAndroid Build Coastguard Worker        # for untracked changes.
629*61c4878aSAndroid Build Coastguard Worker        self._git('add', '--all')
630*61c4878aSAndroid Build Coastguard Worker        self._git('commit', '-m', 'Adding a CSV to a new commit.')
631*61c4878aSAndroid Build Coastguard Worker        # Retrieve the CSV in HEAD~ and discard tokens that exist in
632*61c4878aSAndroid Build Coastguard Worker        # CSV_ALL_DOMAINS and not exist in CSV_TEST_DOMAIN.
633*61c4878aSAndroid Build Coastguard Worker        run_cli(
634*61c4878aSAndroid Build Coastguard Worker            'add',
635*61c4878aSAndroid Build Coastguard Worker            '--database',
636*61c4878aSAndroid Build Coastguard Worker            self._db_dir,
637*61c4878aSAndroid Build Coastguard Worker            '--discard-temporary',
638*61c4878aSAndroid Build Coastguard Worker            'HEAD~2',
639*61c4878aSAndroid Build Coastguard Worker            f'{self._elf}#TEST_DOMAIN',
640*61c4878aSAndroid Build Coastguard Worker        )
641*61c4878aSAndroid Build Coastguard Worker        directory = list(self._db_dir.iterdir())
642*61c4878aSAndroid Build Coastguard Worker
643*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(1, len(directory))
644*61c4878aSAndroid Build Coastguard Worker
645*61c4878aSAndroid Build Coastguard Worker        reused_path_in_db = directory.pop()
646*61c4878aSAndroid Build Coastguard Worker
647*61c4878aSAndroid Build Coastguard Worker        self.assertEqual(
648*61c4878aSAndroid Build Coastguard Worker            CSV_TEST_DOMAIN.splitlines(),
649*61c4878aSAndroid Build Coastguard Worker            reused_path_in_db.read_text().splitlines(),
650*61c4878aSAndroid Build Coastguard Worker        )
651*61c4878aSAndroid Build Coastguard Worker
652*61c4878aSAndroid Build Coastguard Worker
653*61c4878aSAndroid Build Coastguard Workerif __name__ == '__main__':
654*61c4878aSAndroid Build Coastguard Worker    unittest.main()
655