diff --git a/kcidb/db/bigquery/__init__.py b/kcidb/db/bigquery/__init__.py index 9fe8b291..2b70b3c0 100644 --- a/kcidb/db/bigquery/__init__.py +++ b/kcidb/db/bigquery/__init__.py @@ -2,7 +2,7 @@ import textwrap from kcidb.db.schematic import Driver as SchematicDriver -from kcidb.db.bigquery.v04_02 import Schema as LatestSchema +from kcidb.db.bigquery.v04_03 import Schema as LatestSchema class Driver(SchematicDriver): diff --git a/kcidb/db/bigquery/v04_00.py b/kcidb/db/bigquery/v04_00.py index 1229667a..7ddb0e0e 100644 --- a/kcidb/db/bigquery/v04_00.py +++ b/kcidb/db/bigquery/v04_00.py @@ -542,8 +542,12 @@ class Schema(AbstractSchema): " origin,\n" " path,\n" " environment.comment AS environment_comment,\n" + " NULL AS environment_compatible,\n" " environment.misc AS environment_misc,\n" " status,\n" + " NULL AS number_value,\n" + " NULL AS number_unit,\n" + " NULL AS number_prefix,\n" " waived,\n" " start_time,\n" " duration,\n" diff --git a/kcidb/db/bigquery/v04_03.py b/kcidb/db/bigquery/v04_03.py new file mode 100644 index 00000000..c2572a64 --- /dev/null +++ b/kcidb/db/bigquery/v04_03.py @@ -0,0 +1,117 @@ +"""Kernel CI report database - BigQuery schema v4.3""" + +import logging +from google.cloud.bigquery.schema import SchemaField as Field +from google.cloud.bigquery.table import Table +import kcidb.io as io +from kcidb.misc import merge_dicts +from .v04_02 import Schema as PreviousSchema + +# Module's logger +LOGGER = logging.getLogger(__name__) + + +class Schema(PreviousSchema): + """BigQuery database schema v4.3""" + + # The schema's version. + version = (4, 3) + # The I/O schema the database schema supports + io = io.schema.V4_4 + + # Test environment fields + ENVIRONMENT_FIELDS = PreviousSchema.ENVIRONMENT_FIELDS + ( + Field( + "compatible", "STRING", mode="REPEATED", + description="The values from the root-level " + "'compatible' property of the system's " + "device tree, if any, in the same order" + ), + ) + + # Number fields + NUMBER_FIELDS = ( + Field( + "value", "FLOAT64", + description="The floating-point output value", + ), + Field( + "unit", "STRING", + description="The (compound) unit symbol(s) the value " + "is measured in", + ), + Field( + "prefix", "STRING", + description="The type of prefix to add to the " + "unit, after the value is scaled. " + "If unit is not specified, the prefix " + "alone is used in its place. " + "Either \"metric\" or \"binary\".", + ), + ) + + # A map of table names to their BigQuery schemas + TABLE_MAP = merge_dicts( + PreviousSchema.TABLE_MAP, + tests=list(filter( + lambda f: f.name != "environment", + PreviousSchema.TABLE_MAP["tests"] + )) + [ + Field( + "environment", "RECORD", fields=ENVIRONMENT_FIELDS, + description="The environment the test ran in. " + "E.g. a host, a set of hosts, or a lab; " + "amount of memory/storage/CPUs, for each host; " + "process environment variables, etc.", + ), + Field( + "number", "RECORD", fields=NUMBER_FIELDS, + description="The numerical output produced by the test", + ), + ] + ) + + # Queries for each type of raw object-oriented data + OO_QUERIES = merge_dicts( + PreviousSchema.OO_QUERIES, + test="SELECT\n" + " id,\n" + " build_id,\n" + " origin,\n" + " path,\n" + " environment.comment AS environment_comment,\n" + " environment.compatible AS environment_compatible,\n" + " environment.misc AS environment_misc,\n" + " status,\n" + " number.value AS number_value,\n" + " number.unit AS number_unit,\n" + " number.prefix AS number_prefix,\n" + " waived,\n" + " start_time,\n" + " duration,\n" + " output_files,\n" + " log_url,\n" + " log_excerpt,\n" + " comment,\n" + " misc\n" + "FROM tests", + ) + + @classmethod + def _inherit(cls, conn): + """ + Inerit the database data from the previous schema version (if any). + + Args: + conn: Connection to the database to inherit. The database must + comply with the previous version of the schema. + """ + assert isinstance(conn, cls.Connection) + # Add the new fields to the "tests" table. + tests_table = Table(conn.dataset_ref.table("_tests")) + tests_table.schema = cls.TABLE_MAP["tests"] + conn.client.update_table(tests_table, ["schema"]) + # Update the view + tests_view = Table(conn.dataset_ref.table("tests")) + tests_view.view_query = cls._format_view_query(conn, "tests") + conn.client.update_table(tests_view, ["view_query"]) diff --git a/kcidb/db/postgresql/__init__.py b/kcidb/db/postgresql/__init__.py index 6df74568..f8f6abb2 100644 --- a/kcidb/db/postgresql/__init__.py +++ b/kcidb/db/postgresql/__init__.py @@ -2,7 +2,7 @@ import textwrap from kcidb.db.schematic import Driver as SchematicDriver -from kcidb.db.postgresql.v04_07 import Schema as LatestSchema +from kcidb.db.postgresql.v04_08 import Schema as LatestSchema class Driver(SchematicDriver): diff --git a/kcidb/db/postgresql/schema.py b/kcidb/db/postgresql/schema.py index f4c1195b..015f011e 100644 --- a/kcidb/db/postgresql/schema.py +++ b/kcidb/db/postgresql/schema.py @@ -89,6 +89,32 @@ def __init__(self, constraint=None, metadata_expr=metadata_expr) +class TextArrayColumn(Column): + """A text array column schema""" + + def __init__(self, constraint=None, + conflict_func=None, metadata_expr=None): + """ + Initialize the column schema. + + Args: + constraint: The column's constraint. + A member of the Constraint enum, or None, + meaning no constraint. + conflict_func: The (non-empty) string containing the name of the + SQL function to use to resolve insertion conflicts + for this column. None to resolve + non-deterministically. + metadata_expr: A (non-empty) SQL expression string to use as the + value for this (metadata) column, if not supplied + explicitly. None to consider this a normal column. + """ + assert constraint is None or isinstance(constraint, Constraint) + super().__init__("TEXT[]", constraint=constraint, + conflict_func=conflict_func, + metadata_expr=metadata_expr) + + class JSONColumn(Column): """A JSON column schema""" @@ -230,7 +256,7 @@ def __init__(self, columns, primary_key=None): class Index(_SQLIndex): """An index schema""" - def __init__(self, table, columns): + def __init__(self, table, columns, method=None): """ Initialize the index schema. @@ -239,9 +265,30 @@ def __init__(self, table, columns): columns: The list of names of table columns belonging to the index. The names consist of dot-separated parts, same as used for the Table creation parameters. + method: The index method string, if different from default. + None, if the default should be used. """ assert isinstance(table, str) assert isinstance(columns, list) assert all(isinstance(c, str) for c in columns) + assert method is None or isinstance(method, str) and method # TODO: Switch to hardcoding "_" key_sep in base class super().__init__(table, columns, key_sep="_") + self.method = method + + def format_create(self, name): + """ + Format the "CREATE INDEX" command for the table. + + Args: + name: The name of the target index of the command. + + Returns: + The formatted "CREATE INDEX" command. + """ + method = "" if self.method is None else f" USING {self.method}" + return ( + f"CREATE INDEX IF NOT EXISTS {name} ON {self.table}{method} (" + + ", ".join(self.columns.values()) + + ")" + ) diff --git a/kcidb/db/postgresql/v04_00.py b/kcidb/db/postgresql/v04_00.py index 93b6f22b..aa8e47ec 100644 --- a/kcidb/db/postgresql/v04_00.py +++ b/kcidb/db/postgresql/v04_00.py @@ -15,7 +15,7 @@ Connection as AbstractConnection from kcidb.db.postgresql.schema import \ Constraint, BoolColumn, FloatColumn, IntegerColumn, TimestampColumn, \ - VarcharColumn, TextColumn, JSONColumn, Table + VarcharColumn, TextColumn, TextArrayColumn, JSONColumn, Table # Module's logger LOGGER = logging.getLogger(__name__) @@ -381,10 +381,14 @@ class Schema(AbstractSchema): " origin,\n" " path,\n" " environment_comment,\n" + " NULL AS environment_compatible,\n" " environment_misc,\n" " log_url,\n" " log_excerpt,\n" " status,\n" + " NULL AS number_value,\n" + " NULL AS number_unit,\n" + " NULL AS number_prefix,\n" " waived,\n" " start_time,\n" " duration,\n" @@ -398,10 +402,14 @@ class Schema(AbstractSchema): origin=TextColumn(), path=TextColumn(), environment_comment=TextColumn(), + environment_compatible=TextArrayColumn(), environment_misc=JSONColumn(), log_url=TextColumn(), log_excerpt=TextColumn(), status=TextColumn(), + number_value=FloatColumn(), + number_unit=TextColumn(), + number_prefix=TextColumn(), waived=BoolColumn(), start_time=TimestampColumn(), duration=FloatColumn(), diff --git a/kcidb/db/postgresql/v04_08.py b/kcidb/db/postgresql/v04_08.py new file mode 100644 index 00000000..85139420 --- /dev/null +++ b/kcidb/db/postgresql/v04_08.py @@ -0,0 +1,167 @@ +"""Kernel CI report database - PostgreSQL schema v4.8""" + +import textwrap +from kcidb.misc import merge_dicts +import kcidb.io as io +from kcidb.db.postgresql.schema import \ + Column, FloatColumn, TextColumn, TextArrayColumn, JSONColumn, \ + BoolColumn, TimestampColumn, Table, Index +from .v04_07 import Schema as PreviousSchema + + +CREATE_UNIT_PREFIX_TYPE_STATEMENT = textwrap.dedent("""\ + DO $$ BEGIN + CREATE TYPE UNIT_PREFIX AS ENUM ('metric', 'binary'); + EXCEPTION + WHEN duplicate_object THEN null; + END $$ +""") + + +# It's OK, pylint: disable=too-many-ancestors +class Schema(PreviousSchema): + """PostgreSQL database schema v4.8""" + + # The schema's version. + version = (4, 8) + # The I/O schema the database schema supports + io = io.schema.V4_4 + + # A map of table names and Table constructor arguments + # For use by descendants + # Add environment_compatible, and number_* fields + TABLES_ARGS = merge_dicts( + PreviousSchema.TABLES_ARGS, + tests=merge_dicts( + PreviousSchema.TABLES_ARGS["tests"], + columns=merge_dicts( + PreviousSchema.TABLES_ARGS["tests"]["columns"], + { + "environment.compatible": TextArrayColumn(), + "number.value": FloatColumn(), + "number.unit": TextColumn(), + "number.prefix": Column("UNIT_PREFIX") + } + ) + ) + ) + + # A map of table names and schemas + TABLES = { + name: Table(**args) for name, args in TABLES_ARGS.items() + } + + # A map of index names and schemas + INDEXES = merge_dicts(PreviousSchema.INDEXES, dict( + tests_environment_compatible=Index("tests", + ["environment_compatible"], + method="GIN"), + tests_number_value=Index("tests", ["number_value"]), + tests_number_unit=Index("tests", ["number_unit"]), + )) + + # Queries and their columns for each type of raw object-oriented data. + # Both should have columns in the same order. + OO_QUERIES = merge_dicts( + PreviousSchema.OO_QUERIES, + test=dict( + statement="SELECT\n" + " id,\n" + " build_id,\n" + " origin,\n" + " path,\n" + " environment_comment,\n" + " environment_compatible,\n" + " environment_misc,\n" + " log_url,\n" + " log_excerpt,\n" + " status,\n" + " number_value,\n" + " number_unit,\n" + " number_prefix,\n" + " waived,\n" + " start_time,\n" + " duration,\n" + " output_files,\n" + " comment,\n" + " misc\n" + "FROM tests", + schema=Table(dict( + id=TextColumn(), + build_id=TextColumn(), + origin=TextColumn(), + path=TextColumn(), + environment_comment=TextColumn(), + environment_compatible=TextArrayColumn(), + environment_misc=JSONColumn(), + log_url=TextColumn(), + log_excerpt=TextColumn(), + status=TextColumn(), + number_value=FloatColumn(), + number_unit=TextColumn(), + number_prefix=TextColumn(), + waived=BoolColumn(), + start_time=TimestampColumn(), + duration=FloatColumn(), + output_files=JSONColumn(), + comment=TextColumn(), + misc=JSONColumn(), + )), + ), + ) + + def init(self): + """ + Initialize the database. + The database must be uninitialized. + """ + with self.conn, self.conn.cursor() as cursor: + cursor.execute(CREATE_UNIT_PREFIX_TYPE_STATEMENT) + super().init() + + def cleanup(self): + """ + Cleanup (deinitialize) the database, removing all data. + The database must be initialized. + """ + super().cleanup() + with self.conn, self.conn.cursor() as cursor: + cursor.execute("DROP TYPE IF EXISTS UNIT_PREFIX") + + @classmethod + def _inherit(cls, conn): + """ + Inerit the database data from the previous schema version (if any). + + Args: + conn: Connection to the database to inherit. The database must + comply with the previous version of the schema. + """ + assert isinstance(conn, cls.Connection) + with conn, conn.cursor() as cursor: + cursor.execute(CREATE_UNIT_PREFIX_TYPE_STATEMENT) + # For all tables + for name, schema in cls.TABLES.items(): + if name not in PreviousSchema.TABLES: + continue + new_column_names = \ + set(cls.TABLES_ARGS[name]["columns"]) - \ + set(PreviousSchema.TABLES_ARGS[name]["columns"]) + if not new_column_names: + continue + cursor.execute( + f"ALTER TABLE {name}" + ",".join( + " ADD COLUMN " + schema.columns[n].format_def() + for n in new_column_names + ) + ) + + # For all indexes + for index_name, index_schema in cls.INDEXES.items(): + if index_name not in PreviousSchema.INDEXES: + try: + cursor.execute(index_schema.format_create(index_name)) + except Exception as exc: + raise Exception( + f"Failed creating index {index_name!r}" + ) from exc diff --git a/kcidb/db/sqlite/__init__.py b/kcidb/db/sqlite/__init__.py index 4df1d331..7819b473 100644 --- a/kcidb/db/sqlite/__init__.py +++ b/kcidb/db/sqlite/__init__.py @@ -2,7 +2,7 @@ import textwrap from kcidb.db.schematic import Driver as SchematicDriver -from kcidb.db.sqlite.v04_02 import Schema as LatestSchema +from kcidb.db.sqlite.v04_03 import Schema as LatestSchema class Driver(SchematicDriver): diff --git a/kcidb/db/sqlite/v04_00.py b/kcidb/db/sqlite/v04_00.py index f7f55da8..831af754 100644 --- a/kcidb/db/sqlite/v04_00.py +++ b/kcidb/db/sqlite/v04_00.py @@ -343,10 +343,14 @@ class Schema(AbstractSchema): " origin,\n" " path,\n" " \"environment.comment\" AS environment_comment,\n" + " NULL AS environment_compatible,\n" " \"environment.misc\" AS environment_misc,\n" " log_url,\n" " log_excerpt,\n" " status,\n" + " NULL AS number_value,\n" + " NULL AS number_unit,\n" + " NULL AS number_prefix,\n" " waived,\n" " start_time,\n" " duration,\n" @@ -360,10 +364,14 @@ class Schema(AbstractSchema): origin=TextColumn(), path=TextColumn(), environment_comment=TextColumn(), + environment_compatible=JSONColumn(), environment_misc=JSONColumn(), log_url=TextColumn(), log_excerpt=TextColumn(), status=TextColumn(), + number_value=Column("REAL"), + number_unit=TextColumn(), + number_prefix=TextColumn(), waived=BoolColumn(), start_time=TimestampColumn(), duration=Column("REAL"), diff --git a/kcidb/db/sqlite/v04_03.py b/kcidb/db/sqlite/v04_03.py new file mode 100644 index 00000000..b14e86ba --- /dev/null +++ b/kcidb/db/sqlite/v04_03.py @@ -0,0 +1,123 @@ +"""Kernel CI report database - SQLite schema v4.3""" + +import logging +import kcidb.io as io +from kcidb.misc import merge_dicts +from kcidb.db.sqlite.schema import \ + Column, BoolColumn, TextColumn, TimestampColumn, JSONColumn, Table +from .v04_02 import Schema as PreviousSchema + +# Module's logger +LOGGER = logging.getLogger(__name__) + + +class Schema(PreviousSchema): + """SQLite database schema v4.3""" + + # The schema's version. + version = (4, 3) + # The I/O schema the database schema supports + io = io.schema.V4_4 + + # A map of table names and Table constructor arguments + # For use by descendants + # Add environment.compatible, and number.* fields + TABLES_ARGS = merge_dicts( + PreviousSchema.TABLES_ARGS, + tests=merge_dicts( + PreviousSchema.TABLES_ARGS["tests"], + columns=merge_dicts( + PreviousSchema.TABLES_ARGS["tests"]["columns"], + { + "environment.compatible": JSONColumn(), + "number.value": Column("REAL"), + "number.unit": TextColumn(), + "number.prefix": TextColumn() + } + ) + ) + ) + + # A map of table names and schemas + TABLES = { + name: Table(**args) for name, args in TABLES_ARGS.items() + } + + # Queries and their columns for each type of raw object-oriented data. + # Both should have columns in the same order. + OO_QUERIES = merge_dicts( + PreviousSchema.OO_QUERIES, + test=dict( + statement="SELECT\n" + " id,\n" + " build_id,\n" + " origin,\n" + " path,\n" + " \"environment.comment\" AS environment_comment,\n" + " \"environment.compatible\" AS " + "environment_compatible,\n" + " \"environment.misc\" AS environment_misc,\n" + " log_url,\n" + " log_excerpt,\n" + " status,\n" + " \"number.value\" AS number_value,\n" + " \"number.unit\" AS number_unit,\n" + " \"number.prefix\" AS number_prefix,\n" + " waived,\n" + " start_time,\n" + " duration,\n" + " output_files,\n" + " comment,\n" + " misc\n" + "FROM tests", + schema=Table(dict( + id=TextColumn(), + build_id=TextColumn(), + origin=TextColumn(), + path=TextColumn(), + environment_comment=TextColumn(), + environment_compatible=JSONColumn(), + environment_misc=JSONColumn(), + log_url=TextColumn(), + log_excerpt=TextColumn(), + status=TextColumn(), + number_value=Column("REAL"), + number_unit=TextColumn(), + number_prefix=TextColumn(), + waived=BoolColumn(), + start_time=TimestampColumn(), + duration=Column("REAL"), + output_files=JSONColumn(), + comment=TextColumn(), + misc=JSONColumn(), + )), + ), + ) + + @classmethod + def _inherit(cls, conn): + """ + Inerit the database data from the previous schema version (if any). + + Args: + conn: Connection to the database to inherit. The database must + comply with the previous version of the schema. + """ + assert isinstance(conn, cls.Connection) + with conn: + cursor = conn.cursor() + try: + # For all tables + for name, schema in cls.TABLES.items(): + if name not in PreviousSchema.TABLES: + continue + for column_name in sorted( + set(cls.TABLES_ARGS[name]["columns"]) - + set(PreviousSchema.TABLES_ARGS[name]["columns"]) + ): + cursor.execute(f""" + ALTER TABLE {name} ADD COLUMN + {schema.columns[column_name].format_def()} + """) + finally: + cursor.close() diff --git a/kcidb/io.py b/kcidb/io.py index ed0dcca0..cb776b18 100644 --- a/kcidb/io.py +++ b/kcidb/io.py @@ -6,4 +6,4 @@ from kcidb_io import * # noqa: F403 # The I/O schema version used by KCIDB -SCHEMA = schema.V4_3 # noqa: F405 +SCHEMA = schema.V4_4 # noqa: F405 diff --git a/kcidb/orm/__init__.py b/kcidb/orm/__init__.py index 110d545f..6fd9d5ac 100644 --- a/kcidb/orm/__init__.py +++ b/kcidb/orm/__init__.py @@ -75,7 +75,7 @@ def oo_query(self, pattern_set): ) # Prefetch, if generated any patterns if prefetch_pattern_set: - LOGGER.info("Prefetching %r", prefetch_pattern_set) + LOGGER.debug("Prefetching %r", prefetch_pattern_set) self.source.oo_query(prefetch_pattern_set) # Return the response for the original request @@ -213,7 +213,7 @@ def _merge_pattern_response(self, pattern, response): # Log cached patterns LOGGER.debug("Cached patterns %r", cached) if LOGGER.getEffectiveLevel() <= logging.INFO: - LOGGER.info( + LOGGER.debug( "Cache has %s", ", ".join( tuple( diff --git a/kcidb/orm/data.py b/kcidb/orm/data.py index bf88fc9b..d96bd76d 100644 --- a/kcidb/orm/data.py +++ b/kcidb/orm/data.py @@ -319,6 +319,8 @@ def format_dot(self): _INCIDENT = _DEFS['incident']['properties'] # Test environment properties from the current I/O schema _TEST_ENVIRONMENT = _TEST['environment']['properties'] +# Test numerical test output properties from the current I/O schema +_TEST_NUMBER = _TEST['number']['properties'] # The schema of the raw object-oriented data SCHEMA = Schema( @@ -395,8 +397,12 @@ def format_dot(self): origin=_TEST['origin'], path=_TEST['path'], environment_comment=_TEST_ENVIRONMENT['comment'], + environment_compatible=_TEST_ENVIRONMENT['compatible'], environment_misc=_TEST_ENVIRONMENT['misc'], status=_TEST['status'], + number_value=_TEST_NUMBER['value'], + number_unit=_TEST_NUMBER['unit'], + number_prefix=_TEST_NUMBER['prefix'], waived=_TEST['waived'], start_time=_TEST['start_time'], duration=_TEST['duration'], diff --git a/kcidb/test_db.py b/kcidb/test_db.py index c4e8b527..941f87f5 100644 --- a/kcidb/test_db.py +++ b/kcidb/test_db.py @@ -19,7 +19,10 @@ def test_schemas_main(): """Check kcidb-db-schemas works""" argv = ["kcidb.db.schemas_main", "-d", "sqlite::memory:"] assert_executes("", *argv, - stdout_re=r"4\.0: 4\.0\n4\.1: 4\.2\n4\.2: 4\.3\n") + stdout_re=r"4\.0: 4\.0\n" + r"4\.1: 4\.2\n" + r"4\.2: 4\.3\n" + r"4\.3: 4\.4\n") def test_reset(clean_database): @@ -317,12 +320,22 @@ def test_load_main(): foo="bar", baz=42 ), + compatible=[ + "ti,omap3-beagleboard", + "ti,omap3450", + "ti,omap3" + ], ), path="ltp", comment="ltp on Lenovo x230", log_url="https://example.com/test.log", log_excerpt="kernel BUG at net/core/dev.c:2648!\n", status="FAIL", + number=dict( + value=1.6e-7, + unit="s", + prefix="metric", + ), waived=False, start_time="2020-08-14T23:08:07.967000+00:00", duration=600.0, @@ -787,6 +800,7 @@ def test_upgrade(clean_database): 'comment': 'INFO: task hung in ipv6_route_ioctl (2)', 'duration': None, 'environment_comment': None, + 'environment_compatible': None, 'environment_misc': None, 'id': 'syzbot:bf7c6406637722a401e0', 'log_excerpt': None, @@ -823,6 +837,9 @@ def test_upgrade(clean_database): 'path': 'syzkaller', 'start_time': '2023-01-28T02:21:00.000000+00:00', 'status': 'FAIL', + 'number_value': None, + 'number_unit': None, + 'number_prefix': None, 'waived': False }], 'bug': [], @@ -911,6 +928,7 @@ def test_upgrade(clean_database): "comment": None, "duration": None, "environment_comment": None, + "environment_compatible": None, "environment_misc": None, "id": "google:google.org:a19di3j5h67f8d9475f26v11", @@ -922,6 +940,9 @@ def test_upgrade(clean_database): "path": None, "start_time": None, "status": None, + 'number_value': None, + 'number_unit': None, + 'number_prefix': None, "waived": None, }], "bug": [], @@ -1037,6 +1058,7 @@ def test_upgrade(clean_database): "comment": None, "duration": None, "environment_comment": None, + "environment_compatible": None, "environment_misc": None, "id": "google:google.org:a19di3j5h67f8d9475f26v11", @@ -1048,6 +1070,9 @@ def test_upgrade(clean_database): "path": None, "start_time": None, "status": "MISS", + 'number_value': None, + 'number_unit': None, + 'number_prefix': None, "waived": None, }], "bug": [{ @@ -1200,6 +1225,7 @@ def test_upgrade(clean_database): "comment": None, "duration": None, "environment_comment": None, + "environment_compatible": None, "environment_misc": None, "id": "google:google.org:a19di3j5h67f8d9475f26v11", @@ -1211,6 +1237,194 @@ def test_upgrade(clean_database): "path": None, "start_time": None, "status": "MISS", + 'number_value': None, + 'number_unit': None, + 'number_prefix': None, + "waived": None, + }], + "bug": [{ + "culprit_code": True, + "culprit_tool": False, + "culprit_harness": False, + "url": + "https://bugzilla.redhat.com/show_bug.cgi" + "?id=873123", + "subject": + "(cups-usb-quirks) - usb printer doesn't print " + "(usblp0: USB Bidirectional printer dev)", + }], + "issue": [{ + "comment": "Match USB Bidirectional printer dev", + "id": "redhat:878234322", + "misc": None, + "origin": "redhat", + "report_url": + "https://bugzilla.redhat.com/show_bug.cgi?" + "id=873123", + "report_subject": + "(cups-usb-quirks) - usb printer doesn't print " + "(usblp0: USB Bidirectional printer dev)", + "culprit_code": True, + "culprit_tool": False, + "culprit_harness": False, + "build_valid": None, + "test_status": None, + "version": 3, + }], + "incident": [{ + "build_id": None, + "comment": None, + "id": "redhat:2340981234098123409382", + "issue_id": "redhat:878234322", + "issue_version": 3, + "misc": None, + "origin": "redhat", + "test_id": + "google:google.org:a19di3j5h67f8d9475f26v11", + }], + } + ), + kcidb.io.schema.V4_4: dict( + io={ + "version": {"major": 4, "minor": 4}, + "checkouts": [{ + "id": "_:kernelci:5acb9c2a7bc836e" + "9e5172bbcd2311499c5b4e5f1", + "origin": "kernelci", + "git_commit_hash": "5acb9c2a7bc836e9e5172bb" + "cd2311499c5b4e5f1", + "git_commit_name": "v5.15-4077-g5acb9c2a7bc8", + "patchset_hash": "" + }], + "builds": [{ + "id": "google:google.org:a1d993c3n4c448b2j0l1hbf1", + "origin": "google", + "checkout_id": "_:google:bd355732283c23a365f7c" + "55206c0385100d1c389" + }], + "tests": [{ + "id": "google:google.org:a19di3j5h67f8d9475f26v11", + "build_id": "google:google.org:a1d993c3n4c448b2" + "j0l1hbf1", + "origin": "google", + "status": "MISS", + "number": { + "value": 1.6e-7, + "unit": "s", + "prefix": "metric", + }, + "environment": { + "comment": "Tidy", + "misc": {"foo": "bar"}, + "compatible": [ + "ti,omap3-beagleboard", + "ti,omap3450", + "ti,omap3" + ], + }, + }], + "issues": [{ + "id": "redhat:878234322", + "version": 3, + "origin": "redhat", + "report_url": + "https://bugzilla.redhat.com/show_bug.cgi" + "?id=873123", + "report_subject": + "(cups-usb-quirks) - usb printer doesn't print " + "(usblp0: USB Bidirectional printer dev)", + "culprit": { + "code": True, + "tool": False, + "harness": False, + }, + "comment": "Match USB Bidirectional printer dev", + }], + "incidents": [{ + "id": "redhat:2340981234098123409382", + "issue_id": "redhat:878234322", + "issue_version": 3, + "origin": "redhat", + "test_id": + "google:google.org:a19di3j5h67f8d9475f26v11", + "present": True, + }], + }, + oo={ + "revision": [{ + "contacts": None, + "git_commit_hash": + "5acb9c2a7bc836e9e5172bbcd2311499c5b4e5f1", + "git_commit_name": + "v5.15-4077-g5acb9c2a7bc8", + "patchset_files": None, + "patchset_hash": "", + }], + "checkout": [{ + "comment": None, + "git_commit_hash": + "5acb9c2a7bc836e9e5172bbcd2311499c5b4e5f1", + "git_repository_branch": None, + "git_repository_url": None, + "id": + "_:kernelci:" + "5acb9c2a7bc836e9e5172bbcd2311499c5b4e5f1", + "log_excerpt": None, + "log_url": None, + "message_id": None, + "misc": None, + "origin": "kernelci", + "patchset_hash": "", + "start_time": None, + "tree_name": None, + "valid": None, + }], + "build": [{ + "architecture": None, + "checkout_id": + "_:google:" + "bd355732283c23a365f7c55206c0385100d1c389", + "command": None, + "comment": None, + "compiler": None, + "config_name": None, + "config_url": None, + "duration": None, + "id": "google:google.org:a1d993c3n4c448b2j0l1hbf1", + "input_files": None, + "log_excerpt": None, + "log_url": None, + "misc": None, + "origin": "google", + "output_files": None, + "start_time": None, + "valid": None, + }], + "test": [{ + "build_id": + "google:google.org:a1d993c3n4c448b2j0l1hbf1", + "comment": None, + "duration": None, + "environment_comment": "Tidy", + "environment_compatible": [ + "ti,omap3-beagleboard", + "ti,omap3450", + "ti,omap3" + ], + "environment_misc": {"foo": "bar"}, + "id": + "google:google.org:a19di3j5h67f8d9475f26v11", + "log_excerpt": None, + "log_url": None, + "misc": None, + "origin": "google", + "output_files": None, + "path": None, + "start_time": None, + "status": "MISS", + "number_value": 1.6e-7, + "number_unit": "s", + "number_prefix": "metric", "waived": None, }], "bug": [{ @@ -1567,6 +1781,7 @@ def test_purge(empty_database): assert client.purge(None) client.load(io_data_1) assert client.dump(with_metadata=False) == io_data_1 + time.sleep(1) after_first_load = client.get_current_time() time.sleep(1) client.load(io_data_2) @@ -1583,6 +1798,7 @@ def test_purge(empty_database): assert client.purge(after_first_load) assert client.dump(with_metadata=False) == io_data_2 + time.sleep(1) assert client.purge(client.get_current_time()) assert client.dump() == kcidb.io.SCHEMA.new() else: