Example #1
0
    def _load_config_pairs(self, configs):
        """Loads the results of all tests, across all builders (based on the
    files within self._actuals_root), compares them across two configs,
    and stores the summary in self._results.

    Args:
      configs: tuple of strings; pair of configs to compare
    """
        logging.info('Reading actual-results JSON files from %s...' %
                     self._actuals_root)
        actual_builder_dicts = self._read_builder_dicts_from_root(
            self._actuals_root)
        configA, configB = configs
        logging.info('Comparing configs %s and %s...' % (configA, configB))

        all_image_pairs = imagepairset.ImagePairSet(
            descriptions=configs, diff_base_url=self._diff_base_url)
        failing_image_pairs = imagepairset.ImagePairSet(
            descriptions=configs, diff_base_url=self._diff_base_url)

        all_image_pairs.ensure_extra_column_values_in_summary(
            column_id=results.KEY__EXTRACOLUMNS__RESULT_TYPE,
            values=[
                results.KEY__RESULT_TYPE__FAILED,
                results.KEY__RESULT_TYPE__NOCOMPARISON,
                results.KEY__RESULT_TYPE__SUCCEEDED,
            ])
        failing_image_pairs.ensure_extra_column_values_in_summary(
            column_id=results.KEY__EXTRACOLUMNS__RESULT_TYPE,
            values=[
                results.KEY__RESULT_TYPE__FAILED,
                results.KEY__RESULT_TYPE__NOCOMPARISON,
            ])

        builders = sorted(actual_builder_dicts.keys())
        num_builders = len(builders)
        builder_num = 0
        for builder in builders:
            builder_num += 1
            logging.info(
                'Generating pixel diffs for builder #%d of %d, "%s"...' %
                (builder_num, num_builders, builder))
            actual_results_for_this_builder = (
                actual_builder_dicts[builder][gm_json.JSONKEY_ACTUALRESULTS])
            for result_type in sorted(actual_results_for_this_builder.keys()):
                results_of_this_type = actual_results_for_this_builder[
                    result_type]
                if not results_of_this_type:
                    continue

                tests_found = set()
                for image_name in sorted(results_of_this_type.keys()):
                    (test,
                     _) = results.IMAGE_FILENAME_RE.match(image_name).groups()
                    tests_found.add(test)

                for test in tests_found:
                    # Get image_relative_url (or None) for each of configA, configB
                    image_name_A = results.IMAGE_FILENAME_FORMATTER % (test,
                                                                       configA)
                    configA_image_relative_url = ConfigComparisons._create_relative_url(
                        hashtype_and_digest=results_of_this_type.get(
                            image_name_A),
                        test_name=test)
                    image_name_B = results.IMAGE_FILENAME_FORMATTER % (test,
                                                                       configB)
                    configB_image_relative_url = ConfigComparisons._create_relative_url(
                        hashtype_and_digest=results_of_this_type.get(
                            image_name_B),
                        test_name=test)

                    # If we have images for at least one of these two configs,
                    # add them to our list.
                    if configA_image_relative_url or configB_image_relative_url:
                        if configA_image_relative_url == configB_image_relative_url:
                            result_type = results.KEY__RESULT_TYPE__SUCCEEDED
                        elif not configA_image_relative_url:
                            result_type = results.KEY__RESULT_TYPE__NOCOMPARISON
                        elif not configB_image_relative_url:
                            result_type = results.KEY__RESULT_TYPE__NOCOMPARISON
                        else:
                            result_type = results.KEY__RESULT_TYPE__FAILED

                        extra_columns_dict = {
                            results.KEY__EXTRACOLUMNS__RESULT_TYPE:
                            result_type,
                            results.KEY__EXTRACOLUMNS__BUILDER:
                            builder,
                            results.KEY__EXTRACOLUMNS__TEST:
                            test,
                            # TODO(epoger): Right now, the client UI crashes if it receives
                            # results that do not include a 'config' column.
                            # Until we fix that, keep the client happy.
                            results.KEY__EXTRACOLUMNS__CONFIG:
                            'TODO',
                        }

                        try:
                            image_pair = imagepair.ImagePair(
                                image_diff_db=self._image_diff_db,
                                base_url=gm_json.GM_ACTUALS_ROOT_HTTP_URL,
                                imageA_relative_url=configA_image_relative_url,
                                imageB_relative_url=configB_image_relative_url,
                                extra_columns=extra_columns_dict)
                            all_image_pairs.add_image_pair(image_pair)
                            if result_type != results.KEY__RESULT_TYPE__SUCCEEDED:
                                failing_image_pairs.add_image_pair(image_pair)
                        except (KeyError, TypeError):
                            logging.exception(
                                'got exception while creating ImagePair for test '
                                '"%s", builder "%s"' % (test, builder))

        # pylint: disable=W0201
        self._results = {
            results.KEY__HEADER__RESULTS_ALL: all_image_pairs.as_dict(),
            results.KEY__HEADER__RESULTS_FAILURES:
            failing_image_pairs.as_dict(),
        }
  def _load_actual_and_expected(self):
    """Loads the results of all tests, across all builders (based on the
    files within self._actuals_root and self._expected_root),
    and stores them in self._results.
    """
    logging.info('Reading actual-results JSON files from %s...' %
                 self._actuals_root)
    actual_builder_dicts = self._read_builder_dicts_from_root(
        self._actuals_root)
    logging.info('Reading expected-results JSON files from %s...' %
                 self._expected_root)
    expected_builder_dicts = self._read_builder_dicts_from_root(
        self._expected_root)

    all_image_pairs = imagepairset.ImagePairSet(
        descriptions=IMAGEPAIR_SET_DESCRIPTIONS,
        diff_base_url=self._diff_base_url)
    failing_image_pairs = imagepairset.ImagePairSet(
        descriptions=IMAGEPAIR_SET_DESCRIPTIONS,
        diff_base_url=self._diff_base_url)

    all_image_pairs.ensure_extra_column_values_in_summary(
        column_id=results.KEY__EXTRACOLUMNS__RESULT_TYPE, values=[
            results.KEY__RESULT_TYPE__FAILED,
            results.KEY__RESULT_TYPE__FAILUREIGNORED,
            results.KEY__RESULT_TYPE__NOCOMPARISON,
            results.KEY__RESULT_TYPE__SUCCEEDED,
        ])
    failing_image_pairs.ensure_extra_column_values_in_summary(
        column_id=results.KEY__EXTRACOLUMNS__RESULT_TYPE, values=[
            results.KEY__RESULT_TYPE__FAILED,
            results.KEY__RESULT_TYPE__FAILUREIGNORED,
            results.KEY__RESULT_TYPE__NOCOMPARISON,
        ])

    # Only consider builders we have both expected and actual results for.
    # Fixes http://skbug.com/2486 ('rebaseline_server shows actual results
    # (but not expectations) for Test-Ubuntu12-ShuttleA-NoGPU-x86_64-Debug
    # builder')
    actual_builder_set = set(actual_builder_dicts.keys())
    expected_builder_set = set(expected_builder_dicts.keys())
    builders = sorted(actual_builder_set.intersection(expected_builder_set))

    num_builders = len(builders)
    builder_num = 0
    for builder in builders:
      builder_num += 1
      logging.info('Generating pixel diffs for builder #%d of %d, "%s"...' %
                   (builder_num, num_builders, builder))
      actual_results_for_this_builder = (
          actual_builder_dicts[builder][gm_json.JSONKEY_ACTUALRESULTS])
      for result_type in sorted(actual_results_for_this_builder.keys()):
        results_of_this_type = actual_results_for_this_builder[result_type]
        if not results_of_this_type:
          continue
        for image_name in sorted(results_of_this_type.keys()):
          (test, config) = results.IMAGE_FILENAME_RE.match(image_name).groups()
          actual_image_relative_url = (
              ExpectationComparisons._create_relative_url(
                  hashtype_and_digest=results_of_this_type[image_name],
                  test_name=test))

          # Default empty expectations; overwrite these if we find any real ones
          expectations_per_test = None
          expected_image_relative_url = None
          expectations_dict = None
          try:
            expectations_per_test = (
                expected_builder_dicts
                [builder][gm_json.JSONKEY_EXPECTEDRESULTS][image_name])
            # TODO(epoger): assumes a single allowed digest per test, which is
            # fine; see https://code.google.com/p/skia/issues/detail?id=1787
            expected_image_hashtype_and_digest = (
                expectations_per_test
                [gm_json.JSONKEY_EXPECTEDRESULTS_ALLOWEDDIGESTS][0])
            expected_image_relative_url = (
                ExpectationComparisons._create_relative_url(
                    hashtype_and_digest=expected_image_hashtype_and_digest,
                    test_name=test))
            expectations_dict = {}
            for field in EXPECTATION_FIELDS_PASSED_THRU_VERBATIM:
              expectations_dict[field] = expectations_per_test.get(field)
          except (KeyError, TypeError):
            # There are several cases in which we would expect to find
            # no expectations for a given test:
            #
            # 1. result_type == NOCOMPARISON
            #   There are no expectations for this test yet!
            #
            # 2. alternate rendering mode failures (e.g. serialized)
            #   In cases like
            #   https://code.google.com/p/skia/issues/detail?id=1684
            #   ('tileimagefilter GM test failing in serialized render mode'),
            #   the gm-actuals will list a failure for the alternate
            #   rendering mode even though we don't have explicit expectations
            #   for the test (the implicit expectation is that it must
            #   render the same in all rendering modes).
            #
            # Don't log type 1, because it is common.
            # Log other types, because they are rare and we should know about
            # them, but don't throw an exception, because we need to keep our
            # tools working in the meanwhile!
            if result_type != results.KEY__RESULT_TYPE__NOCOMPARISON:
              logging.warning('No expectations found for test: %s' % {
                  results.KEY__EXTRACOLUMNS__BUILDER: builder,
                  results.KEY__EXTRACOLUMNS__RESULT_TYPE: result_type,
                  'image_name': image_name,
                  })

          # If this test was recently rebaselined, it will remain in
          # the 'failed' set of actuals until all the bots have
          # cycled (although the expectations have indeed been set
          # from the most recent actuals).  Treat these as successes
          # instead of failures.
          #
          # TODO(epoger): Do we need to do something similar in
          # other cases, such as when we have recently marked a test
          # as ignoreFailure but it still shows up in the 'failed'
          # category?  Maybe we should not rely on the result_type
          # categories recorded within the gm_actuals AT ALL, and
          # instead evaluate the result_type ourselves based on what
          # we see in expectations vs actual checksum?
          if expected_image_relative_url == actual_image_relative_url:
            updated_result_type = results.KEY__RESULT_TYPE__SUCCEEDED
          elif ((result_type == results.KEY__RESULT_TYPE__FAILED) and
                (test in self._ignore_failures_on_these_tests)):
            updated_result_type = results.KEY__RESULT_TYPE__FAILUREIGNORED
          else:
            updated_result_type = result_type
          extra_columns_dict = {
              results.KEY__EXTRACOLUMNS__RESULT_TYPE: updated_result_type,
              results.KEY__EXTRACOLUMNS__BUILDER: builder,
              results.KEY__EXTRACOLUMNS__TEST: test,
              results.KEY__EXTRACOLUMNS__CONFIG: config,
          }
          try:
            image_pair = imagepair.ImagePair(
                image_diff_db=self._image_diff_db,
                base_url=gm_json.GM_ACTUALS_ROOT_HTTP_URL,
                imageA_relative_url=expected_image_relative_url,
                imageB_relative_url=actual_image_relative_url,
                expectations=expectations_dict,
                extra_columns=extra_columns_dict)
            all_image_pairs.add_image_pair(image_pair)
            if updated_result_type != results.KEY__RESULT_TYPE__SUCCEEDED:
              failing_image_pairs.add_image_pair(image_pair)
          except Exception:
            logging.exception('got exception while creating new ImagePair')

    self._results = {
      results.KEY__HEADER__RESULTS_ALL: all_image_pairs.as_dict(),
      results.KEY__HEADER__RESULTS_FAILURES: failing_image_pairs.as_dict(),
    }
