text: /*global defineSuite*/
  defineSuite([
          'Core/AttributeCompression',
          'Core/Cartesian2',
          'Core/Cartesian3',
          'Core/Math'
      ], function(
          AttributeCompression,
          Cartesian2,
          Cartesian3,
          CesiumMath) {
      'use strict';
  
      var negativeUnitZ = new Cartesian3(0.0, 0.0, -1.0);
      it('oct decode(0, 0)', function() {
          var result = new Cartesian3();
          AttributeCompression.octDecode(0, 0, result);
          expect(result).toEqual(negativeUnitZ);
      });
  
      it('oct encode(0, 0, -1)', function() {
          var result = new Cartesian2();
          AttributeCompression.octEncode(negativeUnitZ, result);
          expect(result).toEqual(new Cartesian2(255, 255));
      });
  
      it('oct encode(0, 0, 1)', function() {
          var result = new Cartesian2();
          AttributeCompression.octEncode(Cartesian3.UNIT_Z, result);
          expect(result).toEqual(new Cartesian2(128, 128));
      });
  
      it('oct extents are equal', function() {
          var result = new Cartesian3();
          // lower left
          AttributeCompression.octDecode(0, 0, result);
          expect(result).toEqual(negativeUnitZ);
          // lower right
          AttributeCompression.octDecode(255, 0, result);
          expect(result).toEqual(negativeUnitZ);
          // upper right
          AttributeCompression.octDecode(255, 255, result);
          expect(result).toEqual(negativeUnitZ);
          // upper left
          AttributeCompression.octDecode(255, 0, result);
          expect(result).toEqual(negativeUnitZ);
      });
  
      it('throws oct encode vector undefined', function() {
          var vector;
          var result = new Cartesian3();
          expect(function() {
              AttributeCompression.octEncode(vector, result);
          }).toThrowDeveloperError();
      });
  
      it('throws oct encode result undefined', function() {
          var result;
          expect(function() {
              AttributeCompression.octEncode(Cartesian3.UNIT_Z, result);
          }).toThrowDeveloperError();
      });
  
      it('throws oct encode non unit vector', function() {
          var nonUnitLengthVector = new Cartesian3(2.0, 0.0, 0.0);
          var result = new Cartesian2();
          expect(function() {
              AttributeCompression.octEncode(nonUnitLengthVector, result);
          }).toThrowDeveloperError();
      });
  
      it('throws oct encode zero length vector', function() {
          var result = new Cartesian2();
          expect(function() {
              AttributeCompression.octEncode(Cartesian3.ZERO, result);
          }).toThrowDeveloperError();
      });
  
      it('throws oct decode result undefined', function() {
          var result;
          expect(function() {
              AttributeCompression.octDecode(Cartesian2.ZERO, result);
          }).toThrowDeveloperError();
      });
  
      it('throws oct decode x out of bounds', function() {
          var result = new Cartesian3();
          var invalidSNorm = new Cartesian2(256, 0);
          expect(function() {
              AttributeCompression.octDecode(invalidSNorm, result);
          }).toThrowDeveloperError();
      });
  
      it('throws oct decode y out of bounds', function() {
          var result = new Cartesian3();
          var invalidSNorm = new Cartesian2(0, 256);
          expect(function() {
              AttributeCompression.octDecode(invalidSNorm, result);
          }).toThrowDeveloperError();
      });
  
      it('oct encoding', function() {
          var epsilon = CesiumMath.EPSILON1;
  
          var encoded = new Cartesian2();
          var result = new Cartesian3();
          var normal = new Cartesian3(0.0, 0.0, 1.0);
          AttributeCompression.octEncode(normal, encoded);
          expect(AttributeCompression.octDecode(encoded.x, encoded.y, result)).toEqualEpsilon(normal, epsilon);
  
          normal = new Cartesian3(0.0, 0.0, -1.0);
          AttributeCompression.octEncode(normal, encoded);
          expect(AttributeCompression.octDecode(encoded.x, encoded.y, result)).toEqualEpsilon(normal, epsilon);
  
          normal = new Cartesian3(0.0, 1.0, 0.0);
          AttributeCompression.octEncode(normal, encoded);
          expect(AttributeCompression.octDecode(encoded.x, encoded.y, result)).toEqualEpsilon(normal, epsilon);
  
          normal = new Cartesian3(0.0, -1.0, 0.0);
          AttributeCompression.octEncode(normal, encoded);
          expect(AttributeCompression.octDecode(encoded.x, encoded.y, result)).toEqualEpsilon(normal, epsilon);
  
          normal = new Cartesian3(1.0, 0.0, 0.0);
          AttributeCompression.octEncode(normal, encoded);
          expect(AttributeCompression.octDecode(encoded.x, encoded.y, result)).toEqualEpsilon(normal, epsilon);
  
          normal = new Cartesian3(-1.0, 0.0, 0.0);
          AttributeCompression.octEncode(normal, encoded);
          expect(AttributeCompression.octDecode(encoded.x, encoded.y, result)).toEqualEpsilon(normal, epsilon);
  
          normal = new Cartesian3(1.0, 1.0, 1.0);
          Cartesian3.normalize(normal, normal);
          AttributeCompression.octEncode(normal, encoded);
          expect(AttributeCompression.octDecode(encoded.x, encoded.y, result)).toEqualEpsilon(normal, epsilon);
  
          normal = new Cartesian3(1.0, -1.0, 1.0);
          Cartesian3.normalize(normal, normal);
          AttributeCompression.octEncode(normal, encoded);
          expect(AttributeCompression.octDecode(encoded.x, encoded.y, result)).toEqualEpsilon(normal, epsilon);
  
          normal = new Cartesian3(-1.0, -1.0, 1.0);
          Cartesian3.normalize(normal, normal);
          AttributeCompression.octEncode(normal, encoded);
          expect(AttributeCompression.octDecode(encoded.x, encoded.y, result)).toEqualEpsilon(normal, epsilon);
  
          normal = new Cartesian3(-1.0, 1.0, 1.0);
          Cartesian3.normalize(normal, normal);
          AttributeCompression.octEncode(normal, encoded);
          expect(AttributeCompression.octDecode(encoded.x, encoded.y, result)).toEqualEpsilon(normal, epsilon);
  
          normal = new Cartesian3(1.0, 1.0, -1.0);
          Cartesian3.normalize(normal, normal);
          AttributeCompression.octEncode(normal, encoded);
          expect(AttributeCompression.octDecode(encoded.x, encoded.y, result)).toEqualEpsilon(normal, epsilon);
  
          normal = new Cartesian3(1.0, -1.0, -1.0);
          Cartesian3.normalize(normal, normal);
          AttributeCompression.octEncode(normal, encoded);
          expect(AttributeCompression.octDecode(encoded.x, encoded.y, result)).toEqualEpsilon(normal, epsilon);
  
          normal = new Cartesian3(-1.0, 1.0, -1.0);
          Cartesian3.normalize(normal, normal);
          AttributeCompression.octEncode(normal, encoded);
          expect(AttributeCompression.octDecode(encoded.x, encoded.y, result)).toEqualEpsilon(normal, epsilon);
  
          normal = new Cartesian3(-1.0, -1.0, -1.0);
          Cartesian3.normalize(normal, normal);
          AttributeCompression.octEncode(normal, encoded);
          expect(AttributeCompression.octDecode(encoded.x, encoded.y, result)).toEqualEpsilon(normal, epsilon);
      });
  
      it('octFloat encoding', function() {
          var epsilon = CesiumMath.EPSILON1;
  
          var result = new Cartesian3();
          var normal = new Cartesian3(0.0, 0.0, 1.0);
          expect(AttributeCompression.octDecodeFloat(AttributeCompression.octEncodeFloat(normal), result)).toEqualEpsilon(normal, epsilon);
  
          normal = new Cartesian3(0.0, 0.0, -1.0);
          expect(AttributeCompression.octDecodeFloat(AttributeCompression.octEncodeFloat(normal), result)).toEqualEpsilon(normal, epsilon);
  
          normal = new Cartesian3(0.0, 1.0, 0.0);
          expect(AttributeCompression.octDecodeFloat(AttributeCompression.octEncodeFloat(normal), result)).toEqualEpsilon(normal, epsilon);
  
          normal = new Cartesian3(0.0, -1.0, 0.0);
          expect(AttributeCompression.octDecodeFloat(AttributeCompression.octEncodeFloat(normal), result)).toEqualEpsilon(normal, epsilon);
  
          normal = new Cartesian3(1.0, 0.0, 0.0);
          expect(AttributeCompression.octDecodeFloat(AttributeCompression.octEncodeFloat(normal), result)).toEqualEpsilon(normal, epsilon);
  
          normal = new Cartesian3(-1.0, 0.0, 0.0);
          expect(AttributeCompression.octDecodeFloat(AttributeCompression.octEncodeFloat(normal), result)).toEqualEpsilon(normal, epsilon);
  
          normal = new Cartesian3(1.0, 1.0, 1.0);
          Cartesian3.normalize(normal, normal);
          expect(AttributeCompression.octDecodeFloat(AttributeCompression.octEncodeFloat(normal), result)).toEqualEpsilon(normal, epsilon);
  
          normal = new Cartesian3(1.0, -1.0, 1.0);
          Cartesian3.normalize(normal, normal);
          expect(AttributeCompression.octDecodeFloat(AttributeCompression.octEncodeFloat(normal), result)).toEqualEpsilon(normal, epsilon);
  
          normal = new Cartesian3(-1.0, -1.0, 1.0);
          Cartesian3.normalize(normal, normal);
          expect(AttributeCompression.octDecodeFloat(AttributeCompression.octEncodeFloat(normal), result)).toEqualEpsilon(normal, epsilon);
  
          normal = new Cartesian3(-1.0, 1.0, 1.0);
          Cartesian3.normalize(normal, normal);
          expect(AttributeCompression.octDecodeFloat(AttributeCompression.octEncodeFloat(normal), result)).toEqualEpsilon(normal, epsilon);
  
          normal = new Cartesian3(1.0, 1.0, -1.0);
          Cartesian3.normalize(normal, normal);
          expect(AttributeCompression.octDecodeFloat(AttributeCompression.octEncodeFloat(normal), result)).toEqualEpsilon(normal, epsilon);
  
          normal = new Cartesian3(1.0, -1.0, -1.0);
          Cartesian3.normalize(normal, normal);
          expect(AttributeCompression.octDecodeFloat(AttributeCompression.octEncodeFloat(normal), result)).toEqualEpsilon(normal, epsilon);
  
          normal = new Cartesian3(-1.0, 1.0, -1.0);
          Cartesian3.normalize(normal, normal);
          expect(AttributeCompression.octDecodeFloat(AttributeCompression.octEncodeFloat(normal), result)).toEqualEpsilon(normal, epsilon);
  
          normal = new Cartesian3(-1.0, -1.0, -1.0);
          Cartesian3.normalize(normal, normal);
          expect(AttributeCompression.octDecodeFloat(AttributeCompression.octEncodeFloat(normal), result)).toEqualEpsilon(normal, epsilon);
      });
  
      it('octFloat encoding is equivalent to oct encoding', function() {
          var encoded = new Cartesian2();
          var result1 = new Cartesian3();
          var result2 = new Cartesian3();
  
          var normal = new Cartesian3(0.0, 0.0, 1.0);
          AttributeCompression.octEncode(normal, encoded);
          AttributeCompression.octDecode(encoded.x, encoded.y, result1);
          AttributeCompression.octDecodeFloat(AttributeCompression.octEncodeFloat(normal), result2);
          expect(result1).toEqual(result2);
  
          normal = new Cartesian3(0.0, 0.0, -1.0);
          AttributeCompression.octEncode(normal, encoded);
          AttributeCompression.octDecode(encoded.x, encoded.y, result1);
          AttributeCompression.octDecodeFloat(AttributeCompression.octEncodeFloat(normal), result2);
          expect(result1).toEqual(result2);
  
          normal = new Cartesian3(0.0, 1.0, 0.0);
          AttributeCompression.octEncode(normal, encoded);
          AttributeCompression.octDecode(encoded.x, encoded.y, result1);
          AttributeCompression.octDecodeFloat(AttributeCompression.octEncodeFloat(normal), result2);
          expect(result1).toEqual(result2);
  
          normal = new Cartesian3(0.0, -1.0, 0.0);
          AttributeCompression.octEncode(normal, encoded);
          AttributeCompression.octDecode(encoded.x, encoded.y, result1);
          AttributeCompression.octDecodeFloat(AttributeCompression.octEncodeFloat(normal), result2);
          expect(result1).toEqual(result2);
  
          normal = new Cartesian3(1.0, 0.0, 0.0);
          AttributeCompression.octEncode(normal, encoded);
          AttributeCompression.octDecode(encoded.x, encoded.y, result1);
          AttributeCompression.octDecodeFloat(AttributeCompression.octEncodeFloat(normal), result2);
          expect(result1).toEqual(result2);
  
          normal = new Cartesian3(-1.0, 0.0, 0.0);
          AttributeCompression.octEncode(normal, encoded);
          AttributeCompression.octDecode(encoded.x, encoded.y, result1);
          AttributeCompression.octDecodeFloat(AttributeCompression.octEncodeFloat(normal), result2);
          expect(result1).toEqual(result2);
  
          normal = new Cartesian3(1.0, 1.0, 1.0);
          Cartesian3.normalize(normal, normal);
          AttributeCompression.octEncode(normal, encoded);
          AttributeCompression.octDecode(encoded.x, encoded.y, result1);
          AttributeCompression.octDecodeFloat(AttributeCompression.octEncodeFloat(normal), result2);
          expect(result1).toEqual(result2);
  
          normal = new Cartesian3(1.0, -1.0, 1.0);
          Cartesian3.normalize(normal, normal);
          AttributeCompression.octEncode(normal, encoded);
          AttributeCompression.octDecode(encoded.x, encoded.y, result1);
          AttributeCompression.octDecodeFloat(AttributeCompression.octEncodeFloat(normal), result2);
          expect(result1).toEqual(result2);
  
          normal = new Cartesian3(-1.0, -1.0, 1.0);
          Cartesian3.normalize(normal, normal);
          AttributeCompression.octEncode(normal, encoded);
          AttributeCompression.octDecode(encoded.x, encoded.y, result1);
          AttributeCompression.octDecodeFloat(AttributeCompression.octEncodeFloat(normal), result2);
          expect(result1).toEqual(result2);
  
          normal = new Cartesian3(-1.0, 1.0, 1.0);
          Cartesian3.normalize(normal, normal);
          AttributeCompression.octEncode(normal, encoded);
          AttributeCompression.octDecode(encoded.x, encoded.y, result1);
          AttributeCompression.octDecodeFloat(AttributeCompression.octEncodeFloat(normal), result2);
          expect(result1).toEqual(result2);
  
          normal = new Cartesian3(1.0, 1.0, -1.0);
          Cartesian3.normalize(normal, normal);
          AttributeCompression.octEncode(normal, encoded);
          AttributeCompression.octDecode(encoded.x, encoded.y, result1);
          AttributeCompression.octDecodeFloat(AttributeCompression.octEncodeFloat(normal), result2);
          expect(result1).toEqual(result2);
  
          normal = new Cartesian3(1.0, -1.0, -1.0);
          Cartesian3.normalize(normal, normal);
          AttributeCompression.octEncode(normal, encoded);
          AttributeCompression.octDecode(encoded.x, encoded.y, result1);
          AttributeCompression.octDecodeFloat(AttributeCompression.octEncodeFloat(normal), result2);
          expect(result1).toEqual(result2);
  
          normal = new Cartesian3(-1.0, 1.0, -1.0);
          Cartesian3.normalize(normal, normal);
          AttributeCompression.octEncode(normal, encoded);
          AttributeCompression.octDecode(encoded.x, encoded.y, result1);
          AttributeCompression.octDecodeFloat(AttributeCompression.octEncodeFloat(normal), result2);
          expect(result1).toEqual(result2);
  
          normal = new Cartesian3(-1.0, -1.0, -1.0);
          Cartesian3.normalize(normal, normal);
          AttributeCompression.octEncode(normal, encoded);
          AttributeCompression.octDecode(encoded.x, encoded.y, result1);
          AttributeCompression.octDecodeFloat(AttributeCompression.octEncodeFloat(normal), result2);
          expect(result1).toEqual(result2);
      });
  
      it('encodeFloat throws without vector', function() {
          expect(function() {
              AttributeCompression.octEncodeFloat(undefined);
          }).toThrowDeveloperError();
      });
  
      it('decodeFloat throws without value', function() {
          expect(function() {
              AttributeCompression.octDecodeFloat(undefined, new Cartesian3());
          }).toThrowDeveloperError();
      });
  
      it('decodeFloat throws without result', function() {
          expect(function() {
              AttributeCompression.octDecodeFloat(0.0, undefined);
          }).toThrowDeveloperError();
      });
  
      it('encode and packFloat is equivalent to oct encoding', function() {
          var vector = new Cartesian3(1.0, 1.0, 1.0);
          Cartesian3.normalize(vector, vector);
  
          var encoded = AttributeCompression.octEncode(vector, new Cartesian2());
          var encodedFloat = AttributeCompression.octPackFloat(encoded);
          expect(AttributeCompression.octDecodeFloat(encodedFloat, new Cartesian3())).toEqual(AttributeCompression.octDecode(encoded.x, encoded.y, new Cartesian3()));
      });
  
      it('packFloat throws without encoded', function() {
          expect(function() {
              AttributeCompression.octPackFloat(undefined);
          }).toThrowDeveloperError();
      });
  
      it('pack is equivalent to oct encoding', function() {
          var x = Cartesian3.UNIT_X;
          var y = Cartesian3.UNIT_Y;
          var z = Cartesian3.UNIT_Z;
  
          var packed = AttributeCompression.octPack(x, y, z, new Cartesian2());
          var decodedX = new Cartesian3();
          var decodedY = new Cartesian3();
          var decodedZ = new Cartesian3();
          AttributeCompression.octUnpack(packed, decodedX, decodedY, decodedZ);
  
          expect(decodedX).toEqual(AttributeCompression.octDecodeFloat(AttributeCompression.octEncodeFloat(x), new Cartesian3()));
          expect(decodedY).toEqual(AttributeCompression.octDecodeFloat(AttributeCompression.octEncodeFloat(y), new Cartesian3()));
          expect(decodedZ).toEqual(AttributeCompression.octDecodeFloat(AttributeCompression.octEncodeFloat(z), new Cartesian3()));
      });
  
      it('pack throws without v1', function() {
          expect(function() {
              AttributeCompression.octPack(undefined, new Cartesian3(), new Cartesian3(), new Cartesian2());
          }).toThrowDeveloperError();
      });
  
      it('pack throws without v2', function() {
          expect(function() {
              AttributeCompression.octPack(new Cartesian3(), undefined, new Cartesian3(), new Cartesian2());
          }).toThrowDeveloperError();
      });
  
      it('pack throws without v3', function() {
          expect(function() {
              AttributeCompression.octPack(new Cartesian3(), new Cartesian3(), undefined, new Cartesian2());
          }).toThrowDeveloperError();
      });
  
      it('pack throws without result', function() {
          expect(function() {
              AttributeCompression.octPack(new Cartesian3(), new Cartesian3(), new Cartesian3(), undefined);
          }).toThrowDeveloperError();
      });
  
      it('unpack throws without packed', function() {
          expect(function() {
              AttributeCompression.octUnpack(undefined, new Cartesian3(), new Cartesian3(), new Cartesian3());
          }).toThrowDeveloperError();
      });
  
      it('unpack throws without v1', function() {
          expect(function() {
              AttributeCompression.octUnpack(new Cartesian2(), undefined, new Cartesian3(), new Cartesian3());
          }).toThrowDeveloperError();
      });
  
      it('unpack throws without v2', function() {
          expect(function() {
              AttributeCompression.octUnpack(new Cartesian2(), new Cartesian3(), undefined, new Cartesian3());
          }).toThrowDeveloperError();
      });
  
      it('unpack throws without v3', function() {
          expect(function() {
              AttributeCompression.octUnpack(new Cartesian2(), new Cartesian3(), new Cartesian3(), undefined);
          }).toThrowDeveloperError();
      });
  
      it('compresses texture coordinates', function() {
          var coords = new Cartesian2(0.5, 0.5);
          expect(AttributeCompression.decompressTextureCoordinates(AttributeCompression.compressTextureCoordinates(coords), new Cartesian2())).toEqual(coords);
      });
      
      it('compress texture coordinates throws without texture coordinates', function() {
          expect(function() {
              AttributeCompression.compressTextureCoordinates(undefined);
          }).toThrowDeveloperError();
      });
      
      it('decompress texture coordinates throws without encoded texture coordinates', function() {
          expect(function() {
              AttributeCompression.decompressTextureCoordinates(undefined, new Cartesian2());
          }).toThrowDeveloperError();
      });
      
      it('decompress texture coordinates throws without result', function() {
          expect(function() {
              AttributeCompression.decompressTextureCoordinates(0.0, undefined);
          }).toThrowDeveloperError();
      });
  });
  