chromium/third_party/blink/tools/blinkpy/web_tests/stale_expectation_removal/queries_unittest.py

#!/usr/bin/env vpython3
# Copyright 2021 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import datetime
import os
import unittest
import six

if six.PY3:
    import unittest.mock as mock

from blinkpy.web_tests.stale_expectation_removal import constants
from blinkpy.web_tests.stale_expectation_removal import data_types
from blinkpy.web_tests.stale_expectation_removal import queries
from blinkpy.web_tests.stale_expectation_removal import unittest_utils as wt_uu
from unexpected_passes_common import constants as common_constants
from unexpected_passes_common import data_types as common_data_types
from unexpected_passes_common import expectations as common_expectations
from unexpected_passes_common import queries as common_queries
from unexpected_passes_common import unittest_utils as common_uu


class ConvertBigQueryRowToResultObjectUnittest(unittest.TestCase):

    def setUp(self) -> None:
        common_data_types.SetResultImplementation(data_types.WebTestResult)
        common_expectations.ClearInstance()
        common_uu.RegisterGenericExpectationsImplementation()

    def tearDown(self) -> None:
        common_data_types.SetResultImplementation(common_data_types.BaseResult)

    def testDurationIsSet(self) -> None:
        """Tests that the duration is set appropriately on the result."""
        row = wt_uu.FakeQueryResult(builder_name='builder_name',
                                    id_='build-1234',
                                    test_id='ninja://:blink_web_tests/test',
                                    status='PASS',
                                    typ_tags=['debug'],
                                    step_name='step_name',
                                    duration='10',
                                    timeout='3')
        querier = wt_uu.CreateGenericWebTestQuerier()
        result = querier._ConvertBigQueryRowToResultObject(row)
        self.assertTrue(result.is_slow_result)
        self.assertEqual(result._duration, datetime.timedelta(seconds=10))

class GetRelevantExpectationFilesForQueryResultUnittest(unittest.TestCase):
    def testNoFiles(self) -> None:
        """Tests that no reported expectation files are handled properly."""
        row = common_queries.QueryResult(data={})
        querier = wt_uu.CreateGenericWebTestQuerier()
        self.assertEqual(
            querier._GetRelevantExpectationFilesForQueryResult(row), [])

    def testAbsolutePath(self) -> None:
        """Tests that absolute paths are ignored."""
        row = common_queries.QueryResult(
            data={'expectation_files': ['/posix/path', '/c:/windows/path']})
        querier = wt_uu.CreateGenericWebTestQuerier()
        self.assertEqual(
            querier._GetRelevantExpectationFilesForQueryResult(row), [])

    def testRelativePath(self) -> None:
        """Tests that relative paths are properly reconstructed."""
        row = common_queries.QueryResult(data={
            'expectation_files':
            ['TestExpectations', 'flag-specific/someflag']
        })
        querier = wt_uu.CreateGenericWebTestQuerier()
        expected_files = [
            os.path.join(constants.WEB_TEST_ROOT_DIR, 'TestExpectations'),
            os.path.join(constants.WEB_TEST_ROOT_DIR, 'flag-specific',
                         'someflag'),
        ]
        self.assertEqual(
            querier._GetRelevantExpectationFilesForQueryResult(row),
            expected_files)