Example #3
0
    def test_endToEnd(self):
        """Tests ImagePair, using a real ImageDiffDB to download real images.

    TODO(epoger): Either in addition to or instead of this end-to-end test,
    we should perform some tests using either:
    1. a mock ImageDiffDB, or
    2. a real ImageDiffDB that doesn't hit Google Storage looking for input
       image files (maybe a file:// IMG_URL_BASE)
    """
        # params for each self-test:
        #
        # inputs:
        #  0. imageA_relative_URL
        #  1. imageB_relative_URL
        #  2. expectations dict
        #  3. extra_columns dict
        # expected output:
        #  4. expected result of ImagePair.as_dict()
        selftests = [
            [
                # inputs:
                'arcofzorro/16206093933823793653.png',
                'arcofzorro/16206093933823793653.png',
                None,
                {
                    'builder': 'MyBuilder',
                    'test': 'MyTest',
                },
                # expected output:
                {
                    'extraColumns': {
                        'builder': 'MyBuilder',
                        'test': 'MyTest',
                    },
                    'imageAUrl': 'arcofzorro/16206093933823793653.png',
                    'imageBUrl': 'arcofzorro/16206093933823793653.png',
                    'isDifferent': False,
                },
            ],
            [
                # inputs:
                'arcofzorro/16206093933823793653.png',
                'arcofzorro/13786535001616823825.png',
                None,
                None,
                # expected output:
                {
                    'differenceData': {
                        'maxDiffPerChannel': [255, 255, 247],
                        'numDifferingPixels':
                        662,
                        'percentDifferingPixels':
                        0.0662,
                        'perceptualDifference':
                        0.06620300000000157,
                        'diffUrl':
                        'arcofzorro_16206093933823793653_png_png-vs-' +
                        'arcofzorro_13786535001616823825_png_png.png',
                        'whiteDiffUrl':
                        'arcofzorro_16206093933823793653_png_png' +
                        '-vs-arcofzorro_13786535001616823825_png_png.png',
                    },
                    'imageAUrl': 'arcofzorro/16206093933823793653.png',
                    'imageBUrl': 'arcofzorro/13786535001616823825.png',
                    'isDifferent': True,
                },
            ],
            [
                # inputs:
                'gradients_degenerate_2pt/10552995703607727960.png',
                'gradients_degenerate_2pt/11198253335583713230.png',
                {
                    'ignoreFailure': True,
                    'bugs': [1001, 1002],
                },
                {
                    'builder': 'MyBuilder',
                    'test': 'MyTest',
                },
                # expected output:
                {
                    'differenceData': {
                        'maxDiffPerChannel': [255, 0, 255],
                        'numDifferingPixels':
                        102400,
                        'percentDifferingPixels':
                        100.00,
                        'perceptualDifference':
                        100.00,
                        'diffUrl':
                        'gradients_degenerate_2pt_10552995703607727960' +
                        '_png_png-vs-gradients_degenerate_2pt_' +
                        '11198253335583713230_png_png.png',
                        'whiteDiffUrl':
                        'gradients_degenerate_2pt_' +
                        '10552995703607727960_png_png-vs-' +
                        'gradients_degenerate_2pt_11198253335583713230' +
                        '_png_png.png'
                    },
                    'expectations': {
                        'bugs': [1001, 1002],
                        'ignoreFailure': True,
                    },
                    'extraColumns': {
                        'builder': 'MyBuilder',
                        'test': 'MyTest',
                    },
                    'imageAUrl':
                    'gradients_degenerate_2pt/10552995703607727960.png',
                    'imageBUrl':
                    'gradients_degenerate_2pt/11198253335583713230.png',
                    'isDifferent': True,
                },
            ],

            # Test fix for http://skbug.com/2368 -- how do we handle an ImagePair
            # missing one of its images?
            [
                # inputs:
                'arcofzorro/16206093933823793653.png',
                'nonexistentDir/111111.png',
                {
                    'ignoreFailure': True,
                    'bugs': [1001, 1002],
                },
                {
                    'builder': 'MyBuilder',
                    'test': 'MyTest',
                },
                # expected output:
                {
                    'expectations': {
                        'bugs': [1001, 1002],
                        'ignoreFailure': True,
                    },
                    'extraColumns': {
                        'builder': 'MyBuilder',
                        'test': 'MyTest',
                    },
                    'imageAUrl': 'arcofzorro/16206093933823793653.png',
                    'imageBUrl': 'nonexistentDir/111111.png',
                    'isDifferent': True,
                },
            ],

            # One of the two images is missing, but download_all_images=True so we
            # should download it anyway.
            [
                # inputs:
                None,
                'arcofzorro/13786535001616823825.png',
                None,
                None,
                # expected output:
                {
                    'imageAUrl': None,
                    'imageBUrl': 'arcofzorro/13786535001616823825.png',
                    'isDifferent': True,
                },
            ],
        ]

        db = imagediffdb.ImageDiffDB(self.temp_dir)
        for selftest in selftests:
            image_pair = imagepair.ImagePair(image_diff_db=db,
                                             imageA_base_url=IMG_URL_BASE,
                                             imageB_base_url=IMG_URL_BASE,
                                             imageA_relative_url=selftest[0],
                                             imageB_relative_url=selftest[1],
                                             expectations=selftest[2],
                                             extra_columns=selftest[3],
                                             download_all_images=True)
            self.assertEqual(image_pair.as_dict(), selftest[4])
