// Copyright 2006 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
goog.provide('goog.colorTest');
goog.setTestOnly('goog.colorTest');

goog.require('goog.array');
goog.require('goog.color');
goog.require('goog.color.names');
goog.require('goog.testing.jsunit');

function testIsValidColor() {
  var goodColors = ['#ffffff', '#ff7812', '#012345', '#Ff003D', '#3CA',
                    '(255, 26, 75)', 'RGB(2, 3, 4)', '(0,0,0)', 'white',
                    'blue'];
  var badColors = ['#xxxxxx', '8899000', 'not_color', '#1234567', 'fffffg',
                   '(2555,0,0)', '(1,2,3,4)', 'rgb(1,20,)', 'RGB(20,20,20,)',
                   'omgwtfbbq'];
  for (var i = 0; i < goodColors.length; i++) {
    assertTrue(goodColors[i], goog.color.isValidColor(goodColors[i]));
  }
  for (var i = 0; i < badColors.length; i++) {
    assertFalse(badColors[i], goog.color.isValidColor(badColors[i]));
  }
}


function testIsValidHexColor() {
  var goodHexColors = ['#ffffff', '#ff7812', '#012345', '#Ff003D', '#3CA'];
  var badHexColors = ['#xxxxxx', '889900', 'not_color', '#1234567', 'fffffg'];
  for (var i = 0; i < goodHexColors.length; i++) {
    assertTrue(goodHexColors[i], goog.color.isValidHexColor_(goodHexColors[i]));
  }
  for (var i = 0; i < badHexColors.length; i++) {
    assertFalse(badHexColors[i], goog.color.isValidHexColor_(badHexColors[i]));
  }
}


function testIsValidRgbColor() {
  var goodRgbColors = ['(255, 26, 75)', 'RGB(2, 3, 4)', '(0,0,0)',
                       'rgb(255,255,255)'];
  var badRgbColors = ['(2555,0,0)', '(1,2,3,4)', 'rgb(1,20,)',
                      'RGB(20,20,20,)'];
  for (var i = 0; i < goodRgbColors.length; i++) {
    assertEquals(goodRgbColors[i],
                 goog.color.isValidRgbColor_(goodRgbColors[i]).length, 3);
  }
  for (var i = 0; i < badRgbColors.length; i++) {
    assertEquals(badRgbColors[i],
                 goog.color.isValidRgbColor_(badRgbColors[i]).length, 0);
  }
}


function testParse() {
  var colors = ['rgb(15, 250, 77)', '(127, 127, 127)', '#ffeedd', '123456',
                'magenta'];
  var parsed = goog.array.map(colors, goog.color.parse);
  assertEquals('rgb', parsed[0].type);
  assertEquals(goog.color.rgbToHex(15, 250, 77), parsed[0].hex);
  assertEquals('rgb', parsed[1].type);
  assertEquals(goog.color.rgbToHex(127, 127, 127), parsed[1].hex);
  assertEquals('hex', parsed[2].type);
  assertEquals('#ffeedd', parsed[2].hex);
  assertEquals('hex', parsed[3].type);
  assertEquals('#123456', parsed[3].hex);
  assertEquals('named', parsed[4].type);
  assertEquals('#ff00ff', parsed[4].hex);

  var badColors = ['rgb(01, 1, 23)', '(256, 256, 256)', '#ffeeddaa'];
  for (var i = 0; i < badColors.length; i++) {
    var e = assertThrows(goog.partial(goog.color.parse, badColors[i]));
    assertContains('is not a valid color string', e.message);
  }
}


function testHexToRgb() {
  var testColors = [['#B0FF2D', [176, 255, 45]],
                    ['#b26e5f', [178, 110, 95]],
                    ['#66f', [102, 102, 255]]];

  for (var i = 0; i < testColors.length; i++) {
    var r = goog.color.hexToRgb(testColors[i][0]);
    var t = testColors[i][1];

    assertEquals('Red channel should match.', t[0], r[0]);
    assertEquals('Green channel should match.', t[1], r[1]);
    assertEquals('Blue channel should match.', t[2], r[2]);
  }

  var badColors = ['', '#g00', 'some words'];
  for (var i = 0; i < badColors.length; i++) {
    var e = assertThrows(goog.partial(goog.color.hexToRgb, badColors[i]));
    assertEquals("'" + badColors[i] + "' is not a valid hex color", e.message);
  }
}


