/* * Copyright 2014 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import static com.google.common.truth.Truth.assertThat; import com.google.common.collect.ImmutableSet; /** * Test case for {@link GatherExternProperties}. */ public final class GatherExternPropertiesTest extends CompilerTestCase { public GatherExternPropertiesTest() { super(); enableTypeCheck(); } @Override protected CompilerPass getProcessor(Compiler compiler) { return new GatherExternProperties(compiler); } public void testGatherExternProperties() { // Properties. assertExternProperties( "foo.bar;", "bar"); // Object literals. assertExternProperties( "foo = {bar: null, 'baz': {foobar: null}};", "bar", "baz", "foobar"); // Object literal with numeric propertic. assertExternProperties( "foo = {0: null};", "0"); // Top-level variables do not count. assertExternProperties( "var foo;"); // String-key access does not count. assertExternProperties( "foo['bar'] = {};"); } public void testGatherExternPropertiesIncludingRecordTypes() { // Properties. assertExternProperties( "foo.bar;", "bar"); // Object literals. assertExternProperties( "foo = {bar: null, 'baz': {foobar: null}};", "bar", "baz", "foobar"); // Object literal with numeric propertic. assertExternProperties( "foo = {0: null};", "0"); // Top-level variables do not count. assertExternProperties( "var foo;"); // String-key access does not count. assertExternProperties( "foo['bar'] = {};"); // Record types on properties. assertExternProperties( "/** @type {{bar: string, baz: string}} */ var foo;", "bar", "baz"); // Record types in typedef. assertExternProperties( "/** @typedef {{bar: string, baz: string}} */ var FooType;", "bar", "baz"); // Record types in type unions. assertExternProperties( "/** @type {string|{bar: string}|{baz: string}} */ var foo;", "bar", "baz"); // Record types in function parameters and return types. assertExternProperties(LINE_JOINER.join( "/** @type {function(string, {bar: string}): {baz: string}} */", "var foo;"), "bar", "baz"); // Record types as template arguments. assertExternProperties( "/** @type {Array.<{bar: string, baz: string}>} */ var foo;", "bar", "baz"); // Record types in implemented interfaces. assertExternProperties(LINE_JOINER.join( "/**", " * @interface", " * @template T", " */", "var Foo;", "/**", " * @constructor", " * @implements {Foo.<{bar: string, baz: string}>}", " */", "var Bar;"), "bar", "baz"); // Record types in extended class. assertExternProperties(LINE_JOINER.join( "/**", " * @constructor", " * @template T", " */", "var Foo = function() {};", "/**", " * @constructor", " * @extends {Foo.<{bar: string, baz: string}>}", " */", "var Bar = function() {};"), "bar", "baz"); // Record types in enum. // Note that "baz" exists only in the type of the enum, // but it is still picked up. assertExternProperties(LINE_JOINER.join( "/** @enum {{bar: string, baz: (string|undefined)}} */", "var FooEnum = {VALUE: {bar: ''}};"), "VALUE", "bar", "baz"); // Nested record types. assertExternProperties( "/** @type {{bar: string, baz: {foobar: string}}} */ var foo;", "bar", "baz", "foobar"); // Recursive types. assertExternProperties(LINE_JOINER.join( "/** @typedef {{a: D2}} */", "var D1;", "", "/** @typedef {{b: D1}} */", "var D2;"), "a", "b"); assertExternProperties(LINE_JOINER.join( "/** @typedef {{a: function(D2)}} */", "var D1;", "", "/** @typedef {{b: D1}} */", "var D2;"), "a", "b"); // Record types defined in normal code and referenced in externs should // not bleed-through. testSame( // Externs. "/** @type {NonExternType} */ var foo;", // Normal code. "/** @typedef {{bar: string, baz: string}} */ var NonExternType;", null); // Check that no properties were found. assertThat(getLastCompiler().getExternProperties()).isEmpty(); } private void assertExternProperties(String externs, String... properties) { testSame(externs, "", null); assertEquals(ImmutableSet.copyOf(properties), getLastCompiler().getExternProperties()); } }