Example #4
0
    def _load_result_pairs(self, actuals_root, subdirs):
        """Loads all JSON files found within two subdirs in actuals_root,
    compares across those two subdirs, and stores the summary in self._results.

    Args:
      actuals_root: root directory containing all render_pictures-generated
          JSON files
      subdirs: (string, string) tuple; pair of subdirectories within
          actuals_root to compare
    """
        logging.info(
            'Reading actual-results JSON files from %s subdirs within %s...' %
            (subdirs, actuals_root))
        subdirA, subdirB = subdirs
        subdirA_builder_dicts = self._read_dicts_from_root(
            os.path.join(actuals_root, subdirA))
        subdirB_builder_dicts = self._read_dicts_from_root(
            os.path.join(actuals_root, subdirB))
        logging.info('Comparing subdirs %s and %s...' % (subdirA, subdirB))

        all_image_pairs = imagepairset.ImagePairSet(
            descriptions=subdirs, diff_base_url=self._diff_base_url)
        failing_image_pairs = imagepairset.ImagePairSet(
            descriptions=subdirs, diff_base_url=self._diff_base_url)

        all_image_pairs.ensure_extra_column_values_in_summary(
            column_id=results.KEY__EXTRACOLUMN__RESULT_TYPE,
            values=[
                results.KEY__RESULT_TYPE__FAILED,
                results.KEY__RESULT_TYPE__NOCOMPARISON,
                results.KEY__RESULT_TYPE__SUCCEEDED,
            ])
        failing_image_pairs.ensure_extra_column_values_in_summary(
            column_id=results.KEY__EXTRACOLUMN__RESULT_TYPE,
            values=[
                results.KEY__RESULT_TYPE__FAILED,
                results.KEY__RESULT_TYPE__NOCOMPARISON,
            ])

        builders = sorted(
            set(subdirA_builder_dicts.keys() + subdirB_builder_dicts.keys()))
        num_builders = len(builders)
        builder_num = 0
        for builder in builders:
            builder_num += 1
            logging.info(
                'Generating pixel diffs for builder #%d of %d, "%s"...' %
                (builder_num, num_builders, builder))
            # TODO(epoger): This will fail if we have results for this builder in
            # subdirA but not subdirB (or vice versa).
            subdirA_results = results.BaseComparisons.combine_subdicts(
                subdirA_builder_dicts[builder][gm_json.JSONKEY_ACTUALRESULTS])
            subdirB_results = results.BaseComparisons.combine_subdicts(
                subdirB_builder_dicts[builder][gm_json.JSONKEY_ACTUALRESULTS])
            image_names = sorted(
                set(subdirA_results.keys() + subdirB_results.keys()))
            for image_name in image_names:
                # The image name may contain funny characters or be ridiculously long
                # (see https://code.google.com/p/skia/issues/detail?id=2344#c10 ),
                # so make sure we sanitize it before using it in a URL path.
                #
                # TODO(epoger): Rather than sanitizing/truncating the image name here,
                # do it in render_pictures instead.
                # Reason: we will need to be consistent in applying this rule, so that
                # the process which uploads the files to GS using these paths will
                # match the paths created by downstream processes.
                # So, we should make render_pictures write out images to paths that are
                # "ready to upload" to Google Storage, like gm does.
                sanitized_test_name = DISALLOWED_FILEPATH_CHAR_REGEX.sub(
                    '_', image_name)[:30]

                subdirA_image_relative_url = (
                    results.BaseComparisons._create_relative_url(
                        hashtype_and_digest=subdirA_results.get(image_name),
                        test_name=sanitized_test_name))
                subdirB_image_relative_url = (
                    results.BaseComparisons._create_relative_url(
                        hashtype_and_digest=subdirB_results.get(image_name),
                        test_name=sanitized_test_name))

                # If we have images for at least one of these two subdirs,
                # add them to our list.
                if subdirA_image_relative_url or subdirB_image_relative_url:
                    if subdirA_image_relative_url == subdirB_image_relative_url:
                        result_type = results.KEY__RESULT_TYPE__SUCCEEDED
                    elif not subdirA_image_relative_url:
                        result_type = results.KEY__RESULT_TYPE__NOCOMPARISON
                    elif not subdirB_image_relative_url:
                        result_type = results.KEY__RESULT_TYPE__NOCOMPARISON
                    else:
                        result_type = results.KEY__RESULT_TYPE__FAILED

                extra_columns_dict = {
                    results.KEY__EXTRACOLUMN__RESULT_TYPE:
                    result_type,
                    results.KEY__EXTRACOLUMN__BUILDER:
                    builder,
                    results.KEY__EXTRACOLUMN__TEST:
                    image_name,
                    # TODO(epoger): Right now, the client UI crashes if it receives
                    # results that do not include a 'config' column.
                    # Until we fix that, keep the client happy.
                    results.KEY__EXTRACOLUMN__CONFIG:
                    'TODO',
                }

                try:
                    image_pair = imagepair.ImagePair(
                        image_diff_db=self._image_diff_db,
                        base_url=self._image_base_url,
                        imageA_relative_url=subdirA_image_relative_url,
                        imageB_relative_url=subdirB_image_relative_url,
                        extra_columns=extra_columns_dict)
                    all_image_pairs.add_image_pair(image_pair)
                    if result_type != results.KEY__RESULT_TYPE__SUCCEEDED:
                        failing_image_pairs.add_image_pair(image_pair)
                except (KeyError, TypeError):
                    logging.exception(
                        'got exception while creating ImagePair for image_name '
                        '"%s", builder "%s"' % (image_name, builder))

        self._results = {
            results.KEY__HEADER__RESULTS_ALL: all_image_pairs.as_dict(),
            results.KEY__HEADER__RESULTS_FAILURES:
            failing_image_pairs.as_dict(),
        }