function testHexToRgbStyle() {
  assertEquals('rgb(255,0,0)', goog.color.hexToRgbStyle(goog.color.names.red));
  assertEquals('rgb(206,206,206)', goog.color.hexToRgbStyle('#cecece'));
  assertEquals('rgb(51,204,170)', goog.color.hexToRgbStyle('#3CA'));
  var badHexColors = ['#1234', null, undefined, '#.1234567890'];
  for (var i = 0; i < badHexColors.length; ++i) {
    var badHexColor = badHexColors[i];
    var e = assertThrows(goog.partial(goog.color.hexToRgbStyle, badHexColor));
    assertEquals("'" + badHexColor + "' is not a valid hex color", e.message);
  }
}


function testRgbToHex() {
  assertEquals(goog.color.names.red, goog.color.rgbToHex(255, 0, 0));
  assertEquals('#af13ff', goog.color.rgbToHex(175, 19, 255));
  var badRgb = [[-1, -1, -1], [256, 0, 0], ['a', 'b', 'c'], [undefined, 5, 5]];
  for (var i = 0; i < badRgb.length; ++i) {
    var e = assertThrows(goog.partial(goog.color.rgbArrayToHex, badRgb[i]));
    assertContains('is not a valid RGB color', e.message);
  }
}


function testRgbToHsl() {
  var rgb = [255, 171, 32];
  var hsl = goog.color.rgbArrayToHsl(rgb);
  assertEquals(37, hsl[0]);
  assertTrue(1.0 - hsl[1] < 0.01);
  assertTrue(hsl[2] - .5625 < 0.01);
}


function testHslToRgb() {
  var hsl = [60, 0.5, 0.1];
  var rgb = goog.color.hslArrayToRgb(hsl);
  assertEquals(38, rgb[0]);
  assertEquals(38, rgb[1]);
  assertEquals(13, rgb[2]);
}


// Tests accuracy of HSL to RGB conversion
function testHSLBidiToRGB() {
  var DELTA = 1;

  var color = [[100, 56, 200],
               [255, 0, 0],
               [0, 0, 255],
               [0, 255, 0],
               [255, 255, 255],
               [0, 0, 0]];

  for (var i = 0; i < color.length; i++) {
    colorConversionTestHelper(
        function(color) {
          return goog.color.rgbToHsl(color[0], color[1], color[2]);
        },
        function(color) {
          return goog.color.hslToRgb(color[0], color[1], color[2]);
        },
        color[i], DELTA);

    colorConversionTestHelper(
        function(color) { return goog.color.rgbArrayToHsl(color); },
        function(color) { return goog.color.hslArrayToRgb(color); },
        color[i], DELTA);
  }
}


// Tests HSV to RGB conversion
function testHSVToRGB() {
  var DELTA = 1;

  var color = [[100, 56, 200],
               [255, 0, 0],
               [0, 0, 255],
               [0, 255, 0],
               [255, 255, 255],
               [0, 0, 0]];

  for (var i = 0; i < color.length; i++) {
    colorConversionTestHelper(
        function(color) {
          return goog.color.rgbToHsv(color[0], color[1], color[2]);
        },
        function(color) {
          return goog.color.hsvToRgb(color[0], color[1], color[2]);
        },
        color[i], DELTA);

    colorConversionTestHelper(
        function(color) { return goog.color.rgbArrayToHsv(color); },
        function(color) { return goog.color.hsvArrayToRgb(color); },
        color[i], DELTA);
  }
}

// Tests that HSV space is (0-360) for hue
function testHSVSpecRangeIsCorrect() {
  var color = [0, 0, 255];  // Blue is in the middle of hue range

  var hsv = goog.color.rgbToHsv(color[0], color[1], color[2]);

  assertTrue("H in HSV space looks like it's not 0-360", hsv[0] > 1);
}

// Tests conversion between HSL and Hex
function testHslToHex() {
  var DELTA = 1;

  var color = [[0, 0, 0],
               [20, 0.5, 0.5],
               [0, 0, 1],
               [255, .45, .76]];

  for (var i = 0; i < color.length; i++) {
    colorConversionTestHelper(
        function(hsl) { return goog.color.hslToHex(hsl[0], hsl[1], hsl[2]); },
        function(hex) { return goog.color.hexToHsl(hex); },
        color[i], DELTA);

    colorConversionTestHelper(
        function(hsl) { return goog.color.hslArrayToHex(hsl); },
        function(hex) { return goog.color.hexToHsl(hex); },
        color[i], DELTA);
  }
}