@unittest.skipIf(six.PY2, 'Script and unittest are Python 3-only')
class GeneratedQueryUnittest(unittest.TestCase):
    maxDiff = None

    def setUp(self):
        self._querier = wt_uu.CreateGenericWebTestQuerier(num_samples=15)

    def testPublicCi(self) -> None:
        """Tests that the generated public CI query is as expected."""
        expected_query = """\
WITH
  builds AS (
    WITH
      all_builds AS (
        SELECT
          DISTINCT exported.id AS build_inv_id,
          variant.*,
          partition_time
        FROM
          `chrome-luci-data.chromium.blink_web_tests_ci_test_results` AS tr,
          UNNEST(variant) AS variant
        WHERE
          DATE(partition_time) > DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
          AND exported.realm = "chromium:ci"
          AND key = "builder"
      ),
      grouped_builds AS (
        SELECT
          build_inv_id,
          value AS builder,
          partition_time,
          RANK() OVER (PARTITION BY value ORDER BY partition_time DESC) AS rank_idx,
        FROM all_builds
      )
    SELECT
      build_inv_id,
      builder,
      partition_time
    FROM grouped_builds
    WHERE rank_idx <= 15
  ),
  results AS (
    SELECT
      exported.id,
      test_id,
      status,
      (
        SELECT value
        FROM tr.variant
        WHERE key = "builder"
      ) as builder_name,
      duration,
      (
        SELECT value
        FROM tr.tags
        WHERE key = "step_name") as step_name,
      (
        SELECT value
        FROM tr.tags
        WHERE key = "web_tests_base_timeout") as timeout,
      ARRAY(
        SELECT value
        FROM tr.tags
        WHERE key = "typ_tag") as typ_tags,
      ARRAY(
        SELECT value
        FROM tr.tags
        WHERE key = "raw_typ_expectation") as typ_expectations,
      ARRAY(
        SELECT value
        FROM tr.tags
        WHERE key = "web_tests_used_expectations_file") as expectation_files
    FROM
      `chrome-luci-data.chromium.blink_web_tests_ci_test_results` tr,
      builds b
    WHERE
      DATE(tr.partition_time) > DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
      AND exported.id = build_inv_id
      AND status != "SKIP"
  )
SELECT id, test_id, builder_name, status, duration, step_name, timeout, typ_tags, expectation_files
FROM results
WHERE
  "Failure" IN UNNEST(typ_expectations)
  OR "Crash" IN UNNEST(typ_expectations)
  OR "Timeout" IN UNNEST(typ_expectations)
  OR "Slow" IN UNNEST(typ_expectations)
ORDER BY builder_name DESC
"""
        self.assertEqual(self._querier._GetPublicCiQuery(), expected_query)

    def testInternalCi(self) -> None:
        """Tests that the generated internal CI query is as expected."""
        expected_query = """\
WITH
  builds AS (
    WITH
      all_builds AS (
        SELECT
          DISTINCT exported.id AS build_inv_id,
          variant.*,
          partition_time
        FROM
          `chrome-luci-data.chrome.blink_web_tests_ci_test_results` AS tr,
          UNNEST(variant) AS variant
        WHERE
          DATE(partition_time) > DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
          AND exported.realm = "chrome:ci"
          AND key = "builder"
      ),
      grouped_builds AS (
        SELECT
          build_inv_id,
          value AS builder,
          partition_time,
          RANK() OVER (PARTITION BY value ORDER BY partition_time DESC) AS rank_idx,
        FROM all_builds
      )
    SELECT
      build_inv_id,
      builder,
      partition_time
    FROM grouped_builds
    WHERE rank_idx <= 15
  ),
  results AS (
    SELECT
      exported.id,
      test_id,
      status,
      (
        SELECT value
        FROM tr.variant
        WHERE key = "builder"
      ) as builder_name,
      duration,
      (
        SELECT value
        FROM tr.tags
        WHERE key = "step_name") as step_name,
      (
        SELECT value
        FROM tr.tags
        WHERE key = "web_tests_base_timeout") as timeout,
      ARRAY(
        SELECT value
        FROM tr.tags
        WHERE key = "typ_tag") as typ_tags,
      ARRAY(
        SELECT value
        FROM tr.tags
        WHERE key = "raw_typ_expectation") as typ_expectations,
      ARRAY(
        SELECT value
        FROM tr.tags
        WHERE key = "web_tests_used_expectations_file") as expectation_files
    FROM
      `chrome-luci-data.chrome.blink_web_tests_ci_test_results` tr,
      builds b
    WHERE
      DATE(tr.partition_time) > DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
      AND exported.id = build_inv_id
      AND status != "SKIP"
  )
SELECT id, test_id, builder_name, status, duration, step_name, timeout, typ_tags, expectation_files
FROM results
WHERE
  "Failure" IN UNNEST(typ_expectations)
  OR "Crash" IN UNNEST(typ_expectations)
  OR "Timeout" IN UNNEST(typ_expectations)
  OR "Slow" IN UNNEST(typ_expectations)
ORDER BY builder_name DESC
"""
        self.assertEqual(self._querier._GetInternalCiQuery(), expected_query)

    def testPublicTry(self) -> None:
        """Tests that the generated public try query is as expected."""
        expected_query = """\
WITH
  submitted_builds AS (
    SELECT
      CONCAT("build-", CAST(unnested_builds.id AS STRING)) as id
    FROM
      `commit-queue.chromium.attempts`,
      UNNEST(builds) as unnested_builds,
      UNNEST(gerrit_changes) as unnested_changes
    WHERE
      unnested_builds.host = "cr-buildbucket.appspot.com"
      AND unnested_changes.submit_status = "SUCCESS"
      AND start_time > TIMESTAMP_SUB(CURRENT_TIMESTAMP(),
                                     INTERVAL 30 DAY)
  ),
  builds AS (
    WITH
      all_builds AS (
        SELECT
          DISTINCT exported.id AS build_inv_id,
          variant.*,
          partition_time
        FROM
          `chrome-luci-data.chromium.blink_web_tests_try_test_results` AS tr,
          UNNEST(variant) AS variant,
          submitted_builds sb
        WHERE
          DATE(partition_time) > DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
          AND exported.realm = "chromium:try"
          AND key = "builder"
          AND exported.id = sb.id
      ),
      grouped_builds AS (
        SELECT
          build_inv_id,
          value AS builder,
          partition_time,
          RANK() OVER (PARTITION BY value ORDER BY partition_time DESC) AS rank_idx,
        FROM all_builds
      )
    SELECT
      build_inv_id,
      builder,
      partition_time
    FROM grouped_builds
    WHERE rank_idx <= 15
  ),
  results AS (
    SELECT
      exported.id,
      test_id,
      status,
      (
        SELECT value
        FROM tr.variant
        WHERE key = "builder"
      ) as builder_name,
      duration,
      (
        SELECT value
        FROM tr.tags
        WHERE key = "step_name") as step_name,
      (
        SELECT value
        FROM tr.tags
        WHERE key = "web_tests_base_timeout") as timeout,
      ARRAY(
        SELECT value
        FROM tr.tags
        WHERE key = "typ_tag") as typ_tags,
      ARRAY(
        SELECT value
        FROM tr.tags
        WHERE key = "raw_typ_expectation") as typ_expectations,
      ARRAY(
        SELECT value
        FROM tr.tags
        WHERE key = "web_tests_used_expectations_file") as expectation_files
    FROM
      `chrome-luci-data.chromium.blink_web_tests_try_test_results` tr,
      builds b
    WHERE
      DATE(tr.partition_time) > DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
      AND exported.id = build_inv_id
      AND status != "SKIP"
  )
SELECT id, test_id, builder_name, status, duration, step_name, timeout, typ_tags, expectation_files
FROM results
WHERE
  "Failure" IN UNNEST(typ_expectations)
  OR "Crash" IN UNNEST(typ_expectations)
  OR "Timeout" IN UNNEST(typ_expectations)
  OR "Slow" IN UNNEST(typ_expectations)
ORDER BY builder_name DESC
"""
        self.assertEqual(self._querier._GetPublicTryQuery(), expected_query)

    def testInternalTry(self) -> None:
        """Tests that the generated internal try query is as expected."""
        expected_query = """\
WITH
  submitted_builds AS (
    SELECT
      CONCAT("build-", CAST(unnested_builds.id AS STRING)) as id
    FROM
      `commit-queue.chrome.attempts`,
      UNNEST(builds) as unnested_builds,
      UNNEST(gerrit_changes) as unnested_changes
    WHERE
      unnested_builds.host = "cr-buildbucket.appspot.com"
      AND unnested_changes.submit_status = "SUCCESS"
      AND start_time > TIMESTAMP_SUB(CURRENT_TIMESTAMP(),
                                     INTERVAL 30 DAY)
  ),
  builds AS (
    WITH
      all_builds AS (
        SELECT
          DISTINCT exported.id AS build_inv_id,
          variant.*,
          partition_time
        FROM
          `chrome-luci-data.chrome.blink_web_tests_try_test_results` AS tr,
          UNNEST(variant) AS variant,
          submitted_builds sb
        WHERE
          DATE(partition_time) > DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
          AND exported.realm = "chrome:try"
          AND key = "builder"
          AND exported.id = sb.id
      ),
      grouped_builds AS (
        SELECT
          build_inv_id,
          value AS builder,
          partition_time,
          RANK() OVER (PARTITION BY value ORDER BY partition_time DESC) AS rank_idx,
        FROM all_builds
      )
    SELECT
      build_inv_id,
      builder,
      partition_time
    FROM grouped_builds
    WHERE rank_idx <= 15
  ),
  results AS (
    SELECT
      exported.id,
      test_id,
      status,
      (
        SELECT value
        FROM tr.variant
        WHERE key = "builder"
      ) as builder_name,
      duration,
      (
        SELECT value
        FROM tr.tags
        WHERE key = "step_name") as step_name,
      (
        SELECT value
        FROM tr.tags
        WHERE key = "web_tests_base_timeout") as timeout,
      ARRAY(
        SELECT value
        FROM tr.tags
        WHERE key = "typ_tag") as typ_tags,
      ARRAY(
        SELECT value
        FROM tr.tags
        WHERE key = "raw_typ_expectation") as typ_expectations,
      ARRAY(
        SELECT value
        FROM tr.tags
        WHERE key = "web_tests_used_expectations_file") as expectation_files
    FROM
      `chrome-luci-data.chrome.blink_web_tests_try_test_results` tr,
      builds b
    WHERE
      DATE(tr.partition_time) > DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
      AND exported.id = build_inv_id
      AND status != "SKIP"
  )
SELECT id, test_id, builder_name, status, duration, step_name, timeout, typ_tags, expectation_files
FROM results
WHERE
  "Failure" IN UNNEST(typ_expectations)
  OR "Crash" IN UNNEST(typ_expectations)
  OR "Timeout" IN UNNEST(typ_expectations)
  OR "Slow" IN UNNEST(typ_expectations)
ORDER BY builder_name DESC
"""
        self.assertEqual(self._querier._GetInternalTryQuery(), expected_query)


class StripPrefixFromTestIdUnittest(unittest.TestCase):
    def testUnknownPrefix(self) -> None:
        """Tests that an error is raised if an unknown prefix is found."""
        querier = wt_uu.CreateGenericWebTestQuerier()
        with self.assertRaises(RuntimeError):
            querier._StripPrefixFromTestId('foobar')

    def testKnownPrefixes(self) -> None:
        """Tests that all known prefixes are properly stripped."""
        querier = wt_uu.CreateGenericWebTestQuerier()
        test_ids = [prefix + 'a' for prefix in queries.KNOWN_TEST_ID_PREFIXES]
        for t in test_ids:
            stripped = querier._StripPrefixFromTestId(t)
            self.assertEqual(stripped, 'a')


if __name__ == '__main__':
    unittest.main(verbosity=2)