Example #5
0
    def _create_image_pair(self, image_dict_A, image_dict_B, image_A_base_url,
                           image_B_base_url, builder_A, render_mode_A,
                           builder_B, render_mode_B, source_json_file,
                           source_skp_name, tilenum):
        """Creates an ImagePair object for this pair of images.

    Args:
      image_dict_A: dict with JSONKEY_IMAGE_* keys, or None if no image
      image_dict_B: dict with JSONKEY_IMAGE_* keys, or None if no image
      image_A_base_url: base URL for image A
      image_B_base_url: base URL for image B
      builder_A: builder that created image set A or None if unknow
      render_mode_A: render mode used to generate image set A or None if
                     unknown.
      builder_B: builder that created image set A or None if unknow
      render_mode_B: render mode used to generate image set A or None if
                     unknown.
      source_json_file: string; relative path of the JSON file where this
                        result came from, within setA and setB.
      source_skp_name: string; name of the source SKP file
      tilenum: which tile, or None if a wholeimage

    Returns:
      An ImagePair object, or None if both image_dict_A and image_dict_B are
      None.
    """
        if (not image_dict_A) and (not image_dict_B):
            return None

        def _checksum_and_relative_url(dic):
            if dic:
                return ((dic[gm_json.JSONKEY_IMAGE_CHECKSUMALGORITHM],
                         int(dic[gm_json.JSONKEY_IMAGE_CHECKSUMVALUE])),
                        dic[gm_json.JSONKEY_IMAGE_FILEPATH])
            else:
                return None, None

        imageA_checksum, imageA_relative_url = _checksum_and_relative_url(
            image_dict_A)
        imageB_checksum, imageB_relative_url = _checksum_and_relative_url(
            image_dict_B)

        if not imageA_checksum:
            result_type = results.KEY__RESULT_TYPE__NOCOMPARISON
        elif not imageB_checksum:
            result_type = results.KEY__RESULT_TYPE__NOCOMPARISON
        elif imageA_checksum == imageB_checksum:
            result_type = results.KEY__RESULT_TYPE__SUCCEEDED
        else:
            result_type = results.KEY__RESULT_TYPE__FAILED

        extra_columns_dict = {
            COLUMN__RESULT_TYPE: result_type,
            COLUMN__SOURCE_SKP: source_skp_name,
            COLUMN__BUILDER_A: builder_A,
            COLUMN__RENDER_MODE_A: render_mode_A,
            COLUMN__BUILDER_B: builder_B,
            COLUMN__RENDER_MODE_B: render_mode_B,
        }
        if tilenum == None:
            extra_columns_dict[
                COLUMN__TILED_OR_WHOLE] = COLUMN__TILED_OR_WHOLE__WHOLE
            extra_columns_dict[COLUMN__TILENUM] = 'N/A'
        else:
            extra_columns_dict[
                COLUMN__TILED_OR_WHOLE] = COLUMN__TILED_OR_WHOLE__TILED
            extra_columns_dict[COLUMN__TILENUM] = str(tilenum)

        try:
            return imagepair.ImagePair(
                image_diff_db=self._image_diff_db,
                imageA_base_url=image_A_base_url,
                imageB_base_url=image_B_base_url,
                imageA_relative_url=imageA_relative_url,
                imageB_relative_url=imageB_relative_url,
                extra_columns=extra_columns_dict,
                source_json_file=source_json_file,
                download_all_images=self._download_all_images)
        except (KeyError, TypeError):
            logging.exception(
                'got exception while creating ImagePair for'
                ' urlPair=("%s","%s"), source_skp_name="%s", tilenum="%s"' %
                (imageA_relative_url, imageB_relative_url, source_skp_name,
                 tilenum))
            return None