// Tests conversion between HSV and Hex
function testHsvToHex() {
  var DELTA = 1;

  var color = [[0, 0, 0],
               [.5, 0.5, 155],
               [0, 0, 255],
               [.7, .45, 21]];

  for (var i = 0; i < color.length; i++) {
    colorConversionTestHelper(
        function(hsl) { return goog.color.hsvToHex(hsl[0], hsl[1], hsl[2]); },
        function(hex) { return goog.color.hexToHsv(hex); },
        color[i], DELTA);

    colorConversionTestHelper(
        function(hsl) { return goog.color.hsvArrayToHex(hsl); },
        function(hex) { return goog.color.hexToHsv(hex); },
        color[i], DELTA);
  }
}


/**
 * This helper method compares two RGB colors, checking that each color
 * component is the same.
 * @param {Array<number>} rgb1 Color represented by a 3-element array with
 *     red, green, and blue values respectively, in the range [0, 255].
 * @param {Array<number>} rgb2 Color represented by a 3-element array with
 *     red, green, and blue values respectively, in the range [0, 255].
 * @return {boolean} True if the colors are the same, false otherwise.
 */
function rgbColorsAreEqual(rgb1, rgb2) {
  return (rgb1[0] == rgb2[0] &&
          rgb1[1] == rgb2[1] &&
          rgb1[2] == rgb2[2]);
}


/**
 * This method runs unit tests against goog.color.blend().  Test cases include:
 * blending arbitrary colors with factors of 0 and 1, blending the same colors
 * using arbitrary factors, blending different colors of varying factors,
 * and blending colors using factors outside the expected range.
 */
function testColorBlend() {
  // Define some RGB colors for our tests.
  var black = [0, 0, 0];
  var blue = [0, 0, 255];
  var gray = [128, 128, 128];
  var green = [0, 255, 0];
  var purple = [128, 0, 128];
  var red = [255, 0, 0];
  var yellow = [255, 255, 0];
  var white = [255, 255, 255];

  // Blend arbitrary colors, using 0 and 1 for factors. Using 0 should return
  // the first color, while using 1 should return the second color.
  var redWithNoGreen = goog.color.blend(red, green, 1);
  assertTrue('red + 0 * green = red',
             rgbColorsAreEqual(red, redWithNoGreen));
  var whiteWithAllBlue = goog.color.blend(white, blue, 0);
  assertTrue('white + 1 * blue = blue',
             rgbColorsAreEqual(blue, whiteWithAllBlue));

  // Blend the same colors using arbitrary factors. This should return the
  // same colors.
  var greenWithGreen = goog.color.blend(green, green, .25);
  assertTrue('green + .25 * green = green',
             rgbColorsAreEqual(green, greenWithGreen));

  // Blend different colors using varying factors.
  var blackWithWhite = goog.color.blend(black, white, .5);
  assertTrue('black + .5 * white = gray',
             rgbColorsAreEqual(gray, blackWithWhite));
  var redAndBlue = goog.color.blend(red, blue, .5);
  assertTrue('red + .5 * blue = purple',
             rgbColorsAreEqual(purple, redAndBlue));
  var lightGreen = goog.color.blend(green, white, .75);
  assertTrue('green + .25 * white = a lighter shade of white',
             lightGreen[0] > 0 &&
             lightGreen[1] == 255 &&
             lightGreen[2] > 0);

  // Blend arbitrary colors using factors outside the expected range.
  var noGreenAllPurple = goog.color.blend(green, purple, -0.5);
  assertTrue('green * -0.5 + purple = purple.',
             rgbColorsAreEqual(purple, noGreenAllPurple));
  var allBlueNoYellow = goog.color.blend(blue, yellow, 1.37);
  assertTrue('blue * 1.37 + yellow = blue.',
             rgbColorsAreEqual(blue, allBlueNoYellow));
}


/**
 * This method runs unit tests against goog.color.darken(). Test cases
 * include darkening black with arbitrary factors, edge cases (using 0 and 1),
 * darkening colors using various factors, and darkening colors using factors
 * outside the expected range.
 */
