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':
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': {
'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '8ef97ff3b7332e38e61b347a2fbed425a4617151',
'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
{
'name': 'Fetch WPR archive files',

@ -3372,6 +3372,61 @@ def CheckForNewDEPSDownloadFromGoogleStorageHooks(input_api, output_api):
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):
"""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

@ -225,6 +225,132 @@ class CheckNoUNIT_TESTInSourceFilesTest(unittest.TestCase):
MockInputApi(), MockFile('some/path/source.cc', lines))
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):
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
$ gclient sync
$ autoninja -C out/Linux 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 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

@ -4,45 +4,161 @@
# found in the LICENSE file.
"""
A wrapper script for //third_party/perfetto/tools/test_data. The wrapper
ensures that we upload the correct directory.
A wrapper script for upload_to_google_storage_first_class.py.
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.
If you want to keep locally modified test data, you should upload it before
running `gclient runhooks` otherwise you will lose this data.
# Uploads file and generates .sha256 file
$ ./test_data.py upload data/trace.pftrace
# 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 os
import subprocess
import sys
import json
import re
def main():
parser = argparse.ArgumentParser()
parser.add_argument('cmd', choices=['status', 'download', 'upload'])
parser.add_argument('--verbose', '-v', action='store_true')
args = parser.parse_args()
SRC_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..'))
TEST_DATA_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), 'data'))
DEPOT_TOOLS_PATH = os.path.abspath(os.path.join(SRC_PATH, 'third_party', 'depot_tools'))
sys.path.append(DEPOT_TOOLS_PATH)
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]
if args.verbose:
command.append('--verbose')
# Write .sha256 file to be rolled into Perfetto.
def write_sha256_file(filepath: str):
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(
command,
check=False,
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__':
sys.exit(main())