text: defineSuite([
          'Scene/LabelCollection',
          'Core/BoundingRectangle',
          'Core/BoundingSphere',
          'Core/Cartesian2',
          'Core/Cartesian3',
          'Core/Color',
          'Core/DistanceDisplayCondition',
          'Core/Math',
          'Core/NearFarScalar',
          'Core/Rectangle',
          'Scene/BlendOption',
          'Scene/Globe',
          'Scene/HeightReference',
          'Scene/HorizontalOrigin',
          'Scene/Label',
          'Scene/LabelStyle',
          'Scene/VerticalOrigin',
          'Specs/createGlobe',
          'Specs/createScene'
      ], function(
          LabelCollection,
          BoundingRectangle,
          BoundingSphere,
          Cartesian2,
          Cartesian3,
          Color,
          DistanceDisplayCondition,
          CesiumMath,
          NearFarScalar,
          Rectangle,
          BlendOption,
          Globe,
          HeightReference,
          HorizontalOrigin,
          Label,
          LabelStyle,
          VerticalOrigin,
          createGlobe,
          createScene) {
      'use strict';
  
      // TODO: rendering tests for pixel offset, eye offset, horizontal origin, vertical origin, font, style, outlineColor, outlineWidth, and fillColor properties
  
      var scene;
      var camera;
      var labels;
      var labelsWithHeight;
  
      // This Unicode square block will more reliably cover the center pixel than an 'x' or a 'w' char.
      var solidBox = '\u25a0';
  
      beforeAll(function() {
          scene = createScene();
          camera = scene.camera;
      });
  
      afterAll(function() {
          scene.destroyForSpecs();
      });
  
      beforeEach(function() {
          scene.morphTo3D(0);
  
          camera.position = new Cartesian3(10.0, 0.0, 0.0);
          camera.direction = Cartesian3.negate(Cartesian3.UNIT_X, new Cartesian3());
          camera.up = Cartesian3.clone(Cartesian3.UNIT_Z);
  
          labels = new LabelCollection();
          scene.primitives.add(labels);
      });
  
      afterEach(function() {
          // labels are destroyed by removeAll().
          scene.primitives.removeAll();
      });
  
      it('has default values when adding a label', function() {
          var label = labels.add();
          expect(label.show).toEqual(true);
          expect(label.position).toEqual(Cartesian3.ZERO);
          expect(label.text).toEqual('');
          expect(label.font).toEqual('30px sans-serif');
          expect(label.fillColor).toEqual(Color.WHITE);
          expect(label.outlineColor).toEqual(Color.BLACK);
          expect(label.outlineWidth).toEqual(1);
          expect(label.showBackground).toEqual(false);
          expect(label.backgroundColor).toEqual(new Color(0.165, 0.165, 0.165, 0.8));
          expect(label.backgroundPadding).toEqual(new Cartesian2(7, 5));
          expect(label.style).toEqual(LabelStyle.FILL);
          expect(label.pixelOffset).toEqual(Cartesian2.ZERO);
          expect(label.eyeOffset).toEqual(Cartesian3.ZERO);
          expect(label.heightReference).toEqual(HeightReference.NONE);
          expect(label.horizontalOrigin).toEqual(HorizontalOrigin.LEFT);
          expect(label.verticalOrigin).toEqual(VerticalOrigin.BASELINE);
          expect(label.scale).toEqual(1.0);
          expect(label.id).toBeUndefined();
          expect(label.translucencyByDistance).toBeUndefined();
          expect(label.pixelOffsetScaleByDistance).toBeUndefined();
          expect(label.scaleByDistance).toBeUndefined();
          expect(label.distanceDisplayCondition).toBeUndefined();
          expect(label.disableDepthTestDistance).toBeUndefined();
      });
  
      it('can add a label with specified values', function() {
          var show = false;
          var position = new Cartesian3(1.0, 2.0, 3.0);
          var text = 'abc';
          var font = '24px "Open Sans"';
          var fillColor = {
              red : 2.0,
              green : 3.0,
              blue : 4.0,
              alpha : 1.0
          };
          var outlineColor = {
              red : 3.0,
              green : 4.0,
              blue : 2.0,
              alpha : 1.0
          };
          var outlineWidth = 2;
  
          var style = LabelStyle.FILL_AND_OUTLINE;
          var pixelOffset = new Cartesian2(4.0, 5.0);
          var eyeOffset = new Cartesian3(6.0, 7.0, 8.0);
          var horizontalOrigin = HorizontalOrigin.LEFT;
          var verticalOrigin = VerticalOrigin.BOTTOM;
          var scale = 2.0;
          var showBackground = true;
          var backgroundColor = Color.BLUE;
          var backgroundPadding = new Cartesian2(11, 12);
          var translucency = new NearFarScalar(1.0e4, 1.0, 1.0e6, 0.0);
          var pixelOffsetScale = new NearFarScalar(1.0e4, 1.0, 1.0e6, 0.0);
          var scaleByDistance = new NearFarScalar(1.0e4, 1.0, 1.0e6, 0.0);
          var distanceDisplayCondition = new DistanceDisplayCondition(10.0, 100.0);
          var disableDepthTestDistance = 10.0;
          var label = labels.add({
              show : show,
              position : position,
              text : text,
              font : font,
              fillColor : fillColor,
              outlineColor : outlineColor,
              outlineWidth : outlineWidth,
              style : style,
              showBackground : showBackground,
              backgroundColor : backgroundColor,
              backgroundPadding : backgroundPadding,
              pixelOffset : pixelOffset,
              eyeOffset : eyeOffset,
              horizontalOrigin : horizontalOrigin,
              verticalOrigin : verticalOrigin,
              scale : scale,
              id : 'id',
              translucencyByDistance : translucency,
              pixelOffsetScaleByDistance : pixelOffsetScale,
              scaleByDistance : scaleByDistance,
              distanceDisplayCondition : distanceDisplayCondition,
              disableDepthTestDistance : disableDepthTestDistance
          });
  
          expect(label.show).toEqual(show);
          expect(label.position).toEqual(position);
          expect(label.text).toEqual(text);
          expect(label.font).toEqual(font);
          expect(label.fillColor).toEqual(fillColor);
          expect(label.outlineColor).toEqual(outlineColor);
          expect(label.outlineWidth).toEqual(outlineWidth);
          expect(label.style).toEqual(style);
          expect(label.showBackground).toEqual(showBackground);
          expect(label.backgroundColor).toEqual(backgroundColor);
          expect(label.backgroundPadding).toEqual(backgroundPadding);
          expect(label.pixelOffset).toEqual(pixelOffset);
          expect(label.eyeOffset).toEqual(eyeOffset);
          expect(label.horizontalOrigin).toEqual(horizontalOrigin);
          expect(label.verticalOrigin).toEqual(verticalOrigin);
          expect(label.scale).toEqual(scale);
          expect(label.id).toEqual('id');
          expect(label.translucencyByDistance).toEqual(translucency);
          expect(label.pixelOffsetScaleByDistance).toEqual(pixelOffsetScale);
          expect(label.scaleByDistance).toEqual(scaleByDistance);
          expect(label.distanceDisplayCondition).toEqual(distanceDisplayCondition);
          expect(label.disableDepthTestDistance).toEqual(disableDepthTestDistance);
      });
  
      it('can specify font using units other than pixels', function() {
          var label = labels.add({
              font : '12pt "Open Sans"',
              text : 'Hello there'
          });
          scene.renderForSpecs();
  
          var dimensions = label._glyphs[0].dimensions;
          expect(dimensions.height).toBeGreaterThan(0);
      });
  
      it('has zero labels when constructed', function() {
          expect(labels.length).toEqual(0);
      });
  
      it('can add a label', function() {
          var label = labels.add();
  
          expect(labels.length).toEqual(1);
          expect(labels.get(0)).toBe(label);
      });
  
      it('can remove the first label', function() {
          var one = labels.add();
          var two = labels.add();
  
          expect(labels.contains(one)).toEqual(true);
          expect(labels.contains(two)).toEqual(true);
  
          expect(labels.remove(one)).toEqual(true);
  
          expect(labels.contains(one)).toEqual(false);
          expect(labels.contains(two)).toEqual(true);
      });
  
      it('can remove the last label', function() {
          var one = labels.add();
          var two = labels.add();
  
          expect(labels.contains(one)).toEqual(true);
          expect(labels.contains(two)).toEqual(true);
  
          expect(labels.remove(two)).toEqual(true);
  
          expect(labels.contains(one)).toEqual(true);
          expect(labels.contains(two)).toEqual(false);
      });
  
      it('returns false when removing undefined', function() {
          labels.add();
          expect(labels.length).toEqual(1);
          expect(labels.remove(undefined)).toEqual(false);
          expect(labels.length).toEqual(1);
      });
  
      it('returns false when removing a previously removed label', function() {
          var label = labels.add();
          expect(labels.length).toEqual(1);
          expect(labels.remove(label)).toEqual(true);
          expect(labels.remove(label)).toEqual(false);
          expect(labels.length).toEqual(0);
      });
  
      it('is not destroyed', function() {
          expect(labels.isDestroyed()).toEqual(false);
      });
  
      it('can add and remove multiple labels', function() {
          var one = labels.add();
          var two = labels.add();
          var three = labels.add();
  
          expect(labels.remove(one)).toEqual(true);
          expect(labels.remove(two)).toEqual(true);
  
          expect(one.isDestroyed()).toEqual(true);
          expect(two.isDestroyed()).toEqual(true);
          expect(three.isDestroyed()).toEqual(false);
  
          expect(labels.contains(one)).toEqual(false);
          expect(labels.contains(two)).toEqual(false);
          expect(labels.contains(three)).toEqual(true);
  
          expect(labels.length).toEqual(1);
          expect(labels.get(0)).toBe(three);
  
          var four = labels.add();
          expect(labels.length).toEqual(2);
          expect(labels.get(0)).toBe(three);
          expect(labels.get(1)).toBe(four);
          expect(labels.contains(three)).toEqual(true);
          expect(labels.contains(four)).toEqual(true);
      });
  
      it('can remove all labels', function() {
          labels.add({
              position : new Cartesian3(1.0, 2.0, 3.0)
          });
          labels.add({
              position : new Cartesian3(4.0, 5.0, 6.0)
          });
          expect(labels.length).toEqual(2);
  
          labels.removeAll();
          expect(labels.length).toEqual(0);
      });
  
      it('can check if it contains a label', function() {
          var label = labels.add();
  
          expect(labels.contains(label)).toEqual(true);
      });
  
      it('returns false when checking if it contains a label it does not contain', function() {
          var label = labels.add();
          labels.remove(label);
  
          expect(labels.contains(label)).toEqual(false);
      });
  
      it('does not contain undefined', function() {
          expect(labels.contains(undefined)).toEqual(false);
      });
  
      it('does not contain random other objects', function() {
          expect(labels.contains({})).toEqual(false);
          expect(labels.contains(new Cartesian2())).toEqual(false);
      });
  
      it('does not render when constructed', function() {
          expect(scene).toRender([0, 0, 0, 255]);
      });
  
      it('can render after modifying and removing a label', function() {
          var labelOne = labels.add({
              position : Cartesian3.ZERO,
              text : 'x',
              horizontalOrigin : HorizontalOrigin.CENTER,
              verticalOrigin : VerticalOrigin.CENTER
          });
          labels.add({
              position : Cartesian3.ZERO,
              text : 'o',
              horizontalOrigin : HorizontalOrigin.CENTER,
              verticalOrigin : VerticalOrigin.CENTER
          });
  
          expect(scene).toRenderAndCall(function(rgba) {
              expect(rgba[0]).toBeGreaterThan(10);
          });
  
          labelOne.scale = 2.0;
          labels.remove(labelOne);
  
          expect(scene).toRender([0, 0, 0, 255]);
      });
  
      it('can render a label', function() {
          labels.add({
              position : Cartesian3.ZERO,
              text : 'x',
              horizontalOrigin : HorizontalOrigin.CENTER,
              verticalOrigin : VerticalOrigin.CENTER
          });
  
          expect(scene).toRenderAndCall(function(rgba) {
              expect(rgba[0]).toBeGreaterThan(10);
          });
      });
  
      it('renders in multiple passes', function() {
          labels.add({
              position : Cartesian3.ZERO,
              text : 'x',
              horizontalOrigin : HorizontalOrigin.CENTER,
              verticalOrigin : VerticalOrigin.CENTER
          });
          camera.position = new Cartesian3(2.0, 0.0, 0.0);
  
          var frameState = scene.frameState;
          frameState.commandList.length = 0;
          labels.blendOption = BlendOption.OPAQUE_AND_TRANSLUCENT;
          labels.update(frameState);
          expect(frameState.commandList.length).toEqual(2);
  
          frameState.commandList.length = 0;
          labels.blendOption = BlendOption.OPAQUE;
          labels.update(frameState);
          expect(frameState.commandList.length).toEqual(1);
  
          frameState.commandList.length = 0;
          labels.blendOption = BlendOption.TRANSLUCENT;
          labels.update(frameState);
          expect(frameState.commandList.length).toEqual(1);
      });
  
      it('can render after adding a label', function() {
          labels.add({
              position : Cartesian3.ZERO,
              text : solidBox,
              horizontalOrigin : HorizontalOrigin.CENTER,
              verticalOrigin : VerticalOrigin.CENTER
          });
  
          expect(scene).toRenderAndCall(function(rgba) {
              expect(rgba[0]).toBeGreaterThan(200);
              expect(rgba[1]).toBeGreaterThan(200);
              expect(rgba[2]).toBeGreaterThan(200);
          });
  
          labels.add({
              position : new Cartesian3(1.0, 0.0, 0.0), // Closer to camera
              text : solidBox,
              fillColor : {
                  red : 1.0,
                  green : 0.0,
                  blue : 0.0,
                  alpha : 1.0
              },
              horizontalOrigin : HorizontalOrigin.CENTER,
              verticalOrigin : VerticalOrigin.CENTER
          });
  
          expect(scene).toRenderAndCall(function(rgba) {
              expect(rgba[0]).toBeGreaterThan(200);
              expect(rgba[1]).toBeLessThan(10);
              expect(rgba[2]).toBeLessThan(10);
          });
      });
  
      it('can render after removing a label', function() {
          var label = labels.add({
              position : Cartesian3.ZERO,
              text : 'x',
              horizontalOrigin : HorizontalOrigin.CENTER,
              verticalOrigin : VerticalOrigin.CENTER
          });
  
          expect(scene).toRenderAndCall(function(rgba) {
              expect(rgba[0]).toBeGreaterThan(10);
          });
  
          labels.remove(label);
          expect(scene).toRender([0, 0, 0, 255]);
      });
  
      it('can render after removing and adding a label', function() {
          var label = labels.add({
              position : Cartesian3.ZERO,
              text : 'x',
              horizontalOrigin : HorizontalOrigin.CENTER,
              verticalOrigin : VerticalOrigin.CENTER
          });
  
          expect(scene).toRenderAndCall(function(rgba) {
              expect(rgba[0]).toBeGreaterThan(10);
          });
  
          labels.remove(label);
          labels.add({
              position : Cartesian3.ZERO,
              text : 'x',
              horizontalOrigin : HorizontalOrigin.CENTER,
              verticalOrigin : VerticalOrigin.CENTER
          });
  
          expect(scene).toRenderAndCall(function(rgba) {
              expect(rgba[0]).toBeGreaterThan(10);
          });
      });
  
      it('can render after removing all labels', function() {
          labels.add({
              position : Cartesian3.ZERO,
              text : 'x',
              horizontalOrigin : HorizontalOrigin.CENTER,
              verticalOrigin : VerticalOrigin.CENTER
          });
  
          expect(scene).toRenderAndCall(function(rgba) {
              expect(rgba[0]).toBeGreaterThan(10);
          });
  
          labels.removeAll();
          expect(scene).toRender([0, 0, 0, 255]);
      });
  
      it('can render after removing all labels and adding a label', function() {
          labels.add({
              position : Cartesian3.ZERO,
              text : 'x',
              horizontalOrigin : HorizontalOrigin.CENTER,
              verticalOrigin : VerticalOrigin.CENTER
          });
  
          expect(scene).toRenderAndCall(function(rgba) {
              expect(rgba[0]).toBeGreaterThan(10);
          });
  
          labels.removeAll();
          labels.add({
              position : Cartesian3.ZERO,
              text : 'x',
              horizontalOrigin : HorizontalOrigin.CENTER,
              verticalOrigin : VerticalOrigin.CENTER
          });
  
          expect(scene).toRenderAndCall(function(rgba) {
              expect(rgba[0]).toBeGreaterThan(10);
          });
      });
  
      it('can render a label background', function() {
          var label = labels.add({
              position : Cartesian3.ZERO,
              text : '_',
              horizontalOrigin : HorizontalOrigin.CENTER,
              verticalOrigin : VerticalOrigin.CENTER,
              showBackground : true,
              backgroundColor : Color.BLUE
          });
  
          expect(scene).toRender([0, 0, 255, 255]);
  
          labels.remove(label);
          expect(scene).toRender([0, 0, 0, 255]);
      });
  
      it('does not render labels with show set to false', function() {
          var label = labels.add({
              position : Cartesian3.ZERO,
              text : 'x',
              horizontalOrigin : HorizontalOrigin.CENTER,
              verticalOrigin : VerticalOrigin.CENTER
          });
  
          expect(scene).toRenderAndCall(function(rgba) {
              expect(rgba[0]).toBeGreaterThan(10);
          });
  
          label.show = false;
          expect(scene).toRender([0, 0, 0, 255]);
  
          label.show = true;
          expect(scene).toRenderAndCall(function(rgba) {
              expect(rgba[0]).toBeGreaterThan(10);
          });
      });
  
      it('does not render label background with show set to false', function() {
          var label = labels.add({
              position : Cartesian3.ZERO,
              text : '_',
              horizontalOrigin : HorizontalOrigin.CENTER,
              verticalOrigin : VerticalOrigin.CENTER,
              showBackground : true,
              backgroundColor : Color.BLUE
          });
  
          expect(scene).toRender([0, 0, 255, 255]);
  
          label.show = false;
          expect(scene).toRender([0, 0, 0, 255]);
  
          label.show = true;
          expect(scene).toRender([0, 0, 255, 255]);
      });
  
      it('does not render labels that are behind the viewer', function() {
          var label = labels.add({
              position : Cartesian3.ZERO, // in front of the camera
              text : 'x',
              horizontalOrigin : HorizontalOrigin.CENTER,
              verticalOrigin : VerticalOrigin.CENTER
          });
  
          expect(scene).toRenderAndCall(function(rgba) {
              expect(rgba[0]).toBeGreaterThan(10);
          });
  
          label.position = new Cartesian3(20.0, 0.0, 0.0); // Behind camera
          expect(scene).toRender([0, 0, 0, 255]);
  
          label.position = new Cartesian3(1.0, 0.0, 0.0);  // Back in front of camera
          expect(scene).toRenderAndCall(function(rgba) {
              expect(rgba[0]).toBeGreaterThan(10);
          });
      });
  
      it('does not render labels with a scale of zero', function() {
          var label = labels.add({
              position : Cartesian3.ZERO,
              text : 'x',
              horizontalOrigin : HorizontalOrigin.CENTER,
              verticalOrigin : VerticalOrigin.CENTER
          });
  
          label.scale = 0.0;
          expect(scene).toRender([0, 0, 0, 255]);
  
          label.scale = 2.0;
          expect(scene).toRenderAndCall(function(rgba) {
              expect(rgba[0]).toBeGreaterThan(10);
          });
      });
  
      it('renders label with translucencyByDistance', function() {
          labels.add({
              position : Cartesian3.ZERO,
              text : 'x',
              horizontalOrigin : HorizontalOrigin.CENTER,
              verticalOrigin : VerticalOrigin.CENTER,
              translucencyByDistance: new NearFarScalar(2.0, 1.0, 4.0, 0.0)
          });
  
          camera.position = new Cartesian3(2.0, 0.0, 0.0);
          expect(scene).toRenderAndCall(function(rgba) {
              expect(rgba[0]).toBeGreaterThan(10);
          });
  
          camera.position = new Cartesian3(4.0, 0.0, 0.0);
          expect(scene).toRender([0, 0, 0, 255]);
      });
  
      it('renders label with pixelOffsetScaleByDistance', function() {
          labels.add({
              position : Cartesian3.ZERO,
              pixelOffset : new Cartesian2(1.0, 0.0),
              text : solidBox,
              horizontalOrigin : HorizontalOrigin.CENTER,
              verticalOrigin : VerticalOrigin.CENTER,
              pixelOffsetScaleByDistance: new NearFarScalar(2.0, 0.0, 4.0, 1000.0)
          });
  
          camera.position = new Cartesian3(2.0, 0.0, 0.0);
          expect(scene).toRenderAndCall(function(rgba) {
              expect(rgba[0]).toBeGreaterThan(10);
          });
  
          camera.position = new Cartesian3(4.0, 0.0, 0.0);
          expect(scene).toRender([0, 0, 0, 255]);
      });
  
      it('renders label with scaleByDistance', function() {
          labels.add({
              position : Cartesian3.ZERO,
              text : solidBox,
              horizontalOrigin : HorizontalOrigin.CENTER,
              verticalOrigin : VerticalOrigin.CENTER,
              scaleByDistance: new NearFarScalar(2.0, 1.0, 4.0, 0.0)
          });
  
          camera.position = new Cartesian3(2.0, 0.0, 0.0);
          expect(scene).toRender([255, 255, 255, 255]);
  
          camera.position = new Cartesian3(4.0, 0.0, 0.0);
          expect(scene).toRender([0, 0, 0, 255]);
      });
  
      it('renders label with distanceDisplayCondition', function() {
          labels.add({
              position : Cartesian3.ZERO,
              text : solidBox,
              distanceDisplayCondition : new DistanceDisplayCondition(10.0, 100.0),
              horizontalOrigin : HorizontalOrigin.CENTER,
              verticalOrigin : VerticalOrigin.CENTER
          });
  
          camera.position = new Cartesian3(200.0, 0.0, 0.0);
          expect(scene).toRender([0, 0, 0, 255]);
  
          camera.position = new Cartesian3(50.0, 0.0, 0.0);
          expect(scene).toRenderAndCall(function(rgba) {
              expect(rgba[0]).toBeGreaterThan(200);
              expect(rgba[1]).toBeGreaterThan(200);
              expect(rgba[2]).toBeGreaterThan(200);
          });
  
          camera.position = new Cartesian3(5.0, 0.0, 0.0);
          expect(scene).toRender([0, 0, 0, 255]);
      });
  
      it('throws new label with invalid distanceDisplayCondition (near >= far)', function() {
          var dc = new DistanceDisplayCondition(100.0, 10.0);
          expect(function() {
              labels.add({
                  distanceDisplayCondition : dc
              });
          }).toThrowDeveloperError();
      });
  
      it('throws distanceDisplayCondition with near >= far', function() {
          var l = labels.add();
          var dc = new DistanceDisplayCondition(100.0, 10.0);
          expect(function() {
              l.distanceDisplayCondition = dc;
          }).toThrowDeveloperError();
      });
  
      it('renders with disableDepthTestDistance', function() {
          var l = labels.add({
              position : new Cartesian3(-1.0, 0.0, 0.0),
              text : solidBox,
              fillColor : Color.LIME,
              horizontalOrigin : HorizontalOrigin.CENTER,
              verticalOrigin : VerticalOrigin.CENTER
          });
          labels.add({
              position : Cartesian3.ZERO,
              text : solidBox,
              fillColor : Color.BLUE,
              horizontalOrigin : HorizontalOrigin.CENTER,
              verticalOrigin : VerticalOrigin.CENTER
          });
  
          expect(scene).toRender([0, 0, 255, 255]);
  
          l.disableDepthTestDistance = Number.POSITIVE_INFINITY;
          expect(scene).toRender([0, 255, 0, 255]);
      });
  
      it('throws with new label with disableDepthTestDistance less than 0.0', function() {
          expect(function() {
              labels.add({
                  disableDepthTestDistance : -1.0
              });
          }).toThrowDeveloperError();
      });
  
      it('throws with disableDepthTestDistance set less than 0.0', function() {
          var l = labels.add();
          expect(function() {
              l.disableDepthTestDistance = -1.0;
          }).toThrowDeveloperError();
      });
  
      it('can pick a label', function() {
          var label = labels.add({
              position : Cartesian3.ZERO,
              text : solidBox,
              horizontalOrigin : HorizontalOrigin.CENTER,
              verticalOrigin : VerticalOrigin.CENTER,
              id : 'id'
          });
  
          expect(scene).toPickAndCall(function(result) {
              expect(result.primitive).toEqual(label);
              expect(result.id).toEqual('id');
          });
      });
  
      it('can change pick id', function() {
          var label = labels.add({
              position : Cartesian3.ZERO,
              text : solidBox,
              horizontalOrigin : HorizontalOrigin.CENTER,
              verticalOrigin : VerticalOrigin.CENTER,
              id : 'id'
          });
  
          expect(scene).toPickAndCall(function(result) {
              expect(result.primitive).toEqual(label);
              expect(result.id).toEqual('id');
          });
  
          label.id = 'id2';
  
          expect(scene).toPickAndCall(function(result) {
              expect(result.primitive).toEqual(label);
              expect(result.id).toEqual('id2');
          });
      });
  
      it('does not pick a label with show set to false', function() {
          labels.add({
              show : false,
              position : Cartesian3.ZERO,
              text : solidBox,
              horizontalOrigin : HorizontalOrigin.CENTER,
              verticalOrigin : VerticalOrigin.CENTER
          });
  
          expect(scene).notToPick();
      });
  
      it('picks a label using translucencyByDistance', function() {
          var label = labels.add({
              position : Cartesian3.ZERO,
              text : solidBox,
              horizontalOrigin : HorizontalOrigin.CENTER,
              verticalOrigin : VerticalOrigin.CENTER
          });
  
          var translucency = new NearFarScalar(1.0, 0.9, 3.0e9, 0.8);
          label.translucencyByDistance = translucency;
  
          expect(scene).toPickPrimitive(label);
  
          translucency.nearValue = 0.0;
          translucency.farValue = 0.0;
          label.translucencyByDistance = translucency;
  
          expect(scene).notToPick();
      });
  
      it('picks a label using pixelOffsetScaleByDistance', function() {
          var label = labels.add({
              position : Cartesian3.ZERO,
              pixelOffset : new Cartesian2(0.0, 100.0),
              text : solidBox,
              horizontalOrigin : HorizontalOrigin.CENTER,
              verticalOrigin : VerticalOrigin.CENTER
          });
  
          var pixelOffsetScale = new NearFarScalar(1.0, 0.0, 3.0e9, 0.0);
          label.pixelOffsetScaleByDistance = pixelOffsetScale;
  
          expect(scene).toPickPrimitive(label);
  
          pixelOffsetScale.nearValue = 10.0;
          pixelOffsetScale.farValue = 10.0;
          label.pixelOffsetScaleByDistance = pixelOffsetScale;
  
          expect(scene).notToPick();
      });
  
      it('throws when calling get without an index', function() {
          expect(function() {
              labels.get();
          }).toThrowDeveloperError();
      });
  
      it('should reuse canvases for letters, but only if other settings are the same', function() {
          labels.add({
              text : 'a'
          });
          scene.renderForSpecs();
          expect(Object.keys(labels._glyphTextureCache).length).toEqual(1);
  
          labels.add({
              text : 'a'
          });
          scene.renderForSpecs();
          expect(Object.keys(labels._glyphTextureCache).length).toEqual(1);
  
          labels.add({
              text : 'abcd'
          });
          scene.renderForSpecs();
          expect(Object.keys(labels._glyphTextureCache).length).toEqual(4);
  
          labels.add({
              text : 'abc'
          });
          scene.renderForSpecs();
          expect(Object.keys(labels._glyphTextureCache).length).toEqual(4);
  
          var label = labels.add({
              text : 'de'
          });
          scene.renderForSpecs();
          expect(Object.keys(labels._glyphTextureCache).length).toEqual(5);
  
          var originalFont = label.font;
          label.font = '30px "Open Sans"';
          expect(label.font).not.toEqual(originalFont); // otherwise this test needs fixing.
          scene.renderForSpecs();
          expect(Object.keys(labels._glyphTextureCache).length).toEqual(7);
  
          label.style = LabelStyle.OUTLINE;
          scene.renderForSpecs();
          expect(Object.keys(labels._glyphTextureCache).length).toEqual(9);
  
          label.fillColor = new Color(1.0, 165.0 / 255.0, 0.0, 1.0);
          scene.renderForSpecs();
          expect(Object.keys(labels._glyphTextureCache).length).toEqual(11);
  
          label.outlineColor = new Color(1.0, 1.0, 1.0, 1.0);
          scene.renderForSpecs();
          expect(Object.keys(labels._glyphTextureCache).length).toEqual(13);
  
          // vertical origin only affects glyph positions, not glyphs themselves.
          label.verticalOrigin = VerticalOrigin.CENTER;
          scene.renderForSpecs();
          expect(Object.keys(labels._glyphTextureCache).length).toEqual(13);
          label.verticalOrigin = VerticalOrigin.TOP;
          scene.renderForSpecs();
          expect(Object.keys(labels._glyphTextureCache).length).toEqual(13);
  
          //even though we're resetting to the original font, other properties used to create the id have changed
          label.font = originalFont;
          scene.renderForSpecs();
          expect(Object.keys(labels._glyphTextureCache).length).toEqual(15);
  
          //Changing thickness requires new glyphs
          label.outlineWidth = 3;
          scene.renderForSpecs();
          expect(Object.keys(labels._glyphTextureCache).length).toEqual(17);
      });
  
      it('should reuse billboards that are not needed any more', function() {
          var label = labels.add({
              text : 'abc'
          });
          scene.renderForSpecs();
          expect(labels._billboardCollection.length).toEqual(3);
          expect(labels._spareBillboards.length).toEqual(0);
  
          label.text = 'a';
          scene.renderForSpecs();
          expect(labels._billboardCollection.length).toEqual(3);
          expect(labels._spareBillboards.length).toEqual(2);
  
          label.text = 'def';
          scene.renderForSpecs();
          expect(labels._billboardCollection.length).toEqual(3);
          expect(labels._spareBillboards.length).toEqual(0);
      });
  
      it('should not reuse background billboards that are not needed any more', function() {
          var label = labels.add({
              text : 'abc',
              showBackground : true
          });
          scene.renderForSpecs();
          expect(labels._backgroundBillboardCollection.length).toEqual(1);
  
          label.showBackground = false;
          scene.renderForSpecs();
          expect(labels._backgroundBillboardCollection.length).toEqual(0);
  
          label.showBackground = true;
          scene.renderForSpecs();
          expect(labels._backgroundBillboardCollection.length).toEqual(1);
      });
  
      describe('Label', function() {
          it('can set properties after being added', function() {
              var label = labels.add();
  
              var show = false;
              var position = new Cartesian3(1.0, 2.0, 3.0);
              var text = 'abc';
              var font = '24px "Open Sans"';
              var fillColor = {
                  red : 2.0,
                  green : 3.0,
                  blue : 4.0,
                  alpha : 1.0
              };
              var outlineColor = {
                  red : 3.0,
                  green : 4.0,
                  blue : 2.0,
                  alpha : 1.0
              };
              var outlineWidth = 2;
              var style = LabelStyle.FILL_AND_OUTLINE;
              var pixelOffset = new Cartesian2(4.0, 5.0);
              var eyeOffset = new Cartesian3(6.0, 7.0, 8.0);
              var horizontalOrigin = HorizontalOrigin.LEFT;
              var verticalOrigin = VerticalOrigin.BOTTOM;
              var scale = 2.0;
              var translucency = new NearFarScalar(1.0e4, 1.0, 1.0e6, 0.0);
              var pixelOffsetScale = new NearFarScalar(1.0e4, 1.0, 1.0e6, 0.0);
              var scaleByDistance = new NearFarScalar(1.0e4, 1.0, 1.0e6, 0.0);
  
              label.show = show;
              label.position = position;
              label.text = text;
              label.font = font;
              label.fillColor = fillColor;
              label.outlineColor = outlineColor;
              label.outlineWidth = outlineWidth;
              label.style = style;
              label.pixelOffset = pixelOffset;
              label.eyeOffset = eyeOffset;
              label.horizontalOrigin = horizontalOrigin;
              label.verticalOrigin = verticalOrigin;
              label.scale = scale;
              label.translucencyByDistance = translucency;
              label.pixelOffsetScaleByDistance = pixelOffsetScale;
              label.scaleByDistance = scaleByDistance;
  
              expect(label.show).toEqual(show);
              expect(label.position).toEqual(position);
              expect(label.text).toEqual(text);
              expect(label.font).toEqual(font);
              expect(label.fillColor).toEqual(fillColor);
              expect(label.outlineColor).toEqual(outlineColor);
              expect(label.outlineWidth).toEqual(outlineWidth);
              expect(label.style).toEqual(style);
              expect(label.pixelOffset).toEqual(pixelOffset);
              expect(label.eyeOffset).toEqual(eyeOffset);
              expect(label.horizontalOrigin).toEqual(horizontalOrigin);
              expect(label.verticalOrigin).toEqual(verticalOrigin);
              expect(label.scale).toEqual(scale);
              expect(label.translucencyByDistance).toEqual(translucency);
              expect(label.pixelOffsetScaleByDistance).toEqual(pixelOffsetScale);
              expect(label.scaleByDistance).toEqual(scaleByDistance);
          });
  
          it('is destroyed after being removed', function() {
              var label = labels.add();
  
              expect(label.isDestroyed()).toEqual(false);
  
              labels.remove(label);
  
              expect(label.isDestroyed()).toEqual(true);
          });
  
          it('throws after being removed', function() {
              var label = labels.add();
              labels.remove(label);
              expect(function() {
                  label.equals(label);
              }).toThrowDeveloperError();
          });
  
          it('can compute screen space position', function() {
              labels.clampToPixel = false;
              var label = labels.add({
                  text : 'abc',
                  position : Cartesian3.ZERO
              });
              scene.renderForSpecs();
              expect(label.computeScreenSpacePosition(scene)).toEqualEpsilon(new Cartesian2(0.5, 0.5), CesiumMath.EPSILON1);
          });
  
          it('stores screen space position in a result', function() {
              labels.clampToPixel = false;
              var label = labels.add({
                  text : 'abc',
                  position : Cartesian3.ZERO
              });
              var result = new Cartesian2();
              scene.renderForSpecs();
              var actual = label.computeScreenSpacePosition(scene, result);
              expect(actual).toEqual(result);
              expect(result).toEqualEpsilon(new Cartesian2(0.5, 0.5), CesiumMath.EPSILON1);
          });
  
          it('can compute screen space position with pixelOffset', function() {
              labels.clampToPixel = false;
              var label = labels.add({
                  text : 'abc',
                  position : Cartesian3.ZERO,
                  pixelOffset : new Cartesian2(0.5, 0.5)
              });
              scene.renderForSpecs();
              expect(label.computeScreenSpacePosition(scene)).toEqualEpsilon(new Cartesian2(1.0, 1.0), CesiumMath.EPSILON1);
          });
  
          it('can compute screen space position with eyeOffset', function() {
              labels.clampToPixel = false;
              var label = labels.add({
                  text : 'abc',
                  position : Cartesian3.ZERO,
                  eyeOffset : new Cartesian3(1.0, 1.0, 0.0)
              });
              scene.renderForSpecs();
              expect(label.computeScreenSpacePosition(scene)).toEqualEpsilon(new Cartesian2(0.5, 0.5), CesiumMath.EPSILON1);
          });
  
          it('computes screen space bounding box', function() {
              var scale = 1.5;
  
              var label = labels.add({
                  text : 'abc',
                  scale : scale
              });
              scene.renderForSpecs();
  
              var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO);
              expect(bbox.x).toBeDefined();
              expect(bbox.y).toBeDefined();
              expect(bbox.width).toBeGreaterThan(30);
              expect(bbox.width).toBeLessThan(200);
              expect(bbox.height).toBeGreaterThan(10);
              expect(bbox.height).toBeLessThan(50);
          });
  
          it('computes screen space bounding box with result', function() {
              var scale = 1.5;
  
              var label = labels.add({
                  text : 'abc',
                  scale : scale
              });
              scene.renderForSpecs();
  
              var result = new BoundingRectangle();
              var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO, result);
              expect(bbox.x).toBeDefined();
              expect(bbox.y).toBeDefined();
              expect(bbox.width).toBeGreaterThan(30);
              expect(bbox.width).toBeLessThan(200);
              expect(bbox.height).toBeGreaterThan(10);
              expect(bbox.height).toBeLessThan(50);
              expect(bbox).toBe(result);
          });
  
          it('computes screen space bounding box with vertical origin center', function() {
              var scale = 1.5;
  
              var label = labels.add({
                  text : 'abc',
                  scale : scale,
                  verticalOrigin : VerticalOrigin.CENTER
              });
              scene.renderForSpecs();
  
              var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO);
              expect(bbox.y).toBeGreaterThan(bbox.height * -0.9);
              expect(bbox.y).toBeLessThan(bbox.height * -0.3);
          });
  
          it('computes screen space bounding box with vertical origin top', function() {
              var scale = 1.5;
  
              var label = labels.add({
                  text : 'abc',
                  scale : scale,
                  verticalOrigin : VerticalOrigin.TOP
              });
              scene.renderForSpecs();
  
              var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO);
              expect(bbox.y).toBeLessThan(5);
              expect(bbox.y).toBeGreaterThan(-5);
          });
  
          it('computes screen space bounding box with vertical origin baseline', function() {
              var scale = 1.5;
  
              var label = labels.add({
                  text : 'abc',
                  scale : scale,
                  verticalOrigin : VerticalOrigin.BASELINE
              });
              scene.renderForSpecs();
  
              var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO);
              expect(bbox.y).toBeLessThan(bbox.height * -0.8);
              expect(bbox.y).toBeGreaterThan(bbox.height * -1.2);
          });
  
          it('computes screen space bounding box with horizontal origin', function() {
              var scale = 1.5;
  
              var label = labels.add({
                  text : 'abc',
                  scale : scale,
                  horizontalOrigin : HorizontalOrigin.CENTER
              });
              scene.renderForSpecs();
  
              var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO);
              expect(bbox.x).toBeLessThan(bbox.width * -0.3);
              expect(bbox.x).toBeGreaterThan(bbox.width * -0.7);
  
              label.horizontalOrigin = HorizontalOrigin.RIGHT;
              scene.renderForSpecs();
              bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO);
              expect(bbox.x).toBeLessThan(bbox.width * -0.8);
              expect(bbox.x).toBeGreaterThan(bbox.width * -1.2);
          });
  
          it('computes screen space bounding box with padded background', function() {
              var scale = 1.5;
  
              var label = labels.add({
                  text : 'abc',
                  scale : scale,
                  showBackground : true,
                  backgroundPadding : new Cartesian2(15, 10)
              });
              scene.renderForSpecs();
  
              var backgroundBillboard = label._backgroundBillboard;
              var width = backgroundBillboard.width * scale;
              var height = backgroundBillboard.height * scale;
              var x = backgroundBillboard._translate.x;
              var y = -(backgroundBillboard._translate.y + height);
  
              var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO);
              expect(bbox.x).toEqual(x);
              expect(bbox.y).toEqual(y);
              expect(bbox.width).toEqual(width);
              expect(bbox.height).toEqual(height);
  
              label.verticalOrigin = VerticalOrigin.CENTER;
              scene.renderForSpecs();
              y = -(backgroundBillboard._translate.y + height * 0.5);
  
              bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO);
              expect(bbox.x).toEqual(x);
              expect(bbox.y).toEqual(y);
              expect(bbox.width).toEqual(width);
              expect(bbox.height).toEqual(height);
          });
  
          it('can equal another label', function() {
              var label = labels.add({
                  position : new Cartesian3(1.0, 2.0, 3.0),
                  text : 'equals'
              });
              var otherLabel = labels.add({
                  position : new Cartesian3(1.0, 2.0, 3.0),
                  text : 'equals'
              });
  
              // This tests the `LabelCollection.equals` function itself, not simple equality.
              expect(label.equals(otherLabel)).toEqual(true);
          });
  
          it('can differ from another label', function() {
              var label = labels.add({
                  position : new Cartesian3(1.0, 2.0, 3.0)
              });
              var otherLabel = labels.add({
                  position : new Cartesian3(4.0, 5.0, 6.0)
              });
  
              // This tests the `LabelCollection.equals` function itself, not simple equality.
              expect(label.equals(otherLabel)).toEqual(false);
          });
  
          it('does not equal undefined', function() {
              // This tests the `LabelCollection.equals` function itself, not simple equality.
              var label = labels.add();
              expect(label.equals(undefined)).toEqual(false);
          });
  
          it('should have a number of glyphs equal to the number of characters', function() {
              var label = labels.add({
                  text : 'abc'
              });
              scene.renderForSpecs();
              expect(label._glyphs.length).toEqual(3);
  
              label.text = 'abcd';
              scene.renderForSpecs();
              expect(label._glyphs.length).toEqual(4);
  
              label.text = '';
              scene.renderForSpecs();
              expect(label._glyphs.length).toEqual(0);
  
              label = labels.add();
              scene.renderForSpecs();
              expect(label._glyphs.length).toEqual(0);
          });
  
          it('does not create billboards for spaces', function() {
              var label = labels.add({
                  text : 'abc'
              });
              scene.renderForSpecs();
              expect(label._glyphs.length).toEqual(3);
              expect(labels._billboardCollection.length).toEqual(3);
  
              label.text = ' ab c';
              scene.renderForSpecs();
              expect(label._glyphs.length).toEqual(5);
              expect(labels._billboardCollection.length).toEqual(3);
          });
  
          function getGlyphBillboardVertexTranslate(label, index) {
              return Cartesian2.clone(label._glyphs[index].billboard._translate, new Cartesian2());
          }
  
          function getBackgroundBillboardVertexTranslate(label) {
              return Cartesian2.clone(label._backgroundBillboard._translate, new Cartesian2());
          }
  
          it('sets billboard properties properly when they change on the label', function() {
              var position1 = new Cartesian3(1.0, 2.0, 3.0);
              var position2 = new Cartesian3(4.0, 5.0, 6.0);
              var pixelOffset1 = new Cartesian2(4.0, 5.0);
              var pixelOffset2 = new Cartesian2(6.0, 7.0);
              var eyeOffset1 = new Cartesian3(6.0, 7.0, 8.0);
              var eyeOffset2 = new Cartesian3(16.0, 17.0, 18.0);
              var verticalOrigin1 = VerticalOrigin.TOP;
              var verticalOrigin2 = VerticalOrigin.BASELINE;
              var scale1 = 2.0;
              var scale2 = 3.0;
              var id1 = 'id1';
              var id2 = 'id2';
              var translucency1 = new NearFarScalar(1.0e4, 1.0, 1.0e6, 0.0);
              var translucency2 = new NearFarScalar(1.1e4, 1.2, 1.3e6, 4.0);
              var pixelOffsetScale1 = new NearFarScalar(1.0e4, 1.0, 1.0e6, 0.0);
              var pixelOffsetScale2 = new NearFarScalar(1.5e4, 1.6, 1.7e6, 8.0);
              var scaleByDistance1 = new NearFarScalar(1.0e4, 1.0, 1.0e6, 0.0);
              var scaleByDistance2 = new NearFarScalar(1.5e4, 1.6, 1.7e6, 8.0);
  
              var label = labels.add({
                  position : position1,
                  text : 'abc',
                  pixelOffset : pixelOffset1,
                  eyeOffset : eyeOffset1,
                  verticalOrigin : verticalOrigin1,
                  scale : scale1,
                  id : id1,
                  translucencyByDistance : translucency1,
                  pixelOffsetScaleByDistance : pixelOffsetScale1,
                  scaleByDistance : scaleByDistance1,
                  showBackground : true
              });
  
              scene.renderForSpecs();
  
              label.position = position2;
              label.text = 'def';
              label.pixelOffset = pixelOffset2;
              label.eyeOffset = eyeOffset2;
              label.verticalOrigin = verticalOrigin2;
              label.scale = scale2;
              label.id = id2;
              label.translucencyByDistance = translucency2;
              label.pixelOffsetScaleByDistance = pixelOffsetScale2;
              label.scaleByDistance = scaleByDistance2;
  
              scene.renderForSpecs();
  
              for (var i = 0; i < label._glyphs.length; ++i) {
                  var glyph = label._glyphs[i];
                  var billboard = glyph.billboard;
                  expect(billboard.show).toEqual(label.show);
                  expect(billboard.position).toEqual(label.position);
                  expect(billboard.eyeOffset).toEqual(label.eyeOffset);
                  expect(billboard.pixelOffset).toEqual(label.pixelOffset);
                  expect(billboard.verticalOrigin).toEqual(label.verticalOrigin);
                  // glyph horizontal origin is always LEFT
                  expect(billboard.scale).toEqual(label.scale);
                  expect(billboard.id).toEqual(label.id);
                  expect(billboard.translucencyByDistance).toEqual(label.translucencyByDistance);
                  expect(billboard.pixelOffsetScaleByDistance).toEqual(label.pixelOffsetScaleByDistance);
                  expect(billboard.scaleByDistance).toEqual(label.scaleByDistance);
  
                  expect(billboard.pickPrimitive).toEqual(label);
              }
          });
  
          describe('sets individual billboard properties properly when they change on the label', function() {
              var label;
              beforeEach(function() {
                  label = labels.add({
                      position : new Cartesian3(1.0, 2.0, 3.0),
                      text : 'abc',
                      pixelOffset : new Cartesian2(4.0, 5.0),
                      eyeOffset : new Cartesian3(6.0, 7.0, 8.0),
                      verticalOrigin : VerticalOrigin.TOP,
                      scale : 2.0,
                      id : 'id1',
                      translucencyByDistance : new NearFarScalar(1.0e4, 1.0, 1.0e6, 0.0),
                      pixelOffsetScaleByDistance : new NearFarScalar(1.0e4, 1.0, 1.0e6, 0.0),
                      scaleByDistance : new NearFarScalar(1.0e4, 1.0, 1.0e6, 0.0),
                      showBackground : true
                  });
                  scene.renderForSpecs();
              });
  
              function getGlyphBillboards() {
                  return label._glyphs.map(function(glyph) {
                      return glyph.billboard;
                  });
              }
  
              it('position', function() {
                  var newValue = new Cartesian3(4.0, 5.0, 6.0);
                  expect(label.position).not.toEqual(newValue);
                  label.position = newValue;
                  scene.renderForSpecs();
  
                  getGlyphBillboards().forEach(function(billboard) {
                      expect(billboard.position).toEqual(label.position);
                  });
              });
  
              it('eyeOffset', function() {
                  var newValue = new Cartesian3(16.0, 17.0, 18.0);
                  expect(label.eyeOffset).not.toEqual(newValue);
                  label.eyeOffset = newValue;
                  scene.renderForSpecs();
  
                  getGlyphBillboards().forEach(function(billboard) {
                      expect(billboard.eyeOffset).toEqual(label.eyeOffset);
                  });
              });
  
              it('pixelOffset', function() {
                  var newValue = new Cartesian3(16.0, 17.0, 18.0);
                  expect(label.pixelOffset).not.toEqual(newValue);
                  label.pixelOffset = newValue;
                  scene.renderForSpecs();
  
                  getGlyphBillboards().forEach(function(billboard) {
                      expect(billboard.pixelOffset).toEqual(label.pixelOffset);
                  });
              });
  
              it('verticalOrigin', function() {
                  var newValue = VerticalOrigin.BOTTOM;
                  expect(label.verticalOrigin).not.toEqual(newValue);
                  label.verticalOrigin = newValue;
                  scene.renderForSpecs();
  
                  getGlyphBillboards().forEach(function(billboard) {
                      expect(billboard.verticalOrigin).toEqual(label.verticalOrigin);
                  });
              });
  
              // glyph horizontal origin is always LEFT
  
              it('scale', function() {
                  var newValue = 3.0;
                  expect(label.scale).not.toEqual(newValue);
                  label.scale = newValue;
                  scene.renderForSpecs();
  
                  getGlyphBillboards().forEach(function(billboard) {
                      expect(billboard.scale).toEqual(label.scale);
                  });
              });
  
              it('showBackground', function() {
                  expect(label.showBackground).toEqual(true);
                  label.showBackground = false;
                  expect(label.showBackground).toEqual(false);
              });
  
              it('backgroundColor', function() {
                  var newValue = Color.RED;
                  expect(label.backgroundColor).not.toEqual(newValue);
                  label.backgroundColor = newValue;
                  expect(label.backgroundColor).toEqual(newValue);
              });
  
              it('backgroundPadding', function() {
                  var newValue = new Cartesian2(8, 5);
                  expect(label.backgroundPadding).not.toEqual(newValue);
                  label.backgroundPadding = newValue;
                  expect(label.backgroundPadding).toEqual(newValue);
              });
  
              it('id', function() {
                  var newValue = 'id2';
                  expect(label.id).not.toEqual(newValue);
                  label.id = newValue;
                  scene.renderForSpecs();
  
                  getGlyphBillboards().forEach(function(billboard) {
                      expect(billboard.id).toEqual(label.id);
                  });
              });
  
              it('translucencyByDistance', function() {
                  var newValue = new NearFarScalar(1.1e4, 1.2, 1.3e6, 4.0);
                  expect(label.translucencyByDistance).not.toEqual(newValue);
                  label.translucencyByDistance = newValue;
                  scene.renderForSpecs();
  
                  getGlyphBillboards().forEach(function(billboard) {
                      expect(billboard.translucencyByDistance).toEqual(label.translucencyByDistance);
                  });
              });
  
              it('pixelOffsetScaleByDistance', function() {
                  var newValue = new NearFarScalar(1.5e4, 1.6, 1.7e6, 8.0);
                  expect(label.pixelOffsetScaleByDistance).not.toEqual(newValue);
                  label.pixelOffsetScaleByDistance = newValue;
                  scene.renderForSpecs();
  
                  getGlyphBillboards().forEach(function(billboard) {
                      expect(billboard.pixelOffsetScaleByDistance).toEqual(label.pixelOffsetScaleByDistance);
                  });
              });
  
              it('scaleByDistance', function() {
                  var newValue = new NearFarScalar(1.5e4, 1.6, 1.7e6, 8.0);
                  expect(label.scaleByDistance).not.toEqual(newValue);
                  label.scaleByDistance = newValue;
                  scene.renderForSpecs();
  
                  getGlyphBillboards().forEach(function(billboard) {
                      expect(billboard.scaleByDistance).toEqual(label.scaleByDistance);
                  });
              });
  
              it('translucencyByDistance to undefined', function() {
                  var newValue;
                  expect(label.translucencyByDistance).not.toEqual(newValue);
                  label.translucencyByDistance = newValue;
                  scene.renderForSpecs();
  
                  getGlyphBillboards().forEach(function(billboard) {
                      expect(billboard.translucencyByDistance).toEqual(label.translucencyByDistance);
                  });
              });
  
              it('pixelOffsetScaleByDistance to undefined', function() {
                  var newValue;
                  expect(label.pixelOffsetScaleByDistance).not.toEqual(newValue);
                  label.pixelOffsetScaleByDistance = newValue;
                  scene.renderForSpecs();
  
                  getGlyphBillboards().forEach(function(billboard) {
                      expect(billboard.pixelOffsetScaleByDistance).toEqual(label.pixelOffsetScaleByDistance);
                  });
              });
  
              it('scaleByDistance to undefined', function() {
                  var newValue;
                  expect(label.scaleByDistance).not.toEqual(newValue);
                  label.scaleByDistance = newValue;
                  scene.renderForSpecs();
  
                  getGlyphBillboards().forEach(function(billboard) {
                      expect(billboard.scaleByDistance).toEqual(label.scaleByDistance);
                  });
              });
  
              it('clusterShow', function() {
                  expect(label.clusterShow).toEqual(true);
                  label.clusterShow = false;
                  expect(label.clusterShow).toEqual(false);
              });
          });
  
          it('should set vertexTranslate of billboards correctly when vertical origin is changed', function() {
              var label = labels.add({
                  text : 'apl',
                  font : '90px "Open Sans"',
                  verticalOrigin : VerticalOrigin.CENTER
              });
              scene.renderForSpecs();
  
              // store the offsets when vertically centered
              var offset0 = getGlyphBillboardVertexTranslate(label, 0);
              var offset1 = getGlyphBillboardVertexTranslate(label, 1);
              var offset2 = getGlyphBillboardVertexTranslate(label, 2);
  
              label.verticalOrigin = VerticalOrigin.TOP;
              scene.renderForSpecs();
  
              // Because changing the label's vertical origin also changes the vertical origin of each
              // individual glyph, it is not safe to assume anything about Y offsets being more or less.
  
              // X offset should be unchanged
              expect(getGlyphBillboardVertexTranslate(label, 0).x).toEqual(offset0.x);
              expect(getGlyphBillboardVertexTranslate(label, 1).x).toEqual(offset1.x);
              expect(getGlyphBillboardVertexTranslate(label, 2).x).toEqual(offset2.x);
  
              label.verticalOrigin = VerticalOrigin.BOTTOM;
              scene.renderForSpecs();
  
              // X offset should be unchanged
              expect(getGlyphBillboardVertexTranslate(label, 0).x).toEqual(offset0.x);
              expect(getGlyphBillboardVertexTranslate(label, 1).x).toEqual(offset1.x);
              expect(getGlyphBillboardVertexTranslate(label, 2).x).toEqual(offset2.x);
          });
  
          it('should set vertexTranslate of billboards correctly when horizontal origin is changed', function() {
              var label = labels.add({
                  text : 'apl',
                  font : '90px "Open Sans"',
                  horizontalOrigin : HorizontalOrigin.CENTER,
                  showBackground : true
              });
              scene.renderForSpecs();
  
              // store the offsets when horizontally centered
              var offset0 = getGlyphBillboardVertexTranslate(label, 0);
              var offset1 = getGlyphBillboardVertexTranslate(label, 1);
              var offset2 = getGlyphBillboardVertexTranslate(label, 2);
              var offsetBack = getBackgroundBillboardVertexTranslate(label);
  
              label.horizontalOrigin = HorizontalOrigin.LEFT;
              scene.renderForSpecs();
  
              // horizontal origin LEFT should increase X offset compared to CENTER
              expect(getGlyphBillboardVertexTranslate(label, 0).x).toBeGreaterThan(offset0.x);
              expect(getGlyphBillboardVertexTranslate(label, 1).x).toBeGreaterThan(offset1.x);
              expect(getGlyphBillboardVertexTranslate(label, 2).x).toBeGreaterThan(offset2.x);
              expect(getBackgroundBillboardVertexTranslate(label).x).toBeGreaterThan(offsetBack.x);
  
              // Y offset should be unchanged
              expect(getGlyphBillboardVertexTranslate(label, 0).y).toEqual(offset0.y);
              expect(getGlyphBillboardVertexTranslate(label, 1).y).toEqual(offset1.y);
              expect(getGlyphBillboardVertexTranslate(label, 2).y).toEqual(offset2.y);
              expect(getBackgroundBillboardVertexTranslate(label).y).toEqual(offsetBack.y);
  
              label.horizontalOrigin = HorizontalOrigin.RIGHT;
              scene.renderForSpecs();
  
              // horizontal origin RIGHT should decrease X offset compared to CENTER
              expect(getGlyphBillboardVertexTranslate(label, 0).x).toBeLessThan(offset0.x);
              expect(getGlyphBillboardVertexTranslate(label, 1).x).toBeLessThan(offset1.x);
              expect(getGlyphBillboardVertexTranslate(label, 2).x).toBeLessThan(offset2.x);
              expect(getBackgroundBillboardVertexTranslate(label).x).toBeLessThan(offsetBack.x);
  
              // Y offset should be unchanged
              expect(getGlyphBillboardVertexTranslate(label, 0).y).toEqual(offset0.y);
              expect(getGlyphBillboardVertexTranslate(label, 1).y).toEqual(offset1.y);
              expect(getGlyphBillboardVertexTranslate(label, 2).y).toEqual(offset2.y);
              expect(getBackgroundBillboardVertexTranslate(label).y).toEqual(offsetBack.y);
          });
  
          it('should set vertexTranslate of billboards correctly when scale is changed', function() {
              var label = labels.add({
                  text : 'apl',
                  font : '90px "Open Sans"',
                  verticalOrigin : VerticalOrigin.CENTER,
                  horizontalOrigin : HorizontalOrigin.CENTER
              });
              scene.renderForSpecs();
  
              // store the offsets when vertically centered at scale 1
              var offset0 = getGlyphBillboardVertexTranslate(label, 0);
              var offset1 = getGlyphBillboardVertexTranslate(label, 1);
              var offset2 = getGlyphBillboardVertexTranslate(label, 2);
  
              label.scale = 2;
              scene.renderForSpecs();
  
              // scaling by 2 should double X and Y offset
              expect(getGlyphBillboardVertexTranslate(label, 0).x).toEqual(2 * offset0.x);
              expect(getGlyphBillboardVertexTranslate(label, 0).y).toEqual(2 * offset0.y);
              expect(getGlyphBillboardVertexTranslate(label, 1).x).toEqual(2 * offset1.x);
              expect(getGlyphBillboardVertexTranslate(label, 1).y).toEqual(2 * offset1.y);
              expect(getGlyphBillboardVertexTranslate(label, 2).x).toEqual(2 * offset2.x);
              expect(getGlyphBillboardVertexTranslate(label, 2).y).toEqual(2 * offset2.y);
  
              // store the offsets when vertically centered at scale 2
              offset0 = getGlyphBillboardVertexTranslate(label, 0);
              offset1 = getGlyphBillboardVertexTranslate(label, 1);
              offset2 = getGlyphBillboardVertexTranslate(label, 2);
  
              // Because changing the label's vertical origin also changes the vertical origin of each
              // individual glyph, it is not safe to assume anything about Y offsets being more or less.
  
              label.horizontalOrigin = HorizontalOrigin.LEFT;
              scene.renderForSpecs();
  
              // horizontal origin LEFT should increase X offset compared to CENTER
              expect(getGlyphBillboardVertexTranslate(label, 0).x).toBeGreaterThan(offset0.x);
              expect(getGlyphBillboardVertexTranslate(label, 1).x).toBeGreaterThan(offset1.x);
              expect(getGlyphBillboardVertexTranslate(label, 2).x).toBeGreaterThan(offset2.x);
  
              // Y offset should be unchanged
              expect(getGlyphBillboardVertexTranslate(label, 0).y).toEqual(offset0.y);
              expect(getGlyphBillboardVertexTranslate(label, 1).y).toEqual(offset1.y);
              expect(getGlyphBillboardVertexTranslate(label, 2).y).toEqual(offset2.y);
  
              label.horizontalOrigin = HorizontalOrigin.RIGHT;
              scene.renderForSpecs();
  
              // horizontal origin RIGHT should decrease X offset compared to CENTER
              expect(getGlyphBillboardVertexTranslate(label, 0).x).toBeLessThan(offset0.x);
              expect(getGlyphBillboardVertexTranslate(label, 1).x).toBeLessThan(offset1.x);
              expect(getGlyphBillboardVertexTranslate(label, 2).x).toBeLessThan(offset2.x);
  
              // Y offset should be unchanged
              expect(getGlyphBillboardVertexTranslate(label, 0).y).toEqual(offset0.y);
              expect(getGlyphBillboardVertexTranslate(label, 1).y).toEqual(offset1.y);
              expect(getGlyphBillboardVertexTranslate(label, 2).y).toEqual(offset2.y);
          });
  
          it('label vertex translate should remain the same when pixel offset is changed', function() {
              var label = labels.add({
                  text : 'apl',
                  font : '90px "Open Sans"'
              });
              scene.renderForSpecs();
  
              var offset0 = getGlyphBillboardVertexTranslate(label, 0);
              var offset1 = getGlyphBillboardVertexTranslate(label, 1);
              var offset2 = getGlyphBillboardVertexTranslate(label, 2);
  
              var xOffset = 20;
              var yOffset = -10;
              label.pixelOffset = new Cartesian2(xOffset, yOffset);
              scene.renderForSpecs();
  
              expect(getGlyphBillboardVertexTranslate(label, 0)).toEqual(offset0);
              expect(getGlyphBillboardVertexTranslate(label, 1)).toEqual(offset1);
              expect(getGlyphBillboardVertexTranslate(label, 2)).toEqual(offset2);
  
              expect(label.pixelOffset.x).toEqual(xOffset);
              expect(label.pixelOffset.y).toEqual(yOffset);
          });
  
          it('Correctly updates billboard position when height reference changes', function() {
              scene.globe = new Globe();
              var labelsWithScene = new LabelCollection({scene : scene});
              scene.primitives.add(labelsWithScene);
  
              var position1 = new Cartesian3(1.0, 2.0, 3.0);
              var label = labelsWithScene.add({
                  position : position1,
                  text : 'abc',
                  heightReference : HeightReference.CLAMP_TO_GROUND
              });
  
              scene.renderForSpecs();
              var glyph = label._glyphs[0];
              var billboard = glyph.billboard;
              expect(billboard.position).toEqual(label.position);
  
              label.heightReference = HeightReference.NONE;
              scene.renderForSpecs();
  
              expect(billboard.position).toEqual(label.position);
              scene.primitives.remove(labelsWithScene);
          });
  
          it('should set vertexTranslate of billboards correctly when font size changes', function() {
              var label = labels.add({
                  text : 'apl',
                  font : '80px "Open Sans"',
                  verticalOrigin : VerticalOrigin.TOP,
                  horizontalOrigin : HorizontalOrigin.LEFT
              });
              scene.renderForSpecs();
  
              var offset0 = getGlyphBillboardVertexTranslate(label, 0);
              var offset1 = getGlyphBillboardVertexTranslate(label, 1);
              var offset2 = getGlyphBillboardVertexTranslate(label, 2);
  
              label.font = '20px "Open Sans"';
              scene.renderForSpecs();
  
              // reducing font size should reduce absolute value of both X and Y offset
  
              expect(Math.abs(getGlyphBillboardVertexTranslate(label, 0).x)).toBeLessThanOrEqualTo(Math.abs(offset0.x));
              expect(Math.abs(getGlyphBillboardVertexTranslate(label, 0).y)).toBeLessThanOrEqualTo(Math.abs(offset0.y));
              expect(Math.abs(getGlyphBillboardVertexTranslate(label, 1).x)).toBeLessThanOrEqualTo(Math.abs(offset1.x));
              expect(Math.abs(getGlyphBillboardVertexTranslate(label, 1).y)).toBeLessThanOrEqualTo(Math.abs(offset1.y));
              expect(Math.abs(getGlyphBillboardVertexTranslate(label, 2).x)).toBeLessThanOrEqualTo(Math.abs(offset2.x));
              expect(Math.abs(getGlyphBillboardVertexTranslate(label, 2).y)).toBeLessThanOrEqualTo(Math.abs(offset2.y));
          });
  
          it('should have the same vertexTranslate of billboards whether values are set at construction or afterwards', function() {
              var text = 'apl';
              var scale = 2.0;
              var font = '20px "Open Sans"';
              var verticalOrigin = VerticalOrigin.CENTER;
              var pixelOffset = new Cartesian2(10.0, 15.0);
  
              var one = labels.add({
                  text : text,
                  scale : scale,
                  font : font,
                  verticalOrigin : verticalOrigin,
                  pixelOffset : pixelOffset
              });
              scene.renderForSpecs();
  
              var two = labels.add();
              two.text = text;
              two.scale = scale;
              two.font = font;
              two.verticalOrigin = verticalOrigin;
              two.pixelOffset = pixelOffset;
  
              scene.renderForSpecs();
  
              expect(getGlyphBillboardVertexTranslate(one, 0)).toEqual(getGlyphBillboardVertexTranslate(two, 0));
              expect(getGlyphBillboardVertexTranslate(one, 1)).toEqual(getGlyphBillboardVertexTranslate(two, 1));
              expect(getGlyphBillboardVertexTranslate(one, 2)).toEqual(getGlyphBillboardVertexTranslate(two, 2));
          });
  
          it('should not change vertexTranslate of billboards when position changes', function() {
              var label = labels.add({
                  text : 'apl'
              });
              scene.renderForSpecs();
  
              var offset0 = getGlyphBillboardVertexTranslate(label, 0);
              var offset1 = getGlyphBillboardVertexTranslate(label, 1);
              var offset2 = getGlyphBillboardVertexTranslate(label, 2);
  
              label.position = new Cartesian3(1.0, 1.0, 1.0);
              scene.renderForSpecs();
  
              expect(getGlyphBillboardVertexTranslate(label, 0)).toEqual(offset0);
              expect(getGlyphBillboardVertexTranslate(label, 1)).toEqual(offset1);
              expect(getGlyphBillboardVertexTranslate(label, 2)).toEqual(offset2);
          });
  
          it('should not change vertexTranslate of billboards when eye offset changes', function() {
              var label = labels.add({
                  text : 'apl'
              });
              scene.renderForSpecs();
  
              var offset0 = getGlyphBillboardVertexTranslate(label, 0);
              var offset1 = getGlyphBillboardVertexTranslate(label, 1);
              var offset2 = getGlyphBillboardVertexTranslate(label, 2);
  
              label.eyeOffset = new Cartesian3(10.0, 10.0, -10.0);
              scene.renderForSpecs();
  
              expect(getGlyphBillboardVertexTranslate(label, 0)).toEqual(offset0);
              expect(getGlyphBillboardVertexTranslate(label, 1)).toEqual(offset1);
              expect(getGlyphBillboardVertexTranslate(label, 2)).toEqual(offset2);
          });
  
          it('should not change label dimensions when scale changes', function() {
              var label = labels.add({
                  text : 'apl'
              });
              scene.renderForSpecs();
  
              var originalDimensions = label._glyphs[0].dimensions;
  
              label.scale = 3;
              scene.renderForSpecs();
  
              var dimensions = label._glyphs[0].dimensions;
              expect(dimensions.width).toEqual(originalDimensions.width);
              expect(dimensions.height).toEqual(originalDimensions.height);
              expect(dimensions.descent).toEqual(originalDimensions.descent);
          });
  
          it('should change label dimensions when font size changes', function() {
              var label = labels.add({
                  text : 'apl',
                  font : '90px "Open Sans"'
              });
              scene.renderForSpecs();
  
              var originalDimensions = label._glyphs[0].dimensions;
  
              label.font = '20px "Open Sans"';
              scene.renderForSpecs();
  
              var dimensions = label._glyphs[0].dimensions;
              expect(dimensions.width).toBeLessThan(originalDimensions.width);
              expect(dimensions.height).toBeLessThan(originalDimensions.height);
              expect(dimensions.descent).toBeLessThanOrEqualTo(originalDimensions.descent);
          });
  
          it('should increase label height and decrease width when adding newlines', function() {
              var label = labels.add({
                  text : 'apl apl apl'
              });
              scene.renderForSpecs();
  
              var originalBbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO);
  
              label.text = 'apl\napl\napl';
              scene.renderForSpecs();
              var newlinesBbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO);
  
              expect(newlinesBbox.width).toBeLessThan(originalBbox.width);
              expect(newlinesBbox.height).toBeGreaterThan(originalBbox.height);
          });
  
          it('should not modify text when rightToLeft is false', function() {
              var text = 'bla bla bla';
              var label = labels.add({
                  text : text
              });
              scene.renderForSpecs();
  
              expect(label.text).toEqual(text);
          });
  
      }, 'WebGL');
  
      describe('right to left detection', function() {
          beforeAll(function() {
              Label.enableRightToLeftDetection = true;
          });
  
          afterAll(function() {
              Label.enableRightToLeftDetection = false;
          });
  
          it('should not modify text when rightToLeft is true and there are no RTL characters', function() {
              var text = 'bla bla bla';
              var label = labels.add({
                  text : text
              });
  
              expect(label.text).toEqual(text);
          });
  
          it('should reverse text when there are only hebrew characters and rightToLeft is true', function() {
              var text = 'שלום';
              var expectedText = 'םולש';
              var label = labels.add({
                  text : text
              });
  
              expect(label.text).toEqual(text);
              expect(label._renderedText).toEqual(expectedText);
          });
  
          it('should reverse text when there are only arabic characters and rightToLeft is true', function() {
              var text = 'مرحبا';
              var expectedText = 'ابحرم';
              var label = labels.add({
                  text : text
              });
  
              expect(label.text).toEqual(text);
              expect(label._renderedText).toEqual(expectedText);
          });
  
          it('should reverse part of text when there is mix of right-to-left and other kind of characters and rightToLeft is true', function() {
              var text = 'Master (אדון): "Hello"\nתלמיד (student): "שלום"';
              var expectedText = 'Master (ןודא): "Hello"\n"םולש" :(student) דימלת';
              var label = labels.add({
                  text : text
              });
  
              expect(label.text).toEqual(text);
              expect(label._renderedText).toEqual(expectedText);
          });
  
          it('should reverse all text and replace brackets when there is right-to-left characters and rightToLeft is true', function() {
              var text = 'משפט [מורכב] {עם} תווים <מיוחדים special>';
              var expectedText = '<special םידחוימ> םיוות {םע} [בכרומ] טפשמ';
              var label = labels.add({
                  text : text
              });
  
              expect(label.text).toEqual(text);
              expect(label._renderedText).toEqual(expectedText);
          });
  
          it('should reverse only text that detected as rtl text when it begin with non rtl characters when rightToLeft is true', function() {
              var text = '(interesting sentence with hebrew characters) שלום(עליך)חביבי.';
              var expectedText = '(interesting sentence with hebrew characters) יביבח(ךילע)םולש.';
              var label = labels.add({
                  text : text
              });
  
              expect(label.text).toEqual(text);
              expect(label._renderedText).toEqual(expectedText);
          });
  
          it('should not change nothing if it only non alphanumeric characters when rightToLeft is true', function() {
              var text = '([{- -}])';
              var expectedText = '([{- -}])';
              var label = labels.add({
                  text : text
              });
  
              expect(label.text).toEqual(expectedText);
          });
  
          it('detects characters in the range \\u05D0-\\u05EA', function() {
              var text = '\u05D1\u05D2';
              var expectedText = '\u05D2\u05D1';
              var label = labels.add({
                  text : text
              });
  
              expect(label.text).toEqual(text);
              expect(label._renderedText).toEqual(expectedText);
          });
  
          it('detects characters in the range \\u0600-\\u06FF', function() {
              var text = '\u0601\u0602';
              var expectedText = '\u0602\u0601';
              var label = labels.add({
                  text : text
              });
  
              expect(label.text).toEqual(text);
              expect(label._renderedText).toEqual(expectedText);
          });
  
          it('detects characters in the range \\u0750-\\u077F', function() {
              var text = '\u0751\u0752';
              var expectedText = '\u0752\u0751';
              var label = labels.add({
                  text : text
              });
  
              expect(label.text).toEqual(text);
              expect(label._renderedText).toEqual(expectedText);
          });
  
          it('detects characters in the range \\u08A0-\\u08FF', function() {
              var text = '\u08A1\u08A2';
              var expectedText = '\u08A2\u08A1';
              var label = labels.add({
                  text : text
              });
  
              expect(label.text).toEqual(text);
              expect(label._renderedText).toEqual(expectedText);
          });
  
          it('should reversing correctly non alphabetic characters', function() {
              var text = 'A אב: ג\nאב: ג';
              var expectedText = 'A ג :בא\nג :בא';
              var label = labels.add({
                  text : text
              });
  
              expect(label.text).toEqual(text);
              expect(label._renderedText).toEqual(expectedText);
          });
      });
  
      it('computes bounding sphere in 3D', function() {
          var one = labels.add({
              position : Cartesian3.fromDegrees(-50.0, -50.0, 0.0),
              text : 'one'
          });
          var two = labels.add({
              position : Cartesian3.fromDegrees(-50.0, 50.0, 0.0),
              text : 'two'
          });
  
          scene.renderForSpecs();
          var actual = scene.frameState.commandList[0].boundingVolume;
  
          var positions = [one.position, two.position];
          var expected = BoundingSphere.fromPoints(positions);
          expect(actual.center).toEqual(expected.center);
          expect(actual.radius).toEqual(expected.radius);
      });
  
      it('computes bounding sphere in Columbus view', function() {
          // Disable collision detection to allow controlled camera position,
          // hence predictable bounding sphere size
          var originalEnableCollisionDetection = scene.screenSpaceCameraController.enableCollisionDetection;
          scene.screenSpaceCameraController.enableCollisionDetection = false;
  
          var projection = scene.mapProjection;
          var ellipsoid = projection.ellipsoid;
  
          var one = labels.add({
              position : Cartesian3.fromDegrees(-50.0, -50.0, 0.0),
              text : 'one'
          });
          var two = labels.add({
              position : Cartesian3.fromDegrees(-50.0, 50.0, 0.0),
              text : 'two'
          });
  
          // Update scene state
          scene.morphToColumbusView(0);
          scene.renderForSpecs();
          var actual = scene.frameState.commandList[0].boundingVolume;
  
          var projectedPositions = [
              projection.project(ellipsoid.cartesianToCartographic(one.position)),
              projection.project(ellipsoid.cartesianToCartographic(two.position))
          ];
          var expected = BoundingSphere.fromPoints(projectedPositions);
          expected.center = new Cartesian3(0.0, expected.center.x, expected.center.y);
          expect(actual.center).toEqualEpsilon(expected.center, CesiumMath.EPSILON8);
          expect(actual.radius).toBeGreaterThanOrEqualTo(expected.radius);
          scene.screenSpaceCameraController.enableCollisionDetection = originalEnableCollisionDetection;
      });
  
      it('computes bounding sphere in 2D', function() {
          var projection = scene.mapProjection;
          var ellipsoid = projection.ellipsoid;
  
          var one = labels.add({
              position : Cartesian3.fromDegrees(-50.0, -50.0),
              text : 'one'
          });
          var two = labels.add({
              position : Cartesian3.fromDegrees(-50.0, 50.0),
              text : 'two'
          });
  
          camera.setView({
              destination : Rectangle.fromDegrees(-60.0, -60.0, -40.0, 60.0)
          });
  
          scene.morphTo2D(0);
          scene.renderForSpecs();
  
          scene.renderForSpecs();
          var actual = scene.frameState.commandList[0].boundingVolume;
  
          var projectedPositions = [
              projection.project(ellipsoid.cartesianToCartographic(one.position)),
              projection.project(ellipsoid.cartesianToCartographic(two.position))
          ];
          var expected = BoundingSphere.fromPoints(projectedPositions);
          expected.center = new Cartesian3(0.0, expected.center.x, expected.center.y);
          expect(actual.center).toEqualEpsilon(expected.center, CesiumMath.EPSILON8);
          expect(actual.radius).toBeGreaterThan(expected.radius);
      });
  
      it('Label.show throws with undefined', function() {
          var label = labels.add();
          expect(function() {
              label.show = undefined;
          }).toThrowDeveloperError();
      });
  
      it('Label.position throws with undefined', function() {
          var label = labels.add();
          expect(function() {
              label.position = undefined;
          }).toThrowDeveloperError();
      });
  
      it('Label.text throws with undefined', function() {
          var label = labels.add();
          expect(function() {
              label.text = undefined;
          }).toThrowDeveloperError();
      });
  
      it('Label.font throws with undefined', function() {
          var label = labels.add();
          expect(function() {
              label.font = undefined;
          }).toThrowDeveloperError();
      });
  
      it('Label.fillColor throws with undefined', function() {
          var label = labels.add();
          expect(function() {
              label.fillColor = undefined;
          }).toThrowDeveloperError();
      });
  
      it('Label.outlineColor throws with undefined', function() {
          var label = labels.add();
          expect(function() {
              label.outlineColor = undefined;
          }).toThrowDeveloperError();
      });
  
      it('Label.outlineWidth throws with undefined', function() {
          var label = labels.add();
          expect(function() {
              label.outlineWidth = undefined;
          }).toThrowDeveloperError();
      });
  
      it('Label.style throws with undefined', function() {
          var label = labels.add();
          expect(function() {
              label.style = undefined;
          }).toThrowDeveloperError();
      });
  
      it('Label.pixelOffset throws with undefined', function() {
          var label = labels.add();
          expect(function() {
              label.pixelOffset = undefined;
          }).toThrowDeveloperError();
      });
  
      it('Label.eyeOffset throws with undefined', function() {
          var label = labels.add();
          expect(function() {
              label.eyeOffset = undefined;
          }).toThrowDeveloperError();
      });
  
      it('Label.horizontalOrigin throws with undefined', function() {
          var label = labels.add();
          expect(function() {
              label.horizontalOrigin = undefined;
          }).toThrowDeveloperError();
      });
  
      it('Label.verticalOrigin throws with undefined', function() {
          var label = labels.add();
          expect(function() {
              label.verticalOrigin = undefined;
          }).toThrowDeveloperError();
      });
  
      it('Label.scale throws with undefined', function() {
          var label = labels.add();
          expect(function() {
              label.scale = undefined;
          }).toThrowDeveloperError();
      });
  
      it('Label.computeScreenSpacePosition throws with undefined scene', function() {
          var label = labels.add();
          expect(function() {
              label.computeScreenSpacePosition();
          }).toThrowDeveloperError();
      });
  
      it('Label.translucencyByDistance throws with nearDistance === farDistance', function() {
          var label = labels.add();
          var translucency = new NearFarScalar(2.0e5, 1.0, 2.0e5, 0.0);
          expect(function() {
              label.translucencyByDistance = translucency;
          }).toThrowDeveloperError();
      });
  
      it('Label.pixelOffsetScaleByDistance throws with nearDistance === farDistance', function() {
          var label = labels.add();
          var pixelOffsetScale = new NearFarScalar(2.0e5, 1.0, 2.0e5, 0.0);
          expect(function() {
              label.pixelOffsetScaleByDistance = pixelOffsetScale;
          }).toThrowDeveloperError();
      });
  
      it('Label.scaleByDistance throws with nearDistance === farDistance', function() {
          var label = labels.add();
          var scaleByDistance = new NearFarScalar(2.0e5, 1.0, 2.0e5, 0.0);
          expect(function() {
              label.scaleByDistance = scaleByDistance;
          }).toThrowDeveloperError();
      });
  
      it('new label throws with invalid translucencyByDistance (nearDistance === farDistance)', function() {
          var translucency = new NearFarScalar(2.0e5, 1.0, 2.0e5, 0.0);
          expect(function() {
              labels.add({
                  translucencyByDistance : translucency
              });
          }).toThrowDeveloperError();
      });
  
      it('new label throws with invalid pixelOffsetScaleByDistance (nearDistance === farDistance)', function() {
          var pixelOffsetScale = new NearFarScalar(2.0e5, 1.0, 2.0e5, 0.0);
          expect(function() {
              labels.add({
                  pixelOffsetScaleByDistance : pixelOffsetScale
              });
          }).toThrowDeveloperError();
      });
  
      it('new label throws with invalid scaleByDistance (nearDistance === farDistance)', function() {
          var scaleByDistance = new NearFarScalar(2.0e5, 1.0, 2.0e5, 0.0);
          expect(function() {
              labels.add({
                  scaleByDistance : scaleByDistance
              });
          }).toThrowDeveloperError();
      });
  
      it('Label.translucencyByDistance throws with nearDistance > farDistance', function() {
          var label = labels.add();
          var translucency = new NearFarScalar(1.0e9, 1.0, 1.0e5, 1.0);
          expect(function() {
              label.translucencyByDistance = translucency;
          }).toThrowDeveloperError();
      });
  
      it('Label.pixelOffsetScaleByDistance throws with nearDistance > farDistance', function() {
          var label = labels.add();
          var pixelOffsetScale = new NearFarScalar(1.0e9, 1.0, 1.0e5, 1.0);
          expect(function() {
              label.pixelOffsetScaleByDistance = pixelOffsetScale;
          }).toThrowDeveloperError();
      });
  
      it('Label.scaleByDistance throws with nearDistance > farDistance', function() {
          var label = labels.add();
          var scaleByDistance = new NearFarScalar(1.0e9, 1.0, 1.0e5, 1.0);
          expect(function() {
              label.scaleByDistance = scaleByDistance;
          }).toThrowDeveloperError();
      });
  
      it('destroys texture atlas when destroying', function() {
          labels.add({
              text : 'a'
          });
          scene.renderForSpecs();
  
          var textureAtlas = labels._textureAtlas;
          expect(textureAtlas.isDestroyed()).toBe(false);
  
          scene.primitives.removeAll();
  
          expect(textureAtlas.isDestroyed()).toBe(true);
      });
  
      describe('height referenced labels', function() {
          beforeEach(function() {
              scene.globe = createGlobe();
  
              labelsWithHeight = new LabelCollection({
                  scene : scene
              });
              scene.primitives.add(labelsWithHeight);
          });
  
          it('explicitly constructs a label with height reference', function() {
              scene.globe = createGlobe();
              var l = labelsWithHeight.add({
                  text : 'test',
                  heightReference : HeightReference.CLAMP_TO_GROUND
              });
  
              expect(l.heightReference).toEqual(HeightReference.CLAMP_TO_GROUND);
          });
  
          it('set label height reference property', function() {
              scene.globe = createGlobe();
              var l = labelsWithHeight.add({
                  text : 'test'
              });
              l.heightReference = HeightReference.CLAMP_TO_GROUND;
  
              expect(l.heightReference).toEqual(HeightReference.CLAMP_TO_GROUND);
          });
  
          it('creating with a height reference creates a height update callback', function() {
              scene.globe = createGlobe();
              labelsWithHeight.add({
                  heightReference : HeightReference.CLAMP_TO_GROUND,
                  position : Cartesian3.fromDegrees(-72.0, 40.0)
              });
              expect(scene.globe.callback).toBeDefined();
          });
  
          it('set height reference property creates a height update callback', function() {
              scene.globe = createGlobe();
              var l = labelsWithHeight.add({
                  position : Cartesian3.fromDegrees(-72.0, 40.0)
              });
              l.heightReference = HeightReference.CLAMP_TO_GROUND;
              expect(scene.globe.callback).toBeDefined();
          });
  
          it('updates the callback when the height reference changes', function() {
              scene.globe = createGlobe();
              var l = labelsWithHeight.add({
                  heightReference : HeightReference.CLAMP_TO_GROUND,
                  position : Cartesian3.fromDegrees(-72.0, 40.0)
              });
              expect(scene.globe.callback).toBeDefined();
  
              l.heightReference = HeightReference.RELATIVE_TO_GROUND;
              expect(scene.globe.removedCallback).toEqual(true);
              expect(scene.globe.callback).toBeDefined();
  
              scene.globe.removedCallback = false;
              l.heightReference = HeightReference.NONE;
              expect(scene.globe.removedCallback).toEqual(true);
              expect(scene.globe.callback).toBeUndefined();
          });
  
          it('changing the position updates the callback', function() {
              scene.globe = createGlobe();
              var l = labelsWithHeight.add({
                  heightReference : HeightReference.CLAMP_TO_GROUND,
                  position : Cartesian3.fromDegrees(-72.0, 40.0)
              });
              expect(scene.globe.callback).toBeDefined();
  
              l.position = Cartesian3.fromDegrees(-73.0, 40.0);
              expect(scene.globe.removedCallback).toEqual(true);
              expect(scene.globe.callback).toBeDefined();
          });
  
          it('callback updates the position', function() {
              scene.globe = createGlobe();
              var l = labelsWithHeight.add({
                  heightReference : HeightReference.CLAMP_TO_GROUND,
                  position : Cartesian3.fromDegrees(-72.0, 40.0)
              });
              expect(scene.globe.callback).toBeDefined();
  
              var cartographic = scene.globe.ellipsoid.cartesianToCartographic(l._clampedPosition);
              expect(cartographic.height).toEqual(0.0);
  
              scene.globe.callback(Cartesian3.fromDegrees(-72.0, 40.0, 100.0));
              cartographic = scene.globe.ellipsoid.cartesianToCartographic(l._clampedPosition);
              expect(cartographic.height).toEqualEpsilon(100.0, CesiumMath.EPSILON9);
          });
  
          it('resets the clamped position when HeightReference.NONE', function() {
              scene.globe = createGlobe();
              spyOn(scene.camera, 'update');
              var l = labelsWithHeight.add({
                  heightReference : HeightReference.CLAMP_TO_GROUND,
                  text: 't',
                  position : Cartesian3.fromDegrees(-72.0, 40.0)
              });
              scene.renderForSpecs();
              expect(l._clampedPosition).toBeDefined();
              expect(l._glyphs[0].billboard._clampedPosition).toBeDefined();
  
              l.heightReference = HeightReference.NONE;
              expect(l._clampedPosition).toBeUndefined();
              expect(l._glyphs[0].billboard._clampedPosition).toBeUndefined();
          });
  
          it('clears the billboard height reference callback when the label is removed', function() {
              scene.globe = createGlobe();
              spyOn(scene.camera, 'update');
              var l = labelsWithHeight.add({
                  heightReference : HeightReference.CLAMP_TO_GROUND,
                  text: 't',
                  position : Cartesian3.fromDegrees(-72.0, 40.0)
              });
              scene.renderForSpecs();
              var billboard = l._glyphs[0].billboard;
              expect(billboard._removeCallbackFunc).toBeDefined();
              var spy = spyOn(billboard, '_removeCallbackFunc');
              labelsWithHeight.remove(l);
              expect(spy).toHaveBeenCalled();
              expect(labelsWithHeight._spareBillboards[0]._removeCallbackFunc).toBeUndefined();
          });
      });
  
  }, 'WebGL');
  