function testColorDarken() {
  // Define some RGB colors
  var black = [0, 0, 0];
  var green = [0, 255, 0];
  var darkGray = [68, 68, 68];
  var olive = [128, 128, 0];
  var purple = [128, 0, 128];
  var white = [255, 255, 255];

  // Darken black by an arbitrary factor, which should still return black.
  var darkBlack = goog.color.darken(black, .63);
  assertTrue('black darkened by .63 is still black.',
             rgbColorsAreEqual(black, darkBlack));

  // Call darken() with edge-case factors (0 and 1).
  var greenNotDarkened = goog.color.darken(green, 0);
  assertTrue('green darkened by 0 is still green.',
             rgbColorsAreEqual(green, greenNotDarkened));
  var whiteFullyDarkened = goog.color.darken(white, 1);
  assertTrue('white darkened by 1 is black.',
             rgbColorsAreEqual(black, whiteFullyDarkened));

  // Call darken() with various colors and factors. The result should be
  // a color with less luminance.
  var whiteHsl = goog.color.rgbToHsl(white[0],
                                     white[1],
                                     white[2]);
  var whiteDarkened = goog.color.darken(white, .43);
  var whiteDarkenedHsl = goog.color.rgbToHsl(whiteDarkened[0],
                                             whiteDarkened[1],
                                             whiteDarkened[2]);
  assertTrue('White that\'s darkened has less luminance than white.',
             whiteDarkenedHsl[2] < whiteHsl[2]);
  var purpleHsl = goog.color.rgbToHsl(purple[0],
                                      purple[1],
                                      purple[2]);
  var purpleDarkened = goog.color.darken(purple, .1);
  var purpleDarkenedHsl = goog.color.rgbToHsl(purpleDarkened[0],
                                              purpleDarkened[1],
                                              purpleDarkened[2]);
  assertTrue('Purple that\'s darkened has less luminance than purple.',
             purpleDarkenedHsl[2] < purpleHsl[2]);

  // Call darken() with factors outside the expected range.
  var darkGrayTurnedBlack = goog.color.darken(darkGray, 2.1);
  assertTrue('Darkening dark gray by 2.1 returns black.',
             rgbColorsAreEqual(black, darkGrayTurnedBlack));
  var whiteNotDarkened = goog.color.darken(white, -0.62);
  assertTrue('Darkening white by -0.62 returns white.',
             rgbColorsAreEqual(white, whiteNotDarkened));
}


/**
 * This method runs unit tests against goog.color.lighten(). Test cases
 * include lightening white with arbitrary factors, edge cases (using 0 and 1),
 * lightening colors using various factors, and lightening colors using factors
 * outside the expected range.
 */
function testColorLighten() {
  // Define some RGB colors
  var black = [0, 0, 0];
  var brown = [165, 42, 42];
  var navy = [0, 0, 128];
  var orange = [255, 165, 0];
  var white = [255, 255, 255];

  // Lighten white by an arbitrary factor, which should still return white.
  var lightWhite = goog.color.lighten(white, .41);
  assertTrue('white lightened by .41 is still white.',
             rgbColorsAreEqual(white, lightWhite));

  // Call lighten() with edge-case factors(0 and 1).
  var navyNotLightened = goog.color.lighten(navy, 0);
  assertTrue('navy lightened by 0 is still navy.',
             rgbColorsAreEqual(navy, navyNotLightened));
  var orangeFullyLightened = goog.color.lighten(orange, 1);
  assertTrue('orange lightened by 1 is white.',
             rgbColorsAreEqual(white, orangeFullyLightened));

  // Call lighten() with various colors and factors. The result should be
  // a color with greater luminance.
  var blackHsl = goog.color.rgbToHsl(black[0],
                                     black[1],
                                     black[2]);
  var blackLightened = goog.color.lighten(black, .33);
  var blackLightenedHsl = goog.color.rgbToHsl(blackLightened[0],
                                              blackLightened[1],
                                              blackLightened[2]);
  assertTrue('Black that\'s lightened has more luminance than black.',
             blackLightenedHsl[2] >= blackHsl[2]);
  var orangeHsl = goog.color.rgbToHsl(orange[0],
                                      orange[1],
                                      orange[2]);
  var orangeLightened = goog.color.lighten(orange, .91);
  var orangeLightenedHsl = goog.color.rgbToHsl(orangeLightened[0],
                                               orangeLightened[1],
                                               orangeLightened[2]);
  assertTrue('Orange that\'s lightened has more luminance than orange.',
             orangeLightenedHsl[2] >= orangeHsl[2]);

  // Call lighten() with factors outside the expected range.
  var navyTurnedWhite = goog.color.lighten(navy, 1.01);
  assertTrue('Lightening navy by 1.01 returns white.',
             rgbColorsAreEqual(white, navyTurnedWhite));
  var brownNotLightened = goog.color.lighten(brown, -0.0000001);
  assertTrue('Lightening brown by -0.0000001 returns brown.',
             rgbColorsAreEqual(brown, brownNotLightened));
}