Example #6
0
  def _create_image_pair(self, test, config, image_dict_A, image_dict_B):
    """Creates an ImagePair object for this pair of images.

    Args:
      test: string; name of the test
      config: string; name of the config
      image_dict_A: dict with JSONKEY_IMAGE_* keys, or None if no image
      image_dict_B: dict with JSONKEY_IMAGE_* keys, or None if no image

    Returns:
      An ImagePair object, or None if both image_dict_A and image_dict_B are
      None.
    """
    if (not image_dict_A) and (not image_dict_B):
      return None

    def _checksum_and_relative_url(dic):
      if dic:
        return ((dic[gm_json.JSONKEY_IMAGE_CHECKSUMALGORITHM],
                 dic[gm_json.JSONKEY_IMAGE_CHECKSUMVALUE]),
                dic[gm_json.JSONKEY_IMAGE_FILEPATH])
      else:
        return None, None

    imageA_checksum, imageA_relative_url = _checksum_and_relative_url(
        image_dict_A)
    imageB_checksum, imageB_relative_url = _checksum_and_relative_url(
        image_dict_B)

    if not imageA_checksum:
      result_type = results.KEY__RESULT_TYPE__NOCOMPARISON
    elif not imageB_checksum:
      result_type = results.KEY__RESULT_TYPE__NOCOMPARISON
    elif imageA_checksum == imageB_checksum:
      result_type = results.KEY__RESULT_TYPE__SUCCEEDED
    else:
      result_type = results.KEY__RESULT_TYPE__FAILED

    extra_columns_dict = {
        results.KEY__EXTRACOLUMNS__CONFIG: config,
        results.KEY__EXTRACOLUMNS__RESULT_TYPE: result_type,
        results.KEY__EXTRACOLUMNS__TEST: test,
        # TODO(epoger): Right now, the client UI crashes if it receives
        # results that do not include this column.
        # Until we fix that, keep the client happy.
        results.KEY__EXTRACOLUMNS__BUILDER: 'TODO',
    }

    try:
      return imagepair.ImagePair(
          image_diff_db=self._image_diff_db,
          base_url=self._image_base_url,
          imageA_relative_url=imageA_relative_url,
          imageB_relative_url=imageB_relative_url,
          extra_columns=extra_columns_dict)
    except (KeyError, TypeError):
      logging.exception(
          'got exception while creating ImagePair for'
          ' test="%s", config="%s", urlPair=("%s","%s")' % (
              test, config, imageA_relative_url, imageB_relative_url))
      return None
