0

Reland "[Chrome Stdlib] Use DEPS to download diff test data"

This is a reland of commit 6c8f992499

This reland adds the deps entry for chrome_5672_histograms.pftrace.gz
which was missing in the original CL, causing b/342182426 which caused
the original CL to be reverted.

Original change's description:
> [Chrome Stdlib] Use DEPS to download diff test data
>
> We want to download Perfetto test data via GCS dependencies in DEPS
> rather than Perfetto's test_data script. To make this change we:
>
> 1. Modify the test_data.py script to wrap
>    `upload_to_google_storage_first_class.py` instead of Perfetto's
>    `test_data` script.
> 2. Add a Presubmit check to ensure the .sha256 files are in sync with
>    the deps entries.
> 3. Update docs to give instructions for the new workflow
>
> Change-Id: I20616c95553f93603e338dd9fa47e84facfb60d8
> Bug: 312895063
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5545295
> Reviewed-by: Stephen Nusko <nuskos@chromium.org>
> Commit-Queue: Rasika Navarange <rasikan@google.com>
> Reviewed-by: Dominic Battre <battre@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1304407}

Bug: 312895063
Change-Id: I6bcd74324612e5c49ae59bee3f0b20f673d70b81
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5562906
Reviewed-by: Dominic Battre <battre@chromium.org>
Reviewed-by: Stephen Nusko <nuskos@chromium.org>
Commit-Queue: Rasika Navarange <rasikan@google.com>
Cr-Commit-Position: refs/heads/main@{#1305095}
This commit is contained in:
Rasika Navarange
2024-05-23 15:19:02 +00:00
committed by Chromium LUCI CQ
parent e877e150ee
commit c2d33d2eab
5 changed files with 421 additions and 35 deletions

87
DEPS

@@ -1958,6 +1958,83 @@ deps = {
'src/third_party/perfetto': 'src/third_party/perfetto':
Var('android_git') + '/platform/external/perfetto.git' + '@' + '03fe17e0be05dd6c60cf6351a696c1864468b982', Var('android_git') + '/platform/external/perfetto.git' + '@' + '03fe17e0be05dd6c60cf6351a696c1864468b982',
'src/base/tracing/test/data': {
'bucket': 'perfetto',
'objects': [
{
'object_name': 'test_data/chrome_fcp_lcp_navigations.pftrace-ae01d849fbd75a98be1b7ddd5a8873217c377b393a1d5bbd788ed3364f7fefc3',
'sha256sum': 'ae01d849fbd75a98be1b7ddd5a8873217c377b393a1d5bbd788ed3364f7fefc3',
'size_bytes': 2398645,
'generation': 1697714434866488,
'output_file': 'chrome_fcp_lcp_navigations.pftrace'
},
{
'object_name': 'test_data/chrome_input_with_frame_view.pftrace-a93548822e481508c728ccc5da3ad34afcd0aec02ca7a7a4dad84ff340ee5975',
'sha256sum': 'a93548822e481508c728ccc5da3ad34afcd0aec02ca7a7a4dad84ff340ee5975',
'size_bytes': 6392331,
'generation': 1711402389089075,
'output_file': 'chrome_input_with_frame_view.pftrace'
},
{
'object_name': 'test_data/scroll_offsets_trace_2.pftrace-2ddd9f78d91d51e39c72c520bb54fdc9dbf1333ae722e87633fc345159296289',
'sha256sum': '2ddd9f78d91d51e39c72c520bb54fdc9dbf1333ae722e87633fc345159296289',
'size_bytes': 1496388,
'generation': 1712592637141461,
'output_file': 'scroll_offsets_trace_2.pftrace'
},
{
'object_name': 'test_data/top_level_java_choreographer_slices-8001e73b2458e94f65a606bb558a645ba5bca553b57fe416001f6c2175675a8a',
'sha256sum': '8001e73b2458e94f65a606bb558a645ba5bca553b57fe416001f6c2175675a8a',
'size_bytes': 5323017,
'generation': 1671708979893186,
'output_file': 'top_level_java_choreographer_slices'
},
{
'object_name': 'test_data/chrome_page_load_all_categories_not_extended.pftrace.gz-6586e9e2bbc0c996caddb321a0374328654983733e6ffd7f4635ac07db32a493',
'sha256sum': '6586e9e2bbc0c996caddb321a0374328654983733e6ffd7f4635ac07db32a493',
'size_bytes': 1277750,
'generation': 1652442088902445,
'output_file': 'chrome_page_load_all_categories_not_extended.pftrace.gz'
},
{
'object_name': 'test_data/speedometer.perfetto_trace.gz-8a159b354d74a3ca0d38ce9cd071ef47de322db4261ee266bfafe04d70310529',
'sha256sum': '8a159b354d74a3ca0d38ce9cd071ef47de322db4261ee266bfafe04d70310529',
'size_bytes': 891088,
'generation': 1684336047660953,
'output_file': 'speedometer.perfetto_trace.gz'
},
{
'object_name': 'test_data/cpu_powerups_1.pb-70f5511ba0cd6ce1359e3cadb4d1d9301fb6e26be85158e3384b06f41418d386',
'sha256sum': '70f5511ba0cd6ce1359e3cadb4d1d9301fb6e26be85158e3384b06f41418d386',
'size_bytes': 2033064,
'generation': 1669652389509708,
'output_file': 'cpu_powerups_1.pb'
},
{
'object_name': 'test_data/chrome_5672_histograms.pftrace.gz-a09bd44078ac71bcfbc901b0544750e8344d0d0f6f96e220f700a5a53fa932ee',
'sha256sum': 'a09bd44078ac71bcfbc901b0544750e8344d0d0f6f96e220f700a5a53fa932ee',
'size_bytes': 1127472,
'generation': 1684946598804577,
'output_file': 'chrome_5672_histograms.pftrace.gz'
},
{
'object_name': 'test_data/chrome_custom_navigation_trace.gz-ff68279e3cec94076b69259d756eed181a63eaf834d8b956a7f4ba665fabf939',
'sha256sum': 'ff68279e3cec94076b69259d756eed181a63eaf834d8b956a7f4ba665fabf939',
'size_bytes': 7572484,
'generation': 1666713705258900,
'output_file': 'chrome_custom_navigation_trace.gz'
},
{
'object_name': 'test_data/scroll_offsets.pftrace-62101edb5204fec8bea30124f65d4e49bda0808d7b036e95f89445aaad6d8d98',
'sha256sum': '62101edb5204fec8bea30124f65d4e49bda0808d7b036e95f89445aaad6d8d98',
'size_bytes': 769741,
'generation': 1693402148909129,
'output_file': 'scroll_offsets.pftrace'
}
],
'dep_type': 'gcs'
},
'src/third_party/perl': { 'src/third_party/perl': {
'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '8ef97ff3b7332e38e61b347a2fbed425a4617151', 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '8ef97ff3b7332e38e61b347a2fbed425a4617151',
'condition': 'checkout_win', 'condition': 'checkout_win',
@@ -5199,16 +5276,6 @@ hooks = [
], ],
}, },
# Download test data for Perfetto diff tests
{
'name': 'perfetto_testdata',
'condition': 'host_os == "linux"',
'pattern': '\\.sha256',
'action': [ 'vpython3',
'src/base/tracing/test/test_data.py',
'download',
],
},
# Pull down WPR Archive files # Pull down WPR Archive files
{ {
'name': 'Fetch WPR archive files', 'name': 'Fetch WPR archive files',

@@ -3372,6 +3372,61 @@ def CheckForNewDEPSDownloadFromGoogleStorageHooks(input_api, output_api):
return [] return []
def CheckEachPerfettoTestDataFileHasDepsEntry(input_api, output_api):
test_data_filter = lambda f: input_api.FilterSourceFile(
f, files_to_check=[r'^base/tracing/test/data/.*\.sha256'])
if not any(input_api.AffectedFiles(file_filter=test_data_filter)):
return []
# Find DEPS entry
deps_entry = []
for f in input_api.AffectedFiles(include_deletes=False):
if f.LocalPath() == 'DEPS':
new_deps = _ParseDeps('\n'.join(f.NewContents()))['deps']
deps_entry = new_deps['src/base/tracing/test/data']
if not deps_entry:
return [output_api.PresubmitError(
'You must update the DEPS file when you update a '
'.sha256 file in base/tracing/test/data'
)]
output = []
for f in input_api.AffectedFiles(file_filter=test_data_filter):
objects = deps_entry['objects']
if not f.NewContents():
# Deleted file so check that DEPS entry removed
sha256_from_file = f.OldContents()[0]
object_entry = next(
(item for item in objects if item["sha256sum"] == sha256_from_file),
None)
if object_entry:
output.append(output_api.PresubmitError(
'You deleted %s so you must also remove the corresponding DEPS entry.'
% f.LocalPath()
))
continue
sha256_from_file = f.NewContents()[0]
object_entry = next(
(item for item in objects if item["sha256sum"] == sha256_from_file),
None)
if not object_entry:
output.append(output_api.PresubmitError(
'No corresponding DEPS entry found for %s. '
'Run `base/tracing/test/test_data.py get_deps --filepath %s` '
'to generate the DEPS entry.'
% (f.LocalPath(), f.LocalPath())
))
if output:
output.append(output_api.PresubmitError(
'The DEPS entry for `src/base/tracing/test/data` in the DEPS file has not been '
'updated properly. Run `base/tracing/test/test_data.py get_all_deps` to see what '
'the DEPS entry should look like.'
))
return output
def CheckAddedDepsHaveTargetApprovals(input_api, output_api): def CheckAddedDepsHaveTargetApprovals(input_api, output_api):
"""When a dependency prefixed with + is added to a DEPS file, we """When a dependency prefixed with + is added to a DEPS file, we
want to make sure that the change is reviewed by an OWNER of the want to make sure that the change is reviewed by an OWNER of the

@@ -225,6 +225,132 @@ class CheckNoUNIT_TESTInSourceFilesTest(unittest.TestCase):
MockInputApi(), MockFile('some/path/source.cc', lines)) MockInputApi(), MockFile('some/path/source.cc', lines))
self.assertEqual(0, len(errors)) self.assertEqual(0, len(errors))
class CheckEachPerfettoTestDataFileHasDepsEntry(unittest.TestCase):
def testNewSha256FileNoDEPS(self):
input_api = MockInputApi()
input_api.files = [
MockFile('base/tracing/test/data/new.pftrace.sha256', []),
]
results = PRESUBMIT.CheckEachPerfettoTestDataFileHasDepsEntry(input_api, MockOutputApi())
self.assertEqual(
('You must update the DEPS file when you update a .sha256 file '
'in base/tracing/test/data'), results[0].message)
def testNewSha256FileSuccess(self):
input_api = MockInputApi()
new_deps = """deps = {
'src/base/tracing/test/data': {
'bucket': 'perfetto',
'objects': [
{
'object_name': 'test_data/new.pftrace-a1b2c3f4',
'sha256sum': 'a1b2c3f4',
'size_bytes': 1,
'generation': 1,
'output_file': 'new.pftrace'
},
],
'dep_type': 'gcs'
},
}""".splitlines()
input_api.files = [
MockFile('base/tracing/test/data/new.pftrace.sha256', ['a1b2c3f4']),
MockFile('DEPS', new_deps),
]
results = PRESUBMIT.CheckEachPerfettoTestDataFileHasDepsEntry(input_api, MockOutputApi())
self.assertEqual(0, len(results))
def testNewSha256FileWrongSha256(self):
input_api = MockInputApi()
new_deps = """deps = {
'src/base/tracing/test/data': {
'bucket': 'perfetto',
'objects': [
{
'object_name': 'test_data/new.pftrace-a1b2c3f4',
'sha256sum': 'wrong_hash',
'size_bytes': 1,
'generation': 1,
'output_file': 'new.pftrace'
},
],
'dep_type': 'gcs'
},
}""".splitlines()
f = MockFile('base/tracing/test/data/new.pftrace.sha256', ['a1b2c3f4'])
input_api.files = [
f,
MockFile('DEPS', new_deps),
]
results = PRESUBMIT.CheckEachPerfettoTestDataFileHasDepsEntry(input_api, MockOutputApi())
self.assertEqual((
'No corresponding DEPS entry found for %s. '
'Run `base/tracing/test/test_data.py get_deps --filepath %s` '
'to generate the DEPS entry.' % (f.LocalPath(), f.LocalPath())),
results[0].message)
def testDeleteSha256File(self):
input_api = MockInputApi()
old_deps = """deps = {
'src/base/tracing/test/data': {
'bucket': 'perfetto',
'objects': [
{
'object_name': 'test_data/new.pftrace-a1b2c3f4',
'sha256sum': 'a1b2c3f4',
'size_bytes': 1,
'generation': 1,
'output_file': 'new.pftrace'
},
],
'dep_type': 'gcs'
},
}""".splitlines()
f = MockFile('base/tracing/test/data/new.pftrace.sha256', [], ['a1b2c3f4'], action='D')
input_api.files = [
f,
MockFile('DEPS', old_deps),
]
results = PRESUBMIT.CheckEachPerfettoTestDataFileHasDepsEntry(input_api, MockOutputApi())
self.assertEqual((
'You deleted %s so you must also remove the corresponding DEPS entry.'
% f.LocalPath()), results[0].message)
def testDeleteSha256Success(self):
input_api = MockInputApi()
new_deps = """deps = {
'src/base/tracing/test/data': {
'bucket': 'perfetto',
'objects': [],
'dep_type': 'gcs'
},
}""".splitlines()
old_deps = """deps = {
'src/base/tracing/test/data': {
'bucket': 'perfetto',
'objects': [
{
'object_name': 'test_data/new.pftrace-a1b2c3f4',
'sha256sum': 'a1b2c3f4',
'size_bytes': 1,
'generation': 1,
'output_file': 'new.pftrace'
},
],
'dep_type': 'gcs'
},
}""".splitlines()
f = MockFile('base/tracing/test/data/new.pftrace.sha256', [], ['a1b2c3f4'], action='D')
input_api.files = [
f,
MockFile('DEPS', new_deps, old_deps),
]
results = PRESUBMIT.CheckEachPerfettoTestDataFileHasDepsEntry(input_api, MockOutputApi())
self.assertEqual(0, len(results))
class CheckAddedDepsHaveTestApprovalsTest(unittest.TestCase): class CheckAddedDepsHaveTestApprovalsTest(unittest.TestCase):
def calculate(self, old_include_rules, old_specific_include_rules, def calculate(self, old_include_rules, old_specific_include_rules,

@@ -10,6 +10,7 @@ Currently, the diff tests only run on Linux. You can build and run the diff test
``` ```
$ gn gen --args='' out/Linux $ gn gen --args='' out/Linux
$ gclient sync
$ autoninja -C out/Linux perfetto_diff_tests $ autoninja -C out/Linux perfetto_diff_tests
$ out/Linux/bin/run_perfetto_diff_tests $ out/Linux/bin/run_perfetto_diff_tests
``` ```
@@ -22,13 +23,34 @@ Your new diff test should go in `base/tracing/test/trace_processor/diff_tests/ch
If you are adding a **new TestSuite**, be sure to add it to `include_index.py` so the runner knows to run this new TestSuite. If you are adding a **new TestSuite**, be sure to add it to `include_index.py` so the runner knows to run this new TestSuite.
If your test requires modifying or adding new test data i.e. a new trace in `base/tracing/test/data`, you will need to upload this to the GCS bucket. These trace files are too large to be checked-in to the codebase so we check-in only `.sha256` files. You can upload any new traces with this script: ### Adding New Test Data
If your test requires modifying or adding new test data i.e. a new trace in `base/tracing/test/data`, you will need to upload this to the GCS bucket.
``` ```
$ base/tracing/test/test_data.py upload --verbose $ base/tracing/test/test_data.py upload <path_to_file>
``` ```
This script will upload your file and generate the `.sha256` file in the `base/tracing/test/data/` directory. You can then upload this file with your CL. This script will output a deps entry which you will need to add to the [DEPS file](../../../DEPS) (see examples in the `src/base/tracing/test/data` entry).
```
{
"path": {
"dep_type": "gcs",
"bucket": "perfetto",
"objects": [
{
"object_name": "test_data/file_name-a1b2c3f4",
"sha256sum": "a1b2c3f4",
"size_bytes": 12345,
"generation": 1234567
}
]
}
}
```
You will need to **manually** add this to the deps entry. After adding this entry, running `gclient sync` will download the test files in your local repo. See these [docs](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/gcs_dependencies.md) for the GCS dependency workflow.
Perfetto has it's own way to download GCS objects with the [test_data](../../../third_party/perfetto/tools/test_data) script. This script uses .sha256 files to download. The `test_data.py upload` command will also generate the `file_name-a1b2c3f4.sha256` in `base/tracing/test/data`. You will need to check-in these files in the codebase so they can by rolled to Perfetto, so the tests can work there too.
## Writing TestTraceProcessor Tests ## Writing TestTraceProcessor Tests

@@ -4,45 +4,161 @@
# found in the LICENSE file. # found in the LICENSE file.
""" """
A wrapper script for //third_party/perfetto/tools/test_data. The wrapper A wrapper script for upload_to_google_storage_first_class.py.
ensures that we upload the correct directory.
Usage: Usage:
./test_data.py status # Prints the status of new & modified files.
./test_data.py download # To sync remote>local (used by gclient runhooks).
./test_data.py upload # To upload newly created and modified files.
WARNING: the `download` command will overwrite any locally modified files. # Uploads file and generates .sha256 file
If you want to keep locally modified test data, you should upload it before $ ./test_data.py upload data/trace.pftrace
running `gclient runhooks` otherwise you will lose this data.
# Generates deps entry for a single file
$ ./test_data.py get_deps data/trace.pftrace
# Generate full deps entry for all files in base/tracing/test/data/
$ ./test_data.py get_all_deps
The upload command uploads the given file to the gs://perfetto bucket and
generates a .sha256 file in the base/tracing/test/data/ directory,
which is rolled to the Perfetto repository.
The .sha256 file is used by Perfetto to download the files with their
own test_data download script (third_party/perfetto/tools/test_data).
The script outputs a GCS entry which should be manually added to the
deps dict in /DEPS. See
https://chromium.googlesource.com/chromium/src/+/HEAD/docs/gcs_dependencies.md.
The files are uploaded as gs://perfetto/test_data/file_name-a1b2c3f4.
""" """
import argparse import argparse
import os import os
import subprocess import subprocess
import sys import sys
import json
import re
def main(): SRC_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..'))
parser = argparse.ArgumentParser() TEST_DATA_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), 'data'))
parser.add_argument('cmd', choices=['status', 'download', 'upload']) DEPOT_TOOLS_PATH = os.path.abspath(os.path.join(SRC_PATH, 'third_party', 'depot_tools'))
parser.add_argument('--verbose', '-v', action='store_true') sys.path.append(DEPOT_TOOLS_PATH)
args = parser.parse_args() from upload_to_google_storage_first_class import get_sha256sum
from download_from_google_storage import Gsutil, GSUTIL_DEFAULT_PATH
src_root = os.path.abspath(os.path.join(__file__, '..', '..', '..', '..'))
perfetto_dir = os.path.join(src_root, 'third_party', 'perfetto')
tool = os.path.join(perfetto_dir, "tools", "test_data")
test_dir = os.path.join(src_root, 'base', 'tracing', 'test', 'data')
command = ['vpython3', tool, '--dir', test_dir, '--overwrite', args.cmd] # Write .sha256 file to be rolled into Perfetto.
if args.verbose: def write_sha256_file(filepath: str):
command.append('--verbose') sha256sum = get_sha256sum(filepath)
with open(filepath + '.sha256', 'w') as sha_file:
sha_file.write(sha256sum)
return sha256sum
# Run `upload_to_google_storage_first_class.py --bucket perfetto <file>`.
def upload_file(filepath: str, dry_run: bool, force: bool):
sha256sum = write_sha256_file(filepath)
# Perfetto uses 'test_data/file_name-a1b2c3f4' object name format.
object_name = '%s/%s-%s' % ('test_data', os.path.basename(filepath), sha256sum)
tool = 'upload_to_google_storage_first_class.py'
command = [tool, '--bucket', 'perfetto', '--object-name', object_name, filepath]
if dry_run:
command.append('--dry-run')
if force:
command.append('--force')
completed_process = subprocess.run( completed_process = subprocess.run(
command, command,
check=False, check=False,
capture_output=True) capture_output=True)
sys.stderr.buffer.write(completed_process.stderr)
sys.stdout.buffer.write(completed_process.stdout) if completed_process.returncode == 0:
print('Manually add the deps entry below to the DEPS file. See '
'https://chromium.googlesource.com/chromium/src/+/HEAD/docs/gcs_dependencies.md '
'for more details. Run `test_data.py get_all_deps` to get the full deps entry.')
sys.stdout.buffer.write(completed_process.stdout)
else:
sys.stderr.buffer.write(completed_process.stderr)
# Generate the deps entry for `filepath`, assuming it has been uploaded already.
def generate_deps_entry(filepath: str):
sha256sum = get_sha256sum(filepath)
object_name = '%s/%s-%s' % ('test_data', os.path.basename(filepath), sha256sum)
# Run `gcloud storage ls -L gs://perfetto/test_data/file_name-a1b2c3f4` to
# get the 'generation' and 'size_bytes' fields for the deps entry
gsutil = Gsutil(GSUTIL_DEFAULT_PATH)
gsutil_args = ['ls', '-L', 'gs://perfetto/%s' % object_name]
code, out, err = gsutil.check_call(*gsutil_args)
if code != 0:
raise Exception(code, err + ' ' + object_name)
generation = int(out.split()[out.split().index('Generation:') + 1])
size = int(out.split()[out.split().index('Content-Length:') + 1])
return {
'object_name': object_name,
'sha256sum': sha256sum,
'size_bytes': size,
'generation': generation,
'output_file': os.path.basename(filepath),
}
# Generate the full deps entry for Perfetto test data
def generate_all_deps():
path = os.path.join(SRC_PATH, 'base/tracing/test/data')
objects = []
for file in os.listdir(path):
if file.endswith('.sha256'):
filepath = os.path.join(path, file)[:-7]
assert os.path.isfile(filepath), 'File does not exist'
object_entry = generate_deps_entry(filepath)
objects.append(object_entry)
return {
'src/base/tracing/test/data': {
'bucket':
'perfetto',
'objects': objects,
'dep_type':
'gcs',
},
}
def main():
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='cmd')
upload_parser = subparsers.add_parser('upload', help='Upload a file to gs://perfetto')
upload_parser.add_argument('filepath', help='Path to file you want to upload')
upload_parser.add_argument('--dry-run', action='store_true',
help='Check if file already exists on GCS without '
'uploading it and output DEP blob.')
upload_parser.add_argument('-f',
'--force',
action='store_true',
help='Force upload even if remote file exists.')
get_deps_parser = subparsers.add_parser('get_deps', help='Print deps entry for a single file')
get_deps_parser.add_argument('filepath', help='Path to test data file you want the deps entry for.')
subparsers.add_parser('get_all_deps', help='Print deps entry for all files in `base/tracing/test/data/`')
args = parser.parse_args()
if args.cmd == 'get_all_deps':
print(json.dumps(generate_all_deps(), indent=2).replace('"', "'"))
return
filepath = os.path.abspath(args.filepath)
assert os.path.dirname(filepath) == TEST_DATA_PATH, ('You can only '
'upload files in base/tracing/test/data/')
if args.cmd == 'upload':
upload_file(filepath, args.dry_run, args.force)
elif args.cmd == 'get_deps':
print(json.dumps(generate_deps_entry(filepath), indent=2).replace('"', "'"))
if __name__ == '__main__': if __name__ == '__main__':
sys.exit(main()) sys.exit(main())