#!/usr/bin/env python3
# Copyright 2015 The Chromium Authors
# 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.
# pylint: disable=line-too-long
fake_idl = """
// Copyright 2014 The Chromium Authors
// 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;
};
callback VoidCallback = void();
callback BazGreekCallback = void(Baz baz, Greek greek);
callback OptionalParamCallback = void(optional Baz baz);
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.
[doesNotSupportPromises="Multi-parameter callback"]
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
// 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
* @externs
*/
/** @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;
/**
* @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.Baz|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
// 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
// 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
* @externs
*/
/** @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
// 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
// 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
* @externs
*/
/** @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)
# pylint: enable=line-too-long
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()