/**
 * This method runs unit tests against goog.color.hslDistance().
 */
function testHslDistance() {
  // Define some HSL colors
  var aliceBlueHsl = goog.color.rgbToHsl(240, 248, 255);
  var blackHsl = goog.color.rgbToHsl(0, 0, 0);
  var ghostWhiteHsl = goog.color.rgbToHsl(248, 248, 255);
  var navyHsl = goog.color.rgbToHsl(0, 0, 128);
  var redHsl = goog.color.rgbToHsl(255, 0, 0);
  var whiteHsl = goog.color.rgbToHsl(255, 255, 255);

  // The distance between the same colors should be 0.
  assertTrue('There is no HSL distance between white and white.',
             goog.color.hslDistance(whiteHsl, whiteHsl) == 0);
  assertTrue('There is no HSL distance between red and red.',
             goog.color.hslDistance(redHsl, redHsl) == 0);

  // The distance between various colors should be within certain thresholds.
  var hslDistance = goog.color.hslDistance(whiteHsl, ghostWhiteHsl);
  assertTrue('The HSL distance between white and ghost white is > 0.',
             hslDistance > 0);
  assertTrue('The HSL distance between white and ghost white is <= 0.02.',
             hslDistance <= 0.02);
  hslDistance = goog.color.hslDistance(whiteHsl, redHsl);
  assertTrue('The HSL distance betwen white and red is > 0.02.',
             hslDistance > 0.02);
  hslDistance = goog.color.hslDistance(navyHsl, aliceBlueHsl);
  assertTrue('The HSL distance between navy and alice blue is > 0.02.',
             hslDistance > 0.02);
  hslDistance = goog.color.hslDistance(blackHsl, whiteHsl);
  assertTrue('The HSL distance between white and black is 1.',
             hslDistance == 1);
}


/**
 * This method runs unit tests against goog.color.yiqBrightness_().
 */
function testYiqBrightness() {
  var white = [255, 255, 255];
  var black = [0, 0, 0];
  var coral = [255, 127, 80];
  var lightgreen = [144, 238, 144];

  var whiteBrightness = goog.color.yiqBrightness_(white);
  var blackBrightness = goog.color.yiqBrightness_(black);
  var coralBrightness = goog.color.yiqBrightness_(coral);
  var lightgreenBrightness = goog.color.yiqBrightness_(lightgreen);

  // brightness should be a number
  assertTrue('White brightness is a number.',
             typeof whiteBrightness == 'number');
  assertTrue('Coral brightness is a number.',
             typeof coralBrightness == 'number');

  // brightness for known colors should match known values
  assertEquals('White brightness is 255', whiteBrightness, 255);
  assertEquals('Black brightness is 0', blackBrightness, 0);
  assertEquals('Coral brightness is 160', coralBrightness, 160);
  assertEquals('Lightgreen brightness is 199', lightgreenBrightness, 199);
}


/**
 * This method runs unit tests against goog.color.yiqBrightnessDiff_().
 */
function testYiqBrightnessDiff() {
  var colors = {
    'deeppink': [255, 20, 147],
    'indigo': [75, 0, 130],
    'saddlebrown': [139, 69, 19]
  };

  var diffs = new Object();
  for (name1 in colors) {
    for (name2 in colors) {
      diffs[name1 + '-' + name2] =
          goog.color.yiqBrightnessDiff_(colors[name1], colors[name2]);
    }
  }

  for (pair in diffs) {
    // each brightness diff should be a number
    assertTrue(pair + ' diff is a number.', typeof diffs[pair] == 'number');
    // each brightness diff should be greater than or equal to 0
    assertTrue(pair + ' diff is greater than or equal to 0.', diffs[pair] >= 0);
  }

  // brightness diff for same-color pairs should be 0
  assertEquals('deeppink-deeppink is 0.', diffs['deeppink-deeppink'], 0);
  assertEquals('indigo-indigo is 0.', diffs['indigo-indigo'], 0);

  // brightness diff for known pairs should match known values
  assertEquals('deeppink-indigo is 68.', diffs['deeppink-indigo'], 68);
  assertEquals('saddlebrown-deeppink is 21.',
               diffs['saddlebrown-deeppink'], 21);

  // reversed pairs should have equal values
  assertEquals('indigo-saddlebrown is 47.', diffs['indigo-saddlebrown'], 47);
  assertEquals('saddlebrown-indigo is also 47.',
               diffs['saddlebrown-indigo'], 47);
}


