Skip to content

Commit 6b4ff0f

Browse files
marco-cLa0
authored andcommitted
bot: Generate a "pure" per-chunk mapping file (#105)
1 parent ee1c32d commit 6b4ff0f

File tree

2 files changed

+161
-92
lines changed

2 files changed

+161
-92
lines changed

bot/code_coverage_bot/chunk_mapping.py

Lines changed: 131 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -173,90 +173,139 @@ def is_chunk_only_suite(suite):
173173
return True
174174

175175

176+
def _inner_generate(
177+
repo_dir, revision, artifactsHandler, per_test_cursor, per_chunk_cursor, executor
178+
):
179+
per_test_cursor.execute(
180+
"CREATE TABLE file_to_chunk (path text, platform text, chunk text)"
181+
)
182+
per_test_cursor.execute(
183+
"CREATE TABLE chunk_to_test (platform text, chunk text, path text)"
184+
)
185+
per_test_cursor.execute("CREATE TABLE file_to_test (source text, test text)")
186+
187+
per_chunk_cursor.execute(
188+
"CREATE TABLE file_to_chunk (path text, platform text, chunk text)"
189+
)
190+
per_chunk_cursor.execute(
191+
"CREATE TABLE chunk_to_test (platform text, chunk text, path text)"
192+
)
193+
194+
logger.info("Populating file_to_test table.")
195+
test_coverage_suites = get_test_coverage_suites()
196+
logger.info("Found {} test suites.".format(len(test_coverage_suites)))
197+
for suites in group_by_20k(test_coverage_suites):
198+
test_coverage_tests = get_test_coverage_tests(suites)
199+
for tests in group_by_20k(test_coverage_tests):
200+
tests_files_data = get_test_coverage_files(tests)
201+
202+
source_names = tests_files_data["source.file.name"]
203+
test_iter = enumerate(tests_files_data["test.name"])
204+
source_test_iter = ((source_names[i], test) for i, test in test_iter)
205+
206+
per_test_cursor.executemany(
207+
"INSERT INTO file_to_test VALUES (?,?)", source_test_iter
208+
)
209+
210+
futures = {}
211+
for platform in PLATFORMS:
212+
logger.info("Reading chunk coverage artifacts for {}.".format(platform))
213+
for chunk in artifactsHandler.get_chunks(platform):
214+
assert chunk.strip() != "", "chunk can not be an empty string"
215+
216+
artifacts = artifactsHandler.get(platform=platform, chunk=chunk)
217+
218+
assert len(artifacts) > 0, "There should be at least one artifact"
219+
220+
future = executor.submit(grcov.files_list, artifacts, source_dir=repo_dir)
221+
futures[future] = (platform, chunk)
222+
223+
logger.info("Populating chunk_to_test table for {}.".format(platform))
224+
for suite in get_suites(revision):
225+
tests_data = get_tests_chunks(revision, platform, suite)
226+
if len(tests_data) == 0:
227+
logger.warn(
228+
"No tests found for platform {} and suite {}.".format(
229+
platform, suite
230+
)
231+
)
232+
continue
233+
234+
logger.info(
235+
"Adding tests for platform {} and suite {}".format(platform, suite)
236+
)
237+
task_names = tests_data["run.key"]
238+
239+
def chunk_test_iter():
240+
test_iter = enumerate(tests_data["result.test"])
241+
return (
242+
(platform, taskcluster.get_chunk(task_names[i]), test)
243+
for i, test in test_iter
244+
)
245+
246+
if is_chunk_only_suite(suite):
247+
per_test_cursor.executemany(
248+
"INSERT INTO chunk_to_test VALUES (?,?,?)", chunk_test_iter()
249+
)
250+
251+
per_chunk_cursor.executemany(
252+
"INSERT INTO chunk_to_test VALUES (?,?,?)", chunk_test_iter()
253+
)
254+
255+
logger.info("Populating file_to_chunk table.")
256+
for future in concurrent.futures.as_completed(futures):
257+
(platform, chunk) = futures[future]
258+
files = future.result()
259+
260+
suite = taskcluster.get_suite(chunk)
261+
if is_chunk_only_suite(suite):
262+
per_test_cursor.executemany(
263+
"INSERT INTO file_to_chunk VALUES (?,?,?)",
264+
((f, platform, chunk) for f in files),
265+
)
266+
267+
per_chunk_cursor.executemany(
268+
"INSERT INTO file_to_chunk VALUES (?,?,?)",
269+
((f, platform, chunk) for f in files),
270+
)
271+
272+
176273
def generate(repo_dir, revision, artifactsHandler, out_dir="."):
177274
logger.info("Generating chunk mapping...")
178-
sqlite_file = os.path.join(out_dir, "chunk_mapping.sqlite")
179-
tarxz_file = os.path.join(out_dir, "chunk_mapping.tar.xz")
180-
181-
with sqlite3.connect(sqlite_file) as conn:
182-
logger.info("Creating tables.")
183-
c = conn.cursor()
184-
c.execute("CREATE TABLE file_to_chunk (path text, platform text, chunk text)")
185-
c.execute("CREATE TABLE chunk_to_test (platform text, chunk text, path text)")
186-
c.execute("CREATE TABLE file_to_test (source text, test text)")
187-
188-
logger.info("Populating file_to_test table.")
189-
test_coverage_suites = get_test_coverage_suites()
190-
logger.info("Found {} test suites.".format(len(test_coverage_suites)))
191-
for suites in group_by_20k(test_coverage_suites):
192-
test_coverage_tests = get_test_coverage_tests(suites)
193-
for tests in group_by_20k(test_coverage_tests):
194-
tests_files_data = get_test_coverage_files(tests)
195-
196-
source_names = tests_files_data["source.file.name"]
197-
test_iter = enumerate(tests_files_data["test.name"])
198-
source_test_iter = ((source_names[i], test) for i, test in test_iter)
199-
200-
c.executemany("INSERT INTO file_to_test VALUES (?,?)", source_test_iter)
201-
202-
with ThreadPoolExecutor(max_workers=4) as executor:
203-
futures = {}
204-
for platform in PLATFORMS:
205-
logger.info("Reading chunk coverage artifacts for {}.".format(platform))
206-
for chunk in artifactsHandler.get_chunks(platform):
207-
suite = taskcluster.get_suite(chunk)
208-
if not is_chunk_only_suite(suite):
209-
continue
210-
211-
assert chunk.strip() != "", "chunk can not be an empty string"
212-
213-
artifacts = artifactsHandler.get(platform=platform, chunk=chunk)
214-
215-
assert len(artifacts) > 0, "There should be at least one artifact"
216-
217-
future = executor.submit(
218-
grcov.files_list, artifacts, source_dir=repo_dir
219-
)
220-
futures[future] = (platform, chunk)
221-
222-
logger.info("Populating chunk_to_test table for {}.".format(platform))
223-
for suite in get_suites(revision):
224-
if not is_chunk_only_suite(suite):
225-
continue
226-
227-
tests_data = get_tests_chunks(revision, platform, suite)
228-
if len(tests_data) == 0:
229-
logger.warn(
230-
"No tests found for platform {} and suite {}.".format(
231-
platform, suite
232-
)
233-
)
234-
continue
235-
236-
logger.info(
237-
"Adding tests for platform {} and suite {}".format(
238-
platform, suite
239-
)
240-
)
241-
task_names = tests_data["run.key"]
242-
test_iter = enumerate(tests_data["result.test"])
243-
chunk_test_iter = (
244-
(platform, taskcluster.get_chunk(task_names[i]), test)
245-
for i, test in test_iter
246-
)
247-
c.executemany(
248-
"INSERT INTO chunk_to_test VALUES (?,?,?)", chunk_test_iter
249-
)
250275

251-
logger.info("Populating file_to_chunk table.")
252-
for future in concurrent.futures.as_completed(futures):
253-
(platform, chunk) = futures[future]
254-
files = future.result()
255-
c.executemany(
256-
"INSERT INTO file_to_chunk VALUES (?,?,?)",
257-
((f, platform, chunk) for f in files),
276+
# TODO: Change chunk_mapping to test_mapping, but the name should be synced in mozilla-central
277+
# in the coverage selector!
278+
per_test_sqlite_file = os.path.join(out_dir, "chunk_mapping.sqlite")
279+
per_test_tarxz_file = os.path.join(out_dir, "chunk_mapping.tar.xz")
280+
281+
per_chunk_sqlite_file = os.path.join(out_dir, "per_chunk_mapping.sqlite")
282+
per_chunk_tarxz_file = os.path.join(out_dir, "per_chunk_mapping.tar.xz")
283+
284+
logger.info("Creating tables.")
285+
with sqlite3.connect(per_test_sqlite_file) as per_test_conn:
286+
per_test_cursor = per_test_conn.cursor()
287+
288+
with sqlite3.connect(per_chunk_sqlite_file) as per_chunk_conn:
289+
per_chunk_cursor = per_chunk_conn.cursor()
290+
291+
with ThreadPoolExecutor(max_workers=4) as executor:
292+
_inner_generate(
293+
repo_dir,
294+
revision,
295+
artifactsHandler,
296+
per_test_cursor,
297+
per_chunk_cursor,
298+
executor,
258299
)
259300

260-
logger.info("Writing the chunk mapping archive at {}.".format(tarxz_file))
261-
with tarfile.open(tarxz_file, "w:xz") as tar:
262-
tar.add(sqlite_file, os.path.basename(sqlite_file))
301+
logger.info(
302+
"Writing the per-test mapping archive at {}.".format(per_test_tarxz_file)
303+
)
304+
with tarfile.open(per_test_tarxz_file, "w:xz") as tar:
305+
tar.add(per_test_sqlite_file, os.path.basename(per_test_sqlite_file))
306+
307+
logger.info(
308+
"Writing the per-chunk mapping archive at {}.".format(per_chunk_tarxz_file)
309+
)
310+
with tarfile.open(per_chunk_tarxz_file, "w:xz") as tar:
311+
tar.add(per_chunk_sqlite_file, os.path.basename(per_chunk_sqlite_file))

bot/tests/test_chunk_mapping.py

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def __init__(self):
2222
pass
2323

2424
def get_chunks(self, platform):
25-
return {"chunk1", "chunk2"}
25+
return {"chunk1", "chunk2", "mochitest"}
2626

2727
def get(self, platform=None, suite=None, chunk=None):
2828
if platform == "linux" and chunk == "chunk1":
@@ -33,6 +33,8 @@ def get(self, platform=None, suite=None, chunk=None):
3333
return [grcov_existing_file_artifact] # code_coverage_bot/cli.py
3434
elif platform == "windows" and chunk == "chunk2":
3535
return [grcov_uncovered_function_artifact] # js/src/jit/JIT.cpp
36+
elif platform in ["linux", "windows"] and chunk == "mochitest":
37+
return [grcov_artifact] # js/src/jit/BitSet.cpp
3638

3739
return FakeArtifactsHandler()
3840

@@ -44,12 +46,10 @@ def assert_file_to_test(c, source_path, test_path):
4446
assert results[0][0] == test_path
4547

4648

47-
def assert_file_to_chunk(c, path, platform, chunk):
49+
def assert_file_to_chunk(c, path, expected_results):
4850
c.execute("SELECT platform, chunk FROM file_to_chunk WHERE path=?", (path,))
4951
results = c.fetchall()
50-
assert len(results) == 1
51-
assert results[0][0] == platform
52-
assert results[0][1] == chunk
52+
assert set(results) == set(expected_results)
5353

5454

5555
def assert_chunk_to_test(c, platform, chunk, tests):
@@ -112,7 +112,7 @@ def request_callback(request):
112112
requested_suite = payload["where"]["and"][2]["eq"][
113113
"run.suite.fullname"
114114
]
115-
if requested_suite == "gtest":
115+
if requested_suite in ["gtest", "talos"]:
116116
data = {}
117117
elif requested_suite == "marionette":
118118
prefix = payload["where"]["and"][3]["prefix"]["run.key"]
@@ -172,12 +172,32 @@ def request_callback(request):
172172
"netwerk/test/unit/test_substituting_protocol_handler.js",
173173
)
174174

175-
assert_file_to_chunk(c, "js/src/jit/BitSet.cpp", "linux", "chunk1")
175+
assert_file_to_chunk(c, "js/src/jit/BitSet.cpp", [("linux", "chunk1")])
176+
assert_file_to_chunk(
177+
c, "toolkit/components/osfile/osfile.jsm", [("linux", "chunk2")]
178+
)
179+
assert_file_to_chunk(c, "code_coverage_bot/cli.py", [("windows", "chunk1")])
180+
assert_file_to_chunk(c, "js/src/jit/JIT.cpp", [("windows", "chunk2")])
181+
182+
assert_chunk_to_test(c, "linux", "marionette-headless", ["marionette-test1"])
183+
assert_chunk_to_test(c, "windows", "marionette", ["marionette-test2"])
184+
185+
with tarfile.open(os.path.join(tmp_path, "per_chunk_mapping.tar.xz")) as t:
186+
t.extract("per_chunk_mapping.sqlite", tmp_path)
187+
188+
with sqlite3.connect(os.path.join(tmp_path, "per_chunk_mapping.sqlite")) as conn:
189+
c = conn.cursor()
190+
191+
assert_file_to_chunk(
192+
c,
193+
"js/src/jit/BitSet.cpp",
194+
[("linux", "chunk1"), ("linux", "mochitest"), ("windows", "mochitest")],
195+
)
176196
assert_file_to_chunk(
177-
c, "toolkit/components/osfile/osfile.jsm", "linux", "chunk2"
197+
c, "toolkit/components/osfile/osfile.jsm", [("linux", "chunk2")]
178198
)
179-
assert_file_to_chunk(c, "code_coverage_bot/cli.py", "windows", "chunk1")
180-
assert_file_to_chunk(c, "js/src/jit/JIT.cpp", "windows", "chunk2")
199+
assert_file_to_chunk(c, "code_coverage_bot/cli.py", [("windows", "chunk1")])
200+
assert_file_to_chunk(c, "js/src/jit/JIT.cpp", [("windows", "chunk2")])
181201

182202
assert_chunk_to_test(c, "linux", "marionette-headless", ["marionette-test1"])
183203
assert_chunk_to_test(c, "windows", "marionette", ["marionette-test2"])

0 commit comments

Comments
 (0)