Branch: refs/heads/main
Home: https://github.com/WebKit/WebKit
Commit: 363100839eacb5718060931341fde67887fbe01d
https://github.com/WebKit/WebKit/commit/363100839eacb5718060931341fde67887fbe01d
Author: Issac Roy <[email protected]>
Date: 2026-08-12 (Wed, 12 Aug 2026)
Changed paths:
M Tools/Scripts/libraries/resultsdbpy/resultsdbpy/controller/api_routes.py
A
Tools/Scripts/libraries/resultsdbpy/resultsdbpy/controller/ews_controller.py
A
Tools/Scripts/libraries/resultsdbpy/resultsdbpy/controller/ews_controller_unittest.py
M Tools/Scripts/libraries/resultsdbpy/resultsdbpy/model/ews_context.py
M
Tools/Scripts/libraries/resultsdbpy/resultsdbpy/model/ews_context_unittest.py
M
Tools/Scripts/libraries/resultsdbpy/resultsdbpy/model/mock_model_factory.py
M Tools/Scripts/libraries/resultsdbpy/resultsdbpy/model/model.py
Log Message:
-----------
Add results database API endpoints to upload and query EWS test results
https://bugs.webkit.org/show_bug.cgi?id=320031
rdar://182965776
Reviewed by Aakash Jain.
resultsdbpy can store EWS test results, but exposes no HTTP interface for
clients to write or read them. Add an EWSController with three endpoints:
POST /api/upload/ews to register a run's per-test results (optionally tagged
with a flaky_type), GET /api/results-ews to query a single test's history for
one or more configurations, selecting either the failure or flaky table and
windowing by time, and GET /api/results-ews/tests to enumerate the recorded
test names. Wire the routes into APIRoutes.
The read endpoints sit beside /api/results-summary rather than under
/api/results/, whose grammar is /results/<path:suite>, so a static rule there
would claim URLs out of a namespace that accepts any suite name.
Results are clustered by start_time, so a commit range could only be honored
as a filter applied in Python to rows Cassandra had already truncated with
LIMIT. Rather than answer such a query wrongly, find rejects ref, uuid,
timestamp, begin and end with a 400 and windows by time alone. Serving them
needs a by-commit table, as suite_results_by_commit is to
suite_results_by_start_time.
Results are partitioned by configuration and test name, so they can only be
read back for a test whose name is already known. Nothing recorded those
names: the shared test_names_by_suite belongs to TestContext and is not
written by EWSContext. Each reported name is now recorded in an index
partitioned by suite and by which of the two tables it belongs to, written in
the same batch as its results and sharing the same TTL, so a name never
outlives the data it points at. Like test_names_by_suite, that index is not
keyed by configuration or branch.
EWSContext was constructed without ttl_seconds, so rows in
ews_failed_tests_by_start_time were written with no TTL and would never
expire; it now inherits default_ttl_seconds while flaky rows keep the shorter
FLAKY_TTL_SECONDS. A TTL is applied per write, so this changes rows written
from now on rather than anything already stored.
* Tools/Scripts/libraries/resultsdbpy/resultsdbpy/controller/api_routes.py:
(APIRoutes.__init__): Construct an EWSController and register the /upload/ews
(POST), /results-ews (GET) and /results-ews/tests (GET) routes.
* Tools/Scripts/libraries/resultsdbpy/resultsdbpy/controller/ews_controller.py:
Added.
(EWSController):
(EWSController.__init__): Hold the commit controller and EWS context.
(EWSController.upload): Handle POST /api/upload/ews; validate the payload
(configuration, suite, test_results, timestamp, flaky_type, details, commits),
aborting 400 on anything missing or malformed, then record the results via
ews_context. Commits are registered only once every check that can abort has
passed.
(EWSController.find): Handle GET /api/results-ews; query a test's results for
the requested configurations from the failure or flaky table, honoring recent
and time bounds, and return them grouped by configuration (404 when none
match).
(EWSController.list_tests): Handle GET /api/results-ews/tests; return the
recorded test names for a suite, optionally filtered by table and name prefix.
*
Tools/Scripts/libraries/resultsdbpy/resultsdbpy/controller/ews_controller_unittest.py:
Added.
(EWSControllerTest):
(EWSControllerTest.setup_webserver): Stand up a mock model and register the
API routes.
(EWSControllerTest.upload_ews_results): Helper that POSTs a results payload to
the upload endpoint.
(EWSControllerTest.find_ews_results): Helper that GETs the results endpoint
with the default configuration.
(EWSControllerTest.test_upload_and_find): Upload failures with metadata and
read them back, asserting details round-trip.
(EWSControllerTest.test_flaky_upload_and_find): Upload a flaky result and
confirm it is queryable via flaky=true, separate from the failure table.
(EWSControllerTest.test_invalid_upload_body_is_not_an_object): A body that is
not a json object is rejected with 400 rather than raising AttributeError.
(EWSControllerTest.test_invalid_upload_missing_suite): A payload without a
suite is rejected with 400.
(EWSControllerTest.test_invalid_upload_malformed_columns): Values that would
otherwise reach a cqlengine column or a commit lookup are rejected with 400.
(EWSControllerTest.test_invalid_upload_missing_commits): A payload without
commits is rejected with 400.
(EWSControllerTest.test_invalid_upload_missing_test_results): A payload
without test results is rejected with 400.
(EWSControllerTest.test_no_results): Querying a test with no stored results
returns 404.
(EWSControllerTest.test_find_rejects_a_commit_range): Commit-range parameters
are rejected with 400.
(EWSControllerTest.test_find_without_test): A query missing the test parameter
is rejected with 400.
(EWSControllerTest.test_upload_omitting_optional_fields): Optional fields may
be omitted from the payload.
(EWSControllerTest.test_invalid_upload_non_string_suite): A payload whose
suite is not a string is rejected with 400.
(EWSControllerTest.test_upload_preserves_commit_metadata): Uploading against a
registered commit leaves its author and message intact.
(EWSControllerTest.test_upload_registers_an_unsynced_commit): A commit the
database has not seen is registered by the upload.
(EWSControllerTest.test_upload_reports_the_tests_it_stored): The response
names the tests that were written.
(EWSControllerTest.test_invalid_upload_partial_configuration): A configuration
that cannot be written is rejected with 400.
(EWSControllerTest.test_time_window_query):
(EWSControllerTest.test_time_window_query.start_times_after): Results are
windowed by after_time and before_time.
(EWSControllerTest.test_list_tests): Recorded test names are enumerable.
(EWSControllerTest.test_list_tests_flaky): The flaky and failure indices are
separate.
(EWSControllerTest.test_list_tests_prefix): Names can be filtered by prefix.
(EWSControllerTest.test_list_tests_honours_the_limit_across_prefixes): The
limit applies to the whole response, not to each prefix.
(EWSControllerTest.test_list_tests_percent_encoded_name): A name containing a
query character survives percent-encoding.
(EWSControllerTest.test_list_tests_without_suite): A query missing the suite
parameter is rejected with 400.
(EWSControllerTest.test_list_tests_empty): Finding no tests returns an empty
list rather than 404.
* Tools/Scripts/libraries/resultsdbpy/resultsdbpy/model/ews_context.py:
(EWSContext):
(EWSContext.EWSTestNameBySuite): Index of test names by suite and table.
(EWSContext.__init__): Create the index table.
(EWSContext.record_results): Renamed from register, which named a callback
contract this does not keep: it returns the stored test names and raises
rather than returning partial_status. Record each reported name once per
table, with the same TTL as its results, coercing that TTL to an int since
cqlengine formats it straight into the CQL.
(EWSContext.names): Return the recorded names, optionally restricted to a name
prefix, as a list rather than a generator built inside the keyspace context.
(EWSContext.find_for_test): Query a test's results, bounded by start time.
* Tools/Scripts/libraries/resultsdbpy/resultsdbpy/model/ews_context_unittest.py:
(EWSContextTest):
(EWSContextTest.test_ttl_is_an_integer): Every TTL reaching Cassandra is an
int.
(EWSContextTest.test_record_results_reports_the_tests_it_stored):
(EWSContextTest.test_record_results_raises_when_the_write_is_rejected):
(EWSContextTest.test_names_enumerates_reported_tests):
(EWSContextTest.test_names_separates_flaky_from_failed):
(EWSContextTest.test_names_filters_by_prefix): Cover the index.
* Tools/Scripts/libraries/resultsdbpy/resultsdbpy/model/mock_model_factory.py:
(MockModelFactory.add_mock_ews_results): Follow the rename.
* Tools/Scripts/libraries/resultsdbpy/resultsdbpy/model/model.py:
(Model.__init__): Pass default_ttl_seconds to EWSContext.
Canonical link: https://commits.webkit.org/319085@main
To unsubscribe from these emails, change your notification settings at
https://github.com/WebKit/WebKit/settings/notifications