/**
 * This method runs unit tests against goog.color.colorDiff_().
 */
function testColorDiff() {
  var colors = {
    'mediumblue': [0, 0, 205],
    'oldlace': [253, 245, 230],
    'orchid': [218, 112, 214]
  };

  var diffs = new Object();
  for (name1 in colors) {
    for (name2 in colors) {
      diffs[name1 + '-' + name2] =
          goog.color.colorDiff_(colors[name1], colors[name2]);
    }
  }

  for (pair in diffs) {
    // each color diff should be a number
    assertTrue(pair + ' diff is a number.', typeof diffs[pair] == 'number');
    // each color diff should be greater than or equal to 0
    assertTrue(pair + ' diff is greater than or equal to 0.', diffs[pair] >= 0);
  }

  // color diff for same-color pairs should be 0
  assertEquals('mediumblue-mediumblue is 0.',
               diffs['mediumblue-mediumblue'], 0);
  assertEquals('oldlace-oldlace is 0.', diffs['oldlace-oldlace'], 0);

  // color diff for known pairs should match known values
  assertEquals('mediumblue-oldlace is 523.', diffs['mediumblue-oldlace'], 523);
  assertEquals('oldlace-orchid is 184.', diffs['oldlace-orchid'], 184);

  // reversed pairs should have equal values
  assertEquals('orchid-mediumblue is 339.', diffs['orchid-mediumblue'], 339);
  assertEquals('mediumblue-orchid is also 339.',
               diffs['mediumblue-orchid'], 339);
}


/**
 * This method runs unit tests against goog.color.highContrast().
 */
function testHighContrast() {
  white = [255, 255, 255];
  black = [0, 0, 0];
  lemonchiffron = [255, 250, 205];
  sienna = [160, 82, 45];

  var suggestion = goog.color.highContrast(
      black, [white, black, sienna, lemonchiffron]);

  // should return an array of three numbers
  assertTrue('Return value is an array.', typeof suggestion == 'object');
  assertTrue('Return value is 3 long.', suggestion.length == 3);

  // known color combos should return a known (i.e. human-verified) suggestion
  assertArrayEquals('White is best on sienna.',
      goog.color.highContrast(
          sienna, [white, black, sienna, lemonchiffron]), white);
  assertArrayEquals('Black is best on lemonchiffron.',
      goog.color.highContrast(
          white, [white, black, sienna, lemonchiffron]), black);
}


/**
 * Helper function for color conversion functions between two colorspaces.
 * @param {Function} funcOne Function that converts from 1st colorspace to 2nd
 * @param {Function} funcTwo Function that converts from 2nd colorspace to 2nd
 * @param {Array<number>} color The color array passed to funcOne
 * @param {number} DELTA Margin of error for each element in color
 */
function colorConversionTestHelper(funcOne, funcTwo, color, DELTA) {

  var temp = funcOne(color);

  if (!goog.color.isValidHexColor_(temp)) {
    assertTrue('First conversion had a NaN: ' + temp, !isNaN(temp[0]));
    assertTrue('First conversion had a NaN: ' + temp, !isNaN(temp[1]));
    assertTrue('First conversion had a NaN: ' + temp, !isNaN(temp[2]));
  }

  var back = funcTwo(temp);

  if (!goog.color.isValidHexColor_(temp)) {
    assertTrue('Second conversion had a NaN: ' + back, !isNaN(back[0]));
    assertTrue('Second conversion had a NaN: ' + back, !isNaN(back[1]));
    assertTrue('Second conversion had a NaN: ' + back, !isNaN(back[2]));
  }

  assertColorFuzzyEquals('Color was off', color, back, DELTA);
}


/**
 * Checks equivalence between two colors' respective values.  Accepts +- delta
 * for each pair of values
 * @param {string} Str
 * @param {Array<number>} expected
 * @param {Array<number>} actual
 * @param {number} delta Margin of error for each element in color array
 */
function assertColorFuzzyEquals(str, expected, actual, delta) {
  assertTrue(str + ' Expected: ' + expected + '  and got: ' + actual +
                 ' w/ delta: ' + delta,
             (Math.abs(expected[0] - actual[0]) <= delta) &&
             (Math.abs(expected[1] - actual[1]) <= delta) &&
             (Math.abs(expected[2] - actual[2]) <= delta));
}