Example #7
0
    def _create_image_pair(self, image_dict_A, image_dict_B, source_skp_name,
                           tilenum):
        """Creates an ImagePair object for this pair of images.

    Args:
      image_dict_A: dict with JSONKEY_IMAGE_* keys, or None if no image
      image_dict_B: dict with JSONKEY_IMAGE_* keys, or None if no image
      source_skp_name: string; name of the source SKP file
      tilenum: which tile, or None if a wholeimage

    Returns:
      An ImagePair object, or None if both image_dict_A and image_dict_B are
      None.
    """
        if (not image_dict_A) and (not image_dict_B):
            return None

        def _checksum_and_relative_url(dic):
            if dic:
                return ((dic[gm_json.JSONKEY_IMAGE_CHECKSUMALGORITHM],
                         dic[gm_json.JSONKEY_IMAGE_CHECKSUMVALUE]),
                        dic[gm_json.JSONKEY_IMAGE_FILEPATH])
            else:
                return None, None

        imageA_checksum, imageA_relative_url = _checksum_and_relative_url(
            image_dict_A)
        imageB_checksum, imageB_relative_url = _checksum_and_relative_url(
            image_dict_B)

        if not imageA_checksum:
            result_type = results.KEY__RESULT_TYPE__NOCOMPARISON
        elif not imageB_checksum:
            result_type = results.KEY__RESULT_TYPE__NOCOMPARISON
        elif imageA_checksum == imageB_checksum:
            result_type = results.KEY__RESULT_TYPE__SUCCEEDED
        else:
            result_type = results.KEY__RESULT_TYPE__FAILED

        extra_columns_dict = {
            COLUMN__RESULT_TYPE: result_type,
            COLUMN__SOURCE_SKP: source_skp_name,
        }
        if tilenum == None:
            extra_columns_dict[COLUMN__TILED_OR_WHOLE] = 'whole'
            extra_columns_dict[COLUMN__TILENUM] = 'N/A'
        else:
            extra_columns_dict[COLUMN__TILED_OR_WHOLE] = 'tiled'
            extra_columns_dict[COLUMN__TILENUM] = str(tilenum)

        try:
            return imagepair.ImagePair(
                image_diff_db=self._image_diff_db,
                base_url=self._image_base_gs_url,
                imageA_relative_url=imageA_relative_url,
                imageB_relative_url=imageB_relative_url,
                extra_columns=extra_columns_dict,
                download_all_images=self._download_all_images)
        except (KeyError, TypeError):
            logging.exception(
                'got exception while creating ImagePair for'
                ' urlPair=("%s","%s"), source_skp_name="%s", tilenum="%s"' %
                (imageA_relative_url, imageB_relative_url, source_skp_name,
                 tilenum))
            return None
