/*global defineSuite*/
defineSuite([
        'DataSources/KmlDataSource',
        'Core/BoundingRectangle',
        'Core/Cartesian2',
        'Core/Cartesian3',
        'Core/ClockRange',
        'Core/ClockStep',
        'Core/Color',
        'Core/DefaultProxy',
        'Core/Event',
        'Core/Iso8601',
        'Core/JulianDate',
        'Core/loadBlob',
        'Core/Math',
        'Core/NearFarScalar',
        'Core/Rectangle',
        'Core/RequestErrorEvent',
        'Core/RuntimeError',
        'DataSources/ColorMaterialProperty',
        'DataSources/EntityCollection',
        'DataSources/ImageMaterialProperty',
        'Scene/HorizontalOrigin',
        'Scene/LabelStyle'
    ], function(
        KmlDataSource,
        BoundingRectangle,
        Cartesian2,
        Cartesian3,
        ClockRange,
        ClockStep,
        Color,
        DefaultProxy,
        Event,
        Iso8601,
        JulianDate,
        loadBlob,
        CesiumMath,
        NearFarScalar,
        Rectangle,
        RequestErrorEvent,
        RuntimeError,
        ColorMaterialProperty,
        EntityCollection,
        ImageMaterialProperty,
        HorizontalOrigin,
        LabelStyle) {
    "use strict";
    /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs*/

    var parser = new DOMParser();

    //simple.png in the DATA/KML directory
    var image = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAMAAAAoyzS7AAADAFBMVEUAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADHM2paAAAAGHRFWHRTb2Z0d2FyZQBQYWludC5ORVQgdjMuMzap5+IlAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg==';

    var uberStyle = '\
        <Style>\
            <LineStyle>\
              <color>aaaaaaaa</color>\
              <width>2</width>\
            </LineStyle>\
            <PolyStyle>\
              <color>cccccccc</color>\
              <fill>0</fill>\
              <outline>0</outline>\
            </PolyStyle>\
            <IconStyle>\
              <color>dddddddd</color>\
              <scale>3</scale>\
              <heading>45</heading>\
              <Icon>\
                <href>test.png</href>\
              </Icon>\
              <hotSpot x="1"  y="2" xunits="pixels" yunits="pixels"/>\
            </IconStyle>\
            <LabelStyle>\
              <color>eeeeeeee</color>\
              <scale>4</scale>\
            </LabelStyle>\
        </Style>';

    var uberLineColor = Color.fromBytes(0xaa, 0xaa, 0xaa, 0xaa);
    var uberLineWidth = 2;

    var uberPolyColor = Color.fromBytes(0xcc, 0xcc, 0xcc, 0xcc);
    var uberPolyFill = false;
    var uberPolyOutline = false;

    var uberIconColor = Color.fromBytes(0xdd, 0xdd, 0xdd, 0xdd);
    var uberIconScale = 3;
    var uberIconHeading = CesiumMath.toRadians(-45);
    var uberIcon = 'test.png';
    var uberIconHotspot = new Cartesian2(45, -46);

    var uberLabelColor = Color.fromBytes(0xee, 0xee, 0xee, 0xee);
    var uberLabelScale = 4;

    it('default constructor has expected values', function() {
        var dataSource = new KmlDataSource();
        expect(dataSource.name).toBeUndefined();
        expect(dataSource.clock).toBeUndefined();
        expect(dataSource.entities).toBeInstanceOf(EntityCollection);
        expect(dataSource.isLoading).toBe(false);
        expect(dataSource.changedEvent).toBeInstanceOf(Event);
        expect(dataSource.errorEvent).toBeInstanceOf(Event);
        expect(dataSource.loadingEvent).toBeInstanceOf(Event);
    });

    it('load throws with undefined KML', function() {
        var dataSource = new KmlDataSource();
        expect(function() {
            dataSource.load(undefined);
        }).toThrowDeveloperError();
    });

    it('load works with a KMZ file', function() {
        var dataSource = new KmlDataSource();
        return loadBlob('Data/KML/simple.kmz').then(function(blob) {
            return dataSource.load(blob);
        }).then(function(source) {
            expect(source).toBe(dataSource);
            expect(source.entities.values.length).toEqual(1);
        });
    });

    it('load rejects loading non-KMZ file', function() {
        var dataSource = new KmlDataSource();
        var spy = jasmine.createSpy('errorEvent');
        dataSource.errorEvent.addEventListener(spy);

        return loadBlob('Data/Images/Blue.png').then(function(blob) {
            return dataSource.load(blob);
        }).otherwise(function(e) {
            expect(e).toBeInstanceOf(RuntimeError);
            expect(spy).toHaveBeenCalled();
        });
    });

    it('load rejects KMZ file with no KML contained', function() {
        return loadBlob('Data/KML/empty.kmz').then(function(blob) {
            return KmlDataSource.load(blob);
        }).otherwise(function(e) {
            expect(e).toBeInstanceOf(RuntimeError);
            expect(e.message).toEqual('KMZ file does not contain a KML document.');
        });
    });

    it('load works with a KML URL', function() {
        var dataSource = new KmlDataSource();
        return dataSource.load('Data/KML/simple.kml').then(function(source) {
            expect(source).toBe(dataSource);
            expect(source.entities.values.length).toEqual(1);
        });
    });

    it('load works with a KMZ URL', function() {
        var dataSource = new KmlDataSource();
        return dataSource.load('Data/KML/simple.kmz').then(function(source) {
            expect(source).toBe(dataSource);
            expect(source.entities.values.length).toEqual(1);
        });
    });

    it('load rejects nonexistent URL', function() {
        return KmlDataSource.load('test.invalid').otherwise(function(e) {
            expect(e).toBeInstanceOf(RequestErrorEvent);
        });
    });

    it('load rejects loading non-KML URL', function() {
        return KmlDataSource.load('Data/Images/Blue.png').otherwise(function(e) {
            expect(e).toBeInstanceOf(RuntimeError);
        });
    });

    it('load rejects valid KMZ zip URL with no KML contained', function() {
        return KmlDataSource.load('Data/KML/empty.kmz').otherwise(function(e) {
            expect(e).toBeInstanceOf(RuntimeError);
            expect(e.message).toEqual('KMZ file does not contain a KML document.');
        });
    });

    it('sets DataSource name from Document', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
            <Document>\
            <name>NameInKml</name>\
            </Document>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml"), 'NameFromUri.kml').then(function(dataSource) {
            expect(dataSource.name).toEqual('NameInKml');
        });
    });

    it('sets DataSource name from Document with KML element', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
            <kml>\
            <Document>\
            <name>NameInKml</name>\
            </Document>\
            </kml>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml"), 'NameFromUri.kml').then(function(dataSource) {
            expect(dataSource.name).toEqual('NameInKml');
        });
    });

    it('sets DataSource name from sourceUri when not in file', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
            <Document>\
            </Document>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml"), {
            sourceUri : 'NameFromUri.kml'
        }).then(function(dataSource) {
            expect(dataSource.name).toEqual('NameFromUri.kml');
        });
    });

    it('raises changed event when the name changes', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
            <Document>\
            <name>NameInKml</name>\
            </Document>';

        var dataSource = new KmlDataSource();

        var spy = jasmine.createSpy('changedEvent');
        dataSource.changedEvent.addEventListener(spy);

        return dataSource.load(parser.parseFromString(kml, "text/xml")).then(function() {
            //Initial load
            expect(spy).toHaveBeenCalledWith(dataSource);

            spy.calls.reset();
            return dataSource.load(parser.parseFromString(kml, "text/xml")).then(function() {
                //Loading KML with same name
                expect(spy).not.toHaveBeenCalled();

                kml = kml.replace('NameInKml', 'newName');
                spy.calls.reset();
                return dataSource.load(parser.parseFromString(kml, "text/xml")).then(function() {
                    //Loading KML with different name.
                    expect(spy).toHaveBeenCalledWith(dataSource);
                });
            });
        });
    });

    it('raises loadingEvent event at start and end of load', function() {
        var dataSource = new KmlDataSource();

        var spy = jasmine.createSpy('loadingEvent');
        dataSource.loadingEvent.addEventListener(spy);

        var promise = dataSource.load('Data/KML/simple.kml');
        expect(spy).toHaveBeenCalledWith(dataSource, true);
        spy.calls.reset();

        return promise.then(function() {
            expect(spy).toHaveBeenCalledWith(dataSource, false);
        });
    });

    it('sets DatasourceClock based on feature availability', function() {
        var beginDate = JulianDate.fromIso8601('2000-01-01');
        var endDate = JulianDate.fromIso8601('2000-01-04');

        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Document xmlns="http://www.opengis.net/kml/2.2"\
                  xmlns:gx="http://www.google.com/kml/ext/2.2">\
          <GroundOverlay>\
            <TimeSpan>\
              <begin>2000-01-01</begin>\
              <end>2000-01-03</end>\
            </TimeSpan>\
          </GroundOverlay>\
          <Placemark>\
            <gx:Track>\
              <when>2000-01-02</when>\
              <gx:coord>1 2 3</gx:coord>\
              <when>2000-01-04</when>\
              <gx:coord>4 5 6</gx:coord>\
            </gx:Track>\
          </Placemark>\
        </Document>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var clock = dataSource.clock;
            expect(dataSource.clock).toBeDefined();
            expect(clock.startTime).toEqual(beginDate);
            expect(clock.stopTime).toEqual(endDate);
            expect(clock.currentTime).toEqual(beginDate);
            expect(clock.clockRange).toEqual(ClockRange.LOOP_STOP);
            expect(clock.clockStep).toEqual(ClockStep.SYSTEM_CLOCK_MULTIPLIER);
            expect(clock.multiplier).toEqual(JulianDate.secondsDifference(endDate, beginDate) / 60);
            return dataSource;
        }).then(function(dataSource) {
            //Loading a static data set should clear the clock.
            kml = '<?xml version="1.0" encoding="UTF-8"?>\
                <Document xmlns="http://www.opengis.net/kml/2.2"\
                          xmlns:gx="http://www.google.com/kml/ext/2.2">\
                  <GroundOverlay>\
                  </GroundOverlay>\
                </Document>';

            return dataSource.load(parser.parseFromString(kml, "text/xml"));
        }).then(function(dataSource) {
            expect(dataSource.clock).toBeUndefined();
        });
    });

    it('Feature: id', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Placemark id="Bob">\
        </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.id).toBe('Bob');
        });
    });

    it('Feature: name', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Placemark>\
            <name>bob</name>\
        </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.name).toBe('bob');
            expect(entity.label).toBeDefined();
            expect(entity.label.text.getValue()).toBe('bob');
        });
    });

    it('Feature: address', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Placemark>\
            <address>1826 South 16th Street</address>\
        </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.kml.address).toBe('1826 South 16th Street');
        });
    });

    it('Feature: phoneNumber', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Placemark>\
            <phoneNumber>555-555-5555</phoneNumber>\
        </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.kml.phoneNumber).toBe('555-555-5555');
        });
    });

    it('Feature: Snippet', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Placemark>\
            <Snippet>Hey!</Snippet>\
        </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.kml.snippet).toBe('Hey!');
        });
    });

    it('Feature: atom:author', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Placemark xmlns="http://www.opengis.net/kml/2.2"\
            xmlns:atom="http://www.w3.org/2005/Atom">\
            <atom:author>\
                <atom:name>J.R.R. Tolkien</atom:name>\
                <atom:email>gandalf@greyhavens.invalid</atom:email>\
                <atom:uri>http://greyhavens.invalid</atom:uri>\
            </atom:author>\
        </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.kml).toBeDefined();
            expect(entity.kml.author).toBeDefined();
            expect(entity.kml.author.name).toBe('J.R.R. Tolkien');
            expect(entity.kml.author.email).toBe('gandalf@greyhavens.invalid');
            expect(entity.kml.author.uri).toBe('http://greyhavens.invalid');
        });
    });

    it('Feature: atom:link', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Placemark xmlns="http://www.opengis.net/kml/2.2"\
                   xmlns:atom="http://www.w3.org/2005/Atom">\
            <atom:link\
                href="http://test.invalid"\
                hreflang="en-US"\
                rel="alternate"\
                type="text/plain"\
                title="Invalid!"\
                length="123"/>\
        </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.kml).toBeDefined();
            expect(entity.kml.link).toBeDefined();
            expect(entity.kml.link.href).toEqual('http://test.invalid');
            expect(entity.kml.link.hreflang).toEqual('en-US');
            expect(entity.kml.link.rel).toEqual('alternate');
            expect(entity.kml.link.type).toEqual('text/plain');
            expect(entity.kml.link.title).toEqual('Invalid!');
            expect(entity.kml.link.length).toEqual('123');
        });
    });

    it('Feature: TimeSpan with begin and end', function() {
        var endDate = JulianDate.fromIso8601('1945-08-06');
        var beginDate = JulianDate.fromIso8601('1941-12-07');

        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Placemark>\
            <TimeSpan>\
              <begin>1945-08-06</begin>\
              <end>1941-12-07</end>\
            </TimeSpan>\
        </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.availability).toBeDefined();
            expect(entity.availability.start).toEqual(beginDate);
            expect(entity.availability.stop).toEqual(endDate);
        });
    });

    it('Feature: TimeSpan flips dates when end is earlier', function() {
        var endDate = JulianDate.fromIso8601('1945-08-06');
        var beginDate = JulianDate.fromIso8601('1941-12-07');

        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Placemark>\
            <TimeSpan>\
              <begin>1941-12-07</begin>\
              <end>1945-08-06</end>\
            </TimeSpan>\
        </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.availability).toBeDefined();
            expect(entity.availability.start).toEqual(beginDate);
            expect(entity.availability.stop).toEqual(endDate);
        });
    });

    it('Feature: TimeSpan gracefully handles empty fields', function() {
        var endDate = JulianDate.fromIso8601('1945-08-06');
        var beginDate = JulianDate.fromIso8601('1941-12-07');

        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Placemark>\
            <TimeSpan>\
            </TimeSpan>\
        </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.availability).toBeUndefined();
        });
    });

    it('Feature: TimeSpan works with end only interval', function() {
        var date = JulianDate.fromIso8601('1941-12-07');

        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Placemark>\
            <TimeSpan>\
              <end>1941-12-07</end>\
            </TimeSpan>\
        </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.availability).toBeDefined();
            expect(entity.availability.start).toEqual(Iso8601.MINIMUM_VALUE);
            expect(entity.availability.stop).toEqual(date);
        });
    });

    it('Feature: TimeSpan works with begin only interval', function() {
        var date = JulianDate.fromIso8601('1941-12-07');

        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Placemark>\
            <TimeSpan>\
              <begin>1941-12-07</begin>\
            </TimeSpan>\
        </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.availability).toBeDefined();
            expect(entity.availability.start).toEqual(date);
            expect(entity.availability.stop).toEqual(Iso8601.MAXIMUM_VALUE);
        });
    });

    it('Feature: TimeStamp works', function() {
        var date = JulianDate.fromIso8601('1941-12-07');

        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Placemark>\
            <TimeStamp>\
              <when>1941-12-07</when>\
            </TimeStamp>\
        </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.availability).toBeDefined();
            expect(entity.availability.start).toEqual(date);
            expect(entity.availability.stop).toEqual(Iso8601.MAXIMUM_VALUE);
        });
    });

    it('Feature: visibility works', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Placemark>\
            <visibility>0</visibility>\
        </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.show).toBe(false);
        });
    });

    it('Feature: TimeStamp gracefully handles empty fields', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Placemark>\
            <TimeStamp>\
            </TimeStamp>\
        </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.availability).toBeUndefined();
        });
    });

    it('Feature: TimeStamp gracefully handles empty when field', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Placemark>\
            <TimeStamp>\
              <when></when>\
            </TimeStamp>\
        </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.availability).toBeUndefined();
        });
    });

    it('Feature: ExtendedData <Data> schema', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Placemark>\
            <ExtendedData>\
                <Data name="prop1">\
                    <displayName>Property 1</displayName>\
                    <value>1</value>\
                </Data>\
                <Data name="prop2">\
                    <value>2</value>\
                </Data>\
                <Data name="prop3">\
                    <displayName>Property 3</displayName>\
                </Data>\
            </ExtendedData>\
        </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.kml.extendedData).toBeDefined();

            expect(entity.kml.extendedData.prop1).toBeDefined();
            expect(entity.kml.extendedData.prop1.displayName).toEqual('Property 1');
            expect(entity.kml.extendedData.prop1.value).toEqual('1');

            expect(entity.kml.extendedData.prop2).toBeDefined();
            expect(entity.kml.extendedData.prop2.displayName).toBeUndefined();
            expect(entity.kml.extendedData.prop2.value).toEqual('2');

            expect(entity.kml.extendedData.prop3).toBeDefined();
            expect(entity.kml.extendedData.prop3.displayName).toEqual('Property 3');
            expect(entity.kml.extendedData.prop3.value).toBeUndefined();
        });
    });

    it('GroundOverlay: Sets defaults', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <GroundOverlay>\
        </GroundOverlay>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.name).toBeUndefined();
            expect(entity.availability).toBeUndefined();
            expect(entity.description).toBeUndefined();
            expect(entity.rectangle).toBeDefined();
            expect(entity.rectangle.height).toBeUndefined();
            expect(entity.rectangle.rotation).toBeUndefined();
            expect(entity.rectangle.coordinates).toBeUndefined();
            expect(entity.rectangle.material).toBeUndefined();
        });
    });

    it('GroundOverlay: Sets rectangle image material', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <GroundOverlay>\
            <Icon>\
                <href>http://test.invalid/image.png</href>\
            </Icon>\
        </GroundOverlay>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.rectangle.material).toBeInstanceOf(ImageMaterialProperty);
            expect(entity.rectangle.material.image.getValue()).toEqual('http://test.invalid/image.png');
        });
    });

    it('GroundOverlay: Sets rectangle color material', function() {
        var color = Color.fromBytes(0xcc, 0xdd, 0xee, 0xff);
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <GroundOverlay>\
            <color>ffeeddcc</color>\
        </GroundOverlay>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.rectangle.material).toBeInstanceOf(ColorMaterialProperty);
            expect(entity.rectangle.material.color.getValue()).toEqual(color);
        });
    });

    it('GroundOverlay: Sets rectangle coordinates and rotation', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <GroundOverlay>\
            <LatLonBox>\
                <west>3</west>\
                <south>1</south>\
                <east>4</east>\
                <north>2</north>\
                <rotation>45</rotation>\
            </LatLonBox>\
        </GroundOverlay>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.polygon).toBeUndefined();
            expect(entity.rectangle.coordinates.getValue()).toEqualEpsilon(Rectangle.fromDegrees(3, 1, 4, 2), CesiumMath.EPSILON14);
            expect(entity.rectangle.rotation.getValue()).toEqual(Math.PI / 4);
        });
    });

    it('GroundOverlay: Handles wrapping longitude.', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <GroundOverlay>\
            <LatLonBox>\
                <west>-180</west>\
                <south>-90</south>\
                <east>180</east>\
                <north>90</north>\
            </LatLonBox>\
        </GroundOverlay>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.polygon).toBeUndefined();
            expect(entity.rectangle.coordinates.getValue()).toEqual(Rectangle.fromDegrees(-180, -90, 180, 90));
        });
    });

    it('GroundOverlay: Sets polygon coordinates for gx:LatLonQuad', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <GroundOverlay xmlns="http://www.opengis.net/kml/2.2"\
                       xmlns:gx="http://www.google.com/kml/ext/2.2">\
            <gx:LatLonQuad>\
                <coordinates>\
                1,2 3,4 5,6 7,8\
                </coordinates>\
            </gx:LatLonQuad>\
        </GroundOverlay>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.rectangle).toBeUndefined();
            expect(entity.polygon.hierarchy.getValue().positions).toEqualEpsilon(Cartesian3.fromDegreesArray([1, 2, 3, 4, 5, 6, 7, 8]), CesiumMath.EPSILON14);
        });
    });

    it('GroundOverlay: Sets polygon image for gx:LatLonQuad', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <GroundOverlay xmlns="http://www.opengis.net/kml/2.2"\
                       xmlns:gx="http://www.google.com/kml/ext/2.2">\
            <Icon>\
                <href>http://test.invalid/image.png</href>\
            </Icon>\
            <gx:LatLonQuad>\
                <coordinates>\
                1,2 3,4 5,6 7,8\
                </coordinates>\
            </gx:LatLonQuad>\
        </GroundOverlay>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.polygon.material).toBeInstanceOf(ImageMaterialProperty);
            expect(entity.polygon.material.image.getValue()).toEqual('http://test.invalid/image.png');
        });
    });

    it('GroundOverlay: Sets rectangle absolute height', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <GroundOverlay>\
            <altitudeMode>absolute</altitudeMode>\
            <altitude>23</altitude>\
        </GroundOverlay>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.rectangle.height.getValue()).toEqual(23);
        });
    });

    it('Styles: supports local styles with styleUrl', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
            <Document>\
            <Style id="testStyle">\
              <IconStyle>\
                  <scale>3</scale>\
              </IconStyle>\
            </Style>\
            <Placemark>\
              <styleUrl>#testStyle</styleUrl>\
            </Placemark>\
            </Document>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities.length).toEqual(1);
            expect(entities[0].billboard.scale.getValue()).toEqual(3.0);
        });
    });

    it('Styles: supports local styles with styleUrl mssing #', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
            <Document>\
            <Style id="testStyle">\
              <IconStyle>\
                  <scale>3</scale>\
              </IconStyle>\
            </Style>\
            <Placemark>\
              <styleUrl>testStyle</styleUrl>\
            </Placemark>\
            </Document>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities.length).toEqual(1);
            expect(entities[0].billboard.scale.getValue()).toEqual(3.0);
        });
    });

    it('Styles: supports external styles with styleUrl', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
            <Placemark>\
              <styleUrl>Data/KML/externalStyle.kml#testStyle</styleUrl>\
            </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities[0].billboard.scale.getValue()).toEqual(3.0);
        });
    });

    it('Styles: inline styles take precedance over shared styles', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
            <Document>\
            <Style id="testStyle">\
              <IconStyle>\
                  <scale>3</scale>\
                  <Icon>\
                    <href>http://test.invalid</href>\
                  </Icon>\
              </IconStyle>\
            </Style>\
            <Placemark>\
              <styleUrl>#testStyle</styleUrl>\
              <Style>\
                <IconStyle>\
                  <scale>2</scale>\
                  <heading>4</heading>\
                </IconStyle>\
              </Style>\
            </Placemark>\
            </Document>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities.length).toEqual(1);

            var billboard = entities[0].billboard;
            expect(billboard.scale.getValue()).toEqual(2.0);
            expect(billboard.rotation.getValue()).toEqual(CesiumMath.toRadians(-4.0));
            expect(billboard.image.getValue()).toEqual('http://test.invalid');
        });
    });

    it('Styles: colorMode random', function() {
        CesiumMath.setRandomNumberSeed(0);

        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
            <Placemark>\
              <Style>\
                  <IconStyle>\
                      <color>ccffffff</color>\
                      <colorMode>random</colorMode>\
                  </IconStyle>\
              </Style>\
            </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var generatedColor = dataSource.entities.values[0].billboard.color.getValue();
            expect(generatedColor.red).toBeLessThan(1.0);
            expect(generatedColor.green).toBeLessThan(1.0);
            expect(generatedColor.blue).toBeLessThan(1.0);
            expect(generatedColor.alpha).toEqual(0.8);
        });
    });

    it('Styles: colorMode random black', function() {
        CesiumMath.setRandomNumberSeed(0);

        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
            <Placemark>\
              <Style>\
                  <IconStyle>\
                      <color>cc000000</color>\
                      <colorMode>random</colorMode>\
                  </IconStyle>\
              </Style>\
            </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var generatedColor = dataSource.entities.values[0].billboard.color.getValue();
            expect(generatedColor.red).toEqual(0);
            expect(generatedColor.green).toEqual(0);
            expect(generatedColor.blue).toEqual(0);
            expect(generatedColor.alpha).toEqual(0.8);
        });
    });

    it('Styles: Applies expected styles to Point geometry', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Document xmlns="http://www.opengis.net/kml/2.2"\
                  xmlns:gx="http://www.google.com/kml/ext/2.2">\
          <Placemark>' + uberStyle + '\
            <name>TheName</name>\
            <Point>\
              <altitudeMode>absolute</altitudeMode>\
              <coordinates>1,2,3</coordinates>\
            </Point>\
          </Placemark>\
        </Document>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];

            expect(entity.label).toBeDefined();
            expect(entity.label.text.getValue()).toEqual('TheName');
            expect(entity.label.fillColor.getValue()).toEqual(uberLabelColor);
            expect(entity.label.scale.getValue()).toEqual(uberLabelScale);

            expect(entity.billboard.color.getValue()).toEqual(uberIconColor);
            expect(entity.billboard.scale.getValue()).toEqual(uberIconScale);
            expect(entity.billboard.rotation.getValue()).toEqual(uberIconHeading);
            expect(entity.billboard.image.getValue()).toEqual('test.png');
            expect(entity.billboard.pixelOffset.getValue()).toEqual(uberIconHotspot);

            expect(entity.polyline).toBeUndefined();
        });
    });

    it('Styles: Applies expected styles to extruded Point geometry', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Document xmlns="http://www.opengis.net/kml/2.2"\
                  xmlns:gx="http://www.google.com/kml/ext/2.2">\
          <Placemark>' + uberStyle + '\
            <name>TheName</name>\
            <Point>\
              <extrude>1</extrude>\
              <altitudeMode>absolute</altitudeMode>\
              <coordinates>1,2,3</coordinates>\
            </Point>\
          </Placemark>\
        </Document>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];

            expect(entity.label.text.getValue()).toEqual('TheName');
            expect(entity.label.fillColor.getValue()).toEqual(uberLabelColor);
            expect(entity.label.scale.getValue()).toEqual(uberLabelScale);

            expect(entity.billboard.color.getValue()).toEqual(uberIconColor);
            expect(entity.billboard.scale.getValue()).toEqual(uberIconScale);
            expect(entity.billboard.rotation.getValue()).toEqual(uberIconHeading);
            expect(entity.billboard.image.getValue()).toEqual('test.png');
            expect(entity.billboard.pixelOffset.getValue()).toEqual(uberIconHotspot);

            expect(entity.polyline.material).toBeInstanceOf(ColorMaterialProperty);
            expect(entity.polyline.material.color.getValue()).toEqual(uberLineColor);
            expect(entity.polyline.width.getValue()).toEqual(uberLineWidth);
        });
    });

    it('Styles: Applies expected styles to LineString geometry', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Document xmlns="http://www.opengis.net/kml/2.2"\
                  xmlns:gx="http://www.google.com/kml/ext/2.2">\
          <Placemark>' + uberStyle + '\
            <name>TheName</name>\
            <LineString>\
            <coordinates>1,2,3 \
                         4,5,6 \
            </coordinates>\
            </LineString>\
          </Placemark>\
        </Document>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.polyline.material).toBeInstanceOf(ColorMaterialProperty);
            expect(entity.polyline.material.color.getValue()).toEqual(uberLineColor);
            expect(entity.polyline.width.getValue()).toEqual(uberLineWidth);

            expect(entity.label).toBeUndefined();
            expect(entity.wall).toBeUndefined();
        });
    });

    it('Styles: Applies expected styles to extruded LineString geometry', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Document xmlns="http://www.opengis.net/kml/2.2"\
                  xmlns:gx="http://www.google.com/kml/ext/2.2">\
          <Placemark>' + uberStyle + '\
            <name>TheName</name>\
            <LineString>\
            <extrude>1</extrude>\
            <altitudeMode>absolute</altitudeMode>\
            <coordinates>1,2,3 \
                         4,5,6 \
            </coordinates>\
            </LineString>\
          </Placemark>\
        </Document>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];

            expect(entity.wall.material).toBeInstanceOf(ColorMaterialProperty);
            expect(entity.wall.material.color.getValue()).toEqual(uberPolyColor);
            expect(entity.wall.fill.getValue()).toEqual(uberPolyFill);
            expect(entity.wall.outline.getValue()).toEqual(uberPolyOutline);
            expect(entity.wall.outlineColor.getValue()).toEqual(uberLineColor);
            expect(entity.wall.outlineWidth.getValue()).toEqual(uberLineWidth);

            expect(entity.polyline).toBeUndefined();
            expect(entity.label).toBeUndefined();
        });
    });

    it('Styles: Applies expected styles to Polygon geometry', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Document xmlns="http://www.opengis.net/kml/2.2"\
                  xmlns:gx="http://www.google.com/kml/ext/2.2">\
          <Placemark>' + uberStyle + '\
            <name>TheName</name>\
            <Polygon>\
            <extrude>1</extrude>\
            <altitudeMode>absolute</altitudeMode>\
              <outerBoundaryIs>\
                <LinearRing>\
                  <coordinates>\
                    1,2,3\
                    4,5,6\
                    7,8,9\
                   </coordinates>\
                </LinearRing>\
              </outerBoundaryIs>\
            </Polygon>\
          </Placemark>\
        </Document>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];

            expect(entity.polygon.material).toBeInstanceOf(ColorMaterialProperty);
            expect(entity.polygon.material.color.getValue()).toEqual(uberPolyColor);
            expect(entity.polygon.fill.getValue()).toEqual(uberPolyFill);
            expect(entity.polygon.outline.getValue()).toEqual(uberPolyOutline);
            expect(entity.polygon.outlineColor.getValue()).toEqual(uberLineColor);
            expect(entity.polygon.outlineWidth.getValue()).toEqual(uberLineWidth);

            expect(entity.label).toBeUndefined();
        });
    });

    it('Styles: Applies expected styles to gx:Track geometry', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Document xmlns="http://www.opengis.net/kml/2.2"\
                  xmlns:gx="http://www.google.com/kml/ext/2.2">\
          <Placemark>' + uberStyle + '\
            <name>TheName</name>\
            <gx:Track>\
              <altitudeMode>absolute</altitudeMode>\
              <when>2000-01-01T00:00:02Z</when>\
            <gx:coord>7 8 9</gx:coord>\
          </gx:Track>\
          </Placemark>\
        </Document>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];

            expect(entity.label).toBeDefined();
            expect(entity.label.text.getValue()).toEqual('TheName');
            expect(entity.label.fillColor.getValue()).toEqual(uberLabelColor);
            expect(entity.label.scale.getValue()).toEqual(uberLabelScale);

            expect(entity.billboard.color.getValue()).toEqual(uberIconColor);
            expect(entity.billboard.scale.getValue()).toEqual(uberIconScale);
            expect(entity.billboard.rotation.getValue()).toEqual(uberIconHeading);
            expect(entity.billboard.image.getValue()).toEqual('test.png');
            expect(entity.billboard.pixelOffset.getValue()).toEqual(uberIconHotspot);

            expect(entity.path).toBeDefined();
            expect(entity.path.material).toBeInstanceOf(ColorMaterialProperty);
            expect(entity.path.material.color.getValue()).toEqual(uberLineColor);
            expect(entity.path.width.getValue()).toEqual(uberLineWidth);
            expect(entity.path.leadTime.getValue()).toEqual(0);
            expect(entity.path.trailTime).toBeUndefined();

            expect(entity.polyline).toBeUndefined();
        });
    });

    it('Styles: Applies expected styles to extruded gx:Track geometry', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Document xmlns="http://www.opengis.net/kml/2.2"\
                  xmlns:gx="http://www.google.com/kml/ext/2.2">\
          <Placemark>' + uberStyle + '\
            <name>TheName</name>\
            <gx:Track>\
              <extrude>1</extrude>\
              <altitudeMode>absolute</altitudeMode>\
              <when>2000-01-01T00:00:02Z</when>\
              <gx:coord>7 8 9</gx:coord>\
            </gx:Track>\
          </Placemark>\
        </Document>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];

            expect(entity.label.text.getValue()).toEqual('TheName');
            expect(entity.label.fillColor.getValue()).toEqual(uberLabelColor);
            expect(entity.label.scale.getValue()).toEqual(uberLabelScale);

            expect(entity.billboard.color.getValue()).toEqual(uberIconColor);
            expect(entity.billboard.scale.getValue()).toEqual(uberIconScale);
            expect(entity.billboard.rotation.getValue()).toEqual(uberIconHeading);
            expect(entity.billboard.image.getValue()).toEqual('test.png');
            expect(entity.billboard.pixelOffset.getValue()).toEqual(uberIconHotspot);

            expect(entity.path).toBeDefined();
            expect(entity.path.material).toBeInstanceOf(ColorMaterialProperty);
            expect(entity.path.material.color.getValue()).toEqual(uberLineColor);
            expect(entity.path.width.getValue()).toEqual(uberLineWidth);
            expect(entity.path.leadTime.getValue()).toEqual(0);
            expect(entity.path.trailTime).toBeUndefined();

            expect(entity.polyline.material).toBeInstanceOf(ColorMaterialProperty);
            expect(entity.polyline.material.color.getValue()).toEqual(uberLineColor);
            expect(entity.polyline.width.getValue()).toEqual(uberLineWidth);
        });
    });

    it('Styles: Applies expected styles to gx:MultiTrack geometry', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Document xmlns="http://www.opengis.net/kml/2.2"\
                  xmlns:gx="http://www.google.com/kml/ext/2.2">\
          <Placemark>' + uberStyle + '\
            <name>TheName</name>\
            <gx:MultiTrack>\
              <gx:Track>\
                <altitudeMode>absolute</altitudeMode>\
                <when>2000-01-01T00:00:02Z</when>\
              <gx:coord>7 8 9</gx:coord>\
              </gx:Track>\
            </gx:MultiTrack>\
          </Placemark>\
        </Document>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];

            expect(entity.label).toBeDefined();
            expect(entity.label.text.getValue()).toEqual('TheName');
            expect(entity.label.fillColor.getValue()).toEqual(uberLabelColor);
            expect(entity.label.scale.getValue()).toEqual(uberLabelScale);

            expect(entity.billboard.color.getValue()).toEqual(uberIconColor);
            expect(entity.billboard.scale.getValue()).toEqual(uberIconScale);
            expect(entity.billboard.rotation.getValue()).toEqual(uberIconHeading);
            expect(entity.billboard.image.getValue()).toEqual('test.png');
            expect(entity.billboard.pixelOffset.getValue()).toEqual(uberIconHotspot);

            expect(entity.path).toBeDefined();
            expect(entity.path.material).toBeInstanceOf(ColorMaterialProperty);
            expect(entity.path.material.color.getValue()).toEqual(uberLineColor);
            expect(entity.path.width.getValue()).toEqual(uberLineWidth);
            expect(entity.path.leadTime.getValue()).toEqual(0);
            expect(entity.path.trailTime).toBeUndefined();

            expect(entity.polyline).toBeUndefined();
        });
    });

    it('Styles: Applies expected styles to extruded gx:MultiTrack geometry', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Document xmlns="http://www.opengis.net/kml/2.2"\
                  xmlns:gx="http://www.google.com/kml/ext/2.2">\
          <Placemark>' + uberStyle + '\
            <name>TheName</name>\
            <gx:MultiTrack>\
              <gx:Track>\
                <extrude>1</extrude>\
                <altitudeMode>absolute</altitudeMode>\
                <when>2000-01-01T00:00:02Z</when>\
                <gx:coord>7 8 9</gx:coord>\
              </gx:Track>\
            </gx:MultiTrack>\
          </Placemark>\
        </Document>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];

            expect(entity.label.text.getValue()).toEqual('TheName');
            expect(entity.label.fillColor.getValue()).toEqual(uberLabelColor);
            expect(entity.label.scale.getValue()).toEqual(uberLabelScale);

            expect(entity.billboard.color.getValue()).toEqual(uberIconColor);
            expect(entity.billboard.scale.getValue()).toEqual(uberIconScale);
            expect(entity.billboard.rotation.getValue()).toEqual(uberIconHeading);
            expect(entity.billboard.image.getValue()).toEqual('test.png');
            expect(entity.billboard.pixelOffset.getValue()).toEqual(uberIconHotspot);

            expect(entity.path).toBeDefined();
            expect(entity.path.material).toBeInstanceOf(ColorMaterialProperty);
            expect(entity.path.material.color.getValue()).toEqual(uberLineColor);
            expect(entity.path.width.getValue()).toEqual(uberLineWidth);
            expect(entity.path.leadTime.getValue()).toEqual(0);
            expect(entity.path.trailTime).toBeUndefined();

            expect(entity.polyline.material).toBeInstanceOf(ColorMaterialProperty);
            expect(entity.polyline.material.color.getValue()).toEqual(uberLineColor);
            expect(entity.polyline.width.getValue()).toEqual(uberLineWidth);
        });
    });

    xit('Styles: Applies local StyleMap', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Document xmlns="http://www.opengis.net/kml/2.2">\
          <Placemark>\
            <StyleMap>\
              <Pair>\
                <key>normal</key>\
                <Style>\
                  <IconStyle>\
                    <scale>2</scale>\
                  </IconStyle>\
                </Style>\
              </Pair>\
            </StyleMap>\
          </Placemark>\
        </Document>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.billboard.scale.getValue()).toBe(2.0);
        });
    });

    it('Styles: Applies normal styleUrl StyleMap', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Document xmlns="http://www.opengis.net/kml/2.2">\
          <StyleMap id="styleMapExample">\
            <Pair>\
              <key>normal</key>\
              <Style>\
                <IconStyle>\
                  <scale>2</scale>\
                </IconStyle>\
              </Style>\
            </Pair>\
          </StyleMap>\
          <Placemark>\
            <styleUrl>#styleMapExample</styleUrl>\
          </Placemark>\
        </Document>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml"), function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.billboard.scale.getValue()).toBe(2.0);
        });
    });

    it('Styles: Applies normal StyleMap containing styleUrl', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Document xmlns="http://www.opengis.net/kml/2.2">\
          <Style id="normalStyle">\
            <IconStyle>\
              <scale>2</scale>\
            </IconStyle>\
          </Style>\
          <StyleMap id="styleMapExample">\
            <Pair>\
              <key>normal</key>\
              <styleUrl>#normalStyle</styleUrl>\
            </Pair>\
          </StyleMap>\
          <Placemark>\
            <styleUrl>#styleMapExample</styleUrl>\
            </Placemark>\
        </Document>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.billboard.scale.getValue()).toBe(2.0);
        });
    });

    it('IconStyle: handles empty element', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark>\
            <Style>\
              <IconStyle>\
              </IconStyle>\
            </Style>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities[0].billboard).toBeDefined();
        });
    });

    it('IconStyle: Sets billboard image absolute path', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark>\
              <Style>\
                  <IconStyle>\
                      <Icon>\
                          <href>http://test.invalid/image.png</href>\
                      </Icon>\
                  </IconStyle>\
              </Style>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            var billboard = entities[0].billboard;
            expect(billboard.image.getValue()).toEqual('http://test.invalid/image.png');
        });
    });

    it('IconStyle: Sets billboard image relative path', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark>\
              <Style>\
                  <IconStyle>\
                      <Icon>\
                          <href>image.png</href>\
                      </Icon>\
                  </IconStyle>\
              </Style>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml"), {
            sourceUri : 'http://test.invalid'
        }).then(function(dataSource) {
            var entities = dataSource.entities.values;
            var billboard = entities[0].billboard;
            expect(billboard.image.getValue()).toEqual('http://test.invalid/image.png');
        });
    });

    it('IconStyle: Sets billboard image inside KMZ', function() {
        return KmlDataSource.load('Data/KML/simple.kmz').then(function(dataSource) {
            var entities = dataSource.entities.values;
            var billboard = entities[0].billboard;
            expect(billboard.image.getValue()).toEqual(image);
        });
    });

    it('IconStyle: Sets billboard image with proxy', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark>\
              <Style>\
                  <IconStyle>\
                      <Icon>\
                          <href>image.png</href>\
                      </Icon>\
                  </IconStyle>\
              </Style>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml"), {
            sourceUri : 'http://test.invalid',
            proxy : new DefaultProxy('/proxy/')
        }).then(function(dataSource) {
            var entities = dataSource.entities.values;
            var billboard = entities[0].billboard;
            expect(billboard.image.getValue()).toEqual(dataSource._proxy.getURL('http://test.invalid/image.png'));
        });
    });

    it('IconStyle: Sets billboard image with subregion', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark xmlns="http://www.opengis.net/kml/2.2"\
                     xmlns:gx="http://www.google.com/kml/ext/2.2">\
            <Style>\
              <IconStyle>\
                <Icon>\
                  <href>whiteShapes.png</href>\
                  <gx:x>49</gx:x>\
                  <gx:y>43</gx:y>\
                  <gx:w>18</gx:w>\
                  <gx:h>18</gx:h>\
                </Icon>\
              </IconStyle>\
            </Style>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var billboard = dataSource.entities.values[0].billboard;
            expect(billboard.image.getValue()).toEqual('whiteShapes.png');
            expect(billboard.imageSubRegion.getValue()).toEqual(new BoundingRectangle(49, 43, 18, 18));
        });
    });

    it('IconStyle: Sets billboard image with hotSpot fractions', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
                  <Placemark>\
                    <Style>\
                      <IconStyle>\
                        <hotSpot x="0.25"  y="0.75" xunits="fraction" yunits="fraction"/>\
                      </IconStyle>\
                    </Style>\
                  </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var billboard = dataSource.entities.values[0].billboard;
            expect(billboard.pixelOffset.getValue()).toEqual(new Cartesian2(8, 8));
        });
    });

    it('IconStyle: Sets billboard image with hotSpot pixels', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
                  <Placemark>\
                    <Style>\
                      <IconStyle>\
                        <hotSpot x="1"  y="2" xunits="pixels" yunits="pixels"/>\
                      </IconStyle>\
                    </Style>\
                  </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var billboard = dataSource.entities.values[0].billboard;
            expect(billboard.pixelOffset.getValue()).toEqual(new Cartesian2(15, -14));
        });
    });

    it('IconStyle: Sets billboard image with hotSpot insetPixels', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
                  <Placemark>\
                    <Style>\
                      <IconStyle>\
                        <hotSpot x="1"  y="2" xunits="insetPixels" yunits="insetPixels"/>\
                      </IconStyle>\
                    </Style>\
                  </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var billboard = dataSource.entities.values[0].billboard;
            expect(billboard.pixelOffset.getValue()).toEqual(new Cartesian2(-15, -18));
        });
    });

    it('IconStyle: Sets color', function() {
        var color = Color.fromBytes(0xcc, 0xdd, 0xee, 0xff);
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark>\
            <Style>\
              <IconStyle>\
                <color>ffeeddcc</color>\
              </IconStyle>\
            </Style>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities[0].billboard.color.getValue()).toEqual(color);
        });
    });

    it('IconStyle: Sets scale', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
            <Placemark>\
            <Style>\
              <IconStyle>\
                <scale>2.2</scale>\
              </IconStyle>\
            </Style>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities[0].billboard.scale.getValue()).toEqual(2.2);
        });
    });

    it('IconStyle: Sets heading', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
            <Placemark>\
            <Style>\
              <IconStyle>\
                <heading>4</heading>\
              </IconStyle>\
            </Style>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities[0].billboard.rotation.getValue()).toEqual(CesiumMath.toRadians(-4));
            expect(entities[0].billboard.alignedAxis.getValue()).toEqual(Cartesian3.UNIT_Z);
        });
    });

    it('BalloonStyle: specify all properties', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Placemark id="The ID">\
            <Style>\
            <BalloonStyle>\
                <bgColor>00224466</bgColor>\
                <textColor>66442200</textColor>\
                <text>$[name] $[description] $[address] $[Snippet] $[id] $[prop1/displayName] $[prop1] $[prop2/displayName] $[prop2]</text>\
            </BalloonStyle>\
            </Style>\
            <name>The Name</name>\
            <description>The Description</description>\
            <address>The Address</address>\
            <Snippet>The Snippet</Snippet>\
            <ExtendedData>\
            <Data name="prop1">\
                <displayName>The Property</displayName>\
                <value>The Value</value>\
            </Data>\
            </ExtendedData>\
        </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];

            var element = document.createElement('div');
            element.innerHTML = entity.description.getValue();

            var div = element.firstChild;
            expect(div.style['word-wrap']).toEqual('break-word');
            expect(div.style['background-color']).toEqual('rgba(102, 68, 34, 0)');
            expect(div.style.color).toEqual('rgba(0, 34, 68, 0.4)');
            expect(div.textContent).toEqual('The Name The Description The Address The Snippet The ID The Property The Value $[prop2/displayName] $[prop2]');
        });
    });

    it('BalloonStyle: entity replacement removes missing values', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Placemark>\
            <Style>\
            <BalloonStyle>\
                <text>$[name] $[description] $[address] $[Snippet]</text>\
            </BalloonStyle>\
            </Style>\
        </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];

            var element = document.createElement('div');
            element.innerHTML = entity.description.getValue();

            var div = element.firstChild;
            expect(div.textContent).toEqual('   ');
        });
    });

    it('BalloonStyle: description without BalloonStyle', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Placemark>\
            <description>The Description</description>\
        </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];

            var element = document.createElement('div');
            element.innerHTML = entity.description.getValue();

            var div = element.firstChild;
            expect(div.style['word-wrap']).toEqual('break-word');
            expect(div.style['background-color']).toEqual('rgb(255, 255, 255)');
            expect(div.style.color).toEqual('rgb(0, 0, 0)');
            expect(div.textContent).toEqual('The Description');
        });
    });

    it('BalloonStyle: creates table from ExtendedData', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
            <Placemark>\
                <ExtendedData>\
                    <Data name="prop1">\
                        <displayName>Property 1</displayName>\
                        <value>1</value>\
                    </Data>\
                    <Data name="prop2">\
                        <value>2</value>\
                    </Data>\
                    <Data name="prop3">\
                        <displayName>Property 3</displayName>\
                    </Data>\
                </ExtendedData>\
            </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];

            var element = document.createElement('div');
            element.innerHTML = entity.description.getValue();

            var div = element.firstChild;
            expect(div.style['word-wrap']).toEqual('break-word');
            expect(div.style['background-color']).toEqual('rgb(255, 255, 255)');
            expect(div.style.color).toEqual('rgb(0, 0, 0)');

            var table = div.firstChild;
            expect(table.localName).toEqual('table');

            expect(table.rows.length).toBe(3);
            expect(table.rows[0].cells.length).toEqual(2);
            expect(table.rows[1].cells.length).toEqual(2);
            expect(table.rows[2].cells.length).toEqual(2);

            expect(table.rows[0].cells[0].textContent).toEqual('Property 1');
            expect(table.rows[1].cells[0].textContent).toEqual('prop2');
            expect(table.rows[2].cells[0].textContent).toEqual('Property 3');

            expect(table.rows[0].cells[1].textContent).toEqual('1');
            expect(table.rows[1].cells[1].textContent).toEqual('2');
            expect(table.rows[2].cells[1].textContent).toEqual('');
        });
    });

    it('BalloonStyle: does not create a description for empty ExtendedData', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
            <Placemark>\
                <ExtendedData>\
                </ExtendedData>\
            </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.description).toBeUndefined();
        });
    });

    it('BalloonStyle: description creates links from text', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Placemark>\
            <description>http://cesiumjs.org</description>\
        </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];

            var element = document.createElement('div');
            element.innerHTML = entity.description.getValue();

            var a = element.firstChild.firstChild;
            expect(a.localName).toEqual('a');
            expect(a.getAttribute('href')).toEqual('http://cesiumjs.org');
            expect(a.getAttribute('target')).toEqual('_blank');
        });
    });

    it('BalloonStyle: description retargets existing links to _blank', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Placemark>\
            <description><![CDATA[<a href="http://cesiumjs.org" target="_self">Homepage</a>]]></description>\
        </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];

            var element = document.createElement('div');
            element.innerHTML = entity.description.getValue();

            var a = element.firstChild.firstChild;
            expect(a.localName).toEqual('a');
            expect(a.textContent).toEqual('Homepage');
            expect(a.getAttribute('href')).toEqual('http://cesiumjs.org');
            expect(a.getAttribute('target')).toEqual('_blank');
        });
    });

    it('BalloonStyle: description does not create links from non-explicit urls', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Placemark>\
            <description>states.id google.com</description>\
        </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];

            var element = document.createElement('div');
            element.innerHTML = entity.description.getValue();

            var div = element.firstChild;
            expect(div.innerHTML).toEqual('states.id google.com');
        });
    });

    it('BalloonStyle: description is rewritten for embedded kmz links and images', function() {
        return KmlDataSource.load('Data/KML/simple.kmz').then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            var description = entity.description.getValue();
            var div = document.createElement('div');
            div.innerHTML = description;

            expect(div.textContent).toEqual('image.png image.png');
            var children = div.firstChild.querySelectorAll('*');
            expect(children.length).toEqual(2);

            var link = children[0];
            expect(link.localName).toEqual('a');
            expect(link.getAttribute('href')).toEqual(image);
            expect(link.getAttribute('download')).toEqual('image.png');

            var img = children[1];
            expect(img.localName).toEqual('img');
            expect(img.src).toEqual(image);
        });
    });

    it('LabelStyle: Sets defaults', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark>\
            <Style>\
              <LabelStyle>\
              </LabelStyle>\
            </Style>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            var label = entities[0].label;
            expect(label).toBeDefined();

            expect(label.text).toBeUndefined();
            expect(label.fillColor).toBeUndefined();
            expect(label.outlineColor).toBeUndefined();
            expect(label.outlineWidth).toBeUndefined();
            expect(label.show).toBeUndefined();
            expect(label.scale).toBeUndefined();
            expect(label.verticalOrigin).toBeUndefined();
            expect(label.eyeOffset).toBeUndefined();
            expect(label.pixelOffsetScaleByDistance).toBeUndefined();

        expect(label.text).toBeUndefined();
        expect(label.fillColor).toBeUndefined();
        expect(label.outlineColor).toBeUndefined();
        expect(label.outlineWidth).toBeUndefined();
        expect(label.show).toBeUndefined();
        expect(label.scale).toBeUndefined();
        expect(label.verticalOrigin).toBeUndefined();
        expect(label.eyeOffset).toBeUndefined();
        expect(label.pixelOffsetScaleByDistance).toBeUndefined();

        expect(label.font.getValue()).toEqual('16px sans-serif');
        expect(label.style.getValue()).toEqual(LabelStyle.FILL_AND_OUTLINE);
        expect(label.horizontalOrigin.getValue()).toEqual(HorizontalOrigin.LEFT);
        expect(label.pixelOffset.getValue()).toEqual(new Cartesian2(17, 0));
        expect(label.translucencyByDistance.getValue()).toEqual(new NearFarScalar(3000000, 1.0, 5000000, 0.0));
        });
    });

    it('LabelStyle: Sets color', function() {
        var color = Color.fromBytes(0xcc, 0xdd, 0xee, 0xff);
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark>\
            <Style>\
              <LabelStyle>\
                <color>ffeeddcc</color>\
              </LabelStyle>\
            </Style>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities[0].label.fillColor.getValue()).toEqual(color);
        });
    });

    it('LabelStyle: Sets scale', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
            <Placemark>\
              <Style>\
                <IconStyle>\
                    <scale>2</scale>\
                </IconStyle>\
                <LabelStyle>\
                </LabelStyle>\
              </Style>\
            </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities[0].label.pixelOffset.getValue()).toEqual(new Cartesian2(33, 0));
        });
    });

    it('LabelStyle: Sets pixelOffset when billboard scaled', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark>\
            <Style>\
              <IconStyle>\
                <scale>3</scale>\
              </IconStyle>\
            </Style>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities[0].label.pixelOffset.getValue()).toEqual(new Cartesian3(3 * 16 + 1, 0));
        });
    });

    it('LabelStyle: Sets pixelOffset when billboard scaled', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark>\
            <Style>\
              <IconStyle>\
                <scale>0</scale>\
              </IconStyle>\
            </Style>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities[0].label.pixelOffset).toBeUndefined();
            expect(entities[0].label.horizontalOrigin).toBeUndefined();
        });
    });

    it('LineStyle: Sets defaults', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark>\
            <Style>\
              <LineStyle>\
              </LineStyle>\
            </Style>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            var polyline = entities[0].polyline;
            expect(polyline).toBeDefined();

            expect(polyline.positions).toBeUndefined();
            expect(polyline.followSurface).toBeUndefined();
            expect(polyline.width).toBeUndefined();
            expect(polyline.show).toBeUndefined();
            expect(polyline.material).toBeUndefined();
            expect(polyline.granularity).toBeUndefined();
        });
    });

    it('LineStyle: Sets color', function() {
        var color = Color.fromBytes(0xcc, 0xdd, 0xee, 0xff);
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark>\
            <Style>\
              <LineStyle>\
                <color>ffeeddcc</color>\
              </LineStyle>\
            </Style>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            var polyline = entities[0].polyline;
            expect(polyline.material).toBeInstanceOf(ColorMaterialProperty);
            expect(polyline.material.color.getValue()).toEqual(color);
        });
    });

    it('LineStyle: Sets width', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark>\
            <Style>\
              <LineStyle>\
                <width>2.75</width>\
              </LineStyle>\
            </Style>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            var polyline = entities[0].polyline;
            expect(polyline.width.getValue()).toEqual(2.75);
        });
    });

    it('PolyStyle: Sets defaults', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark>\
            <Style>\
              <PolyStyle>\
              </PolyStyle>\
            </Style>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            var polygon = entities[0].polygon;
            expect(polygon).toBeDefined();

            expect(polygon.hierarchy).toBeUndefined();
            expect(polygon.height).toBeUndefined();
            expect(polygon.extrudedHeight).toBeUndefined();
            expect(polygon.show).toBeUndefined();
            expect(polygon.fill).toBeUndefined();
            expect(polygon.material).toBeUndefined();
            expect(polygon.outlineWidth).toBeUndefined();
            expect(polygon.stRotation).toBeUndefined();
            expect(polygon.granularity).toBeUndefined();
            expect(polygon.perPositionHeight).toBeUndefined();

            expect(polygon.outline.getValue()).toBe(true);
            expect(polygon.outlineColor.getValue()).toEqual(Color.WHITE);
        });
    });

    it('PolyStyle: Sets color', function() {
        var color = Color.fromBytes(0xcc, 0xdd, 0xee, 0xff);
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark>\
            <Style>\
              <PolyStyle>\
                <color>ffeeddcc</color>\
              </PolyStyle>\
            </Style>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            var polygon = entities[0].polygon;
            expect(polygon.material).toBeInstanceOf(ColorMaterialProperty);
            expect(polygon.material.color.getValue()).toEqual(color);
        });
    });

    it('PolyStyle: Sets fill', function() {
        var color = Color.fromBytes(0xcc, 0xdd, 0xee, 0xff);
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark>\
            <Style>\
              <PolyStyle>\
                <fill>0</fill>\
              </PolyStyle>\
            </Style>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            var polygon = entities[0].polygon;
            expect(polygon.fill.getValue()).toEqual(false);
        });
    });

    it('PolyStyle: Sets outline', function() {
        var color = Color.fromBytes(0xcc, 0xdd, 0xee, 0xff);
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark>\
            <Style>\
              <PolyStyle>\
                <outline>0</outline>\
              </PolyStyle>\
            </Style>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            var polygon = entities[0].polygon;
            expect(polygon.outline.getValue()).toEqual(false);
        });
    });

    it('Folder: sets parent property', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Folder id="parent">\
            <Placemark id="child">\
            </Placemark>\
        </Folder>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var folder = dataSource.entities.getById('parent');
            var placemark = dataSource.entities.getById('child');

            expect(dataSource.entities.values.length).toBe(2);
            expect(folder).toBeDefined();
            expect(placemark.parent).toBe(folder);
        });
    });

    it('Geometry Point: handles empty Point', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark>\
            <Point>\
            </Point>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities.length).toEqual(1);
            expect(entities[0].position).toBeUndefined();
            expect(entities[0].polyline).toBeUndefined();
        });
    });

    it('Geometry Point: handles invalid coordinates', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark>\
            <Point>\
            <coordinates>1,2,3,4</coordinates>\
            </Point>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities.length).toEqual(1);
            expect(entities[0].position).toBeUndefined();
            expect(entities[0].polyline).toBeUndefined();
        });
    });

    it('Geometry Point: sets position clampToGround (the default)', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark>\
            <Point>\
              <coordinates>1,2,3</coordinates>\
            </Point>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities.length).toEqual(1);
            expect(entities[0].position.getValue(Iso8601.MINIMUM_VALUE)).toEqualEpsilon(Cartesian3.fromDegrees(1, 2, 0), CesiumMath.EPSILON13);
            expect(entities[0].polyline).toBeUndefined();
        });
    });

    it('Geometry Point: sets position altitudeMode absolute', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark>\
            <Point>\
              <altitudeMode>absolute</altitudeMode>\
              <coordinates>1,2,3</coordinates>\
            </Point>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities.length).toEqual(1);
            expect(entities[0].position.getValue(Iso8601.MINIMUM_VALUE)).toEqual(Cartesian3.fromDegrees(1, 2, 3));
            expect(entities[0].polyline).toBeUndefined();
        });
    });

    it('Geometry Point: sets position altitudeMode relativeToGround', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark>\
            <Point>\
              <altitudeMode>absolute</altitudeMode>\
              <coordinates>1,2,3</coordinates>\
            </Point>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities.length).toEqual(1);
            expect(entities[0].position.getValue(Iso8601.MINIMUM_VALUE)).toEqual(Cartesian3.fromDegrees(1, 2, 3));
            expect(entities[0].polyline).toBeUndefined();
        });
    });

    it('Geometry Point: does not extrude when altitudeMode is clampToGround', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark>\
            <Point>\
              <altitudeMode>clampToGround</altitudeMode>\
              <coordinates>1,2</coordinates>\
              <extrude>1</extrude>\
            </Point>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities.length).toEqual(1);
            expect(entities[0].position.getValue(Iso8601.MINIMUM_VALUE)).toEqual(Cartesian3.fromDegrees(1, 2));
            expect(entities[0].polyline).toBeUndefined();
        });
    });

    it('Geometry Point: does not extrude when gx:altitudeMode is clampToSeaFloor', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark xmlns="http://www.opengis.net/kml/2.2"\
                     xmlns:gx="http://www.google.com/kml/ext/2.2">\
            <Point>\
              <gx:altitudeMode>clampToSeaFloor</gx:altitudeMode>\
              <coordinates>1,2</coordinates>\
              <extrude>1</extrude>\
            </Point>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities.length).toEqual(1);
            expect(entities[0].position.getValue(Iso8601.MINIMUM_VALUE)).toEqual(Cartesian3.fromDegrees(1, 2));
            expect(entities[0].polyline).toBeUndefined();
        });
    });

    it('Geometry Point: extrudes when altitudeMode is relativeToGround', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark>\
            <Point>\
              <altitudeMode>relativeToGround</altitudeMode>\
              <coordinates>1,2,3</coordinates>\
              <extrude>1</extrude>\
            </Point>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities.length).toEqual(1);
            expect(entities[0].position.getValue(Iso8601.MINIMUM_VALUE)).toEqual(Cartesian3.fromDegrees(1, 2, 3));
            expect(entities[0].polyline).toBeDefined();

            var positions = entities[0].polyline.positions.getValue(Iso8601.MINIMUM_VALUE);
            expect(positions).toEqualEpsilon([Cartesian3.fromDegrees(1, 2, 3), Cartesian3.fromDegrees(1, 2, 0)], CesiumMath.EPSILON13);
        });
    });

    it('Geometry Point: extrudes when gx:altitudeMode is relativeToSeaFloor', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark xmlns="http://www.opengis.net/kml/2.2"\
                     xmlns:gx="http://www.google.com/kml/ext/2.2">\
            <Point>\
              <gx:altitudeMode>relativeToSeaFloor</gx:altitudeMode>\
              <coordinates>1,2,3</coordinates>\
              <extrude>1</extrude>\
            </Point>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities.length).toEqual(1);
            expect(entities[0].position.getValue(Iso8601.MINIMUM_VALUE)).toEqual(Cartesian3.fromDegrees(1, 2, 3));
            expect(entities[0].polyline).toBeDefined();

            var positions = entities[0].polyline.positions.getValue(Iso8601.MINIMUM_VALUE);
            expect(positions).toEqualEpsilon([Cartesian3.fromDegrees(1, 2, 3), Cartesian3.fromDegrees(1, 2, 0)], CesiumMath.EPSILON13);
        });
    });

    it('Geometry Point: extrudes when altitudeMode is absolute', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark>\
            <Point>\
              <altitudeMode>absolute</altitudeMode>\
              <coordinates>1,2,3</coordinates>\
              <extrude>1</extrude>\
            </Point>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities.length).toEqual(1);
            expect(entities[0].position.getValue(Iso8601.MINIMUM_VALUE)).toEqual(Cartesian3.fromDegrees(1, 2, 3));
            expect(entities[0].polyline).toBeDefined();

            var positions = entities[0].polyline.positions.getValue(Iso8601.MINIMUM_VALUE);
            expect(positions).toEqualEpsilon([Cartesian3.fromDegrees(1, 2, 3), Cartesian3.fromDegrees(1, 2, 0)], CesiumMath.EPSILON13);
        });
    });

    it('Geometry Polygon: without holes', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark>\
            <Polygon>\
              <outerBoundaryIs>\
                <LinearRing>\
                  <coordinates>\
                    1,2,3\
                    4,5,6\
                    7,8,9\
                 </coordinates>\
                </LinearRing>\
              </outerBoundaryIs>\
            </Polygon>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            var coordinates = [Cartesian3.fromDegrees(1, 2, 3), Cartesian3.fromDegrees(4, 5, 6), Cartesian3.fromDegrees(7, 8, 9)];
            expect(entity.polygon.hierarchy.getValue().positions).toEqual(coordinates);
        });
    });

    it('Geometry Polygon: with holes', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark>\
            <Polygon>\
            <outerBoundaryIs>\
            <LinearRing>\
              <coordinates>\
                1,2,3\
                4,5,6\
                7,8,9\
             </coordinates>\
            </LinearRing>\
            </outerBoundaryIs>\
            <innerBoundaryIs>\
            <LinearRing>\
              <coordinates>\
                1.1,2.1,3.1\
                4.1,5.1,6.1\
                7.1,8.1,9.1\
             </coordinates>\
            </LinearRing>\
            </innerBoundaryIs>\
            <innerBoundaryIs>\
            <LinearRing>\
              <coordinates>\
                1.2,2.2,3.2\
                4.2,5.2,6.2\
                7.2,8.2,9.2\
             </coordinates>\
            </LinearRing>\
            </innerBoundaryIs>\
            </Polygon>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            var coordinates = [Cartesian3.fromDegrees(1, 2, 3), Cartesian3.fromDegrees(4, 5, 6), Cartesian3.fromDegrees(7, 8, 9)];
            var holeCoordinates = [Cartesian3.fromDegrees(1.1, 2.1, 3.1), Cartesian3.fromDegrees(4.1, 5.1, 6.1), Cartesian3.fromDegrees(7.1, 8.1, 9.1)];
            var holeCoordinates2 = [Cartesian3.fromDegrees(1.2, 2.2, 3.2), Cartesian3.fromDegrees(4.2, 5.2, 6.2), Cartesian3.fromDegrees(7.2, 8.2, 9.2)];

            var hierarchy = entity.polygon.hierarchy.getValue();
            expect(hierarchy.positions).toEqual(coordinates);
            expect(hierarchy.holes.length).toEqual(2);

            expect(hierarchy.holes[0].positions).toEqual(holeCoordinates);
            expect(hierarchy.holes[0].holes).toEqual([]);

            expect(hierarchy.holes[1].positions).toEqual(holeCoordinates2);
            expect(hierarchy.holes[1].holes).toEqual([]);
        });
    });

    it('Geometry Polygon: altitudeMode relativeToGround and can extrude', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark>\
            <Polygon>\
              <altitudeMode>relativeToGround</altitudeMode>\
              <extrude>1</extrude>\
            </Polygon>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.polygon.perPositionHeight.getValue()).toEqual(true);
            expect(entity.polygon.extrudedHeight.getValue()).toEqual(0);
        });
    });

    it('Geometry Polygon: altitudeMode absolute and can extrude', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark>\
            <Polygon>\
              <altitudeMode>absolute</altitudeMode>\
              <extrude>1</extrude>\
            </Polygon>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.polygon.perPositionHeight.getValue()).toEqual(true);
            expect(entity.polygon.extrudedHeight.getValue()).toEqual(0);
        });
    });

    it('Geometry Polygon: altitudeMode clampToGround and cannot extrude', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark xmlns="http://www.opengis.net/kml/2.2"\
                     xmlns:gx="http://www.google.com/kml/ext/2.2">\
            <Polygon>\
              <altitudeMode>clampToGround</altitudeMode>\
              <extrude>1</extrude>\
            </Polygon>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.polygon.perPositionHeight).toBeUndefined();
            expect(entity.polygon.extrudedHeight).toBeUndefined();
        });
    });

    it('Geometry Polygon: gx:altitudeMode relativeToSeaFloor and can extrude', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark xmlns="http://www.opengis.net/kml/2.2"\
                     xmlns:gx="http://www.google.com/kml/ext/2.2">\
            <Polygon>\
              <gx:altitudeMode>relativeToSeaFloor</gx:altitudeMode>\
              <extrude>1</extrude>\
            </Polygon>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.polygon.perPositionHeight.getValue()).toEqual(true);
            expect(entity.polygon.extrudedHeight.getValue()).toEqual(0);
        });
    });

    it('Geometry Polygon: gx:altitudeMode clampToSeaFloor and can extrude', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark xmlns="http://www.opengis.net/kml/2.2"\
                     xmlns:gx="http://www.google.com/kml/ext/2.2">\
            <Polygon>\
              <gx:altitudeMode>clampToSeaFloor</gx:altitudeMode>\
              <extrude>1</extrude>\
            </Polygon>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.polygon.perPositionHeight).toBeUndefined();
            expect(entity.polygon.extrudedHeight).toBeUndefined();
        });
    });

    it('Geometry LineString: handles empty element', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
            <Placemark>\
            <LineString>\
            </LineString>\
            </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities.length).toEqual(1);

            var entity = entities[0];
            expect(entity.wall).toBeUndefined();
            expect(entity.polyline).toBeDefined();
            expect(entity.polyline.followSurface.getValue()).toEqual(false);
        });
    });

    it('Geometry LineString: sets positions (clampToGround default)', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
            <Placemark>\
            <LineString>\
              <coordinates>1,2,3 \
                           4,5,6 \
              </coordinates>\
            </LineString>\
            </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities.length).toEqual(1);

            var entity = entities[0];
            expect(entity.wall).toBeUndefined();
            expect(entity.polyline).toBeDefined();

            var positions = entity.polyline.positions.getValue(Iso8601.MINIMUM_VALUE);
            expect(positions).toEqualEpsilon([Cartesian3.fromDegrees(1, 2), Cartesian3.fromDegrees(4, 5)], CesiumMath.EPSILON10);
            expect(entity.polyline.followSurface.getValue()).toEqual(false);
        });
    });

    it('Geometry LineString: sets wall positions when extruded', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
            <Placemark>\
            <LineString>\
              <altitudeMode>absolute</altitudeMode>\
              <extrude>1</extrude>\
              <coordinates>1,2,3 \
                           4,5,6 \
              </coordinates>\
            </LineString>\
            </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities.length).toEqual(1);

            var entity = entities[0];
            expect(entity.polyline).toBeUndefined();
            expect(entity.wall).toBeDefined();

            var positions = entity.wall.positions.getValue(Iso8601.MINIMUM_VALUE);
            expect(positions).toEqualEpsilon([Cartesian3.fromDegrees(1, 2, 3), Cartesian3.fromDegrees(4, 5, 6)], CesiumMath.EPSILON10);
        });
    });

    it('Geometry LineString: sets positions altitudeMode clampToGround, cannot extrude, can tessellate', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
            <Placemark>\
            <LineString>\
                <altitudeMode>clampToGround</altitudeMode>\
                <extrude>1</extrude>\
                <tessellate>1</tessellate>\
                <coordinates>1,2,3 4,5,6</coordinates>\
            </LineString>\
            </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities.length).toEqual(1);

            var entity = entities[0];
            expect(entity.polyline.followSurface).toBeUndefined();
            var positions = entity.polyline.positions.getValue(Iso8601.MINIMUM_VALUE);
            expect(positions).toEqualEpsilon([Cartesian3.fromDegrees(1, 2), Cartesian3.fromDegrees(4, 5)], CesiumMath.EPSILON10);
        });
    });

    it('Geometry LineString: sets positions altitudeMode gx:clampToSeaFloor, cannot extrude, can tessellate', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
            <Placemark xmlns="http://www.opengis.net/kml/2.2"\
                       xmlns:gx="http://www.google.com/kml/ext/2.2">\
            <LineString>\
                <gx:altitudeMode>clampToSeaFloor</gx:altitudeMode>\
                <extrude>1</extrude>\
                <tessellate>1</tessellate>\
                <coordinates>1,2,3 4,5,6</coordinates>\
            </LineString>\
            </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities.length).toEqual(1);

            var entity = entities[0];
            expect(entity.polyline.followSurface).toBeUndefined();
            var positions = entity.polyline.positions.getValue(Iso8601.MINIMUM_VALUE);
            expect(positions).toEqualEpsilon([Cartesian3.fromDegrees(1, 2), Cartesian3.fromDegrees(4, 5)], CesiumMath.EPSILON10);
        });
    });

    it('Geometry LineString: sets positions altitudeMode gx:relativeToSeaFloor, can extrude, cannot tessellate', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
        <Placemark xmlns="http://www.opengis.net/kml/2.2"\
                   xmlns:gx="http://www.google.com/kml/ext/2.2">\
        <LineString>\
            <gx:altitudeMode>relativeToSeaFloor</gx:altitudeMode>\
            <extrude>1</extrude>\
            <tessellate>1</tessellate>\
            <coordinates>1,2,3 4,5,6</coordinates>\
        </LineString>\
        </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities.length).toEqual(1);

            var entity = entities[0];
            expect(entity.polyline).toBeUndefined(true);
            expect(entity.wall).toBeDefined();
            var positions = entity.wall.positions.getValue(Iso8601.MINIMUM_VALUE);
            expect(positions).toEqualEpsilon([Cartesian3.fromDegrees(1, 2, 3), Cartesian3.fromDegrees(4, 5, 6)], CesiumMath.EPSILON10);
        });
    });

    it('Geometry LineString: sets positions altitudeMode relativeToGround, can extrude, cannot tessellate', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
            <Placemark>\
            <LineString>\
                <altitudeMode>relativeToGround</altitudeMode>\
                <extrude>1</extrude>\
                <tessellate>1</tessellate>\
                <coordinates>1,2,3 4,5,6</coordinates>\
            </LineString>\
            </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities.length).toEqual(1);

            var entity = entities[0];
            expect(entity.polyline).toBeUndefined();
            expect(entity.wall).toBeDefined();
            var positions = entity.wall.positions.getValue(Iso8601.MINIMUM_VALUE);
            expect(positions).toEqualEpsilon([Cartesian3.fromDegrees(1, 2, 3), Cartesian3.fromDegrees(4, 5, 6)], CesiumMath.EPSILON10);
        });
    });

    it('Geometry LineString: sets positions altitudeMode absolute, can extrude, cannot tessellate', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
            <Placemark>\
            <LineString>\
                <altitudeMode>absolute</altitudeMode>\
                <extrude>1</extrude>\
                <tessellate>1</tessellate>\
                <coordinates>1,2,3 4,5,6</coordinates>\
            </LineString>\
            </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities.length).toEqual(1);

            var entity = entities[0];
            expect(entity.polyline).toBeUndefined();
            expect(entity.wall).toBeDefined();
            var positions = entity.wall.positions.getValue(Iso8601.MINIMUM_VALUE);
            expect(positions).toEqualEpsilon([Cartesian3.fromDegrees(1, 2, 3), Cartesian3.fromDegrees(4, 5, 6)], CesiumMath.EPSILON10);
        });
    });

    it('Geometry gx:Track: sets position and availability (clampToGround default)', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
            <Placemark xmlns="http://www.opengis.net/kml/2.2"\
                       xmlns:gx="http://www.google.com/kml/ext/2.2">\
              <gx:Track>\
                <when>2000-01-01T00:00:00Z</when>\
                <gx:coord>1 2 3</gx:coord>\
                <when>2000-01-01T00:00:01Z</when>\
                <gx:coord>4 5 6</gx:coord>\
                <when>2000-01-01T00:00:02Z</when>\
                <gx:coord>7 8 9</gx:coord>\
              </gx:Track>\
            </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var time1 = JulianDate.fromIso8601('2000-01-01T00:00:00Z');
            var time2 = JulianDate.fromIso8601('2000-01-01T00:00:01Z');
            var time3 = JulianDate.fromIso8601('2000-01-01T00:00:02Z');

            var entity = dataSource.entities.values[0];
            expect(entity.position.getValue(time1)).toEqualEpsilon(Cartesian3.fromDegrees(1, 2), CesiumMath.EPSILON12);
            expect(entity.position.getValue(time2)).toEqualEpsilon(Cartesian3.fromDegrees(4, 5), CesiumMath.EPSILON12);
            expect(entity.position.getValue(time3)).toEqualEpsilon(Cartesian3.fromDegrees(7, 8), CesiumMath.EPSILON12);
            expect(entity.polyline).toBeUndefined();

            expect(entity.availability.start).toEqual(time1);
            expect(entity.availability.stop).toEqual(time3);
        });
    });

    it('Geometry gx:Track: sets position clampToGround, cannot extrude', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
            <Placemark xmlns="http://www.opengis.net/kml/2.2"\
                       xmlns:gx="http://www.google.com/kml/ext/2.2">\
              <gx:Track>\
                <altitudeMode>clampToGround</altitudeMode>\
                <extrude>1</extrude>\
                <when>2000-01-01T00:00:00Z</when>\
                <gx:coord>1 2 3</gx:coord>\
                <when>2000-01-01T00:00:01Z</when>\
                <gx:coord>4 5 6</gx:coord>\
                <when>2000-01-01T00:00:02Z</when>\
                <gx:coord>7 8 9</gx:coord>\
              </gx:Track>\
            </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var time1 = JulianDate.fromIso8601('2000-01-01T00:00:00Z');
            var time2 = JulianDate.fromIso8601('2000-01-01T00:00:01Z');
            var time3 = JulianDate.fromIso8601('2000-01-01T00:00:02Z');

            var entity = dataSource.entities.values[0];
            expect(entity.position.getValue(time1)).toEqualEpsilon(Cartesian3.fromDegrees(1, 2), CesiumMath.EPSILON12);
            expect(entity.position.getValue(time2)).toEqualEpsilon(Cartesian3.fromDegrees(4, 5), CesiumMath.EPSILON12);
            expect(entity.position.getValue(time3)).toEqualEpsilon(Cartesian3.fromDegrees(7, 8), CesiumMath.EPSILON12);
            expect(entity.polyline).toBeUndefined();
        });
    });

    it('Geometry gx:Track: sets position altitudeMode absolute, can extrude', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
            <Placemark xmlns="http://www.opengis.net/kml/2.2"\
                       xmlns:gx="http://www.google.com/kml/ext/2.2">\
              <gx:Track>\
                <altitudeMode>absolute</altitudeMode>\
                <extrude>1</extrude>\
                <when>2000-01-01T00:00:00Z</when>\
                <gx:coord>1 2 3</gx:coord>\
                <when>2000-01-01T00:00:01Z</when>\
                <gx:coord>4 5 6</gx:coord>\
                <when>2000-01-01T00:00:02Z</when>\
                <gx:coord>7 8 9</gx:coord>\
              </gx:Track>\
            </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var time1 = JulianDate.fromIso8601('2000-01-01T00:00:00Z');
            var time2 = JulianDate.fromIso8601('2000-01-01T00:00:01Z');
            var time3 = JulianDate.fromIso8601('2000-01-01T00:00:02Z');

            var entity = dataSource.entities.values[0];
            expect(entity.position.getValue(time1)).toEqualEpsilon(Cartesian3.fromDegrees(1, 2, 3), CesiumMath.EPSILON12);
            expect(entity.position.getValue(time2)).toEqualEpsilon(Cartesian3.fromDegrees(4, 5, 6), CesiumMath.EPSILON12);
            expect(entity.position.getValue(time3)).toEqualEpsilon(Cartesian3.fromDegrees(7, 8, 9), CesiumMath.EPSILON12);

            expect(entity.polyline.positions.getValue(time1)).toEqualEpsilon([Cartesian3.fromDegrees(1, 2, 3), Cartesian3.fromDegrees(1, 2)], CesiumMath.EPSILON12);
            expect(entity.polyline.positions.getValue(time2)).toEqualEpsilon([Cartesian3.fromDegrees(4, 5, 6), Cartesian3.fromDegrees(4, 5)], CesiumMath.EPSILON12);
            expect(entity.polyline.positions.getValue(time3)).toEqualEpsilon([Cartesian3.fromDegrees(7, 8, 9), Cartesian3.fromDegrees(7, 8)], CesiumMath.EPSILON12);
        });
    });

    it('Geometry gx:Track: sets position altitudeMode relativeToGround, can extrude', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
            <Placemark xmlns="http://www.opengis.net/kml/2.2"\
                       xmlns:gx="http://www.google.com/kml/ext/2.2">\
              <gx:Track>\
                <altitudeMode>relativeToGround</altitudeMode>\
                <extrude>1</extrude>\
                <when>2000-01-01T00:00:00Z</when>\
                <gx:coord>1 2 3</gx:coord>\
                <when>2000-01-01T00:00:01Z</when>\
                <gx:coord>4 5 6</gx:coord>\
                <when>2000-01-01T00:00:02Z</when>\
                <gx:coord>7 8 9</gx:coord>\
              </gx:Track>\
            </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var time1 = JulianDate.fromIso8601('2000-01-01T00:00:00Z');
            var time2 = JulianDate.fromIso8601('2000-01-01T00:00:01Z');
            var time3 = JulianDate.fromIso8601('2000-01-01T00:00:02Z');

            var entity = dataSource.entities.values[0];
            expect(entity.position.getValue(time1)).toEqualEpsilon(Cartesian3.fromDegrees(1, 2, 3), CesiumMath.EPSILON12);
            expect(entity.position.getValue(time2)).toEqualEpsilon(Cartesian3.fromDegrees(4, 5, 6), CesiumMath.EPSILON12);
            expect(entity.position.getValue(time3)).toEqualEpsilon(Cartesian3.fromDegrees(7, 8, 9), CesiumMath.EPSILON12);

            expect(entity.polyline.positions.getValue(time1)).toEqualEpsilon([Cartesian3.fromDegrees(1, 2, 3), Cartesian3.fromDegrees(1, 2)], CesiumMath.EPSILON12);
            expect(entity.polyline.positions.getValue(time2)).toEqualEpsilon([Cartesian3.fromDegrees(4, 5, 6), Cartesian3.fromDegrees(4, 5)], CesiumMath.EPSILON12);
            expect(entity.polyline.positions.getValue(time3)).toEqualEpsilon([Cartesian3.fromDegrees(7, 8, 9), Cartesian3.fromDegrees(7, 8)], CesiumMath.EPSILON12);
        });
    });

    it('Geometry gx:Track: sets position and availability when missing values', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark xmlns="http://www.opengis.net/kml/2.2"\
                     xmlns:gx="http://www.google.com/kml/ext/2.2">\
                <gx:Track>\
                    <when>2000-01-01T00:00:00Z</when>\
                    <gx:coord>1 2 3</gx:coord>\
                    <when>2000-01-01T00:00:01Z</when>\
                    <gx:coord>4 5 6</gx:coord>\
                    <when>2000-01-01T00:00:02Z</when>\
                </gx:Track>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var time1 = JulianDate.fromIso8601('2000-01-01T00:00:00Z');
            var time2 = JulianDate.fromIso8601('2000-01-01T00:00:01Z');
            var time3 = JulianDate.fromIso8601('2000-01-01T00:00:02Z');

            var entity = dataSource.entities.values[0];
            expect(entity.position.getValue(time1)).toEqualEpsilon(Cartesian3.fromDegrees(1, 2), CesiumMath.EPSILON12);
            expect(entity.position.getValue(time2)).toEqualEpsilon(Cartesian3.fromDegrees(4, 5), CesiumMath.EPSILON12);
            expect(entity.position.getValue(time3)).toBeUndefined();

            expect(entity.availability.start).toEqual(time1);
            expect(entity.availability.stop).toEqual(time2);
        });
    });

    it('Geometry gx:MultiTrack: sets position and availability without interpolate', function() {
        var time = new JulianDate.fromIso8601('2010-05-28T02:02:09Z');
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark xmlns="http://www.opengis.net/kml/2.2"\
                     xmlns:gx="http://www.google.com/kml/ext/2.2">\
              <gx:MultiTrack>\
                  <gx:Track>\
                    <when>2000-01-01T00:00:00Z</when>\
                    <gx:coord>1 2 3</gx:coord>\
                    <when>2000-01-01T00:00:01Z</when>\
                    <gx:coord>4 5 6</gx:coord>\
                  </gx:Track>\
                  <gx:Track>\
                    <when>2000-01-01T00:00:02Z</when>\
                    <gx:coord>6 5 4</gx:coord>\
                    <when>2000-01-01T00:00:03Z</when>\
                    <gx:coord>3 2 1</gx:coord>\
                  </gx:Track>\
              </gx:MultiTrack>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var time1 = JulianDate.fromIso8601('2000-01-01T00:00:00Z');
            var time2 = JulianDate.fromIso8601('2000-01-01T00:00:01Z');
            var time3 = JulianDate.fromIso8601('2000-01-01T00:00:02Z');
            var time4 = JulianDate.fromIso8601('2000-01-01T00:00:03Z');

            var entity = dataSource.entities.values[0];
            expect(entity.availability.length).toEqual(2);
            expect(entity.availability.get(0).start).toEqual(time1);
            expect(entity.availability.get(0).stop).toEqual(time2);
            expect(entity.availability.get(1).start).toEqual(time3);
            expect(entity.availability.get(1).stop).toEqual(time4);

            expect(entity.position.getValue(time1)).toEqualEpsilon(Cartesian3.fromDegrees(1, 2), CesiumMath.EPSILON12);
            expect(entity.position.getValue(time2)).toEqualEpsilon(Cartesian3.fromDegrees(4, 5), CesiumMath.EPSILON12);
            expect(entity.position.getValue(time3)).toEqualEpsilon(Cartesian3.fromDegrees(6, 5), CesiumMath.EPSILON12);
            expect(entity.position.getValue(time4)).toEqualEpsilon(Cartesian3.fromDegrees(3, 2), CesiumMath.EPSILON12);
        });
    });

    it('Geometry gx:MultiTrack: sets position and availability with interpolate', function() {
        var time = new JulianDate.fromIso8601('2010-05-28T02:02:09Z');
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark xmlns="http://www.opengis.net/kml/2.2"\
                     xmlns:gx="http://www.google.com/kml/ext/2.2">\
              <gx:MultiTrack>\
                  <gx:interpolate>1</gx:interpolate>\
                  <gx:Track>\
                    <when>2000-01-01T00:00:00Z</when>\
                    <gx:coord>1 2 3</gx:coord>\
                    <when>2000-01-01T00:00:01Z</when>\
                    <gx:coord>4 5 6</gx:coord>\
                  </gx:Track>\
                  <gx:Track>\
                    <when>2000-01-01T00:00:02Z</when>\
                    <gx:coord>6 5 4</gx:coord>\
                    <when>2000-01-01T00:00:03Z</when>\
                    <gx:coord>3 2 1</gx:coord>\
                  </gx:Track>\
              </gx:MultiTrack>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var time1 = JulianDate.fromIso8601('2000-01-01T00:00:00Z');
            var time2 = JulianDate.fromIso8601('2000-01-01T00:00:01Z');
            var time3 = JulianDate.fromIso8601('2000-01-01T00:00:02Z');
            var time4 = JulianDate.fromIso8601('2000-01-01T00:00:03Z');

            var entity = dataSource.entities.values[0];
            expect(entity.availability.length).toEqual(1);
            expect(entity.availability.get(0).start).toEqual(time1);
            expect(entity.availability.get(0).stop).toEqual(time4);

            expect(entity.position.getValue(time1)).toEqualEpsilon(Cartesian3.fromDegrees(1, 2), CesiumMath.EPSILON12);
            expect(entity.position.getValue(time2)).toEqualEpsilon(Cartesian3.fromDegrees(4, 5), CesiumMath.EPSILON12);
            expect(entity.position.getValue(time3)).toEqualEpsilon(Cartesian3.fromDegrees(6, 5), CesiumMath.EPSILON12);
            expect(entity.position.getValue(time4)).toEqualEpsilon(Cartesian3.fromDegrees(3, 2), CesiumMath.EPSILON12);
        });
    });

    it('Geometry gx:MultiTrack: sets position and availability altitudeMode absolute, extrude, with interpolate', function() {
        var time = new JulianDate.fromIso8601('2010-05-28T02:02:09Z');
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark xmlns="http://www.opengis.net/kml/2.2"\
                     xmlns:gx="http://www.google.com/kml/ext/2.2">\
              <gx:MultiTrack>\
                  <gx:interpolate>1</gx:interpolate>\
                  <gx:Track>\
                    <altitudeMode>absolute</altitudeMode>\
                    <extrude>1</extrude>\
                    <when>2000-01-01T00:00:00Z</when>\
                    <gx:coord>1 2 3</gx:coord>\
                    <when>2000-01-01T00:00:01Z</when>\
                    <gx:coord>4 5 6</gx:coord>\
                  </gx:Track>\
                  <gx:Track>\
                    <altitudeMode>absolute</altitudeMode>\
                    <extrude>1</extrude>\
                    <when>2000-01-01T00:00:02Z</when>\
                    <gx:coord>6 5 4</gx:coord>\
                    <when>2000-01-01T00:00:03Z</when>\
                    <gx:coord>3 2 1</gx:coord>\
                  </gx:Track>\
              </gx:MultiTrack>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var time1 = JulianDate.fromIso8601('2000-01-01T00:00:00Z');
            var time2 = JulianDate.fromIso8601('2000-01-01T00:00:01Z');
            var time3 = JulianDate.fromIso8601('2000-01-01T00:00:02Z');
            var time4 = JulianDate.fromIso8601('2000-01-01T00:00:03Z');

            var entity = dataSource.entities.values[0];
            expect(entity.availability.length).toEqual(1);
            expect(entity.availability.get(0).start).toEqual(time1);
            expect(entity.availability.get(0).stop).toEqual(time4);

            expect(entity.position.getValue(time1)).toEqual(Cartesian3.fromDegrees(1, 2, 3));
            expect(entity.position.getValue(time2)).toEqual(Cartesian3.fromDegrees(4, 5, 6));
            expect(entity.position.getValue(time3)).toEqual(Cartesian3.fromDegrees(6, 5, 4));
            expect(entity.position.getValue(time4)).toEqual(Cartesian3.fromDegrees(3, 2, 1));
        });
    });

    it('Geometry MultiGeometry: sets expected properties', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark id="testID">\
          <MultiGeometry>\
            <name>TheName</name>\
            <description>TheDescription</description>\
              <Point id="point1">\
                <coordinates>1,2</coordinates>\
              </Point>\
              <Point id="point2">\
                <coordinates>3,4</coordinates>\
              </Point>\
          </MultiGeometry>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities.length).toEqual(3);

            var multi = dataSource.entities.getById('testID');
            expect(multi).toBeDefined();

            var point1 = dataSource.entities.getById('point1');
            expect(point1).toBeDefined();
            expect(point1.parent).toBe(multi);
            expect(point1.name).toBe(multi.name);
            expect(point1.description).toBe(multi.description);
            expect(point1.kml).toBe(multi.kml);
            expect(point1.position.getValue(Iso8601.MINIMUM_VALUE)).toEqualEpsilon(Cartesian3.fromDegrees(1, 2), CesiumMath.EPSILON13);

            var point2 = dataSource.entities.getById('point2');
            expect(point2).toBeDefined();
            expect(point2.parent).toBe(multi);
            expect(point2.name).toBe(multi.name);
            expect(point2.description).toBe(multi.description);
            expect(point2.kml).toBe(multi.kml);
            expect(point2.position.getValue(Iso8601.MINIMUM_VALUE)).toEqualEpsilon(Cartesian3.fromDegrees(3, 4), CesiumMath.EPSILON13);
        });
    });

    it('NetworkLink: Loads data', function() {
        return KmlDataSource.load('Data/KML/networkLink.kml').then(function(dataSource) {
            var entities = dataSource.entities.values;
            expect(entities.length).toEqual(2);
            expect(entities[0].id).toEqual('link');
            expect(entities[1].parent).toBe(entities[0]);
        });
    });

    it('can load a KML file with explicit namespaces', function() {
        return KmlDataSource.load('Data/KML/namespaced.kml').then(function(dataSource) {
            expect(dataSource.entities.values.length).toBe(2);
        });
    });

    it('Boolean values can use true string', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark>\
            <Polygon>\
              <altitudeMode>relativeToGround</altitudeMode>\
              <extrude>1</extrude>\
            </Polygon>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.polygon.perPositionHeight.getValue()).toEqual(true);
        });
    });

    it('Boolean values can use false string', function() {
        var kml = '<?xml version="1.0" encoding="UTF-8"?>\
          <Placemark>\
            <Polygon>\
              <altitudeMode>relativeToGround</altitudeMode>\
              <extrude>1</extrude>\
            </Polygon>\
          </Placemark>';

        return KmlDataSource.load(parser.parseFromString(kml, "text/xml")).then(function(dataSource) {
            var entity = dataSource.entities.values[0];
            expect(entity.polygon.perPositionHeight.getValue()).toEqual(true);
        });
    });
});