aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gm/gm_json.py52
-rw-r--r--gm/rebaseline_server/column.py1
-rw-r--r--gm/rebaseline_server/imagediffdb.py29
-rw-r--r--gm/rebaseline_server/imagepair.py11
-rwxr-xr-xgm/rebaseline_server/imagepair_test.py2
-rw-r--r--gm/rebaseline_server/imagepairset.py28
-rwxr-xr-xgm/rebaseline_server/imagepairset_test.py2
-rwxr-xr-xgm/rebaseline_server/results.py354
-rwxr-xr-xgm/rebaseline_server/results_test.py5
-rwxr-xr-xgm/rebaseline_server/server.py74
-rw-r--r--gm/rebaseline_server/static/constants.js69
-rw-r--r--gm/rebaseline_server/static/loader.js317
-rw-r--r--gm/rebaseline_server/static/view.html166
-rw-r--r--gm/rebaseline_server/testdata/outputs/actual/results_test.ResultsTest.test_gm/gm.json526
-rw-r--r--gm/rebaseline_server/testdata/outputs/expected/results_test.ResultsTest.test_gm/gm.json935
15 files changed, 1091 insertions, 1480 deletions
diff --git a/gm/gm_json.py b/gm/gm_json.py
index 1aac6632f5..6f4b324d46 100644
--- a/gm/gm_json.py
+++ b/gm/gm_json.py
@@ -15,12 +15,14 @@ __author__ = 'Elliot Poger'
import io
import json
import os
+import posixpath
+import re
# Key strings used in GM results JSON files (both expected-results.json and
# actual-results.json).
#
-# These constants must be kept in sync with the kJsonKey_ constants in
+# NOTE: These constants must be kept in sync with the kJsonKey_ constants in
# gm_expectations.cpp !
@@ -93,6 +95,14 @@ SKIMAGE_EXPECTATIONS_ROOT = os.path.join('expectations', 'skimage')
# Pattern used to assemble each image's filename
IMAGE_FILENAME_PATTERN = '(.+)_(.+)\.png' # matches (testname, config)
+# Pattern used to create image URLs, relative to some base URL.
+GM_RELATIVE_URL_FORMATTER = '%s/%s/%s.png' # pass in (hash_type, test_name,
+ # hash_digest)
+GM_RELATIVE_URL_PATTERN = '(.+)/(.+)/(.+).png' # matches (hash_type, test_name,
+ # hash_digest)
+GM_RELATIVE_URL_RE = re.compile(GM_RELATIVE_URL_PATTERN)
+
+
def CreateGmActualUrl(test_name, hash_type, hash_digest,
gm_actuals_root_url=GM_ACTUALS_ROOT_HTTP_URL):
"""Return the URL we can use to download a particular version of
@@ -104,10 +114,40 @@ def CreateGmActualUrl(test_name, hash_type, hash_digest,
hash_digest: the hash digest of the image to retrieve
gm_actuals_root_url: root url where actual images are stored
"""
- # TODO(epoger): Maybe use url_or_path.join() so that, for testing, this can
- # return either a URL or a local filepath?
- return '%s/%s/%s/%s.png' % (gm_actuals_root_url, hash_type, test_name,
- hash_digest)
+ return posixpath.join(
+ gm_actuals_root_url, CreateGmRelativeUrl(
+ test_name=test_name, hash_type=hash_type, hash_digest=hash_digest))
+
+
+def CreateGmRelativeUrl(test_name, hash_type, hash_digest):
+ """Returns a relative URL pointing at a test result's image.
+
+ Returns the URL we can use to download a particular version of
+ the actually-generated image for this particular GM test,
+ relative to the URL root.
+
+ Args:
+ test_name: name of the test, e.g. 'perlinnoise'
+ hash_type: string indicating the hash type used to generate hash_digest,
+ e.g. JSONKEY_HASHTYPE_BITMAP_64BITMD5
+ hash_digest: the hash digest of the image to retrieve
+ """
+ return GM_RELATIVE_URL_FORMATTER % (hash_type, test_name, hash_digest)
+
+
+def SplitGmRelativeUrl(url):
+ """Splits the relative URL into (test_name, hash_type, hash_digest) tuple.
+
+ This is the inverse of CreateGmRelativeUrl().
+
+ Args:
+ url: a URL generated with CreateGmRelativeUrl().
+
+ Returns: (test_name, hash_type, hash_digest) tuple.
+ """
+ hash_type, test_name, hash_digest = GM_RELATIVE_URL_RE.match(url).groups()
+ return (test_name, hash_type, hash_digest)
+
def LoadFromString(file_contents):
"""Loads the JSON summary written out by the GM tool.
@@ -119,6 +159,7 @@ def LoadFromString(file_contents):
json_dict = json.loads(file_contents)
return json_dict
+
def LoadFromFile(file_path):
"""Loads the JSON summary written out by the GM tool.
Returns a dictionary keyed by the values listed as JSONKEY_ constants
@@ -126,6 +167,7 @@ def LoadFromFile(file_path):
file_contents = open(file_path, 'r').read()
return LoadFromString(file_contents)
+
def WriteToFile(json_dict, file_path):
"""Writes the JSON summary in json_dict out to file_path.
diff --git a/gm/rebaseline_server/column.py b/gm/rebaseline_server/column.py
index 7bce15a250..d8d119d130 100644
--- a/gm/rebaseline_server/column.py
+++ b/gm/rebaseline_server/column.py
@@ -10,6 +10,7 @@ ColumnHeaderFactory class (see class docstring for details)
"""
# Keys used within dictionary representation of each column header.
+# NOTE: Keep these in sync with static/constants.js
KEY__HEADER_TEXT = 'headerText'
KEY__HEADER_URL = 'headerUrl'
KEY__IS_FILTERABLE = 'isFilterable'
diff --git a/gm/rebaseline_server/imagediffdb.py b/gm/rebaseline_server/imagediffdb.py
index 8cec46bc94..f3347f75af 100644
--- a/gm/rebaseline_server/imagediffdb.py
+++ b/gm/rebaseline_server/imagediffdb.py
@@ -43,6 +43,14 @@ WHITEDIFFS_SUBDIR = 'whitediffs'
VALUES_PER_BAND = 256
+# Keys used within DiffRecord dictionary representations.
+# NOTE: Keep these in sync with static/constants.js
+KEY__DIFFERENCE_DATA__MAX_DIFF_PER_CHANNEL = 'maxDiffPerChannel'
+KEY__DIFFERENCE_DATA__NUM_DIFF_PIXELS = 'numDifferingPixels'
+KEY__DIFFERENCE_DATA__PERCENT_DIFF_PIXELS = 'percentDifferingPixels'
+KEY__DIFFERENCE_DATA__PERCEPTUAL_DIFF = 'perceptualDifference'
+KEY__DIFFERENCE_DATA__WEIGHTED_DIFF = 'weightedDiffMeasure'
+
class DiffRecord(object):
""" Record of differences between two images. """
@@ -186,10 +194,12 @@ class DiffRecord(object):
"""Returns a dictionary representation of this DiffRecord, as needed when
constructing the JSON representation."""
return {
- 'numDifferingPixels': self._num_pixels_differing,
- 'percentDifferingPixels': self.get_percent_pixels_differing(),
- 'weightedDiffMeasure': self.get_weighted_diff_measure(),
- 'maxDiffPerChannel': self._max_diff_per_channel,
+ KEY__DIFFERENCE_DATA__NUM_DIFF_PIXELS: self._num_pixels_differing,
+ KEY__DIFFERENCE_DATA__PERCENT_DIFF_PIXELS:
+ self.get_percent_pixels_differing(),
+ KEY__DIFFERENCE_DATA__WEIGHTED_DIFF: self.get_weighted_diff_measure(),
+ KEY__DIFFERENCE_DATA__MAX_DIFF_PER_CHANNEL: self._max_diff_per_channel,
+ KEY__DIFFERENCE_DATA__PERCEPTUAL_DIFF: self._perceptual_difference,
}
@@ -286,6 +296,7 @@ def _calculate_weighted_diff_metric(histogram, num_pixels):
total_diff += histogram[index] * (index % VALUES_PER_BAND)**2
return float(100 * total_diff) / max_diff
+
def _max_per_band(histogram):
"""Given the histogram of an image, return the maximum value of each band
(a.k.a. "color channel", such as R/G/B) across the entire image.
@@ -312,6 +323,7 @@ def _max_per_band(histogram):
break
return max_per_band
+
def _generate_image_diff(image1, image2):
"""Wrapper for ImageChops.difference(image1, image2) that will handle some
errors automatically, or at least yield more useful error messages.
@@ -333,6 +345,7 @@ def _generate_image_diff(image1, image2):
repr(image1), repr(image2)))
raise
+
def _download_and_open_image(local_filepath, url):
"""Open the image at local_filepath; if there is no file at that path,
download it from url to that path and then open it.
@@ -350,6 +363,7 @@ def _download_and_open_image(local_filepath, url):
shutil.copyfileobj(fsrc=url_handle, fdst=file_handle)
return _open_image(local_filepath)
+
def _open_image(filepath):
"""Wrapper for Image.open(filepath) that yields more useful error messages.
@@ -364,6 +378,7 @@ def _open_image(filepath):
logging.error('IOError loading image file %s' % filepath)
raise
+
def _save_image(image, filepath, format='PNG'):
"""Write an image to disk, creating any intermediate directories as needed.
@@ -376,6 +391,7 @@ def _save_image(image, filepath, format='PNG'):
_mkdir_unless_exists(os.path.dirname(filepath))
image.save(filepath, format)
+
def _mkdir_unless_exists(path):
"""Unless path refers to an already-existing directory, create it.
@@ -385,6 +401,7 @@ def _mkdir_unless_exists(path):
if not os.path.isdir(path):
os.makedirs(path)
+
def _sanitize_locator(locator):
"""Returns a sanitized version of a locator (one in which we know none of the
characters will have special meaning in filenames).
@@ -394,10 +411,14 @@ def _sanitize_locator(locator):
"""
return DISALLOWED_FILEPATH_CHAR_REGEX.sub('_', str(locator))
+
def _get_difference_locator(expected_image_locator, actual_image_locator):
"""Returns the locator string used to look up the diffs between expected_image
and actual_image.
+ We must keep this function in sync with getImageDiffRelativeUrl() in
+ static/loader.js
+
Args:
expected_image_locator: locator string pointing at expected image
actual_image_locator: locator string pointing at actual image
diff --git a/gm/rebaseline_server/imagepair.py b/gm/rebaseline_server/imagepair.py
index bba36fa8c4..f02704e082 100644
--- a/gm/rebaseline_server/imagepair.py
+++ b/gm/rebaseline_server/imagepair.py
@@ -12,6 +12,7 @@ ImagePair class (see class docstring for details)
import posixpath
# Keys used within ImagePair dictionary representations.
+# NOTE: Keep these in sync with static/constants.js
KEY__DIFFERENCE_DATA = 'differenceData'
KEY__EXPECTATIONS_DATA = 'expectations'
KEY__EXTRA_COLUMN_VALUES = 'extraColumns'
@@ -31,8 +32,10 @@ class ImagePair(object):
Args:
image_diff_db: ImageDiffDB instance we use to generate/store image diffs
base_url: base of all image URLs
- imageA_relative_url: URL pointing at an image, relative to base_url
- imageB_relative_url: URL pointing at an image, relative to base_url
+ imageA_relative_url: string; URL pointing at an image, relative to
+ base_url; or None, if this image is missing
+ imageB_relative_url: string; URL pointing at an image, relative to
+ base_url; or None, if this image is missing
expectations: optional dictionary containing expectations-specific
metadata (ignore-failure, bug numbers, etc.)
extra_columns: optional dictionary containing more metadata (test name,
@@ -43,7 +46,9 @@ class ImagePair(object):
self.imageB_relative_url = imageB_relative_url
self.expectations_dict = expectations
self.extra_columns_dict = extra_columns
- if imageA_relative_url == imageB_relative_url:
+ if not imageA_relative_url or not imageB_relative_url:
+ self.diff_record = None
+ elif imageA_relative_url == imageB_relative_url:
self.diff_record = None
else:
# TODO(epoger): Rather than blocking until image_diff_db can read in
diff --git a/gm/rebaseline_server/imagepair_test.py b/gm/rebaseline_server/imagepair_test.py
index d29438e530..b2cae31a51 100755
--- a/gm/rebaseline_server/imagepair_test.py
+++ b/gm/rebaseline_server/imagepair_test.py
@@ -87,6 +87,7 @@ class ImagePairTest(unittest.TestCase):
'maxDiffPerChannel': [255, 255, 247],
'numDifferingPixels': 662,
'percentDifferingPixels': 0.0662,
+ 'perceptualDifference': 0.06620000000000914,
'weightedDiffMeasure': 0.01127756555171088,
},
'imageAUrl': 'arcofzorro/16206093933823793653.png',
@@ -113,6 +114,7 @@ class ImagePairTest(unittest.TestCase):
'maxDiffPerChannel': [255, 0, 255],
'numDifferingPixels': 102400,
'percentDifferingPixels': 100.00,
+ 'perceptualDifference': 100.00,
'weightedDiffMeasure': 66.66666666666667,
},
'expectations': {
diff --git a/gm/rebaseline_server/imagepairset.py b/gm/rebaseline_server/imagepairset.py
index 2e173f537f..26c833e1b0 100644
--- a/gm/rebaseline_server/imagepairset.py
+++ b/gm/rebaseline_server/imagepairset.py
@@ -12,7 +12,8 @@ ImagePairSet class; see its docstring below.
import column
# Keys used within dictionary representation of ImagePairSet.
-KEY__COLUMNHEADERS = 'columnHeaders'
+# NOTE: Keep these in sync with static/constants.js
+KEY__EXTRACOLUMNHEADERS = 'extraColumnHeaders'
KEY__IMAGEPAIRS = 'imagePairs'
KEY__IMAGESETS = 'imageSets'
KEY__IMAGESETS__BASE_URL = 'baseUrl'
@@ -55,7 +56,7 @@ class ImagePairSet(object):
extra_columns_dict = image_pair.extra_columns_dict
if extra_columns_dict:
for column_id, value in extra_columns_dict.iteritems():
- self._add_extra_column_entry(column_id, value)
+ self._add_extra_column_value_to_summary(column_id, value)
def set_column_header_factory(self, column_id, column_header_factory):
"""Overrides the default settings for one of the extraColumn headers.
@@ -80,19 +81,36 @@ class ImagePairSet(object):
self._column_header_factories[column_id] = column_header_factory
return column_header_factory
- def _add_extra_column_entry(self, column_id, value):
+ def ensure_extra_column_values_in_summary(self, column_id, values):
+ """Ensure this column_id/value pair is part of the extraColumns summary.
+
+ Args:
+ column_id: string; unique ID of this column
+ value: string; a possible value for this column
+ """
+ for value in values:
+ self._add_extra_column_value_to_summary(
+ column_id=column_id, value=value, addend=0)
+
+ def _add_extra_column_value_to_summary(self, column_id, value, addend=1):
"""Records one column_id/value extraColumns pair found within an ImagePair.
We use this information to generate tallies within the column header
(how many instances we saw of a particular value, within a particular
extraColumn).
+
+ Args:
+ column_id: string; unique ID of this column (must match a key within
+ an ImagePair's extra_columns dictionary)
+ value: string; a possible value for this column
+ addend: integer; how many instances to add to the tally
"""
known_values_for_column = self._extra_column_tallies.get(column_id, None)
if not known_values_for_column:
known_values_for_column = {}
self._extra_column_tallies[column_id] = known_values_for_column
instances_of_this_value = known_values_for_column.get(value, 0)
- instances_of_this_value += 1
+ instances_of_this_value += addend
known_values_for_column[value] = instances_of_this_value
def _column_headers_as_dict(self):
@@ -110,7 +128,7 @@ class ImagePairSet(object):
Uses the KEY__* constants as keys.
"""
return {
- KEY__COLUMNHEADERS: self._column_headers_as_dict(),
+ KEY__EXTRACOLUMNHEADERS: self._column_headers_as_dict(),
KEY__IMAGEPAIRS: self._image_pair_dicts,
KEY__IMAGESETS: [{
KEY__IMAGESETS__BASE_URL: self._base_url,
diff --git a/gm/rebaseline_server/imagepairset_test.py b/gm/rebaseline_server/imagepairset_test.py
index 8f1edfce40..815fd74056 100755
--- a/gm/rebaseline_server/imagepairset_test.py
+++ b/gm/rebaseline_server/imagepairset_test.py
@@ -85,7 +85,7 @@ class ImagePairSetTest(unittest.TestCase):
MockImagePair(base_url=BASE_URL_1, dict_to_return=IMAGEPAIR_3_AS_DICT),
]
expected_imageset_dict = {
- 'columnHeaders': {
+ 'extraColumnHeaders': {
'builder': {
'headerText': 'builder',
'isFilterable': True,
diff --git a/gm/rebaseline_server/results.py b/gm/rebaseline_server/results.py
index 3b57bc1e20..c7915f261d 100755
--- a/gm/rebaseline_server/results.py
+++ b/gm/rebaseline_server/results.py
@@ -32,27 +32,43 @@ if GM_DIRECTORY not in sys.path:
sys.path.append(GM_DIRECTORY)
import gm_json
import imagediffdb
+import imagepair
+import imagepairset
+
+# Keys used to link an image to a particular GM test.
+# NOTE: Keep these in sync with static/constants.js
+KEY__EXPECTATIONS__BUGS = gm_json.JSONKEY_EXPECTEDRESULTS_BUGS
+KEY__EXPECTATIONS__IGNOREFAILURE = gm_json.JSONKEY_EXPECTEDRESULTS_IGNOREFAILURE
+KEY__EXPECTATIONS__REVIEWED = gm_json.JSONKEY_EXPECTEDRESULTS_REVIEWED
+KEY__EXTRACOLUMN__BUILDER = 'builder'
+KEY__EXTRACOLUMN__CONFIG = 'config'
+KEY__EXTRACOLUMN__RESULT_TYPE = 'resultType'
+KEY__EXTRACOLUMN__TEST = 'test'
+KEY__HEADER__RESULTS_ALL = 'all'
+KEY__HEADER__RESULTS_FAILURES = 'failures'
+KEY__NEW_IMAGE_URL = 'newImageUrl'
+KEY__RESULT_TYPE__FAILED = gm_json.JSONKEY_ACTUALRESULTS_FAILED
+KEY__RESULT_TYPE__FAILUREIGNORED = gm_json.JSONKEY_ACTUALRESULTS_FAILUREIGNORED
+KEY__RESULT_TYPE__NOCOMPARISON = gm_json.JSONKEY_ACTUALRESULTS_NOCOMPARISON
+KEY__RESULT_TYPE__SUCCEEDED = gm_json.JSONKEY_ACTUALRESULTS_SUCCEEDED
+
+EXPECTATION_FIELDS_PASSED_THRU_VERBATIM = [
+ KEY__EXPECTATIONS__BUGS,
+ KEY__EXPECTATIONS__IGNOREFAILURE,
+ KEY__EXPECTATIONS__REVIEWED,
+]
IMAGE_FILENAME_RE = re.compile(gm_json.IMAGE_FILENAME_PATTERN)
IMAGE_FILENAME_FORMATTER = '%s_%s.png' # pass in (testname, config)
-FIELDS_PASSED_THRU_VERBATIM = [
- gm_json.JSONKEY_EXPECTEDRESULTS_BUGS,
- gm_json.JSONKEY_EXPECTEDRESULTS_IGNOREFAILURE,
- gm_json.JSONKEY_EXPECTEDRESULTS_REVIEWED,
-]
-CATEGORIES_TO_SUMMARIZE = [
- 'builder', 'test', 'config', 'resultType',
- gm_json.JSONKEY_EXPECTEDRESULTS_IGNOREFAILURE,
- gm_json.JSONKEY_EXPECTEDRESULTS_REVIEWED,
-]
+IMAGEPAIR_SET_DESCRIPTIONS = ('expected image', 'actual image')
-RESULTS_ALL = 'all'
-RESULTS_FAILURES = 'failures'
class Results(object):
- """ Loads actual and expected results from all builders, supplying combined
- reports as requested.
+ """ Loads actual and expected GM results into an ImagePairSet.
+
+ Loads actual and expected results from all builders, except for those skipped
+ by _ignore_builder().
Once this object has been constructed, the results (in self._results[])
are immutable. If you want to update the results based on updated JSON
@@ -94,14 +110,17 @@ class Results(object):
[
{
- 'builder': 'Test-Mac10.6-MacMini4.1-GeForce320M-x86-Debug',
- 'test': 'bigmatrix',
- 'config': '8888',
- 'expectedHashType': 'bitmap-64bitMD5',
- 'expectedHashDigest': '10894408024079689926',
- 'bugs': [123, 456],
- 'ignore-failure': false,
- 'reviewed-by-human': true,
+ imagepair.KEY__EXPECTATIONS_DATA: {
+ KEY__EXPECTATIONS__BUGS: [123, 456],
+ KEY__EXPECTATIONS__IGNOREFAILURE: false,
+ KEY__EXPECTATIONS__REVIEWED: true,
+ },
+ imagepair.KEY__EXTRA_COLUMN_VALUES: {
+ KEY__EXTRACOLUMN__BUILDER: 'Test-Mac10.6-MacMini4.1-GeForce320M-x86-Debug',
+ KEY__EXTRACOLUMN__CONFIG: '8888',
+ KEY__EXTRACOLUMN__TEST: 'bigmatrix',
+ },
+ KEY__NEW_IMAGE_URL: 'bitmap-64bitMD5/bigmatrix/10894408024079689926.png',
},
...
]
@@ -109,18 +128,21 @@ class Results(object):
"""
expected_builder_dicts = Results._read_dicts_from_root(self._expected_root)
for mod in modifications:
- image_name = IMAGE_FILENAME_FORMATTER % (mod['test'], mod['config'])
- # TODO(epoger): assumes a single allowed digest per test
- allowed_digests = [[mod['expectedHashType'],
- int(mod['expectedHashDigest'])]]
+ image_name = IMAGE_FILENAME_FORMATTER % (
+ mod[imagepair.KEY__EXTRA_COLUMN_VALUES][KEY__EXTRACOLUMN__TEST],
+ mod[imagepair.KEY__EXTRA_COLUMN_VALUES][KEY__EXTRACOLUMN__CONFIG])
+ _, hash_type, hash_digest = gm_json.SplitGmRelativeUrl(
+ mod[KEY__NEW_IMAGE_URL])
+ allowed_digests = [[hash_type, int(hash_digest)]]
new_expectations = {
gm_json.JSONKEY_EXPECTEDRESULTS_ALLOWEDDIGESTS: allowed_digests,
}
- for field in FIELDS_PASSED_THRU_VERBATIM:
- value = mod.get(field)
+ for field in EXPECTATION_FIELDS_PASSED_THRU_VERBATIM:
+ value = mod[imagepair.KEY__EXPECTATIONS_DATA].get(field)
if value is not None:
new_expectations[field] = value
- builder_dict = expected_builder_dicts[mod['builder']]
+ builder_dict = expected_builder_dicts[
+ mod[imagepair.KEY__EXTRA_COLUMN_VALUES][KEY__EXTRACOLUMN__BUILDER]]
builder_expectations = builder_dict.get(gm_json.JSONKEY_EXPECTEDRESULTS)
if not builder_expectations:
builder_expectations = {}
@@ -135,47 +157,7 @@ class Results(object):
type: string describing which types of results to include; must be one
of the RESULTS_* constants
- Results are returned as a dictionary in this form:
-
- {
- 'categories': # dictionary of categories listed in
- # CATEGORIES_TO_SUMMARIZE, with the number of times
- # each value appears within its category
- {
- 'resultType': # category name
- {
- 'failed': 29, # category value and total number found of that value
- 'failure-ignored': 948,
- 'no-comparison': 4502,
- 'succeeded': 38609,
- },
- 'builder':
- {
- 'Test-Mac10.6-MacMini4.1-GeForce320M-x86-Debug': 1286,
- 'Test-Mac10.6-MacMini4.1-GeForce320M-x86-Release': 1134,
- ...
- },
- ... # other categories from CATEGORIES_TO_SUMMARIZE
- }, # end of 'categories' dictionary
-
- 'testData': # list of test results, with a dictionary for each
- [
- {
- 'resultType': 'failed',
- 'builder': 'Test-Mac10.6-MacMini4.1-GeForce320M-x86-Debug',
- 'test': 'bigmatrix',
- 'config': '8888',
- 'expectedHashType': 'bitmap-64bitMD5',
- 'expectedHashDigest': '10894408024079689926',
- 'actualHashType': 'bitmap-64bitMD5',
- 'actualHashDigest': '2409857384569',
- 'bugs': [123, 456],
- 'ignore-failure': false,
- 'reviewed-by-human': true,
- },
- ...
- ], # end of 'testData' list
- }
+ Results are returned in a dictionary as output by ImagePairSet.as_dict().
"""
return self._results[type]
@@ -227,6 +209,24 @@ class Results(object):
return meta_dict
@staticmethod
+ def _create_relative_url(hashtype_and_digest, test_name):
+ """Returns the URL for this image, relative to GM_ACTUALS_ROOT_HTTP_URL.
+
+ If we don't have a record of this image, returns None.
+
+ Args:
+ hashtype_and_digest: (hash_type, hash_digest) tuple, or None if we
+ don't have a record of this image
+ test_name: string; name of the GM test that created this image
+ """
+ if not hashtype_and_digest:
+ return None
+ return gm_json.CreateGmRelativeUrl(
+ test_name=test_name,
+ hash_type=hashtype_and_digest[0],
+ hash_digest=hashtype_and_digest[1])
+
+ @staticmethod
def _write_dicts_to_root(meta_dict, root, pattern='*.json'):
"""Write all per-builder dictionaries within meta_dict to files under
the root path.
@@ -272,36 +272,6 @@ class Results(object):
'for builders %s' % (
expected_builders_written, actual_builders_written))
- def _generate_pixel_diffs_if_needed(self, test, expected_image, actual_image):
- """If expected_image and actual_image both exist but are different,
- add the image pair to self._image_diff_db and generate pixel diffs.
-
- Args:
- test: string; name of test
- expected_image: (hashType, hashDigest) tuple describing the expected image
- actual_image: (hashType, hashDigest) tuple describing the actual image
- """
- if expected_image == actual_image:
- return
-
- (expected_hashtype, expected_hashdigest) = expected_image
- (actual_hashtype, actual_hashdigest) = actual_image
- if None in [expected_hashtype, expected_hashdigest,
- actual_hashtype, actual_hashdigest]:
- return
-
- expected_url = gm_json.CreateGmActualUrl(
- test_name=test, hash_type=expected_hashtype,
- hash_digest=expected_hashdigest)
- actual_url = gm_json.CreateGmActualUrl(
- test_name=test, hash_type=actual_hashtype,
- hash_digest=actual_hashdigest)
- self._image_diff_db.add_image_pair(
- expected_image_locator=expected_hashdigest,
- expected_image_url=expected_url,
- actual_image_locator=actual_hashdigest,
- actual_image_url=actual_url)
-
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),
@@ -314,25 +284,23 @@ class Results(object):
self._expected_root)
expected_builder_dicts = Results._read_dicts_from_root(self._expected_root)
- categories_all = {}
- categories_failures = {}
+ all_image_pairs = imagepairset.ImagePairSet(IMAGEPAIR_SET_DESCRIPTIONS)
+ failing_image_pairs = imagepairset.ImagePairSet(IMAGEPAIR_SET_DESCRIPTIONS)
- Results._ensure_included_in_category_dict(categories_all,
- 'resultType', [
- gm_json.JSONKEY_ACTUALRESULTS_FAILED,
- gm_json.JSONKEY_ACTUALRESULTS_FAILUREIGNORED,
- gm_json.JSONKEY_ACTUALRESULTS_NOCOMPARISON,
- gm_json.JSONKEY_ACTUALRESULTS_SUCCEEDED,
+ all_image_pairs.ensure_extra_column_values_in_summary(
+ column_id=KEY__EXTRACOLUMN__RESULT_TYPE, values=[
+ KEY__RESULT_TYPE__FAILED,
+ KEY__RESULT_TYPE__FAILUREIGNORED,
+ KEY__RESULT_TYPE__NOCOMPARISON,
+ KEY__RESULT_TYPE__SUCCEEDED,
])
- Results._ensure_included_in_category_dict(categories_failures,
- 'resultType', [
- gm_json.JSONKEY_ACTUALRESULTS_FAILED,
- gm_json.JSONKEY_ACTUALRESULTS_FAILUREIGNORED,
- gm_json.JSONKEY_ACTUALRESULTS_NOCOMPARISON,
+ failing_image_pairs.ensure_extra_column_values_in_summary(
+ column_id=KEY__EXTRACOLUMN__RESULT_TYPE, values=[
+ KEY__RESULT_TYPE__FAILED,
+ KEY__RESULT_TYPE__FAILUREIGNORED,
+ KEY__RESULT_TYPE__NOCOMPARISON,
])
- data_all = []
- data_failures = []
builders = sorted(actual_builder_dicts.keys())
num_builders = len(builders)
builder_num = 0
@@ -347,19 +315,30 @@ class Results(object):
if not results_of_this_type:
continue
for image_name in sorted(results_of_this_type.keys()):
- actual_image = results_of_this_type[image_name]
+ (test, config) = IMAGE_FILENAME_RE.match(image_name).groups()
+ actual_image_relative_url = Results._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 = [None, 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
- expected_image = (
+ # 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 = Results._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:
@@ -380,11 +359,11 @@ class Results(object):
# 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 != gm_json.JSONKEY_ACTUALRESULTS_NOCOMPARISON:
+ if result_type != KEY__RESULT_TYPE__NOCOMPARISON:
logging.warning('No expectations found for test: %s' % {
- 'builder': builder,
+ KEY__EXTRACOLUMN__BUILDER: builder,
+ KEY__EXTRACOLUMN__RESULT_TYPE: result_type,
'image_name': image_name,
- 'result_type': result_type,
})
# If this test was recently rebaselined, it will remain in
@@ -400,122 +379,32 @@ class Results(object):
# 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 == actual_image:
- updated_result_type = gm_json.JSONKEY_ACTUALRESULTS_SUCCEEDED
+ if expected_image_relative_url == actual_image_relative_url:
+ updated_result_type = KEY__RESULT_TYPE__SUCCEEDED
else:
updated_result_type = result_type
-
- (test, config) = IMAGE_FILENAME_RE.match(image_name).groups()
- self._generate_pixel_diffs_if_needed(
- test=test, expected_image=expected_image,
- actual_image=actual_image)
- results_for_this_test = {
- 'resultType': updated_result_type,
- 'builder': builder,
- 'test': test,
- 'config': config,
- 'actualHashType': actual_image[0],
- 'actualHashDigest': str(actual_image[1]),
- 'expectedHashType': expected_image[0],
- 'expectedHashDigest': str(expected_image[1]),
-
- # FIELDS_PASSED_THRU_VERBATIM that may be overwritten below...
- gm_json.JSONKEY_EXPECTEDRESULTS_IGNOREFAILURE: False,
+ extra_columns_dict = {
+ KEY__EXTRACOLUMN__RESULT_TYPE: updated_result_type,
+ KEY__EXTRACOLUMN__BUILDER: builder,
+ KEY__EXTRACOLUMN__TEST: test,
+ KEY__EXTRACOLUMN__CONFIG: config,
}
- if expectations_per_test:
- for field in FIELDS_PASSED_THRU_VERBATIM:
- results_for_this_test[field] = expectations_per_test.get(field)
-
- if updated_result_type == gm_json.JSONKEY_ACTUALRESULTS_NOCOMPARISON:
- pass # no diff record to calculate at all
- elif updated_result_type == gm_json.JSONKEY_ACTUALRESULTS_SUCCEEDED:
- results_for_this_test['numDifferingPixels'] = 0
- results_for_this_test['percentDifferingPixels'] = 0
- results_for_this_test['weightedDiffMeasure'] = 0
- results_for_this_test['perceptualDifference'] = 0
- results_for_this_test['maxDiffPerChannel'] = 0
- else:
- try:
- diff_record = self._image_diff_db.get_diff_record(
- expected_image_locator=expected_image[1],
- actual_image_locator=actual_image[1])
- results_for_this_test['numDifferingPixels'] = (
- diff_record.get_num_pixels_differing())
- results_for_this_test['percentDifferingPixels'] = (
- diff_record.get_percent_pixels_differing())
- results_for_this_test['weightedDiffMeasure'] = (
- diff_record.get_weighted_diff_measure())
- results_for_this_test['perceptualDifference'] = (
- diff_record.get_perceptual_difference())
- results_for_this_test['maxDiffPerChannel'] = (
- diff_record.get_max_diff_per_channel())
- except KeyError:
- logging.warning('unable to find diff_record for ("%s", "%s")' %
- (expected_image[1], actual_image[1]))
- pass
-
- Results._add_to_category_dict(categories_all, results_for_this_test)
- data_all.append(results_for_this_test)
-
- # TODO(epoger): In effect, we have a list of resultTypes that we
- # include in the different result lists (data_all and data_failures).
- # This same list should be used by the calls to
- # Results._ensure_included_in_category_dict() earlier on.
- if updated_result_type != gm_json.JSONKEY_ACTUALRESULTS_SUCCEEDED:
- Results._add_to_category_dict(categories_failures,
- results_for_this_test)
- data_failures.append(results_for_this_test)
+ 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 != KEY__RESULT_TYPE__SUCCEEDED:
+ failing_image_pairs.add_image_pair(image_pair)
self._results = {
- RESULTS_ALL:
- {'categories': categories_all, 'testData': data_all},
- RESULTS_FAILURES:
- {'categories': categories_failures, 'testData': data_failures},
+ KEY__HEADER__RESULTS_ALL: all_image_pairs.as_dict(),
+ KEY__HEADER__RESULTS_FAILURES: failing_image_pairs.as_dict(),
}
- @staticmethod
- def _add_to_category_dict(category_dict, test_results):
- """Add test_results to the category dictionary we are building.
- (See documentation of self.get_results_of_type() for the format of this
- dictionary.)
-
- Args:
- category_dict: category dict-of-dicts to add to; modify this in-place
- test_results: test data with which to update category_list, in a dict:
- {
- 'category_name': 'category_value',
- 'category_name': 'category_value',
- ...
- }
- """
- for category in CATEGORIES_TO_SUMMARIZE:
- category_value = test_results.get(category)
- if not category_dict.get(category):
- category_dict[category] = {}
- if not category_dict[category].get(category_value):
- category_dict[category][category_value] = 0
- category_dict[category][category_value] += 1
-
- @staticmethod
- def _ensure_included_in_category_dict(category_dict,
- category_name, category_values):
- """Ensure that the category name/value pairs are included in category_dict,
- even if there aren't any results with that name/value pair.
- (See documentation of self.get_results_of_type() for the format of this
- dictionary.)
-
- Args:
- category_dict: category dict-of-dicts to modify
- category_name: category name, as a string
- category_values: list of values we want to make sure are represented
- for this category
- """
- if not category_dict.get(category_name):
- category_dict[category_name] = {}
- for category_value in category_values:
- if not category_dict[category_name].get(category_value):
- category_dict[category_name][category_value] = 0
-
def main():
logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s',
@@ -538,7 +427,8 @@ def main():
results = Results(actuals_root=args.actuals,
expected_root=args.expectations,
generated_images_root=args.workdir)
- gm_json.WriteToFile(results.get_results_of_type(RESULTS_ALL), args.outfile)
+ gm_json.WriteToFile(results.get_results_of_type(KEY__HEADER__RESULTS_ALL),
+ args.outfile)
if __name__ == '__main__':
diff --git a/gm/rebaseline_server/results_test.py b/gm/rebaseline_server/results_test.py
index 482fd9f7cd..958222c516 100755
--- a/gm/rebaseline_server/results_test.py
+++ b/gm/rebaseline_server/results_test.py
@@ -35,8 +35,9 @@ class ResultsTest(base_unittest.TestCase):
actuals_root=os.path.join(self._input_dir, 'gm-actuals'),
expected_root=os.path.join(self._input_dir, 'gm-expectations'),
generated_images_root=self._temp_dir)
- gm_json.WriteToFile(results_obj.get_results_of_type(results.RESULTS_ALL),
- os.path.join(self._output_dir_actual, 'gm.json'))
+ gm_json.WriteToFile(
+ results_obj.get_results_of_type(results.KEY__HEADER__RESULTS_ALL),
+ os.path.join(self._output_dir_actual, 'gm.json'))
def main():
diff --git a/gm/rebaseline_server/server.py b/gm/rebaseline_server/server.py
index 0906c4e7ee..bbce2d0a7d 100755
--- a/gm/rebaseline_server/server.py
+++ b/gm/rebaseline_server/server.py
@@ -40,6 +40,7 @@ if TOOLS_DIRECTORY not in sys.path:
import svn
# Imports from local dir
+import imagepairset
import results
ACTUALS_SVN_REPO = 'http://skia-autogen.googlecode.com/svn/gm-actual'
@@ -59,6 +60,20 @@ MIME_TYPE_MAP = {'': 'application/octet-stream',
'json': 'application/json'
}
+# Keys that server.py uses to create the toplevel content header.
+# NOTE: Keep these in sync with static/constants.js
+KEY__EDITS__MODIFICATIONS = 'modifications'
+KEY__EDITS__OLD_RESULTS_HASH = 'oldResultsHash'
+KEY__EDITS__OLD_RESULTS_TYPE = 'oldResultsType'
+KEY__HEADER = 'header'
+KEY__HEADER__DATAHASH = 'dataHash'
+KEY__HEADER__IS_EDITABLE = 'isEditable'
+KEY__HEADER__IS_EXPORTED = 'isExported'
+KEY__HEADER__IS_STILL_LOADING = 'resultsStillLoading'
+KEY__HEADER__TIME_NEXT_UPDATE_AVAILABLE = 'timeNextUpdateAvailable'
+KEY__HEADER__TIME_UPDATED = 'timeUpdated'
+KEY__HEADER__TYPE = 'type'
+
DEFAULT_ACTUALS_DIR = '.gm-actuals'
DEFAULT_PORT = 8888
@@ -313,10 +328,11 @@ class HTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
else:
now = int(time.time())
response_dict = {
- 'header': {
- 'resultsStillLoading': True,
- 'timeUpdated': now,
- 'timeNextUpdateAvailable': now + RELOAD_INTERVAL_UNTIL_READY,
+ KEY__HEADER: {
+ KEY__HEADER__IS_STILL_LOADING: True,
+ KEY__HEADER__TIME_UPDATED: now,
+ KEY__HEADER__TIME_NEXT_UPDATE_AVAILABLE:
+ now + RELOAD_INTERVAL_UNTIL_READY,
},
}
self.send_json_dict(response_dict)
@@ -332,7 +348,7 @@ class HTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
"""
response_dict = results_obj.get_results_of_type(type)
time_updated = results_obj.get_timestamp()
- response_dict['header'] = {
+ response_dict[KEY__HEADER] = {
# Timestamps:
# 1. when this data was last updated
# 2. when the caller should check back for new data (if ever)
@@ -340,23 +356,25 @@ class HTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
# We only return these timestamps if the --reload argument was passed;
# otherwise, we have no idea when the expectations were last updated
# (we allow the user to maintain her own expectations as she sees fit).
- 'timeUpdated': time_updated if _SERVER.reload_seconds else None,
- 'timeNextUpdateAvailable': (
+ KEY__HEADER__TIME_UPDATED:
+ time_updated if _SERVER.reload_seconds else None,
+ KEY__HEADER__TIME_NEXT_UPDATE_AVAILABLE:
(time_updated+_SERVER.reload_seconds) if _SERVER.reload_seconds
- else None),
+ else None,
# The type we passed to get_results_of_type()
- 'type': type,
+ KEY__HEADER__TYPE: type,
- # Hash of testData, which the client must return with any edits--
+ # Hash of dataset, which the client must return with any edits--
# this ensures that the edits were made to a particular dataset.
- 'dataHash': str(hash(repr(response_dict['testData']))),
+ KEY__HEADER__DATAHASH: str(hash(repr(
+ response_dict[imagepairset.KEY__IMAGEPAIRS]))),
# Whether the server will accept edits back.
- 'isEditable': _SERVER.is_editable,
+ KEY__HEADER__IS_EDITABLE: _SERVER.is_editable,
# Whether the service is accessible from other hosts.
- 'isExported': _SERVER.is_exported,
+ KEY__HEADER__IS_EXPORTED: _SERVER.is_exported,
}
return response_dict
@@ -406,19 +424,15 @@ class HTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
format:
{
- 'oldResultsType': 'all', # type of results that the client loaded
- # and then made modifications to
- 'oldResultsHash': 39850913, # hash of results when the client loaded them
- # (ensures that the client and server apply
- # modifications to the same base)
- 'modifications': [
- {
- 'builder': 'Test-Android-Nexus10-MaliT604-Arm7-Debug',
- 'test': 'strokerect',
- 'config': 'gpu',
- 'expectedHashType': 'bitmap-64bitMD5',
- 'expectedHashDigest': '1707359671708613629',
- },
+ KEY__EDITS__OLD_RESULTS_TYPE: 'all', # type of results that the client
+ # loaded and then made
+ # modifications to
+ KEY__EDITS__OLD_RESULTS_HASH: 39850913, # hash of results when the client
+ # loaded them (ensures that the
+ # client and server apply
+ # modifications to the same base)
+ KEY__EDITS__MODIFICATIONS: [
+ # as needed by results.edit_expectations()
...
],
}
@@ -445,15 +459,15 @@ class HTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
# no other thread updates expectations (from the Skia repo) while we are
# updating them (using the info we received from the client).
with _SERVER.results_rlock:
- oldResultsType = data['oldResultsType']
+ oldResultsType = data[KEY__EDITS__OLD_RESULTS_TYPE]
oldResults = _SERVER.results.get_results_of_type(oldResultsType)
- oldResultsHash = str(hash(repr(oldResults['testData'])))
- if oldResultsHash != data['oldResultsHash']:
+ oldResultsHash = str(hash(repr(oldResults[imagepairset.KEY__IMAGEPAIRS])))
+ if oldResultsHash != data[KEY__EDITS__OLD_RESULTS_HASH]:
raise Exception('results of type "%s" changed while the client was '
'making modifications. The client should reload the '
'results and submit the modifications again.' %
oldResultsType)
- _SERVER.results.edit_expectations(data['modifications'])
+ _SERVER.results.edit_expectations(data[KEY__EDITS__MODIFICATIONS])
# Read the updated results back from disk.
# We can do this in a separate thread; we should return our success message
diff --git a/gm/rebaseline_server/static/constants.js b/gm/rebaseline_server/static/constants.js
new file mode 100644
index 0000000000..00c1852458
--- /dev/null
+++ b/gm/rebaseline_server/static/constants.js
@@ -0,0 +1,69 @@
+/*
+ * Constants used by our imagediff-viewing Javascript code.
+ */
+var module = angular.module(
+ 'ConstantsModule',
+ []
+);
+
+module.constant('constants', (function() {
+ return {
+ // NOTE: Keep these in sync with ../column.py
+ KEY__HEADER_TEXT: 'headerText',
+ KEY__HEADER_URL: 'headerUrl',
+ KEY__IS_FILTERABLE: 'isFilterable',
+ KEY__IS_SORTABLE: 'isSortable',
+ KEY__VALUES_AND_COUNTS: 'valuesAndCounts',
+
+ // NOTE: Keep these in sync with ../imagediffdb.py
+ KEY__DIFFERENCE_DATA__MAX_DIFF_PER_CHANNEL: 'maxDiffPerChannel',
+ KEY__DIFFERENCE_DATA__NUM_DIFF_PIXELS: 'numDifferingPixels',
+ KEY__DIFFERENCE_DATA__PERCENT_DIFF_PIXELS: 'percentDifferingPixels',
+ KEY__DIFFERENCE_DATA__PERCEPTUAL_DIFF: 'perceptualDifference',
+ KEY__DIFFERENCE_DATA__WEIGHTED_DIFF: 'weightedDiffMeasure',
+
+ // NOTE: Keep these in sync with ../imagepair.py
+ KEY__DIFFERENCE_DATA: 'differenceData',
+ KEY__EXPECTATIONS_DATA: 'expectations',
+ KEY__EXTRA_COLUMN_VALUES: 'extraColumns',
+ KEY__IMAGE_A_URL: 'imageAUrl',
+ KEY__IMAGE_B_URL: 'imageBUrl',
+ KEY__IS_DIFFERENT: 'isDifferent',
+
+ // NOTE: Keep these in sync with ../imagepairset.py
+ KEY__EXTRACOLUMNHEADERS: 'extraColumnHeaders',
+ KEY__IMAGEPAIRS: 'imagePairs',
+ KEY__IMAGESETS: 'imageSets',
+ KEY__IMAGESETS__BASE_URL: 'baseUrl',
+ KEY__IMAGESETS__DESCRIPTION: 'description',
+
+ // NOTE: Keep these in sync with ../results.py
+ KEY__EXPECTATIONS__BUGS: 'bugs',
+ KEY__EXPECTATIONS__IGNOREFAILURE: 'ignore-failure',
+ KEY__EXPECTATIONS__REVIEWED: 'reviewed-by-human',
+ KEY__EXTRACOLUMN__BUILDER: 'builder',
+ KEY__EXTRACOLUMN__CONFIG: 'config',
+ KEY__EXTRACOLUMN__RESULT_TYPE: 'resultType',
+ KEY__EXTRACOLUMN__TEST: 'test',
+ KEY__HEADER__RESULTS_ALL: 'all',
+ KEY__HEADER__RESULTS_FAILURES: 'failures',
+ KEY__NEW_IMAGE_URL: 'newImageUrl',
+ KEY__RESULT_TYPE__FAILED: 'failed',
+ KEY__RESULT_TYPE__FAILUREIGNORED: 'failure-ignored',
+ KEY__RESULT_TYPE__NOCOMPARISON: 'no-comparison',
+ KEY__RESULT_TYPE__SUCCEEDED: 'succeeded',
+
+ // NOTE: Keep these in sync with ../server.py
+ KEY__EDITS__MODIFICATIONS: 'modifications',
+ KEY__EDITS__OLD_RESULTS_HASH: 'oldResultsHash',
+ KEY__EDITS__OLD_RESULTS_TYPE: 'oldResultsType',
+ KEY__HEADER: 'header',
+ KEY__HEADER__DATAHASH: 'dataHash',
+ KEY__HEADER__IS_EDITABLE: 'isEditable',
+ KEY__HEADER__IS_EXPORTED: 'isExported',
+ KEY__HEADER__IS_STILL_LOADING: 'resultsStillLoading',
+ KEY__HEADER__TIME_NEXT_UPDATE_AVAILABLE: 'timeNextUpdateAvailable',
+ KEY__HEADER__TIME_UPDATED: 'timeUpdated',
+ KEY__HEADER__TYPE: 'type',
+ }
+})())
diff --git a/gm/rebaseline_server/static/loader.js b/gm/rebaseline_server/static/loader.js
index 9ae84d613c..ab79df8189 100644
--- a/gm/rebaseline_server/static/loader.js
+++ b/gm/rebaseline_server/static/loader.js
@@ -1,38 +1,42 @@
/*
* Loader:
* Reads GM result reports written out by results.py, and imports
- * them into $scope.categories and $scope.testData .
+ * them into $scope.extraColumnHeaders and $scope.imagePairs .
*/
var Loader = angular.module(
'Loader',
- ['diff_viewer']
+ ['ConstantsModule', 'diff_viewer']
);
-
// TODO(epoger): Combine ALL of our filtering operations (including
// truncation) into this one filter, so that runs most efficiently?
// (We would have to make sure truncation still took place after
// sorting, though.)
Loader.filter(
- 'removeHiddenItems',
- function() {
- return function(unfilteredItems, hiddenResultTypes, hiddenConfigs,
+ 'removeHiddenImagePairs',
+ function(constants) {
+ return function(unfilteredImagePairs, hiddenResultTypes, hiddenConfigs,
builderSubstring, testSubstring, viewingTab) {
- var filteredItems = [];
- for (var i = 0; i < unfilteredItems.length; i++) {
- var item = unfilteredItems[i];
+ var filteredImagePairs = [];
+ for (var i = 0; i < unfilteredImagePairs.length; i++) {
+ var imagePair = unfilteredImagePairs[i];
+ var extraColumnValues = imagePair[constants.KEY__EXTRA_COLUMN_VALUES];
// For performance, we examine the "set" objects directly rather
// than calling $scope.isValueInSet().
// Besides, I don't think we have access to $scope in here...
- if (!(true == hiddenResultTypes[item.resultType]) &&
- !(true == hiddenConfigs[item.config]) &&
- !(-1 == item.builder.indexOf(builderSubstring)) &&
- !(-1 == item.test.indexOf(testSubstring)) &&
- (viewingTab == item.tab)) {
- filteredItems.push(item);
+ if (!(true == hiddenResultTypes[extraColumnValues[
+ constants.KEY__EXTRACOLUMN__RESULT_TYPE]]) &&
+ !(true == hiddenConfigs[extraColumnValues[
+ constants.KEY__EXTRACOLUMN__CONFIG]]) &&
+ !(-1 == extraColumnValues[constants.KEY__EXTRACOLUMN__BUILDER]
+ .indexOf(builderSubstring)) &&
+ !(-1 == extraColumnValues[constants.KEY__EXTRACOLUMN__TEST]
+ .indexOf(testSubstring)) &&
+ (viewingTab == imagePair.tab)) {
+ filteredImagePairs.push(imagePair);
}
}
- return filteredItems;
+ return filteredImagePairs;
};
}
);
@@ -40,7 +44,8 @@ Loader.filter(
Loader.controller(
'Loader.Controller',
- function($scope, $http, $filter, $location, $timeout) {
+ function($scope, $http, $filter, $location, $timeout, constants) {
+ $scope.constants = constants;
$scope.windowTitle = "Loading GM Results...";
$scope.resultsToLoad = $location.search().resultsToLoad;
$scope.loadingMessage = "Loading results of type '" + $scope.resultsToLoad +
@@ -53,26 +58,33 @@ Loader.controller(
*/
$http.get("/results/" + $scope.resultsToLoad).success(
function(data, status, header, config) {
- if (data.header.resultsStillLoading) {
+ var dataHeader = data[constants.KEY__HEADER];
+ if (dataHeader[constants.KEY__HEADER__IS_STILL_LOADING]) {
$scope.loadingMessage =
"Server is still loading results; will retry at " +
- $scope.localTimeString(data.header.timeNextUpdateAvailable);
+ $scope.localTimeString(dataHeader[
+ constants.KEY__HEADER__TIME_NEXT_UPDATE_AVAILABLE]);
$timeout(
function(){location.reload();},
- (data.header.timeNextUpdateAvailable * 1000) - new Date().getTime());
+ (dataHeader[constants.KEY__HEADER__TIME_NEXT_UPDATE_AVAILABLE]
+ * 1000) - new Date().getTime());
} else {
$scope.loadingMessage = "Processing data, please wait...";
- $scope.header = data.header;
- $scope.categories = data.categories;
- $scope.testData = data.testData;
- $scope.sortColumn = 'weightedDiffMeasure';
+ $scope.header = dataHeader;
+ $scope.extraColumnHeaders = data[constants.KEY__EXTRACOLUMNHEADERS];
+ $scope.imagePairs = data[constants.KEY__IMAGEPAIRS];
+ $scope.imageSets = data[constants.KEY__IMAGESETS];
+ $scope.sortColumnSubdict = constants.KEY__DIFFERENCE_DATA;
+ $scope.sortColumnKey = constants.KEY__DIFFERENCE_DATA__WEIGHTED_DIFF;
$scope.showTodos = false;
$scope.showSubmitAdvancedSettings = false;
$scope.submitAdvancedSettings = {};
- $scope.submitAdvancedSettings['reviewed-by-human'] = true;
- $scope.submitAdvancedSettings['ignore-failure'] = false;
+ $scope.submitAdvancedSettings[
+ constants.KEY__EXPECTATIONS__REVIEWED] = true;
+ $scope.submitAdvancedSettings[
+ constants.KEY__EXPECTATIONS__IGNOREFAILURE] = false;
$scope.submitAdvancedSettings['bug'] = '';
// Create the list of tabs (lists into which the user can file each
@@ -80,7 +92,7 @@ Loader.controller(
$scope.tabs = [
'Unfiled', 'Hidden'
];
- if (data.header.isEditable) {
+ if (dataHeader[constants.KEY__HEADER__IS_EDITABLE]) {
$scope.tabs = $scope.tabs.concat(
['Pending Approval']);
}
@@ -92,26 +104,32 @@ Loader.controller(
for (var i = 0; i < $scope.tabs.length; i++) {
$scope.numResultsPerTab[$scope.tabs[i]] = 0;
}
- $scope.numResultsPerTab[$scope.defaultTab] = $scope.testData.length;
+ $scope.numResultsPerTab[$scope.defaultTab] = $scope.imagePairs.length;
// Add index and tab fields to all records.
- for (var i = 0; i < $scope.testData.length; i++) {
- $scope.testData[i].index = i;
- $scope.testData[i].tab = $scope.defaultTab;
+ for (var i = 0; i < $scope.imagePairs.length; i++) {
+ $scope.imagePairs[i].index = i;
+ $scope.imagePairs[i].tab = $scope.defaultTab;
}
// Arrays within which the user can toggle individual elements.
- $scope.selectedItems = [];
+ $scope.selectedImagePairs = [];
// Sets within which the user can toggle individual elements.
- $scope.hiddenResultTypes = {
- 'failure-ignored': true,
- 'no-comparison': true,
- 'succeeded': true,
- };
- $scope.allResultTypes = Object.keys(data.categories['resultType']);
+ $scope.hiddenResultTypes = {};
+ $scope.hiddenResultTypes[
+ constants.KEY__RESULT_TYPE__FAILUREIGNORED] = true;
+ $scope.hiddenResultTypes[
+ constants.KEY__RESULT_TYPE__NOCOMPARISON] = true;
+ $scope.hiddenResultTypes[
+ constants.KEY__RESULT_TYPE__SUCCEEDED] = true;
+ $scope.allResultTypes = Object.keys(
+ $scope.extraColumnHeaders[constants.KEY__EXTRACOLUMN__RESULT_TYPE]
+ [constants.KEY__VALUES_AND_COUNTS]);
$scope.hiddenConfigs = {};
- $scope.allConfigs = Object.keys(data.categories['config']);
+ $scope.allConfigs = Object.keys(
+ $scope.extraColumnHeaders[constants.KEY__EXTRACOLUMN__CONFIG]
+ [constants.KEY__VALUES_AND_COUNTS]);
// Associative array of partial string matches per category.
$scope.categoryValueMatch = {};
@@ -142,12 +160,12 @@ Loader.controller(
/**
* Select all currently showing tests.
*/
- $scope.selectAllItems = function() {
- var numItemsShowing = $scope.limitedTestData.length;
- for (var i = 0; i < numItemsShowing; i++) {
- var index = $scope.limitedTestData[i].index;
- if (!$scope.isValueInArray(index, $scope.selectedItems)) {
- $scope.toggleValueInArray(index, $scope.selectedItems);
+ $scope.selectAllImagePairs = function() {
+ var numImagePairsShowing = $scope.limitedImagePairs.length;
+ for (var i = 0; i < numImagePairsShowing; i++) {
+ var index = $scope.limitedImagePairs[i].index;
+ if (!$scope.isValueInArray(index, $scope.selectedImagePairs)) {
+ $scope.toggleValueInArray(index, $scope.selectedImagePairs);
}
}
}
@@ -155,12 +173,12 @@ Loader.controller(
/**
* Deselect all currently showing tests.
*/
- $scope.clearAllItems = function() {
- var numItemsShowing = $scope.limitedTestData.length;
- for (var i = 0; i < numItemsShowing; i++) {
- var index = $scope.limitedTestData[i].index;
- if ($scope.isValueInArray(index, $scope.selectedItems)) {
- $scope.toggleValueInArray(index, $scope.selectedItems);
+ $scope.clearAllImagePairs = function() {
+ var numImagePairsShowing = $scope.limitedImagePairs.length;
+ for (var i = 0; i < numImagePairsShowing; i++) {
+ var index = $scope.limitedImagePairs[i].index;
+ if ($scope.isValueInArray(index, $scope.selectedImagePairs)) {
+ $scope.toggleValueInArray(index, $scope.selectedImagePairs);
}
}
}
@@ -168,11 +186,11 @@ Loader.controller(
/**
* Toggle selection of all currently showing tests.
*/
- $scope.toggleAllItems = function() {
- var numItemsShowing = $scope.limitedTestData.length;
- for (var i = 0; i < numItemsShowing; i++) {
- var index = $scope.limitedTestData[i].index;
- $scope.toggleValueInArray(index, $scope.selectedItems);
+ $scope.toggleAllImagePairs = function() {
+ var numImagePairsShowing = $scope.limitedImagePairs.length;
+ for (var i = 0; i < numImagePairsShowing; i++) {
+ var index = $scope.limitedImagePairs[i].index;
+ $scope.toggleValueInArray(index, $scope.selectedImagePairs);
}
}
@@ -192,33 +210,33 @@ Loader.controller(
}
/**
- * Move the items in $scope.selectedItems to a different tab,
- * and then clear $scope.selectedItems.
+ * Move the imagePairs in $scope.selectedImagePairs to a different tab,
+ * and then clear $scope.selectedImagePairs.
*
* @param newTab (string): name of the tab to move the tests to
*/
- $scope.moveSelectedItemsToTab = function(newTab) {
- $scope.moveItemsToTab($scope.selectedItems, newTab);
- $scope.selectedItems = [];
+ $scope.moveSelectedImagePairsToTab = function(newTab) {
+ $scope.moveImagePairsToTab($scope.selectedImagePairs, newTab);
+ $scope.selectedImagePairs = [];
$scope.updateResults();
}
/**
- * Move a subset of $scope.testData to a different tab.
+ * Move a subset of $scope.imagePairs to a different tab.
*
- * @param itemIndices (array of ints): indices into $scope.testData
+ * @param imagePairIndices (array of ints): indices into $scope.imagePairs
* indicating which test results to move
* @param newTab (string): name of the tab to move the tests to
*/
- $scope.moveItemsToTab = function(itemIndices, newTab) {
- var itemIndex;
- var numItems = itemIndices.length;
- for (var i = 0; i < numItems; i++) {
- itemIndex = itemIndices[i];
- $scope.numResultsPerTab[$scope.testData[itemIndex].tab]--;
- $scope.testData[itemIndex].tab = newTab;
+ $scope.moveImagePairsToTab = function(imagePairIndices, newTab) {
+ var imagePairIndex;
+ var numImagePairs = imagePairIndices.length;
+ for (var i = 0; i < numImagePairs; i++) {
+ imagePairIndex = imagePairIndices[i];
+ $scope.numResultsPerTab[$scope.imagePairs[imagePairIndex].tab]--;
+ $scope.imagePairs[imagePairIndex].tab = newTab;
}
- $scope.numResultsPerTab[newTab] += numItems;
+ $scope.numResultsPerTab[newTab] += numImagePairs;
}
@@ -277,14 +295,16 @@ Loader.controller(
'resultsToLoad': $scope.queryParameters.copiers.simple,
'displayLimitPending': $scope.queryParameters.copiers.simple,
'imageSizePending': $scope.queryParameters.copiers.simple,
- 'sortColumn': $scope.queryParameters.copiers.simple,
-
- 'builder': $scope.queryParameters.copiers.categoryValueMatch,
- 'test': $scope.queryParameters.copiers.categoryValueMatch,
+ 'sortColumnSubdict': $scope.queryParameters.copiers.simple,
+ 'sortColumnKey': $scope.queryParameters.copiers.simple,
'hiddenResultTypes': $scope.queryParameters.copiers.set,
'hiddenConfigs': $scope.queryParameters.copiers.set,
};
+ $scope.queryParameters.map[constants.KEY__EXTRACOLUMN__BUILDER] =
+ $scope.queryParameters.copiers.categoryValueMatch;
+ $scope.queryParameters.map[constants.KEY__EXTRACOLUMN__TEST] =
+ $scope.queryParameters.copiers.categoryValueMatch;
// Loads all parameters into $scope from the URL query string;
// any which are not found within the URL will keep their current value.
@@ -339,7 +359,7 @@ Loader.controller(
$scope.displayLimit = $scope.displayLimitPending;
// TODO(epoger): Every time we apply a filter, AngularJS creates
// another copy of the array. Is there a way we can filter out
- // the items as they are displayed, rather than storing multiple
+ // the imagePairs as they are displayed, rather than storing multiple
// array copies? (For better performance.)
if ($scope.viewingTab == $scope.defaultTab) {
@@ -347,32 +367,34 @@ Loader.controller(
// TODO(epoger): Until we allow the user to reverse sort order,
// there are certain columns we want to sort in a different order.
var doReverse = (
- ($scope.sortColumn == 'percentDifferingPixels') ||
- ($scope.sortColumn == 'weightedDiffMeasure'));
+ ($scope.sortColumnKey ==
+ constants.KEY__DIFFERENCE_DATA__PERCENT_DIFF_PIXELS) ||
+ ($scope.sortColumnKey ==
+ constants.KEY__DIFFERENCE_DATA__WEIGHTED_DIFF));
- $scope.filteredTestData =
+ $scope.filteredImagePairs =
$filter("orderBy")(
- $filter("removeHiddenItems")(
- $scope.testData,
+ $filter("removeHiddenImagePairs")(
+ $scope.imagePairs,
$scope.hiddenResultTypes,
$scope.hiddenConfigs,
$scope.categoryValueMatch.builder,
$scope.categoryValueMatch.test,
$scope.viewingTab
),
- $scope.sortColumn, doReverse);
- $scope.limitedTestData = $filter("limitTo")(
- $scope.filteredTestData, $scope.displayLimit);
+ $scope.getSortColumnValue, doReverse);
+ $scope.limitedImagePairs = $filter("limitTo")(
+ $scope.filteredImagePairs, $scope.displayLimit);
} else {
- $scope.filteredTestData =
+ $scope.filteredImagePairs =
$filter("orderBy")(
$filter("filter")(
- $scope.testData,
+ $scope.imagePairs,
{tab: $scope.viewingTab},
true
),
- $scope.sortColumn);
- $scope.limitedTestData = $scope.filteredTestData;
+ $scope.getSortColumnValue);
+ $scope.limitedImagePairs = $scope.filteredImagePairs;
}
$scope.imageSize = $scope.imageSizePending;
$scope.setUpdatesPending(false);
@@ -382,14 +404,33 @@ Loader.controller(
/**
* Re-sort the displayed results.
*
- * @param sortColumn (string): name of the column to sort on
+ * @param subdict (string): which subdictionary
+ * (constants.KEY__DIFFERENCE_DATA, constants.KEY__EXPECTATIONS_DATA,
+ * constants.KEY__EXTRA_COLUMN_VALUES) the sort column key is within
+ * @param key (string): sort by value associated with this key in subdict
*/
- $scope.sortResultsBy = function(sortColumn) {
- $scope.sortColumn = sortColumn;
+ $scope.sortResultsBy = function(subdict, key) {
+ $scope.sortColumnSubdict = subdict;
+ $scope.sortColumnKey = key;
$scope.updateResults();
}
/**
+ * For a particular ImagePair, return the value of the column we are
+ * sorting on (according to $scope.sortColumnSubdict and
+ * $scope.sortColumnKey).
+ *
+ * @param imagePair: imagePair to get a column value out of.
+ */
+ $scope.getSortColumnValue = function(imagePair) {
+ if ($scope.sortColumnSubdict in imagePair) {
+ return imagePair[$scope.sortColumnSubdict][$scope.sortColumnKey];
+ } else {
+ return undefined;
+ }
+ }
+
+ /**
* Set $scope.categoryValueMatch[name] = value, and update results.
*
* @param name
@@ -456,10 +497,13 @@ Loader.controller(
* Tell the server that the actual results of these particular tests
* are acceptable.
*
- * @param testDataSubset an array of test results, most likely a subset of
- * $scope.testData (perhaps with some modifications)
+ * TODO(epoger): This assumes that the original expectations are in
+ * imageSetA, and the actuals are in imageSetB.
+ *
+ * @param imagePairsSubset an array of test results, most likely a subset of
+ * $scope.imagePairs (perhaps with some modifications)
*/
- $scope.submitApprovals = function(testDataSubset) {
+ $scope.submitApprovals = function(imagePairsSubset) {
$scope.submitPending = true;
// Convert bug text field to null or 1-item array.
@@ -476,30 +520,40 @@ Loader.controller(
// result type, RenderModeMismatch')
var encounteredComparisonConfig = false;
- var newResults = [];
- for (var i = 0; i < testDataSubset.length; i++) {
- var actualResult = testDataSubset[i];
- var expectedResult = {
- builder: actualResult['builder'],
- test: actualResult['test'],
- config: actualResult['config'],
- expectedHashType: actualResult['actualHashType'],
- expectedHashDigest: actualResult['actualHashDigest'],
- };
- if (0 == expectedResult.config.indexOf('comparison-')) {
+ var updatedExpectations = [];
+ for (var i = 0; i < imagePairsSubset.length; i++) {
+ var imagePair = imagePairsSubset[i];
+ var updatedExpectation = {};
+ updatedExpectation[constants.KEY__EXPECTATIONS_DATA] =
+ imagePair[constants.KEY__EXPECTATIONS_DATA];
+ updatedExpectation[constants.KEY__EXTRA_COLUMN_VALUES] =
+ imagePair[constants.KEY__EXTRA_COLUMN_VALUES];
+ updatedExpectation[constants.KEY__NEW_IMAGE_URL] =
+ imagePair[constants.KEY__IMAGE_B_URL];
+ if (0 == updatedExpectation[constants.KEY__EXTRA_COLUMN_VALUES]
+ [constants.KEY__EXTRACOLUMN__CONFIG]
+ .indexOf('comparison-')) {
encounteredComparisonConfig = true;
}
// Advanced settings...
- expectedResult['reviewed-by-human'] =
- $scope.submitAdvancedSettings['reviewed-by-human'];
- if (true == $scope.submitAdvancedSettings['ignore-failure']) {
+ if (null == updatedExpectation[constants.KEY__EXPECTATIONS_DATA]) {
+ updatedExpectation[constants.KEY__EXPECTATIONS_DATA] = {};
+ }
+ updatedExpectation[constants.KEY__EXPECTATIONS_DATA]
+ [constants.KEY__EXPECTATIONS__REVIEWED] =
+ $scope.submitAdvancedSettings[
+ constants.KEY__EXPECTATIONS__REVIEWED];
+ if (true == $scope.submitAdvancedSettings[
+ constants.KEY__EXPECTATIONS__IGNOREFAILURE]) {
// if it's false, don't send it at all (just keep the default)
- expectedResult['ignore-failure'] = true;
+ updatedExpectation[constants.KEY__EXPECTATIONS_DATA]
+ [constants.KEY__EXPECTATIONS__IGNOREFAILURE] = true;
}
- expectedResult['bugs'] = bugs;
+ updatedExpectation[constants.KEY__EXPECTATIONS_DATA]
+ [constants.KEY__EXPECTATIONS__BUGS] = bugs;
- newResults.push(expectedResult);
+ updatedExpectations.push(updatedExpectation);
}
if (encounteredComparisonConfig) {
alert("Approval failed -- you cannot approve results with config " +
@@ -507,21 +561,24 @@ Loader.controller(
$scope.submitPending = false;
return;
}
+ var modificationData = {};
+ modificationData[constants.KEY__EDITS__MODIFICATIONS] =
+ updatedExpectations;
+ modificationData[constants.KEY__EDITS__OLD_RESULTS_HASH] =
+ $scope.header[constants.KEY__HEADER__DATAHASH];
+ modificationData[constants.KEY__EDITS__OLD_RESULTS_TYPE] =
+ $scope.header[constants.KEY__HEADER__TYPE];
$http({
method: "POST",
url: "/edits",
- data: {
- oldResultsType: $scope.header.type,
- oldResultsHash: $scope.header.dataHash,
- modifications: newResults
- }
+ data: modificationData
}).success(function(data, status, headers, config) {
- var itemIndicesToMove = [];
- for (var i = 0; i < testDataSubset.length; i++) {
- itemIndicesToMove.push(testDataSubset[i].index);
+ var imagePairIndicesToMove = [];
+ for (var i = 0; i < imagePairsSubset.length; i++) {
+ imagePairIndicesToMove.push(imagePairsSubset[i].index);
}
- $scope.moveItemsToTab(itemIndicesToMove,
- "HackToMakeSureThisItemDisappears");
+ $scope.moveImagePairsToTab(imagePairIndicesToMove,
+ "HackToMakeSureThisImagePairDisappears");
$scope.updateResults();
alert("New baselines submitted successfully!\n\n" +
"You still need to commit the updated expectations files on " +
@@ -682,5 +739,23 @@ Loader.controller(
return $scope.hexColorString(v, v, v);
}
+ /**
+ * Returns the last path component of image diff URL for a given ImagePair.
+ *
+ * Depending on which diff this is (whitediffs, pixeldiffs, etc.) this
+ * will be relative to different base URLs.
+ *
+ * We must keep this function in sync with _get_difference_locator() in
+ * ../imagediffdb.py
+ *
+ * @param imagePair: ImagePair to generate image diff URL for
+ */
+ $scope.getImageDiffRelativeUrl = function(imagePair) {
+ var before =
+ imagePair[constants.KEY__IMAGE_A_URL] + "-vs-" +
+ imagePair[constants.KEY__IMAGE_B_URL];
+ return before.replace(/[^\w\-]/g, "_") + ".png";
+ }
+
}
);
diff --git a/gm/rebaseline_server/static/view.html b/gm/rebaseline_server/static/view.html
index 9ce3fae140..36fca750b1 100644
--- a/gm/rebaseline_server/static/view.html
+++ b/gm/rebaseline_server/static/view.html
@@ -5,8 +5,9 @@
<head>
<title ng-bind="windowTitle"></title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.js"></script>
- <script src="loader.js"></script>
+ <script src="constants.js"></script>
<script src="diff_viewer.js"></script>
+ <script src="loader.js"></script>
<link rel="stylesheet" href="view.css">
</head>
@@ -22,16 +23,16 @@
{{loadingMessage}}
</em>
- <div ng-hide="!categories"><!-- everything: hide until data is loaded -->
+ <div ng-hide="!extraColumnHeaders"><!-- everything: hide until data is loaded -->
<div class="warning-div"
- ng-hide="!(header.isEditable && header.isExported)">
+ ng-hide="!(header[constants.KEY__HEADER__IS_EDITABLE] && header[constants.KEY__HEADER__IS_EXPORTED])">
WARNING! These results are editable and exported, so any user
who can connect to this server over the network can modify them.
</div>
- <div ng-hide="!(header.timeUpdated)">
- Results current as of {{localTimeString(header.timeUpdated)}}
+ <div ng-hide="!(header[constants.KEY__HEADER___TIME_UPDATED])">
+ Results current as of {{localTimeString(header[constants.KEY__HEADER__TIME_UPDATED])}}
</div>
<div><!-- tabs -->
@@ -60,10 +61,9 @@
</th>
</tr>
<tr valign="top">
- <!-- TODO(epoger): make this an ng-repeat over resultType, config, etc? -->
<td>
resultType<br>
- <label ng-repeat="(resultType, count) in categories['resultType'] track by $index">
+ <label ng-repeat="(resultType, count) in extraColumnHeaders[constants.KEY__EXTRACOLUMN__RESULT_TYPE][constants.KEY__VALUES_AND_COUNTS] track by $index">
<input type="checkbox"
name="resultTypes"
value="{{resultType}}"
@@ -81,7 +81,7 @@
toggle
</button>
</td>
- <td ng-repeat="category in ['builder', 'test']">
+ <td ng-repeat="category in [constants.KEY__EXTRACOLUMN__BUILDER, constants.KEY__EXTRACOLUMN__TEST]">
{{category}}
<br>
<input type="text"
@@ -95,7 +95,7 @@
</td>
<td>
config<br>
- <label ng-repeat="(config, count) in categories['config'] track by $index">
+ <label ng-repeat="(config, count) in extraColumnHeaders[constants.KEY__EXTRACOLUMN__CONFIG][constants.KEY__VALUES_AND_COUNTS] track by $index">
<input type="checkbox"
name="configs"
value="{{config}}"
@@ -145,9 +145,9 @@
<div ng-hide="'Pending Approval' != viewingTab">
<div style="display:inline-block">
<button style="font-size:20px"
- ng-click="submitApprovals(filteredTestData)"
- ng-disabled="submitPending || (filteredTestData.length == 0)">
- Update these {{filteredTestData.length}} expectations on the server
+ ng-click="submitApprovals(filteredImagePairs)"
+ ng-disabled="submitPending || (filteredImagePairs.length == 0)">
+ Update these {{filteredImagePairs.length}} expectations on the server
</button>
</div>
<div style="display:inline-block">
@@ -161,7 +161,7 @@
<input type="checkbox" ng-model="showSubmitAdvancedSettings">
show
<ul ng-hide="!showSubmitAdvancedSettings">
- <li ng-repeat="setting in ['reviewed-by-human', 'ignore-failure']">
+ <li ng-repeat="setting in [constants.KEY__EXPECTATIONS__REVIEWED, constants.KEY__EXPECTATIONS__IGNOREFAILURE]">
{{setting}}
<input type="checkbox" ng-model="submitAdvancedSettings[setting]">
</li>
@@ -179,11 +179,11 @@
<table border="0" width="100%"> <!-- results header -->
<tr>
<td>
- Found {{filteredTestData.length}} matches;
- <span ng-hide="filteredTestData.length <= limitedTestData.length">
- displaying the first {{limitedTestData.length}}
+ Found {{filteredImagePairs.length}} matches;
+ <span ng-hide="filteredImagePairs.length <= limitedImagePairs.length">
+ displaying the first {{limitedImagePairs.length}}
</span>
- <span ng-hide="filteredTestData.length > limitedTestData.length">
+ <span ng-hide="filteredImagePairs.length > limitedImagePairs.length">
displaying them all
</span>
<br>
@@ -192,21 +192,21 @@
<td align="right">
<div>
all tests shown:
- <button ng-click="selectAllItems()">
+ <button ng-click="selectAllImagePairs()">
select
</button>
- <button ng-click="clearAllItems()">
+ <button ng-click="clearAllImagePairs()">
clear
</button>
- <button ng-click="toggleAllItems()">
+ <button ng-click="toggleAllImagePairs()">
toggle
</button>
</div>
<div ng-repeat="otherTab in tabs">
- <button ng-click="moveSelectedItemsToTab(otherTab)"
- ng-disabled="selectedItems.length == 0"
+ <button ng-click="moveSelectedImagePairsToTab(otherTab)"
+ ng-disabled="selectedImagePairs.length == 0"
ng-hide="otherTab == viewingTab">
- move {{selectedItems.length}} selected tests to {{otherTab}} tab
+ move {{selectedImagePairs.length}} selected tests to {{otherTab}} tab
</button>
</div>
</td>
@@ -216,12 +216,12 @@
<table border="1" ng-app="diff_viewer"> <!-- results -->
<tr>
<!-- Most column headers are displayed in a common fashion... -->
- <th ng-repeat="categoryName in ['resultType', 'builder', 'test', 'config']">
+ <th ng-repeat="categoryName in [constants.KEY__EXTRACOLUMN__RESULT_TYPE, constants.KEY__EXTRACOLUMN__BUILDER, constants.KEY__EXTRACOLUMN__TEST, constants.KEY__EXTRACOLUMN__CONFIG]">
<input type="radio"
name="sortColumnRadio"
value="{{categoryName}}"
- ng-checked="(sortColumn == categoryName)"
- ng-click="sortResultsBy(categoryName)">
+ ng-checked="(sortColumnKey == categoryName)"
+ ng-click="sortResultsBy(constants.KEY__EXTRA_COLUMN_VALUES, categoryName)">
{{categoryName}}
</th>
<!-- ... but there are a few columns where we display things differently. -->
@@ -229,40 +229,30 @@
<input type="radio"
name="sortColumnRadio"
value="bugs"
- ng-checked="(sortColumn == 'bugs')"
- ng-click="sortResultsBy('bugs')">
+ ng-checked="(sortColumnKey == constants.KEY__EXPECTATIONS__BUGS)"
+ ng-click="sortResultsBy(constants.KEY__EXPECTATIONS_DATA, constants.KEY__EXPECTATIONS__BUGS)">
bugs
</th>
<th width="{{imageSize}}">
- <input type="radio"
- name="sortColumnRadio"
- value="expectedHashDigest"
- ng-checked="(sortColumn == 'expectedHashDigest')"
- ng-click="sortResultsBy('expectedHashDigest')">
- expected image
+ {{imageSets[0][constants.KEY__IMAGESETS__DESCRIPTION]}}
</th>
<th width="{{imageSize}}">
- <input type="radio"
- name="sortColumnRadio"
- value="actualHashDigest"
- ng-checked="(sortColumn == 'actualHashDigest')"
- ng-click="sortResultsBy('actualHashDigest')">
- actual image
+ {{imageSets[1][constants.KEY__IMAGESETS__DESCRIPTION]}}
</th>
<th width="{{imageSize}}">
<input type="radio"
name="sortColumnRadio"
value="percentDifferingPixels"
- ng-checked="(sortColumn == 'percentDifferingPixels')"
- ng-click="sortResultsBy('percentDifferingPixels')">
+ ng-checked="(sortColumnKey == constants.KEY__DIFFERENCE_DATA__PERCENT_DIFF_PIXELS)"
+ ng-click="sortResultsBy(constants.KEY__DIFFERENCE_DATA, constants.KEY__DIFFERENCE_DATA__PERCENT_DIFF_PIXELS)">
differing pixels in white
</th>
<th width="{{imageSize}}">
<input type="radio"
name="sortColumnRadio"
value="weightedDiffMeasure"
- ng-checked="(sortColumn == 'weightedDiffMeasure')"
- ng-click="sortResultsBy('weightedDiffMeasure')">
+ ng-checked="(sortColumnKey == constants.KEY__DIFFERENCE_DATA__WEIGHTED_DIFF)"
+ ng-click="sortResultsBy(constants.KEY__DIFFERENCE_DATA, constants.KEY__DIFFERENCE_DATA__WEIGHTED_DIFF)">
perceptual difference
<br>
<input type="range" ng-model="pixelDiffBgColorBrightness"
@@ -272,18 +262,18 @@
min="0" max="255"/>
</th>
<th>
- <!-- item-selection checkbox column -->
+ <!-- imagepair-selection checkbox column -->
</th>
</tr>
- <tr ng-repeat="result in limitedTestData" ng-controller="ImageController">
+ <tr ng-repeat="imagePair in limitedImagePairs" ng-controller="ImageController">
<td>
- {{result.resultType}}
+ {{imagePair[constants.KEY__EXTRA_COLUMN_VALUES][constants.KEY__EXTRACOLUMN__RESULT_TYPE]}}
<br>
<button class="show-only-button"
ng-hide="viewingTab != defaultTab"
- ng-click="showOnlyResultType(result.resultType)"
- title="show only results of type '{{result.resultType}}'">
+ ng-click="showOnlyResultType(imagePair[constants.KEY__EXTRA_COLUMN_VALUES][constants.KEY__EXTRACOLUMN__RESULT_TYPE])"
+ title="show only results of type {{imagePair[constants.KEY__EXTRA_COLUMN_VALUES][constants.KEY__EXTRACOLUMN__RESULT_TYPE]}}">
show only
</button>
<br>
@@ -295,14 +285,14 @@
show all
</button>
</td>
- <td ng-repeat="categoryName in ['builder', 'test']">
- {{result[categoryName]}}
+ <td ng-repeat="categoryName in [constants.KEY__EXTRACOLUMN__BUILDER, constants.KEY__EXTRACOLUMN__TEST]">
+ {{imagePair[constants.KEY__EXTRA_COLUMN_VALUES][categoryName]}}
<br>
<button class="show-only-button"
ng-hide="viewingTab != defaultTab"
- ng-disabled="result[categoryName] == categoryValueMatch[categoryName]"
- ng-click="setCategoryValueMatch(categoryName, result[categoryName])"
- title="show only results of {{categoryName}} '{{result[categoryName]}}'">
+ ng-disabled="imagePair[constants.KEY__EXTRA_COLUMN_VALUES][categoryName] == categoryValueMatch[categoryName]"
+ ng-click="setCategoryValueMatch(categoryName, imagePair[constants.KEY__EXTRA_COLUMN_VALUES][categoryName])"
+ title="show only results of {{categoryName}} {{imagePair[constants.KEY__EXTRA_COLUMN_VALUES][categoryName]}}">
show only
</button>
<br>
@@ -315,12 +305,12 @@
</button>
</td>
<td>
- {{result.config}}
+ {{imagePair[constants.KEY__EXTRA_COLUMN_VALUES][constants.KEY__EXTRACOLUMN__CONFIG]}}
<br>
<button class="show-only-button"
ng-hide="viewingTab != defaultTab"
- ng-click="showOnlyConfig(result.config)"
- title="show only results of config '{{result.config}}'">
+ ng-click="showOnlyConfig(imagePair[constants.KEY__EXTRA_COLUMN_VALUES][constants.KEY__EXTRACOLUMN__CONFIG])"
+ title="show only results of config {{imagePair[constants.KEY__EXTRA_COLUMN_VALUES][constants.KEY__EXTRACOLUMN__CONFIG]}}">
show only
</button>
<br>
@@ -333,43 +323,41 @@
</button>
</td>
<td>
- <a ng-repeat="bug in result['bugs']"
+ <a ng-repeat="bug in imagePair[constants.KEY__EXPECTATIONS_DATA][constants.KEY__EXPECTATIONS__BUGS]"
href="https://code.google.com/p/skia/issues/detail?id={{bug}}"
target="_blank">
{{bug}}
</a>
</td>
- <!-- expected image -->
+ <!-- image A -->
<td valign="bottom" width="{{imageSize}}">
- <a href="http://chromium-skia-gm.commondatastorage.googleapis.com/gm/{{result.expectedHashType}}/{{result.test}}/{{result.expectedHashDigest}}.png" target="_blank">View Image</a><br/>
+ <a href="{{imageSets[0][constants.KEY__IMAGESETS__BASE_URL]}}/{{imagePair[constants.KEY__IMAGE_A_URL]}}" target="_blank">View Image</a><br/>
<img-compare type="baseline" width="{{imageSize}}"
- src="http://chromium-skia-gm.commondatastorage.googleapis.com/gm/{{result.expectedHashType}}/{{result.test}}/{{result.expectedHashDigest}}.png" />
-
+ src="{{imageSets[0][constants.KEY__IMAGESETS__BASE_URL]}}/{{imagePair[constants.KEY__IMAGE_A_URL]}}" />
</td>
- <!-- actual image -->
+ <!-- image B -->
<td valign="bottom" width="{{imageSize}}">
- <a href="http://chromium-skia-gm.commondatastorage.googleapis.com/gm/{{result.actualHashType}}/{{result.test}}/{{result.actualHashDigest}}.png" target="_blank">View Image</a><br/>
+ <a href="{{imageSets[1][constants.KEY__IMAGESETS__BASE_URL]}}/{{imagePair[constants.KEY__IMAGE_B_URL]}}" target="_blank">View Image</a><br/>
<img-compare type="test" width="{{imageSize}}"
- src="http://chromium-skia-gm.commondatastorage.googleapis.com/gm/{{result.actualHashType}}/{{result.test}}/{{result.actualHashDigest}}.png" />
-
+ src="{{imageSets[1][constants.KEY__IMAGESETS__BASE_URL]}}/{{imagePair[constants.KEY__IMAGE_B_URL]}}" />
</td>
<!-- whitediffs: every differing pixel shown in white -->
<td valign="bottom" width="{{imageSize}}">
- <div ng-hide="result.expectedHashDigest == result.actualHashDigest"
- title="{{result.numDifferingPixels | number:0}} of {{(100 * result.numDifferingPixels / result.percentDifferingPixels) | number:0}} pixels ({{result.percentDifferingPixels.toFixed(4)}}%) differ from expectation.">
+ <div ng-hide="!imagePair[constants.KEY__IS_DIFFERENT]"
+ title="{{imagePair[constants.KEY__DIFFERENCE_DATA][constants.KEY__DIFFERENCE_DATA__NUM_DIFF_PIXELS] | number:0}} of {{(100 * imagePair[constants.KEY__DIFFERENCE_DATA][constants.KEY__DIFFERENCE_DATA__NUM_DIFF_PIXELS] / imagePair[constants.KEY__DIFFERENCE_DATA][constants.KEY__DIFFERENCE_DATA__PERCENT_DIFF_PIXELS]) | number:0}} pixels ({{imagePair[constants.KEY__DIFFERENCE_DATA][constants.KEY__DIFFERENCE_DATA__PERCENT_DIFF_PIXELS].toFixed(4)}}%) differ from expectation.">
- {{result.percentDifferingPixels.toFixed(4)}}%
- ({{result.numDifferingPixels}})
+ {{imagePair[constants.KEY__DIFFERENCE_DATA][constants.KEY__DIFFERENCE_DATA__PERCENT_DIFF_PIXELS].toFixed(4)}}%
+ ({{imagePair[constants.KEY__DIFFERENCE_DATA][constants.KEY__DIFFERENCE_DATA__NUM_DIFF_PIXELS]}})
<br/>
- <a href="/static/generated-images/whitediffs/{{result.expectedHashDigest}}-vs-{{result.actualHashDigest}}.png" target="_blank">View Image</a><br/>
+ <a href="/static/generated-images/whitediffs/{{getImageDiffRelativeUrl(imagePair)}}" target="_blank">View Image</a><br/>
<img-compare type="differingPixelsInWhite" width="{{imageSize}}"
- src="/static/generated-images/whitediffs/{{result.expectedHashDigest}}-vs-{{result.actualHashDigest}}.png" />
+ src="/static/generated-images/whitediffs/{{getImageDiffRelativeUrl(imagePair)}}" />
</div>
- <div ng-hide="result.expectedHashDigest != result.actualHashDigest"
+ <div ng-hide="imagePair[constants.KEY__IS_DIFFERENT]"
style="text-align:center">
&ndash;none&ndash;
</div>
@@ -377,23 +365,23 @@
<!-- diffs: per-channel RGB deltas -->
<td valign="bottom" width="{{imageSize}}">
- <div ng-hide="result.expectedHashDigest == result.actualHashDigest"
- title="Perceptual difference measure is {{result.perceptualDifference.toFixed(4)}}%. Maximum difference per channel: R={{result.maxDiffPerChannel[0]}}, G={{result.maxDiffPerChannel[1]}}, B={{result.maxDiffPerChannel[2]}}">
+ <div ng-hide="!imagePair[constants.KEY__IS_DIFFERENT]"
+ title="Perceptual difference measure is {{imagePair[constants.KEY__DIFFERENCE_DATA][constants.KEY__DIFFERENCE_DATA__PERCEPTUAL_DIFF].toFixed(4)}}%. Maximum difference per channel: R={{imagePair[constants.KEY__DIFFERENCE_DATA][constants.KEY__DIFFERENCE_DATA__MAX_DIFF_PER_CHANNEL][0]}}, G={{imagePair[constants.KEY__DIFFERENCE_DATA][constants.KEY__DIFFERENCE_DATA__MAX_DIFF_PER_CHANNEL][1]}}, B={{imagePair[constants.KEY__DIFFERENCE_DATA][constants.KEY__DIFFERENCE_DATA__MAX_DIFF_PER_CHANNEL][2]}}">
- {{result.perceptualDifference.toFixed(4)}}%
- {{result.maxDiffPerChannel}}
+ {{imagePair[constants.KEY__DIFFERENCE_DATA][constants.KEY__DIFFERENCE_DATA__PERCEPTUAL_DIFF].toFixed(4)}}%
+ {{imagePair[constants.KEY__DIFFERENCE_DATA][constants.KEY__DIFFERENCE_DATA__MAX_DIFF_PER_CHANNEL]}}
<br/>
- <a href="/static/generated-images/diffs/{{result.expectedHashDigest}}-vs-{{result.actualHashDigest}}.png" target="_blank">View Image</a><br/>
+ <a href="/static/generated-images/diffs/{{getImageDiffRelativeUrl(imagePair)}}" target="_blank">View Image</a><br/>
<img-compare ng-style="{backgroundColor: pixelDiffBgColor}"
type="differencePerPixel" width="{{imageSize}}"
- src="/static/generated-images/diffs/{{result.expectedHashDigest}}-vs-{{result.actualHashDigest}}.png"
+ src="/static/generated-images/diffs/{{getImageDiffRelativeUrl(imagePair)}}"
ng-mousedown="MagnifyDraw($event, true)"
ng-mousemove="MagnifyDraw($event, false)"
ng-mouseup="MagnifyEnd($event)"
ng-mouseleave="MagnifyEnd($event)" />
</div>
- <div ng-hide="result.expectedHashDigest != result.actualHashDigest"
+ <div ng-hide="imagePair[constants.KEY__IS_DIFFERENT]"
style="text-align:center">
&ndash;none&ndash;
</div>
@@ -402,23 +390,15 @@
<td>
<input type="checkbox"
name="rowSelect"
- value="{{result.index}}"
- ng-checked="isValueInArray(result.index, selectedItems)"
- ng-click="toggleValueInArray(result.index, selectedItems)">
+ value="{{imagePair.index}}"
+ ng-checked="isValueInArray(imagePair.index, selectedImagePairs)"
+ ng-click="toggleValueInArray(imagePair.index, selectedImagePairs)">
</tr>
- </table> <!-- results -->
- </td></tr></table> <!-- table holding results header + results table -->
+ </table> <!-- imagePairs -->
+ </td></tr></table> <!-- table holding results header + imagePairs table -->
</div><!-- main display area of selected tab -->
</div><!-- everything: hide until data is loaded -->
- <!-- TODO(epoger): Can we get the base URLs (commondatastorage and
- issues list) from
- https://skia.googlesource.com/buildbot/+/master/site_config/global_variables.json ?
- I tried importing the
- http://skia.googlecode.com/svn/buildbot/skia_tools.js script and using
- that to do so, but I got Access-Control-Allow-Origin errors.
- -->
-
</body>
</html>
diff --git a/gm/rebaseline_server/testdata/outputs/actual/results_test.ResultsTest.test_gm/gm.json b/gm/rebaseline_server/testdata/outputs/actual/results_test.ResultsTest.test_gm/gm.json
deleted file mode 100644
index 9aef669384..0000000000
--- a/gm/rebaseline_server/testdata/outputs/actual/results_test.ResultsTest.test_gm/gm.json
+++ /dev/null
@@ -1,526 +0,0 @@
-{
- "categories": {
- "builder": {
- "Test-Android-GalaxyNexus-SGX540-Arm7-Release": 13,
- "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug": 15
- },
- "config": {
- "565": 9,
- "8888": 9,
- "gpu": 4,
- "pdf-mac": 3,
- "pdf-poppler": 3
- },
- "ignore-failure": {
- "null": 12,
- "false": 16
- },
- "resultType": {
- "failed": 1,
- "failure-ignored": 4,
- "no-comparison": 9,
- "succeeded": 14
- },
- "reviewed-by-human": {
- "null": 16,
- "false": 12
- },
- "test": {
- "3x3bitmaprect": 7,
- "aaclip": 2,
- "bigblurs": 7,
- "bitmapsource": 2,
- "displacement": 5,
- "filterbitmap_checkerboard_192_192": 2,
- "filterbitmap_checkerboard_32_2": 2,
- "texdata": 1
- }
- },
- "testData": [
- {
- "actualHashDigest": "3695033638604474475",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": null,
- "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
- "config": "gpu",
- "expectedHashDigest": "2736593828543197285",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": false,
- "maxDiffPerChannel": [
- 128,
- 128,
- 128
- ],
- "numDifferingPixels": 120000,
- "percentDifferingPixels": 75.0,
- "perceptualDifference": 50.122499999999995,
- "resultType": "failed",
- "reviewed-by-human": null,
- "test": "texdata",
- "weightedDiffMeasure": 8.413046264257337
- },
- {
- "actualHashDigest": "4719210487426381700",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": [
- 1578
- ],
- "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
- "config": "565",
- "expectedHashDigest": "13270012198930365496",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": null,
- "maxDiffPerChannel": [
- 222,
- 223,
- 222
- ],
- "numDifferingPixels": 77691,
- "percentDifferingPixels": 17.593070652173914,
- "perceptualDifference": 100,
- "resultType": "failure-ignored",
- "reviewed-by-human": false,
- "test": "filterbitmap_checkerboard_192_192",
- "weightedDiffMeasure": 3.661184599893761
- },
- {
- "actualHashDigest": "3154864687054945306",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": [
- 1578
- ],
- "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
- "config": "8888",
- "expectedHashDigest": "14746826424040775628",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": null,
- "maxDiffPerChannel": [
- 221,
- 221,
- 221
- ],
- "numDifferingPixels": 82952,
- "percentDifferingPixels": 18.784420289855074,
- "perceptualDifference": 100,
- "resultType": "failure-ignored",
- "reviewed-by-human": false,
- "test": "filterbitmap_checkerboard_192_192",
- "weightedDiffMeasure": 3.6140912497422955
- },
- {
- "actualHashDigest": "15528304435129737588",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": [
- 1578
- ],
- "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
- "config": "565",
- "expectedHashDigest": "16197252621792695154",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": null,
- "maxDiffPerChannel": [
- 222,
- 223,
- 222
- ],
- "numDifferingPixels": 53150,
- "percentDifferingPixels": 12.035778985507246,
- "perceptualDifference": 100,
- "resultType": "failure-ignored",
- "reviewed-by-human": false,
- "test": "filterbitmap_checkerboard_32_2",
- "weightedDiffMeasure": 3.713243437353155
- },
- {
- "actualHashDigest": "712827739969462165",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": [
- 1578
- ],
- "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
- "config": "8888",
- "expectedHashDigest": "7634650333321761866",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": null,
- "maxDiffPerChannel": [
- 221,
- 221,
- 221
- ],
- "numDifferingPixels": 53773,
- "percentDifferingPixels": 12.17685688405797,
- "perceptualDifference": 100,
- "resultType": "failure-ignored",
- "reviewed-by-human": false,
- "test": "filterbitmap_checkerboard_32_2",
- "weightedDiffMeasure": 3.6723483686597684
- },
- {
- "actualHashDigest": "2422083043229439955",
- "actualHashType": "bitmap-64bitMD5",
- "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
- "config": "565",
- "expectedHashDigest": "None",
- "expectedHashType": null,
- "ignore-failure": false,
- "resultType": "no-comparison",
- "test": "bigblurs"
- },
- {
- "actualHashDigest": "17309852422285247848",
- "actualHashType": "bitmap-64bitMD5",
- "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
- "config": "8888",
- "expectedHashDigest": "None",
- "expectedHashType": null,
- "ignore-failure": false,
- "resultType": "no-comparison",
- "test": "bigblurs"
- },
- {
- "actualHashDigest": "17503582803589749280",
- "actualHashType": "bitmap-64bitMD5",
- "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
- "config": "565",
- "expectedHashDigest": "None",
- "expectedHashType": null,
- "ignore-failure": false,
- "resultType": "no-comparison",
- "test": "bitmapsource"
- },
- {
- "actualHashDigest": "16289727936158057543",
- "actualHashType": "bitmap-64bitMD5",
- "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
- "config": "8888",
- "expectedHashDigest": "None",
- "expectedHashType": null,
- "ignore-failure": false,
- "resultType": "no-comparison",
- "test": "bitmapsource"
- },
- {
- "actualHashDigest": "16998423976396106083",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": [
- 1578
- ],
- "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
- "config": "565",
- "expectedHashDigest": "16998423976396106083",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": null,
- "maxDiffPerChannel": 0,
- "numDifferingPixels": 0,
- "percentDifferingPixels": 0,
- "perceptualDifference": 0,
- "resultType": "succeeded",
- "reviewed-by-human": false,
- "test": "3x3bitmaprect",
- "weightedDiffMeasure": 0
- },
- {
- "actualHashDigest": "2054956815327187963",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": [
- 1578
- ],
- "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
- "config": "8888",
- "expectedHashDigest": "2054956815327187963",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": null,
- "maxDiffPerChannel": 0,
- "numDifferingPixels": 0,
- "percentDifferingPixels": 0,
- "perceptualDifference": 0,
- "resultType": "succeeded",
- "reviewed-by-human": false,
- "test": "3x3bitmaprect",
- "weightedDiffMeasure": 0
- },
- {
- "actualHashDigest": "6190901827590820995",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": null,
- "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
- "config": "565",
- "expectedHashDigest": "6190901827590820995",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": false,
- "maxDiffPerChannel": 0,
- "numDifferingPixels": 0,
- "percentDifferingPixels": 0,
- "perceptualDifference": 0,
- "resultType": "succeeded",
- "reviewed-by-human": null,
- "test": "aaclip",
- "weightedDiffMeasure": 0
- },
- {
- "actualHashDigest": "14456211900777561488",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": null,
- "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
- "config": "8888",
- "expectedHashDigest": "14456211900777561488",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": false,
- "maxDiffPerChannel": 0,
- "numDifferingPixels": 0,
- "percentDifferingPixels": 0,
- "perceptualDifference": 0,
- "resultType": "succeeded",
- "reviewed-by-human": null,
- "test": "aaclip",
- "weightedDiffMeasure": 0
- },
- {
- "actualHashDigest": "4569468668668628191",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": null,
- "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
- "config": "565",
- "expectedHashDigest": "4569468668668628191",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": false,
- "maxDiffPerChannel": 0,
- "numDifferingPixels": 0,
- "percentDifferingPixels": 0,
- "perceptualDifference": 0,
- "resultType": "succeeded",
- "reviewed-by-human": null,
- "test": "displacement",
- "weightedDiffMeasure": 0
- },
- {
- "actualHashDigest": "11401048196735046263",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": null,
- "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
- "config": "8888",
- "expectedHashDigest": "11401048196735046263",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": false,
- "maxDiffPerChannel": 0,
- "numDifferingPixels": 0,
- "percentDifferingPixels": 0,
- "perceptualDifference": 0,
- "resultType": "succeeded",
- "reviewed-by-human": null,
- "test": "displacement",
- "weightedDiffMeasure": 0
- },
- {
- "actualHashDigest": "5698561127291561694",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": null,
- "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
- "config": "gpu",
- "expectedHashDigest": "5698561127291561694",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": false,
- "maxDiffPerChannel": 0,
- "numDifferingPixels": 0,
- "percentDifferingPixels": 0,
- "perceptualDifference": 0,
- "resultType": "succeeded",
- "reviewed-by-human": null,
- "test": "displacement",
- "weightedDiffMeasure": 0
- },
- {
- "actualHashDigest": "12901125495691049846",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": null,
- "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
- "config": "pdf-mac",
- "expectedHashDigest": "12901125495691049846",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": false,
- "maxDiffPerChannel": 0,
- "numDifferingPixels": 0,
- "percentDifferingPixels": 0,
- "perceptualDifference": 0,
- "resultType": "succeeded",
- "reviewed-by-human": null,
- "test": "displacement",
- "weightedDiffMeasure": 0
- },
- {
- "actualHashDigest": "16285974094717334658",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": [
- 1578
- ],
- "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
- "config": "pdf-poppler",
- "expectedHashDigest": "16285974094717334658",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": null,
- "maxDiffPerChannel": 0,
- "numDifferingPixels": 0,
- "percentDifferingPixels": 0,
- "perceptualDifference": 0,
- "resultType": "succeeded",
- "reviewed-by-human": false,
- "test": "displacement",
- "weightedDiffMeasure": 0
- },
- {
- "actualHashDigest": "14704206703218007573",
- "actualHashType": "bitmap-64bitMD5",
- "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
- "config": "565",
- "expectedHashDigest": "None",
- "expectedHashType": null,
- "ignore-failure": false,
- "resultType": "no-comparison",
- "test": "bigblurs"
- },
- {
- "actualHashDigest": "17309852422285247848",
- "actualHashType": "bitmap-64bitMD5",
- "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
- "config": "8888",
- "expectedHashDigest": "None",
- "expectedHashType": null,
- "ignore-failure": false,
- "resultType": "no-comparison",
- "test": "bigblurs"
- },
- {
- "actualHashDigest": "1822195599289208664",
- "actualHashType": "bitmap-64bitMD5",
- "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
- "config": "gpu",
- "expectedHashDigest": "None",
- "expectedHashType": null,
- "ignore-failure": false,
- "resultType": "no-comparison",
- "test": "bigblurs"
- },
- {
- "actualHashDigest": "16171608477794909861",
- "actualHashType": "bitmap-64bitMD5",
- "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
- "config": "pdf-mac",
- "expectedHashDigest": "None",
- "expectedHashType": null,
- "ignore-failure": false,
- "resultType": "no-comparison",
- "test": "bigblurs"
- },
- {
- "actualHashDigest": "6539050160610613353",
- "actualHashType": "bitmap-64bitMD5",
- "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
- "config": "pdf-poppler",
- "expectedHashDigest": "None",
- "expectedHashType": null,
- "ignore-failure": false,
- "resultType": "no-comparison",
- "test": "bigblurs"
- },
- {
- "actualHashDigest": "16998423976396106083",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": [
- 1578
- ],
- "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
- "config": "565",
- "expectedHashDigest": "16998423976396106083",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": null,
- "maxDiffPerChannel": 0,
- "numDifferingPixels": 0,
- "percentDifferingPixels": 0,
- "perceptualDifference": 0,
- "resultType": "succeeded",
- "reviewed-by-human": false,
- "test": "3x3bitmaprect",
- "weightedDiffMeasure": 0
- },
- {
- "actualHashDigest": "2054956815327187963",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": [
- 1578
- ],
- "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
- "config": "8888",
- "expectedHashDigest": "2054956815327187963",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": null,
- "maxDiffPerChannel": 0,
- "numDifferingPixels": 0,
- "percentDifferingPixels": 0,
- "perceptualDifference": 0,
- "resultType": "succeeded",
- "reviewed-by-human": false,
- "test": "3x3bitmaprect",
- "weightedDiffMeasure": 0
- },
- {
- "actualHashDigest": "2054956815327187963",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": [
- 1578
- ],
- "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
- "config": "gpu",
- "expectedHashDigest": "2054956815327187963",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": null,
- "maxDiffPerChannel": 0,
- "numDifferingPixels": 0,
- "percentDifferingPixels": 0,
- "perceptualDifference": 0,
- "resultType": "succeeded",
- "reviewed-by-human": false,
- "test": "3x3bitmaprect",
- "weightedDiffMeasure": 0
- },
- {
- "actualHashDigest": "8518347971308375604",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": [
- 1578
- ],
- "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
- "config": "pdf-mac",
- "expectedHashDigest": "8518347971308375604",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": null,
- "maxDiffPerChannel": 0,
- "numDifferingPixels": 0,
- "percentDifferingPixels": 0,
- "perceptualDifference": 0,
- "resultType": "succeeded",
- "reviewed-by-human": false,
- "test": "3x3bitmaprect",
- "weightedDiffMeasure": 0
- },
- {
- "actualHashDigest": "16723580409414313678",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": [
- 1578
- ],
- "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
- "config": "pdf-poppler",
- "expectedHashDigest": "16723580409414313678",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": null,
- "maxDiffPerChannel": 0,
- "numDifferingPixels": 0,
- "percentDifferingPixels": 0,
- "perceptualDifference": 0,
- "resultType": "succeeded",
- "reviewed-by-human": false,
- "test": "3x3bitmaprect",
- "weightedDiffMeasure": 0
- }
- ]
-} \ No newline at end of file
diff --git a/gm/rebaseline_server/testdata/outputs/expected/results_test.ResultsTest.test_gm/gm.json b/gm/rebaseline_server/testdata/outputs/expected/results_test.ResultsTest.test_gm/gm.json
index 9aef669384..38216557bc 100644
--- a/gm/rebaseline_server/testdata/outputs/expected/results_test.ResultsTest.test_gm/gm.json
+++ b/gm/rebaseline_server/testdata/outputs/expected/results_test.ResultsTest.test_gm/gm.json
@@ -1,526 +1,545 @@
{
- "categories": {
+ "extraColumnHeaders": {
"builder": {
- "Test-Android-GalaxyNexus-SGX540-Arm7-Release": 13,
- "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug": 15
+ "headerText": "builder",
+ "isFilterable": true,
+ "isSortable": true,
+ "valuesAndCounts": {
+ "Test-Android-GalaxyNexus-SGX540-Arm7-Release": 13,
+ "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug": 15
+ }
},
"config": {
- "565": 9,
- "8888": 9,
- "gpu": 4,
- "pdf-mac": 3,
- "pdf-poppler": 3
- },
- "ignore-failure": {
- "null": 12,
- "false": 16
+ "headerText": "config",
+ "isFilterable": true,
+ "isSortable": true,
+ "valuesAndCounts": {
+ "565": 9,
+ "8888": 9,
+ "gpu": 4,
+ "pdf-mac": 3,
+ "pdf-poppler": 3
+ }
},
"resultType": {
- "failed": 1,
- "failure-ignored": 4,
- "no-comparison": 9,
- "succeeded": 14
- },
- "reviewed-by-human": {
- "null": 16,
- "false": 12
+ "headerText": "resultType",
+ "isFilterable": true,
+ "isSortable": true,
+ "valuesAndCounts": {
+ "failed": 1,
+ "failure-ignored": 4,
+ "no-comparison": 9,
+ "succeeded": 14
+ }
},
"test": {
- "3x3bitmaprect": 7,
- "aaclip": 2,
- "bigblurs": 7,
- "bitmapsource": 2,
- "displacement": 5,
- "filterbitmap_checkerboard_192_192": 2,
- "filterbitmap_checkerboard_32_2": 2,
- "texdata": 1
+ "headerText": "test",
+ "isFilterable": true,
+ "isSortable": true,
+ "valuesAndCounts": {
+ "3x3bitmaprect": 7,
+ "aaclip": 2,
+ "bigblurs": 7,
+ "bitmapsource": 2,
+ "displacement": 5,
+ "filterbitmap_checkerboard_192_192": 2,
+ "filterbitmap_checkerboard_32_2": 2,
+ "texdata": 1
+ }
}
},
- "testData": [
+ "imagePairs": [
+ {
+ "differenceData": {
+ "maxDiffPerChannel": [
+ 128,
+ 128,
+ 128
+ ],
+ "numDifferingPixels": 120000,
+ "percentDifferingPixels": 75.0,
+ "perceptualDifference": 50.122499999999995,
+ "weightedDiffMeasure": 8.413046264257337
+ },
+ "expectations": {
+ "bugs": null,
+ "ignore-failure": false,
+ "reviewed-by-human": null
+ },
+ "extraColumns": {
+ "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
+ "config": "gpu",
+ "resultType": "failed",
+ "test": "texdata"
+ },
+ "imageAUrl": "bitmap-64bitMD5/texdata/2736593828543197285.png",
+ "imageBUrl": "bitmap-64bitMD5/texdata/3695033638604474475.png",
+ "isDifferent": true
+ },
{
- "actualHashDigest": "3695033638604474475",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": null,
- "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
- "config": "gpu",
- "expectedHashDigest": "2736593828543197285",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": false,
- "maxDiffPerChannel": [
- 128,
- 128,
- 128
- ],
- "numDifferingPixels": 120000,
- "percentDifferingPixels": 75.0,
- "perceptualDifference": 50.122499999999995,
- "resultType": "failed",
- "reviewed-by-human": null,
- "test": "texdata",
- "weightedDiffMeasure": 8.413046264257337
+ "differenceData": {
+ "maxDiffPerChannel": [
+ 222,
+ 223,
+ 222
+ ],
+ "numDifferingPixels": 77691,
+ "percentDifferingPixels": 17.593070652173914,
+ "perceptualDifference": 100,
+ "weightedDiffMeasure": 3.661184599893761
+ },
+ "expectations": {
+ "bugs": [
+ 1578
+ ],
+ "ignore-failure": null,
+ "reviewed-by-human": false
+ },
+ "extraColumns": {
+ "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
+ "config": "565",
+ "resultType": "failure-ignored",
+ "test": "filterbitmap_checkerboard_192_192"
+ },
+ "imageAUrl": "bitmap-64bitMD5/filterbitmap_checkerboard_192_192/13270012198930365496.png",
+ "imageBUrl": "bitmap-64bitMD5/filterbitmap_checkerboard_192_192/4719210487426381700.png",
+ "isDifferent": true
},
{
- "actualHashDigest": "4719210487426381700",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": [
- 1578
- ],
- "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
- "config": "565",
- "expectedHashDigest": "13270012198930365496",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": null,
- "maxDiffPerChannel": [
- 222,
- 223,
- 222
- ],
- "numDifferingPixels": 77691,
- "percentDifferingPixels": 17.593070652173914,
- "perceptualDifference": 100,
- "resultType": "failure-ignored",
- "reviewed-by-human": false,
- "test": "filterbitmap_checkerboard_192_192",
- "weightedDiffMeasure": 3.661184599893761
+ "differenceData": {
+ "maxDiffPerChannel": [
+ 221,
+ 221,
+ 221
+ ],
+ "numDifferingPixels": 82952,
+ "percentDifferingPixels": 18.784420289855074,
+ "perceptualDifference": 100,
+ "weightedDiffMeasure": 3.6140912497422955
+ },
+ "expectations": {
+ "bugs": [
+ 1578
+ ],
+ "ignore-failure": null,
+ "reviewed-by-human": false
+ },
+ "extraColumns": {
+ "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
+ "config": "8888",
+ "resultType": "failure-ignored",
+ "test": "filterbitmap_checkerboard_192_192"
+ },
+ "imageAUrl": "bitmap-64bitMD5/filterbitmap_checkerboard_192_192/14746826424040775628.png",
+ "imageBUrl": "bitmap-64bitMD5/filterbitmap_checkerboard_192_192/3154864687054945306.png",
+ "isDifferent": true
},
{
- "actualHashDigest": "3154864687054945306",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": [
- 1578
- ],
- "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
- "config": "8888",
- "expectedHashDigest": "14746826424040775628",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": null,
- "maxDiffPerChannel": [
- 221,
- 221,
- 221
- ],
- "numDifferingPixels": 82952,
- "percentDifferingPixels": 18.784420289855074,
- "perceptualDifference": 100,
- "resultType": "failure-ignored",
- "reviewed-by-human": false,
- "test": "filterbitmap_checkerboard_192_192",
- "weightedDiffMeasure": 3.6140912497422955
+ "differenceData": {
+ "maxDiffPerChannel": [
+ 222,
+ 223,
+ 222
+ ],
+ "numDifferingPixels": 53150,
+ "percentDifferingPixels": 12.035778985507246,
+ "perceptualDifference": 100,
+ "weightedDiffMeasure": 3.713243437353155
+ },
+ "expectations": {
+ "bugs": [
+ 1578
+ ],
+ "ignore-failure": null,
+ "reviewed-by-human": false
+ },
+ "extraColumns": {
+ "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
+ "config": "565",
+ "resultType": "failure-ignored",
+ "test": "filterbitmap_checkerboard_32_2"
+ },
+ "imageAUrl": "bitmap-64bitMD5/filterbitmap_checkerboard_32_2/16197252621792695154.png",
+ "imageBUrl": "bitmap-64bitMD5/filterbitmap_checkerboard_32_2/15528304435129737588.png",
+ "isDifferent": true
},
{
- "actualHashDigest": "15528304435129737588",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": [
- 1578
- ],
- "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
- "config": "565",
- "expectedHashDigest": "16197252621792695154",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": null,
- "maxDiffPerChannel": [
- 222,
- 223,
- 222
- ],
- "numDifferingPixels": 53150,
- "percentDifferingPixels": 12.035778985507246,
- "perceptualDifference": 100,
- "resultType": "failure-ignored",
- "reviewed-by-human": false,
- "test": "filterbitmap_checkerboard_32_2",
- "weightedDiffMeasure": 3.713243437353155
+ "differenceData": {
+ "maxDiffPerChannel": [
+ 221,
+ 221,
+ 221
+ ],
+ "numDifferingPixels": 53773,
+ "percentDifferingPixels": 12.17685688405797,
+ "perceptualDifference": 100,
+ "weightedDiffMeasure": 3.6723483686597684
+ },
+ "expectations": {
+ "bugs": [
+ 1578
+ ],
+ "ignore-failure": null,
+ "reviewed-by-human": false
+ },
+ "extraColumns": {
+ "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
+ "config": "8888",
+ "resultType": "failure-ignored",
+ "test": "filterbitmap_checkerboard_32_2"
+ },
+ "imageAUrl": "bitmap-64bitMD5/filterbitmap_checkerboard_32_2/7634650333321761866.png",
+ "imageBUrl": "bitmap-64bitMD5/filterbitmap_checkerboard_32_2/712827739969462165.png",
+ "isDifferent": true
},
{
- "actualHashDigest": "712827739969462165",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": [
- 1578
- ],
- "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
- "config": "8888",
- "expectedHashDigest": "7634650333321761866",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": null,
- "maxDiffPerChannel": [
- 221,
- 221,
- 221
- ],
- "numDifferingPixels": 53773,
- "percentDifferingPixels": 12.17685688405797,
- "perceptualDifference": 100,
- "resultType": "failure-ignored",
- "reviewed-by-human": false,
- "test": "filterbitmap_checkerboard_32_2",
- "weightedDiffMeasure": 3.6723483686597684
+ "extraColumns": {
+ "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
+ "config": "565",
+ "resultType": "no-comparison",
+ "test": "bigblurs"
+ },
+ "imageAUrl": null,
+ "imageBUrl": "bitmap-64bitMD5/bigblurs/2422083043229439955.png",
+ "isDifferent": false
},
{
- "actualHashDigest": "2422083043229439955",
- "actualHashType": "bitmap-64bitMD5",
- "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
- "config": "565",
- "expectedHashDigest": "None",
- "expectedHashType": null,
- "ignore-failure": false,
- "resultType": "no-comparison",
- "test": "bigblurs"
+ "extraColumns": {
+ "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
+ "config": "8888",
+ "resultType": "no-comparison",
+ "test": "bigblurs"
+ },
+ "imageAUrl": null,
+ "imageBUrl": "bitmap-64bitMD5/bigblurs/17309852422285247848.png",
+ "isDifferent": false
},
{
- "actualHashDigest": "17309852422285247848",
- "actualHashType": "bitmap-64bitMD5",
- "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
- "config": "8888",
- "expectedHashDigest": "None",
- "expectedHashType": null,
- "ignore-failure": false,
- "resultType": "no-comparison",
- "test": "bigblurs"
+ "extraColumns": {
+ "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
+ "config": "565",
+ "resultType": "no-comparison",
+ "test": "bitmapsource"
+ },
+ "imageAUrl": null,
+ "imageBUrl": "bitmap-64bitMD5/bitmapsource/17503582803589749280.png",
+ "isDifferent": false
},
{
- "actualHashDigest": "17503582803589749280",
- "actualHashType": "bitmap-64bitMD5",
- "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
- "config": "565",
- "expectedHashDigest": "None",
- "expectedHashType": null,
- "ignore-failure": false,
- "resultType": "no-comparison",
- "test": "bitmapsource"
+ "extraColumns": {
+ "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
+ "config": "8888",
+ "resultType": "no-comparison",
+ "test": "bitmapsource"
+ },
+ "imageAUrl": null,
+ "imageBUrl": "bitmap-64bitMD5/bitmapsource/16289727936158057543.png",
+ "isDifferent": false
},
{
- "actualHashDigest": "16289727936158057543",
- "actualHashType": "bitmap-64bitMD5",
- "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
- "config": "8888",
- "expectedHashDigest": "None",
- "expectedHashType": null,
- "ignore-failure": false,
- "resultType": "no-comparison",
- "test": "bitmapsource"
+ "expectations": {
+ "bugs": [
+ 1578
+ ],
+ "ignore-failure": null,
+ "reviewed-by-human": false
+ },
+ "extraColumns": {
+ "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
+ "config": "565",
+ "resultType": "succeeded",
+ "test": "3x3bitmaprect"
+ },
+ "imageAUrl": "bitmap-64bitMD5/3x3bitmaprect/16998423976396106083.png",
+ "imageBUrl": "bitmap-64bitMD5/3x3bitmaprect/16998423976396106083.png",
+ "isDifferent": false
},
{
- "actualHashDigest": "16998423976396106083",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": [
- 1578
- ],
- "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
- "config": "565",
- "expectedHashDigest": "16998423976396106083",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": null,
- "maxDiffPerChannel": 0,
- "numDifferingPixels": 0,
- "percentDifferingPixels": 0,
- "perceptualDifference": 0,
- "resultType": "succeeded",
- "reviewed-by-human": false,
- "test": "3x3bitmaprect",
- "weightedDiffMeasure": 0
+ "expectations": {
+ "bugs": [
+ 1578
+ ],
+ "ignore-failure": null,
+ "reviewed-by-human": false
+ },
+ "extraColumns": {
+ "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
+ "config": "8888",
+ "resultType": "succeeded",
+ "test": "3x3bitmaprect"
+ },
+ "imageAUrl": "bitmap-64bitMD5/3x3bitmaprect/2054956815327187963.png",
+ "imageBUrl": "bitmap-64bitMD5/3x3bitmaprect/2054956815327187963.png",
+ "isDifferent": false
},
{
- "actualHashDigest": "2054956815327187963",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": [
- 1578
- ],
- "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
- "config": "8888",
- "expectedHashDigest": "2054956815327187963",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": null,
- "maxDiffPerChannel": 0,
- "numDifferingPixels": 0,
- "percentDifferingPixels": 0,
- "perceptualDifference": 0,
- "resultType": "succeeded",
- "reviewed-by-human": false,
- "test": "3x3bitmaprect",
- "weightedDiffMeasure": 0
+ "expectations": {
+ "bugs": null,
+ "ignore-failure": false,
+ "reviewed-by-human": null
+ },
+ "extraColumns": {
+ "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
+ "config": "565",
+ "resultType": "succeeded",
+ "test": "aaclip"
+ },
+ "imageAUrl": "bitmap-64bitMD5/aaclip/6190901827590820995.png",
+ "imageBUrl": "bitmap-64bitMD5/aaclip/6190901827590820995.png",
+ "isDifferent": false
},
{
- "actualHashDigest": "6190901827590820995",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": null,
- "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
- "config": "565",
- "expectedHashDigest": "6190901827590820995",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": false,
- "maxDiffPerChannel": 0,
- "numDifferingPixels": 0,
- "percentDifferingPixels": 0,
- "perceptualDifference": 0,
- "resultType": "succeeded",
- "reviewed-by-human": null,
- "test": "aaclip",
- "weightedDiffMeasure": 0
+ "expectations": {
+ "bugs": null,
+ "ignore-failure": false,
+ "reviewed-by-human": null
+ },
+ "extraColumns": {
+ "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
+ "config": "8888",
+ "resultType": "succeeded",
+ "test": "aaclip"
+ },
+ "imageAUrl": "bitmap-64bitMD5/aaclip/14456211900777561488.png",
+ "imageBUrl": "bitmap-64bitMD5/aaclip/14456211900777561488.png",
+ "isDifferent": false
},
{
- "actualHashDigest": "14456211900777561488",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": null,
- "builder": "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
- "config": "8888",
- "expectedHashDigest": "14456211900777561488",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": false,
- "maxDiffPerChannel": 0,
- "numDifferingPixels": 0,
- "percentDifferingPixels": 0,
- "perceptualDifference": 0,
- "resultType": "succeeded",
- "reviewed-by-human": null,
- "test": "aaclip",
- "weightedDiffMeasure": 0
+ "expectations": {
+ "bugs": null,
+ "ignore-failure": false,
+ "reviewed-by-human": null
+ },
+ "extraColumns": {
+ "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
+ "config": "565",
+ "resultType": "succeeded",
+ "test": "displacement"
+ },
+ "imageAUrl": "bitmap-64bitMD5/displacement/4569468668668628191.png",
+ "imageBUrl": "bitmap-64bitMD5/displacement/4569468668668628191.png",
+ "isDifferent": false
},
{
- "actualHashDigest": "4569468668668628191",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": null,
- "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
- "config": "565",
- "expectedHashDigest": "4569468668668628191",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": false,
- "maxDiffPerChannel": 0,
- "numDifferingPixels": 0,
- "percentDifferingPixels": 0,
- "perceptualDifference": 0,
- "resultType": "succeeded",
- "reviewed-by-human": null,
- "test": "displacement",
- "weightedDiffMeasure": 0
+ "expectations": {
+ "bugs": null,
+ "ignore-failure": false,
+ "reviewed-by-human": null
+ },
+ "extraColumns": {
+ "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
+ "config": "8888",
+ "resultType": "succeeded",
+ "test": "displacement"
+ },
+ "imageAUrl": "bitmap-64bitMD5/displacement/11401048196735046263.png",
+ "imageBUrl": "bitmap-64bitMD5/displacement/11401048196735046263.png",
+ "isDifferent": false
},
{
- "actualHashDigest": "11401048196735046263",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": null,
- "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
- "config": "8888",
- "expectedHashDigest": "11401048196735046263",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": false,
- "maxDiffPerChannel": 0,
- "numDifferingPixels": 0,
- "percentDifferingPixels": 0,
- "perceptualDifference": 0,
- "resultType": "succeeded",
- "reviewed-by-human": null,
- "test": "displacement",
- "weightedDiffMeasure": 0
+ "expectations": {
+ "bugs": null,
+ "ignore-failure": false,
+ "reviewed-by-human": null
+ },
+ "extraColumns": {
+ "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
+ "config": "gpu",
+ "resultType": "succeeded",
+ "test": "displacement"
+ },
+ "imageAUrl": "bitmap-64bitMD5/displacement/5698561127291561694.png",
+ "imageBUrl": "bitmap-64bitMD5/displacement/5698561127291561694.png",
+ "isDifferent": false
},
{
- "actualHashDigest": "5698561127291561694",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": null,
- "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
- "config": "gpu",
- "expectedHashDigest": "5698561127291561694",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": false,
- "maxDiffPerChannel": 0,
- "numDifferingPixels": 0,
- "percentDifferingPixels": 0,
- "perceptualDifference": 0,
- "resultType": "succeeded",
- "reviewed-by-human": null,
- "test": "displacement",
- "weightedDiffMeasure": 0
+ "expectations": {
+ "bugs": null,
+ "ignore-failure": false,
+ "reviewed-by-human": null
+ },
+ "extraColumns": {
+ "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
+ "config": "pdf-mac",
+ "resultType": "succeeded",
+ "test": "displacement"
+ },
+ "imageAUrl": "bitmap-64bitMD5/displacement/12901125495691049846.png",
+ "imageBUrl": "bitmap-64bitMD5/displacement/12901125495691049846.png",
+ "isDifferent": false
},
{
- "actualHashDigest": "12901125495691049846",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": null,
- "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
- "config": "pdf-mac",
- "expectedHashDigest": "12901125495691049846",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": false,
- "maxDiffPerChannel": 0,
- "numDifferingPixels": 0,
- "percentDifferingPixels": 0,
- "perceptualDifference": 0,
- "resultType": "succeeded",
- "reviewed-by-human": null,
- "test": "displacement",
- "weightedDiffMeasure": 0
+ "expectations": {
+ "bugs": [
+ 1578
+ ],
+ "ignore-failure": null,
+ "reviewed-by-human": false
+ },
+ "extraColumns": {
+ "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
+ "config": "pdf-poppler",
+ "resultType": "succeeded",
+ "test": "displacement"
+ },
+ "imageAUrl": "bitmap-64bitMD5/displacement/16285974094717334658.png",
+ "imageBUrl": "bitmap-64bitMD5/displacement/16285974094717334658.png",
+ "isDifferent": false
},
{
- "actualHashDigest": "16285974094717334658",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": [
- 1578
- ],
- "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
- "config": "pdf-poppler",
- "expectedHashDigest": "16285974094717334658",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": null,
- "maxDiffPerChannel": 0,
- "numDifferingPixels": 0,
- "percentDifferingPixels": 0,
- "perceptualDifference": 0,
- "resultType": "succeeded",
- "reviewed-by-human": false,
- "test": "displacement",
- "weightedDiffMeasure": 0
+ "extraColumns": {
+ "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
+ "config": "565",
+ "resultType": "no-comparison",
+ "test": "bigblurs"
+ },
+ "imageAUrl": null,
+ "imageBUrl": "bitmap-64bitMD5/bigblurs/14704206703218007573.png",
+ "isDifferent": false
},
{
- "actualHashDigest": "14704206703218007573",
- "actualHashType": "bitmap-64bitMD5",
- "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
- "config": "565",
- "expectedHashDigest": "None",
- "expectedHashType": null,
- "ignore-failure": false,
- "resultType": "no-comparison",
- "test": "bigblurs"
+ "extraColumns": {
+ "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
+ "config": "8888",
+ "resultType": "no-comparison",
+ "test": "bigblurs"
+ },
+ "imageAUrl": null,
+ "imageBUrl": "bitmap-64bitMD5/bigblurs/17309852422285247848.png",
+ "isDifferent": false
},
{
- "actualHashDigest": "17309852422285247848",
- "actualHashType": "bitmap-64bitMD5",
- "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
- "config": "8888",
- "expectedHashDigest": "None",
- "expectedHashType": null,
- "ignore-failure": false,
- "resultType": "no-comparison",
- "test": "bigblurs"
+ "extraColumns": {
+ "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
+ "config": "gpu",
+ "resultType": "no-comparison",
+ "test": "bigblurs"
+ },
+ "imageAUrl": null,
+ "imageBUrl": "bitmap-64bitMD5/bigblurs/1822195599289208664.png",
+ "isDifferent": false
},
{
- "actualHashDigest": "1822195599289208664",
- "actualHashType": "bitmap-64bitMD5",
- "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
- "config": "gpu",
- "expectedHashDigest": "None",
- "expectedHashType": null,
- "ignore-failure": false,
- "resultType": "no-comparison",
- "test": "bigblurs"
+ "extraColumns": {
+ "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
+ "config": "pdf-mac",
+ "resultType": "no-comparison",
+ "test": "bigblurs"
+ },
+ "imageAUrl": null,
+ "imageBUrl": "bitmap-64bitMD5/bigblurs/16171608477794909861.png",
+ "isDifferent": false
},
{
- "actualHashDigest": "16171608477794909861",
- "actualHashType": "bitmap-64bitMD5",
- "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
- "config": "pdf-mac",
- "expectedHashDigest": "None",
- "expectedHashType": null,
- "ignore-failure": false,
- "resultType": "no-comparison",
- "test": "bigblurs"
+ "extraColumns": {
+ "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
+ "config": "pdf-poppler",
+ "resultType": "no-comparison",
+ "test": "bigblurs"
+ },
+ "imageAUrl": null,
+ "imageBUrl": "bitmap-64bitMD5/bigblurs/6539050160610613353.png",
+ "isDifferent": false
},
{
- "actualHashDigest": "6539050160610613353",
- "actualHashType": "bitmap-64bitMD5",
- "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
- "config": "pdf-poppler",
- "expectedHashDigest": "None",
- "expectedHashType": null,
- "ignore-failure": false,
- "resultType": "no-comparison",
- "test": "bigblurs"
+ "expectations": {
+ "bugs": [
+ 1578
+ ],
+ "ignore-failure": null,
+ "reviewed-by-human": false
+ },
+ "extraColumns": {
+ "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
+ "config": "565",
+ "resultType": "succeeded",
+ "test": "3x3bitmaprect"
+ },
+ "imageAUrl": "bitmap-64bitMD5/3x3bitmaprect/16998423976396106083.png",
+ "imageBUrl": "bitmap-64bitMD5/3x3bitmaprect/16998423976396106083.png",
+ "isDifferent": false
},
{
- "actualHashDigest": "16998423976396106083",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": [
- 1578
- ],
- "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
- "config": "565",
- "expectedHashDigest": "16998423976396106083",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": null,
- "maxDiffPerChannel": 0,
- "numDifferingPixels": 0,
- "percentDifferingPixels": 0,
- "perceptualDifference": 0,
- "resultType": "succeeded",
- "reviewed-by-human": false,
- "test": "3x3bitmaprect",
- "weightedDiffMeasure": 0
+ "expectations": {
+ "bugs": [
+ 1578
+ ],
+ "ignore-failure": null,
+ "reviewed-by-human": false
+ },
+ "extraColumns": {
+ "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
+ "config": "8888",
+ "resultType": "succeeded",
+ "test": "3x3bitmaprect"
+ },
+ "imageAUrl": "bitmap-64bitMD5/3x3bitmaprect/2054956815327187963.png",
+ "imageBUrl": "bitmap-64bitMD5/3x3bitmaprect/2054956815327187963.png",
+ "isDifferent": false
},
{
- "actualHashDigest": "2054956815327187963",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": [
- 1578
- ],
- "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
- "config": "8888",
- "expectedHashDigest": "2054956815327187963",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": null,
- "maxDiffPerChannel": 0,
- "numDifferingPixels": 0,
- "percentDifferingPixels": 0,
- "perceptualDifference": 0,
- "resultType": "succeeded",
- "reviewed-by-human": false,
- "test": "3x3bitmaprect",
- "weightedDiffMeasure": 0
+ "expectations": {
+ "bugs": [
+ 1578
+ ],
+ "ignore-failure": null,
+ "reviewed-by-human": false
+ },
+ "extraColumns": {
+ "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
+ "config": "gpu",
+ "resultType": "succeeded",
+ "test": "3x3bitmaprect"
+ },
+ "imageAUrl": "bitmap-64bitMD5/3x3bitmaprect/2054956815327187963.png",
+ "imageBUrl": "bitmap-64bitMD5/3x3bitmaprect/2054956815327187963.png",
+ "isDifferent": false
},
{
- "actualHashDigest": "2054956815327187963",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": [
- 1578
- ],
- "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
- "config": "gpu",
- "expectedHashDigest": "2054956815327187963",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": null,
- "maxDiffPerChannel": 0,
- "numDifferingPixels": 0,
- "percentDifferingPixels": 0,
- "perceptualDifference": 0,
- "resultType": "succeeded",
- "reviewed-by-human": false,
- "test": "3x3bitmaprect",
- "weightedDiffMeasure": 0
+ "expectations": {
+ "bugs": [
+ 1578
+ ],
+ "ignore-failure": null,
+ "reviewed-by-human": false
+ },
+ "extraColumns": {
+ "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
+ "config": "pdf-mac",
+ "resultType": "succeeded",
+ "test": "3x3bitmaprect"
+ },
+ "imageAUrl": "bitmap-64bitMD5/3x3bitmaprect/8518347971308375604.png",
+ "imageBUrl": "bitmap-64bitMD5/3x3bitmaprect/8518347971308375604.png",
+ "isDifferent": false
},
{
- "actualHashDigest": "8518347971308375604",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": [
- 1578
- ],
- "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
- "config": "pdf-mac",
- "expectedHashDigest": "8518347971308375604",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": null,
- "maxDiffPerChannel": 0,
- "numDifferingPixels": 0,
- "percentDifferingPixels": 0,
- "perceptualDifference": 0,
- "resultType": "succeeded",
- "reviewed-by-human": false,
- "test": "3x3bitmaprect",
- "weightedDiffMeasure": 0
+ "expectations": {
+ "bugs": [
+ 1578
+ ],
+ "ignore-failure": null,
+ "reviewed-by-human": false
+ },
+ "extraColumns": {
+ "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
+ "config": "pdf-poppler",
+ "resultType": "succeeded",
+ "test": "3x3bitmaprect"
+ },
+ "imageAUrl": "bitmap-64bitMD5/3x3bitmaprect/16723580409414313678.png",
+ "imageBUrl": "bitmap-64bitMD5/3x3bitmaprect/16723580409414313678.png",
+ "isDifferent": false
+ }
+ ],
+ "imageSets": [
+ {
+ "baseUrl": "http://chromium-skia-gm.commondatastorage.googleapis.com/gm",
+ "description": "expected image"
},
{
- "actualHashDigest": "16723580409414313678",
- "actualHashType": "bitmap-64bitMD5",
- "bugs": [
- 1578
- ],
- "builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
- "config": "pdf-poppler",
- "expectedHashDigest": "16723580409414313678",
- "expectedHashType": "bitmap-64bitMD5",
- "ignore-failure": null,
- "maxDiffPerChannel": 0,
- "numDifferingPixels": 0,
- "percentDifferingPixels": 0,
- "perceptualDifference": 0,
- "resultType": "succeeded",
- "reviewed-by-human": false,
- "test": "3x3bitmaprect",
- "weightedDiffMeasure": 0
+ "baseUrl": "http://chromium-skia-gm.commondatastorage.googleapis.com/gm",
+ "description": "actual image"
}
]
} \ No newline at end of file