Example #8
0
    def test_endToEnd(self):
        """Tests ImagePair, using a real ImageDiffDB to download real images.

    TODO(epoger): Either in addition to or instead of this end-to-end test,
    we should perform some tests using either:
    1. a mock ImageDiffDB, or
    2. a real ImageDiffDB that doesn't hit Google Storage looking for input
       image files (maybe a file:// IMG_URL_BASE)
    """
        # params for each self-test:
        #
        # inputs:
        #  0. imageA_relative_URL
        #  1. imageB_relative_URL
        #  2. expectations dict
        #  3. extra_columns dict
        # expected output:
        #  4. expected result of ImagePair.as_dict()
        selftests = [
            [
                # inputs:
                'arcofzorro/16206093933823793653.png',
                'arcofzorro/16206093933823793653.png',
                None,
                {
                    'builder': 'MyBuilder',
                    'test': 'MyTest',
                },
                # expected output:
                {
                    'extraColumns': {
                        'builder': 'MyBuilder',
                        'test': 'MyTest',
                    },
                    'imageAUrl': 'arcofzorro/16206093933823793653.png',
                    'imageBUrl': 'arcofzorro/16206093933823793653.png',
                    'isDifferent': False,
                },
            ],
            [
                # inputs:
                'arcofzorro/16206093933823793653.png',
                'arcofzorro/13786535001616823825.png',
                None,
                None,
                # expected output:
                {
                    'differenceData': {
                        'maxDiffPerChannel': [255, 255, 247],
                        'numDifferingPixels': 662,
                        'percentDifferingPixels': 0.0662,
                        'perceptualDifference': 0.06620000000000914,
                        'weightedDiffMeasure': 0.01127756555171088,
                    },
                    'imageAUrl': 'arcofzorro/16206093933823793653.png',
                    'imageBUrl': 'arcofzorro/13786535001616823825.png',
                    'isDifferent': True,
                },
            ],
            [
                # inputs:
                'gradients_degenerate_2pt/10552995703607727960.png',
                'gradients_degenerate_2pt/11198253335583713230.png',
                {
                    'ignoreFailure': True,
                    'bugs': [1001, 1002],
                },
                {
                    'builder': 'MyBuilder',
                    'test': 'MyTest',
                },
                # expected output:
                {
                    'differenceData': {
                        'maxDiffPerChannel': [255, 0, 255],
                        'numDifferingPixels': 102400,
                        'percentDifferingPixels': 100.00,
                        'perceptualDifference': 100.00,
                        'weightedDiffMeasure': 66.66666666666667,
                    },
                    'expectations': {
                        'bugs': [1001, 1002],
                        'ignoreFailure': True,
                    },
                    'extraColumns': {
                        'builder': 'MyBuilder',
                        'test': 'MyTest',
                    },
                    'imageAUrl':
                    'gradients_degenerate_2pt/10552995703607727960.png',
                    'imageBUrl':
                    'gradients_degenerate_2pt/11198253335583713230.png',
                    'isDifferent': True,
                },
            ],
        ]

        db = imagediffdb.ImageDiffDB(self._temp_dir)
        for selftest in selftests:
            image_pair = imagepair.ImagePair(image_diff_db=db,
                                             base_url=IMG_URL_BASE,
                                             imageA_relative_url=selftest[0],
                                             imageB_relative_url=selftest[1],
                                             expectations=selftest[2],
                                             extra_columns=selftest[3])
            self.assertEqual(image_pair.as_dict(), selftest[4])