| #!/usr/bin/env python3 |
| # Copyright 2015 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import idl_schema |
| import json_parse |
| from js_externs_generator import JsExternsGenerator |
| from datetime import datetime |
| import model |
| import sys |
| import unittest |
| |
| # The contents of a fake idl file. |
| fake_idl = """ |
| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // A totally fake API. |
| namespace fakeApi { |
| enum Greek { |
| ALPHA, |
| BETA, |
| GAMMA, |
| DELTA |
| }; |
| |
| dictionary Bar { |
| long num; |
| }; |
| |
| dictionary Baz { |
| DOMString str; |
| long num; |
| boolean b; |
| Greek letter; |
| Greek? optionalLetter; |
| long[] arr; |
| Bar[]? optionalObjArr; |
| Greek[] enumArr; |
| any[] anythingGoes; |
| Bar obj; |
| long? maybe; |
| (DOMString or Greek or long[]) choice; |
| object plainObj; |
| ArrayBuffer arrayBuff; |
| }; |
| |
| dictionary Qux { |
| long notOptionalLong; |
| long? optionalLong; |
| |
| // A map from string to number. |
| // <jsexterns>@type {Object<string, number>}</jsexterns> |
| object dict; |
| |
| static void go(); |
| static void stop(); |
| }; |
| |
| callback VoidCallback = void(); |
| |
| callback BazGreekCallback = void(Baz baz, Greek greek); |
| |
| callback OptionalParamCallback = void(optional Qux qux); |
| |
| interface Properties { |
| static DOMString lastError(); |
| }; |
| |
| interface Functions { |
| // Does something exciting! And what's more, this is a multiline function |
| // comment! It goes onto multiple lines! |
| // |baz| : The baz to use. |
| static void doSomething(Baz baz, VoidCallback callback); |
| |
| // |callback| : The callback which will most assuredly in all cases be |
| // called; that is, of course, iff such a callback was provided and is |
| // not at all null. |
| static void bazGreek(optional BazGreekCallback callback); |
| |
| [deprecated="Use a new method."] static DOMString returnString(); |
| |
| static void instanceOfObjectParam([instanceOf=SomeType] object obj); |
| |
| static void instanceOfBarObjectParam([instanceOf=Bar] object barObj); |
| |
| static void optionalParam(optional OptionalParamCallback callback); |
| |
| static void nonFinalOptionalParams( |
| DOMString string, |
| optional double num, |
| object obj, |
| [instanceOf = SomeType] object someType, |
| [instanceOf = SomeType] optional object optionalSomeType, |
| optional boolean bool, |
| optional Baz baz, |
| double num, |
| VoidCallback callback); |
| |
| static void multipleOptionalParams( |
| optional DOMString param1, |
| optional DOMString param2, |
| optional object obj, |
| [instanceOf = SomeType] optional object optionalSomeType, |
| optional VoidCallback callback); |
| }; |
| |
| interface Events { |
| // Fired when we realize it's a trap! |
| static void onTrapDetected(Baz baz); |
| }; |
| }; |
| """ |
| |
| # The output we expect from our fake idl file. |
| fake_idl_expected = """// Copyright %s The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // This file was generated by: |
| // tools/json_schema_compiler/compiler.py. |
| // NOTE: The format of types has changed. 'FooType' is now |
| // 'chrome.fakeApi.FooType'. |
| // Please run the closure compiler before committing changes. |
| // See https://chromium.googlesource.com/chromium/src/+/main/docs/closure_compilation.md |
| |
| /** @fileoverview Externs generated from namespace: fakeApi */ |
| |
| /** @const */ |
| chrome.fakeApi = {}; |
| |
| /** |
| * @enum {string} |
| * @see https://developer.chrome.com/extensions/fakeApi#type-Greek |
| */ |
| chrome.fakeApi.Greek = { |
| ALPHA: 'ALPHA', |
| BETA: 'BETA', |
| GAMMA: 'GAMMA', |
| DELTA: 'DELTA', |
| }; |
| |
| /** |
| * @typedef {{ |
| * num: number |
| * }} |
| * @see https://developer.chrome.com/extensions/fakeApi#type-Bar |
| */ |
| chrome.fakeApi.Bar; |
| |
| /** |
| * @typedef {{ |
| * str: string, |
| * num: number, |
| * b: boolean, |
| * letter: !chrome.fakeApi.Greek, |
| * optionalLetter: (!chrome.fakeApi.Greek|undefined), |
| * arr: !Array<number>, |
| * optionalObjArr: (!Array<!chrome.fakeApi.Bar>|undefined), |
| * enumArr: !Array<!chrome.fakeApi.Greek>, |
| * anythingGoes: !Array<*>, |
| * obj: !chrome.fakeApi.Bar, |
| * maybe: (number|undefined), |
| * choice: (string|!chrome.fakeApi.Greek|!Array<number>), |
| * plainObj: Object, |
| * arrayBuff: ArrayBuffer |
| * }} |
| * @see https://developer.chrome.com/extensions/fakeApi#type-Baz |
| */ |
| chrome.fakeApi.Baz; |
| |
| /** |
| * @constructor |
| * @private |
| * @see https://developer.chrome.com/extensions/fakeApi#type-Qux |
| */ |
| chrome.fakeApi.Qux = function() {}; |
| |
| /** |
| * @type {number} |
| * @see https://developer.chrome.com/extensions/fakeApi#type-notOptionalLong |
| */ |
| chrome.fakeApi.Qux.prototype.notOptionalLong; |
| |
| /** |
| * @type {(number|undefined)} |
| * @see https://developer.chrome.com/extensions/fakeApi#type-optionalLong |
| */ |
| chrome.fakeApi.Qux.prototype.optionalLong; |
| |
| /** |
| * A map from string to number. |
| * @type {Object<string, number>} |
| * @see https://developer.chrome.com/extensions/fakeApi#type-dict |
| */ |
| chrome.fakeApi.Qux.prototype.dict; |
| |
| /** |
| * @see https://developer.chrome.com/extensions/fakeApi#method-go |
| */ |
| chrome.fakeApi.Qux.prototype.go = function() {}; |
| |
| /** |
| * @see https://developer.chrome.com/extensions/fakeApi#method-stop |
| */ |
| chrome.fakeApi.Qux.prototype.stop = function() {}; |
| |
| |
| /** |
| * @type {string} |
| * @see https://developer.chrome.com/extensions/fakeApi#type-lastError |
| */ |
| chrome.fakeApi.lastError; |
| |
| /** |
| * Does something exciting! And what's more, this is a multiline function |
| * comment! It goes onto multiple lines! |
| * @param {!chrome.fakeApi.Baz} baz The baz to use. |
| * @param {function(): void} callback |
| * @see https://developer.chrome.com/extensions/fakeApi#method-doSomething |
| */ |
| chrome.fakeApi.doSomething = function(baz, callback) {}; |
| |
| /** |
| * @param {function(!chrome.fakeApi.Baz, !chrome.fakeApi.Greek): void=} callback |
| * The callback which will most assuredly in all cases be called; that is, |
| * of course, iff such a callback was provided and is not at all null. |
| * @see https://developer.chrome.com/extensions/fakeApi#method-bazGreek |
| */ |
| chrome.fakeApi.bazGreek = function(callback) {}; |
| |
| /** |
| * @return {string} |
| * @deprecated Use a new method. |
| * @see https://developer.chrome.com/extensions/fakeApi#method-returnString |
| */ |
| chrome.fakeApi.returnString = function() {}; |
| |
| /** |
| * @param {SomeType} obj |
| * @see https://developer.chrome.com/extensions/fakeApi#method-instanceOfObjectParam |
| */ |
| chrome.fakeApi.instanceOfObjectParam = function(obj) {}; |
| |
| /** |
| * @param {chrome.fakeApi.Bar} barObj |
| * @see https://developer.chrome.com/extensions/fakeApi#method-instanceOfBarObjectParam |
| */ |
| chrome.fakeApi.instanceOfBarObjectParam = function(barObj) {}; |
| |
| /** |
| * @param {function((!chrome.fakeApi.Qux|undefined)): void=} callback |
| * @see https://developer.chrome.com/extensions/fakeApi#method-optionalParam |
| */ |
| chrome.fakeApi.optionalParam = function(callback) {}; |
| |
| /** |
| * @param {string} string |
| * @param {?number|undefined} num |
| * @param {Object} obj |
| * @param {SomeType} someType |
| * @param {?SomeType|undefined} optionalSomeType |
| * @param {?boolean|undefined} bool |
| * @param {?chrome.fakeApi.Baz|undefined} baz |
| * @param {number} num |
| * @param {function(): void} callback |
| * @see https://developer.chrome.com/extensions/fakeApi#method-nonFinalOptionalParams |
| */ |
| chrome.fakeApi.nonFinalOptionalParams = function(string, num, obj, someType, optionalSomeType, bool, baz, num, callback) {}; |
| |
| /** |
| * @param {string=} param1 |
| * @param {string=} param2 |
| * @param {Object=} obj |
| * @param {SomeType=} optionalSomeType |
| * @param {function(): void=} callback |
| * @see https://developer.chrome.com/extensions/fakeApi#method-multipleOptionalParams |
| */ |
| chrome.fakeApi.multipleOptionalParams = function(param1, param2, obj, optionalSomeType, callback) {}; |
| |
| /** |
| * Fired when we realize it's a trap! |
| * @type {!ChromeEvent} |
| * @see https://developer.chrome.com/extensions/fakeApi#event-onTrapDetected |
| */ |
| chrome.fakeApi.onTrapDetected;""" % datetime.now().year |
| |
| # A subset of fake_idl. The key difference is that the namespace is private, |
| # which means that @see links shouldn't be generated. |
| fake_private_idl = """ |
| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // A totally fake API. |
| namespace fakeApiPrivate { |
| enum Greek { |
| ALPHA, |
| BETA, |
| GAMMA, |
| DELTA |
| }; |
| |
| dictionary Bar { |
| long num; |
| }; |
| |
| interface Events { |
| // Fired when we realize it's a trap! |
| static void onTrapDetected(Baz baz); |
| }; |
| }; |
| """ |
| |
| fake_private_idl_expected = """// Copyright %s The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // This file was generated by: |
| // tools/json_schema_compiler/compiler.py. |
| // NOTE: The format of types has changed. 'FooType' is now |
| // 'chrome.fakeApiPrivate.FooType'. |
| // Please run the closure compiler before committing changes. |
| // See https://chromium.googlesource.com/chromium/src/+/main/docs/closure_compilation.md |
| |
| /** @fileoverview Externs generated from namespace: fakeApiPrivate */ |
| |
| /** @const */ |
| chrome.fakeApiPrivate = {}; |
| |
| /** |
| * @enum {string} |
| */ |
| chrome.fakeApiPrivate.Greek = { |
| ALPHA: 'ALPHA', |
| BETA: 'BETA', |
| GAMMA: 'GAMMA', |
| DELTA: 'DELTA', |
| }; |
| |
| /** |
| * @typedef {{ |
| * num: number |
| * }} |
| */ |
| chrome.fakeApiPrivate.Bar; |
| |
| /** |
| * Fired when we realize it's a trap! |
| * @type {!ChromeEvent} |
| */ |
| chrome.fakeApiPrivate.onTrapDetected;""" % datetime.now().year |
| |
| fake_json = """// Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| [ |
| { |
| "namespace": "fakeJson", |
| "description": "Fake JSON API Stuff", |
| "types": [ |
| { |
| "id": "CrazyEnum", |
| "type": "string", |
| "enum": ["camelCaseEnum", "Non-Characters", "5NumFirst", \ |
| "3Just-plainOld_MEAN"] |
| }, |
| { |
| "id": "CrazyObject", |
| "type": "object", |
| "additionalProperties": {"type": "string"} |
| } |
| ], |
| "properties": { |
| "lastError": { |
| "type": "string", |
| "description": "The lastError." |
| } |
| }, |
| "functions": [ { |
| "name": "funcWithInlineObj", |
| "type": "function", |
| "parameters": [ |
| { |
| "type": "object", |
| "name": "inlineObj", |
| "description": "Evil inline object! With a super duper duper long\ |
| string description that causes problems!", |
| "properties": { |
| "foo": { |
| "type": "boolean", |
| "optional": "true", |
| "description": "The foo." |
| }, |
| "bar": { |
| "type": "integer", |
| "description": "The bar." |
| }, |
| "baz": { |
| "type": "object", |
| "description": "Inception object.", |
| "properties": { |
| "depth": { |
| "type": "integer" |
| } |
| } |
| }, |
| "quu": { |
| "type": "binary", |
| "description": "The array buffer" |
| } |
| } |
| }, |
| { |
| "name": "callback", |
| "type": "function", |
| "parameters": [ |
| { |
| "type": "object", |
| "name": "returnObj", |
| "properties": { |
| "str": { "type": "string"} |
| } |
| } |
| ], |
| "description": "The callback to this heinous method" |
| } |
| ], |
| "returns": { |
| "type": "object", |
| "properties": { |
| "str": { "type": "string" }, |
| "int": { "type": "number" } |
| } |
| } |
| }, |
| { |
| "name": "funcWithReturnsAsync", |
| "type": "function", |
| "parameters": [ |
| { |
| "type": "integer", |
| "name": "someNumber", |
| "description": "A number parameter" |
| } |
| ], |
| "returns_async": { |
| "name": "callback", |
| "optional": true, |
| "parameters": [ |
| { |
| "name": "anotherNumber", |
| "type": "integer", |
| "description": "A number that comes back" |
| } |
| ] |
| } |
| } ] |
| } |
| ]""" |
| |
| fake_json_expected = """// Copyright %s The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // This file was generated by: |
| // tools/json_schema_compiler/compiler.py. |
| // NOTE: The format of types has changed. 'FooType' is now |
| // 'chrome.fakeJson.FooType'. |
| // Please run the closure compiler before committing changes. |
| // See https://chromium.googlesource.com/chromium/src/+/main/docs/closure_compilation.md |
| |
| /** @fileoverview Externs generated from namespace: fakeJson */ |
| |
| /** @const */ |
| chrome.fakeJson = {}; |
| |
| /** |
| * @enum {string} |
| * @see https://developer.chrome.com/extensions/fakeJson#type-CrazyEnum |
| */ |
| chrome.fakeJson.CrazyEnum = { |
| CAMEL_CASE_ENUM: 'camelCaseEnum', |
| NON_CHARACTERS: 'Non-Characters', |
| _5NUM_FIRST: '5NumFirst', |
| _3JUST_PLAIN_OLD_MEAN: '3Just-plainOld_MEAN', |
| }; |
| |
| /** |
| * @typedef {Object} |
| * @see https://developer.chrome.com/extensions/fakeJson#type-CrazyObject |
| */ |
| chrome.fakeJson.CrazyObject; |
| |
| /** |
| * The lastError. |
| * @type {string} |
| * @see https://developer.chrome.com/extensions/fakeJson#type-lastError |
| */ |
| chrome.fakeJson.lastError; |
| |
| /** |
| * @param {{ |
| * foo: (boolean|undefined), |
| * bar: number, |
| * baz: { |
| * depth: number |
| * }, |
| * quu: ArrayBuffer |
| * }} inlineObj Evil inline object! With a super duper duper long string |
| * description that causes problems! |
| * @param {function({ |
| * str: string |
| * }): void} callback The callback to this heinous method |
| * @return {{ |
| * str: string, |
| * int: number |
| * }} |
| * @see https://developer.chrome.com/extensions/fakeJson#method-funcWithInlineObj |
| */ |
| chrome.fakeJson.funcWithInlineObj = function(inlineObj, callback) {}; |
| |
| /** |
| * @param {number} someNumber A number parameter |
| * @param {function(number): void=} callback |
| * @see https://developer.chrome.com/extensions/fakeJson#method-funcWithReturnsAsync |
| */ |
| chrome.fakeJson.funcWithReturnsAsync = function(someNumber, callback) {};""" % ( |
| datetime.now().year) |
| |
| |
| class JsExternGeneratorTest(unittest.TestCase): |
| def _GetNamespace(self, fake_content, filename, is_idl): |
| """Returns a namespace object for the given content""" |
| api_def = (idl_schema.Process(fake_content, filename) if is_idl |
| else json_parse.Parse(fake_content)) |
| m = model.Model() |
| return m.AddNamespace(api_def[0], filename) |
| |
| def setUp(self): |
| self.maxDiff = None # Lets us see the full diff when inequal. |
| |
| def testBasic(self): |
| namespace = self._GetNamespace(fake_idl, 'fake_api.idl', True) |
| self.assertMultiLineEqual(fake_idl_expected, |
| JsExternsGenerator().Generate(namespace).Render()) |
| |
| def testPrivate(self): |
| namespace = self._GetNamespace(fake_private_idl, 'fake_api_private.idl', |
| True) |
| self.assertMultiLineEqual(fake_private_idl_expected, |
| JsExternsGenerator().Generate(namespace).Render()) |
| |
| def testJsonWithInlineObjectsAndAsyncReturn(self): |
| namespace = self._GetNamespace(fake_json, 'fake_api.json', False) |
| self.assertMultiLineEqual(fake_json_expected, |
| JsExternsGenerator().Generate(namespace).Render()) |
| |
| |
| if __name__ == '__main__': |
| unittest.main() |