js_externs_generator_test.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. #!/usr/bin/env python3
  2. # Copyright 2015 The Chromium Authors. All rights reserved.
  3. # Use of this source code is governed by a BSD-style license that can be
  4. # found in the LICENSE file.
  5. import idl_schema
  6. import json_parse
  7. from js_externs_generator import JsExternsGenerator
  8. from datetime import datetime
  9. import model
  10. import sys
  11. import unittest
  12. # The contents of a fake idl file.
  13. fake_idl = """
  14. // Copyright 2014 The Chromium Authors. All rights reserved.
  15. // Use of this source code is governed by a BSD-style license that can be
  16. // found in the LICENSE file.
  17. // A totally fake API.
  18. namespace fakeApi {
  19. enum Greek {
  20. ALPHA,
  21. BETA,
  22. GAMMA,
  23. DELTA
  24. };
  25. dictionary Bar {
  26. long num;
  27. };
  28. dictionary Baz {
  29. DOMString str;
  30. long num;
  31. boolean b;
  32. Greek letter;
  33. Greek? optionalLetter;
  34. long[] arr;
  35. Bar[]? optionalObjArr;
  36. Greek[] enumArr;
  37. any[] anythingGoes;
  38. Bar obj;
  39. long? maybe;
  40. (DOMString or Greek or long[]) choice;
  41. object plainObj;
  42. ArrayBuffer arrayBuff;
  43. };
  44. dictionary Qux {
  45. long notOptionalLong;
  46. long? optionalLong;
  47. // A map from string to number.
  48. // <jsexterns>@type {Object<string, number>}</jsexterns>
  49. object dict;
  50. static void go();
  51. static void stop();
  52. };
  53. callback VoidCallback = void();
  54. callback BazGreekCallback = void(Baz baz, Greek greek);
  55. callback OptionalParamCallback = void(optional Qux qux);
  56. interface Properties {
  57. static DOMString lastError();
  58. };
  59. interface Functions {
  60. // Does something exciting! And what's more, this is a multiline function
  61. // comment! It goes onto multiple lines!
  62. // |baz| : The baz to use.
  63. static void doSomething(Baz baz, VoidCallback callback);
  64. // |callback| : The callback which will most assuredly in all cases be
  65. // called; that is, of course, iff such a callback was provided and is
  66. // not at all null.
  67. static void bazGreek(optional BazGreekCallback callback);
  68. [deprecated="Use a new method."] static DOMString returnString();
  69. static void instanceOfObjectParam([instanceOf=SomeType] object obj);
  70. static void instanceOfBarObjectParam([instanceOf=Bar] object barObj);
  71. static void optionalParam(optional OptionalParamCallback callback);
  72. static void nonFinalOptionalParams(
  73. DOMString string,
  74. optional double num,
  75. object obj,
  76. [instanceOf = SomeType] object someType,
  77. [instanceOf = SomeType] optional object optionalSomeType,
  78. optional boolean bool,
  79. optional Baz baz,
  80. double num,
  81. VoidCallback callback);
  82. static void multipleOptionalParams(
  83. optional DOMString param1,
  84. optional DOMString param2,
  85. optional object obj,
  86. [instanceOf = SomeType] optional object optionalSomeType,
  87. optional VoidCallback callback);
  88. };
  89. interface Events {
  90. // Fired when we realize it's a trap!
  91. static void onTrapDetected(Baz baz);
  92. };
  93. };
  94. """
  95. # The output we expect from our fake idl file.
  96. fake_idl_expected = """// Copyright %s The Chromium Authors. All rights reserved.
  97. // Use of this source code is governed by a BSD-style license that can be
  98. // found in the LICENSE file.
  99. // This file was generated by:
  100. // tools/json_schema_compiler/compiler.py.
  101. // NOTE: The format of types has changed. 'FooType' is now
  102. // 'chrome.fakeApi.FooType'.
  103. // Please run the closure compiler before committing changes.
  104. // See https://chromium.googlesource.com/chromium/src/+/main/docs/closure_compilation.md
  105. /** @fileoverview Externs generated from namespace: fakeApi */
  106. /** @const */
  107. chrome.fakeApi = {};
  108. /**
  109. * @enum {string}
  110. * @see https://developer.chrome.com/extensions/fakeApi#type-Greek
  111. */
  112. chrome.fakeApi.Greek = {
  113. ALPHA: 'ALPHA',
  114. BETA: 'BETA',
  115. GAMMA: 'GAMMA',
  116. DELTA: 'DELTA',
  117. };
  118. /**
  119. * @typedef {{
  120. * num: number
  121. * }}
  122. * @see https://developer.chrome.com/extensions/fakeApi#type-Bar
  123. */
  124. chrome.fakeApi.Bar;
  125. /**
  126. * @typedef {{
  127. * str: string,
  128. * num: number,
  129. * b: boolean,
  130. * letter: !chrome.fakeApi.Greek,
  131. * optionalLetter: (!chrome.fakeApi.Greek|undefined),
  132. * arr: !Array<number>,
  133. * optionalObjArr: (!Array<!chrome.fakeApi.Bar>|undefined),
  134. * enumArr: !Array<!chrome.fakeApi.Greek>,
  135. * anythingGoes: !Array<*>,
  136. * obj: !chrome.fakeApi.Bar,
  137. * maybe: (number|undefined),
  138. * choice: (string|!chrome.fakeApi.Greek|!Array<number>),
  139. * plainObj: Object,
  140. * arrayBuff: ArrayBuffer
  141. * }}
  142. * @see https://developer.chrome.com/extensions/fakeApi#type-Baz
  143. */
  144. chrome.fakeApi.Baz;
  145. /**
  146. * @constructor
  147. * @private
  148. * @see https://developer.chrome.com/extensions/fakeApi#type-Qux
  149. */
  150. chrome.fakeApi.Qux = function() {};
  151. /**
  152. * @type {number}
  153. * @see https://developer.chrome.com/extensions/fakeApi#type-notOptionalLong
  154. */
  155. chrome.fakeApi.Qux.prototype.notOptionalLong;
  156. /**
  157. * @type {(number|undefined)}
  158. * @see https://developer.chrome.com/extensions/fakeApi#type-optionalLong
  159. */
  160. chrome.fakeApi.Qux.prototype.optionalLong;
  161. /**
  162. * A map from string to number.
  163. * @type {Object<string, number>}
  164. * @see https://developer.chrome.com/extensions/fakeApi#type-dict
  165. */
  166. chrome.fakeApi.Qux.prototype.dict;
  167. /**
  168. * @see https://developer.chrome.com/extensions/fakeApi#method-go
  169. */
  170. chrome.fakeApi.Qux.prototype.go = function() {};
  171. /**
  172. * @see https://developer.chrome.com/extensions/fakeApi#method-stop
  173. */
  174. chrome.fakeApi.Qux.prototype.stop = function() {};
  175. /**
  176. * @type {string}
  177. * @see https://developer.chrome.com/extensions/fakeApi#type-lastError
  178. */
  179. chrome.fakeApi.lastError;
  180. /**
  181. * Does something exciting! And what's more, this is a multiline function
  182. * comment! It goes onto multiple lines!
  183. * @param {!chrome.fakeApi.Baz} baz The baz to use.
  184. * @param {function(): void} callback
  185. * @see https://developer.chrome.com/extensions/fakeApi#method-doSomething
  186. */
  187. chrome.fakeApi.doSomething = function(baz, callback) {};
  188. /**
  189. * @param {function(!chrome.fakeApi.Baz, !chrome.fakeApi.Greek): void=} callback
  190. * The callback which will most assuredly in all cases be called; that is,
  191. * of course, iff such a callback was provided and is not at all null.
  192. * @see https://developer.chrome.com/extensions/fakeApi#method-bazGreek
  193. */
  194. chrome.fakeApi.bazGreek = function(callback) {};
  195. /**
  196. * @return {string}
  197. * @deprecated Use a new method.
  198. * @see https://developer.chrome.com/extensions/fakeApi#method-returnString
  199. */
  200. chrome.fakeApi.returnString = function() {};
  201. /**
  202. * @param {SomeType} obj
  203. * @see https://developer.chrome.com/extensions/fakeApi#method-instanceOfObjectParam
  204. */
  205. chrome.fakeApi.instanceOfObjectParam = function(obj) {};
  206. /**
  207. * @param {chrome.fakeApi.Bar} barObj
  208. * @see https://developer.chrome.com/extensions/fakeApi#method-instanceOfBarObjectParam
  209. */
  210. chrome.fakeApi.instanceOfBarObjectParam = function(barObj) {};
  211. /**
  212. * @param {function((!chrome.fakeApi.Qux|undefined)): void=} callback
  213. * @see https://developer.chrome.com/extensions/fakeApi#method-optionalParam
  214. */
  215. chrome.fakeApi.optionalParam = function(callback) {};
  216. /**
  217. * @param {string} string
  218. * @param {?number|undefined} num
  219. * @param {Object} obj
  220. * @param {SomeType} someType
  221. * @param {?SomeType|undefined} optionalSomeType
  222. * @param {?boolean|undefined} bool
  223. * @param {?chrome.fakeApi.Baz|undefined} baz
  224. * @param {number} num
  225. * @param {function(): void} callback
  226. * @see https://developer.chrome.com/extensions/fakeApi#method-nonFinalOptionalParams
  227. */
  228. chrome.fakeApi.nonFinalOptionalParams = function(string, num, obj, someType, optionalSomeType, bool, baz, num, callback) {};
  229. /**
  230. * @param {string=} param1
  231. * @param {string=} param2
  232. * @param {Object=} obj
  233. * @param {SomeType=} optionalSomeType
  234. * @param {function(): void=} callback
  235. * @see https://developer.chrome.com/extensions/fakeApi#method-multipleOptionalParams
  236. */
  237. chrome.fakeApi.multipleOptionalParams = function(param1, param2, obj, optionalSomeType, callback) {};
  238. /**
  239. * Fired when we realize it's a trap!
  240. * @type {!ChromeEvent}
  241. * @see https://developer.chrome.com/extensions/fakeApi#event-onTrapDetected
  242. */
  243. chrome.fakeApi.onTrapDetected;""" % datetime.now().year
  244. # A subset of fake_idl. The key difference is that the namespace is private,
  245. # which means that @see links shouldn't be generated.
  246. fake_private_idl = """
  247. // Copyright 2014 The Chromium Authors. All rights reserved.
  248. // Use of this source code is governed by a BSD-style license that can be
  249. // found in the LICENSE file.
  250. // A totally fake API.
  251. namespace fakeApiPrivate {
  252. enum Greek {
  253. ALPHA,
  254. BETA,
  255. GAMMA,
  256. DELTA
  257. };
  258. dictionary Bar {
  259. long num;
  260. };
  261. interface Events {
  262. // Fired when we realize it's a trap!
  263. static void onTrapDetected(Baz baz);
  264. };
  265. };
  266. """
  267. fake_private_idl_expected = """// Copyright %s The Chromium Authors. All rights reserved.
  268. // Use of this source code is governed by a BSD-style license that can be
  269. // found in the LICENSE file.
  270. // This file was generated by:
  271. // tools/json_schema_compiler/compiler.py.
  272. // NOTE: The format of types has changed. 'FooType' is now
  273. // 'chrome.fakeApiPrivate.FooType'.
  274. // Please run the closure compiler before committing changes.
  275. // See https://chromium.googlesource.com/chromium/src/+/main/docs/closure_compilation.md
  276. /** @fileoverview Externs generated from namespace: fakeApiPrivate */
  277. /** @const */
  278. chrome.fakeApiPrivate = {};
  279. /**
  280. * @enum {string}
  281. */
  282. chrome.fakeApiPrivate.Greek = {
  283. ALPHA: 'ALPHA',
  284. BETA: 'BETA',
  285. GAMMA: 'GAMMA',
  286. DELTA: 'DELTA',
  287. };
  288. /**
  289. * @typedef {{
  290. * num: number
  291. * }}
  292. */
  293. chrome.fakeApiPrivate.Bar;
  294. /**
  295. * Fired when we realize it's a trap!
  296. * @type {!ChromeEvent}
  297. */
  298. chrome.fakeApiPrivate.onTrapDetected;""" % datetime.now().year
  299. fake_json = """// Copyright 2014 The Chromium Authors. All rights reserved.
  300. // Use of this source code is governed by a BSD-style license that can be
  301. // found in the LICENSE file.
  302. [
  303. {
  304. "namespace": "fakeJson",
  305. "description": "Fake JSON API Stuff",
  306. "types": [
  307. {
  308. "id": "CrazyEnum",
  309. "type": "string",
  310. "enum": ["camelCaseEnum", "Non-Characters", "5NumFirst", \
  311. "3Just-plainOld_MEAN"]
  312. },
  313. {
  314. "id": "CrazyObject",
  315. "type": "object",
  316. "additionalProperties": {"type": "string"}
  317. }
  318. ],
  319. "properties": {
  320. "lastError": {
  321. "type": "string",
  322. "description": "The lastError."
  323. }
  324. },
  325. "functions": [ {
  326. "name": "funcWithInlineObj",
  327. "type": "function",
  328. "parameters": [
  329. {
  330. "type": "object",
  331. "name": "inlineObj",
  332. "description": "Evil inline object! With a super duper duper long\
  333. string description that causes problems!",
  334. "properties": {
  335. "foo": {
  336. "type": "boolean",
  337. "optional": "true",
  338. "description": "The foo."
  339. },
  340. "bar": {
  341. "type": "integer",
  342. "description": "The bar."
  343. },
  344. "baz": {
  345. "type": "object",
  346. "description": "Inception object.",
  347. "properties": {
  348. "depth": {
  349. "type": "integer"
  350. }
  351. }
  352. },
  353. "quu": {
  354. "type": "binary",
  355. "description": "The array buffer"
  356. }
  357. }
  358. },
  359. {
  360. "name": "callback",
  361. "type": "function",
  362. "parameters": [
  363. {
  364. "type": "object",
  365. "name": "returnObj",
  366. "properties": {
  367. "str": { "type": "string"}
  368. }
  369. }
  370. ],
  371. "description": "The callback to this heinous method"
  372. }
  373. ],
  374. "returns": {
  375. "type": "object",
  376. "properties": {
  377. "str": { "type": "string" },
  378. "int": { "type": "number" }
  379. }
  380. }
  381. },
  382. {
  383. "name": "funcWithReturnsAsync",
  384. "type": "function",
  385. "parameters": [
  386. {
  387. "type": "integer",
  388. "name": "someNumber",
  389. "description": "A number parameter"
  390. }
  391. ],
  392. "returns_async": {
  393. "name": "callback",
  394. "optional": true,
  395. "parameters": [
  396. {
  397. "name": "anotherNumber",
  398. "type": "integer",
  399. "description": "A number that comes back"
  400. }
  401. ]
  402. }
  403. } ]
  404. }
  405. ]"""
  406. fake_json_expected = """// Copyright %s The Chromium Authors. All rights reserved.
  407. // Use of this source code is governed by a BSD-style license that can be
  408. // found in the LICENSE file.
  409. // This file was generated by:
  410. // tools/json_schema_compiler/compiler.py.
  411. // NOTE: The format of types has changed. 'FooType' is now
  412. // 'chrome.fakeJson.FooType'.
  413. // Please run the closure compiler before committing changes.
  414. // See https://chromium.googlesource.com/chromium/src/+/main/docs/closure_compilation.md
  415. /** @fileoverview Externs generated from namespace: fakeJson */
  416. /** @const */
  417. chrome.fakeJson = {};
  418. /**
  419. * @enum {string}
  420. * @see https://developer.chrome.com/extensions/fakeJson#type-CrazyEnum
  421. */
  422. chrome.fakeJson.CrazyEnum = {
  423. CAMEL_CASE_ENUM: 'camelCaseEnum',
  424. NON_CHARACTERS: 'Non-Characters',
  425. _5NUM_FIRST: '5NumFirst',
  426. _3JUST_PLAIN_OLD_MEAN: '3Just-plainOld_MEAN',
  427. };
  428. /**
  429. * @typedef {Object}
  430. * @see https://developer.chrome.com/extensions/fakeJson#type-CrazyObject
  431. */
  432. chrome.fakeJson.CrazyObject;
  433. /**
  434. * The lastError.
  435. * @type {string}
  436. * @see https://developer.chrome.com/extensions/fakeJson#type-lastError
  437. */
  438. chrome.fakeJson.lastError;
  439. /**
  440. * @param {{
  441. * foo: (boolean|undefined),
  442. * bar: number,
  443. * baz: {
  444. * depth: number
  445. * },
  446. * quu: ArrayBuffer
  447. * }} inlineObj Evil inline object! With a super duper duper long string
  448. * description that causes problems!
  449. * @param {function({
  450. * str: string
  451. * }): void} callback The callback to this heinous method
  452. * @return {{
  453. * str: string,
  454. * int: number
  455. * }}
  456. * @see https://developer.chrome.com/extensions/fakeJson#method-funcWithInlineObj
  457. */
  458. chrome.fakeJson.funcWithInlineObj = function(inlineObj, callback) {};
  459. /**
  460. * @param {number} someNumber A number parameter
  461. * @param {function(number): void=} callback
  462. * @see https://developer.chrome.com/extensions/fakeJson#method-funcWithReturnsAsync
  463. */
  464. chrome.fakeJson.funcWithReturnsAsync = function(someNumber, callback) {};""" % (
  465. datetime.now().year)
  466. class JsExternGeneratorTest(unittest.TestCase):
  467. def _GetNamespace(self, fake_content, filename, is_idl):
  468. """Returns a namespace object for the given content"""
  469. api_def = (idl_schema.Process(fake_content, filename) if is_idl
  470. else json_parse.Parse(fake_content))
  471. m = model.Model()
  472. return m.AddNamespace(api_def[0], filename)
  473. def setUp(self):
  474. self.maxDiff = None # Lets us see the full diff when inequal.
  475. def testBasic(self):
  476. namespace = self._GetNamespace(fake_idl, 'fake_api.idl', True)
  477. self.assertMultiLineEqual(fake_idl_expected,
  478. JsExternsGenerator().Generate(namespace).Render())
  479. def testPrivate(self):
  480. namespace = self._GetNamespace(fake_private_idl, 'fake_api_private.idl',
  481. True)
  482. self.assertMultiLineEqual(fake_private_idl_expected,
  483. JsExternsGenerator().Generate(namespace).Render())
  484. def testJsonWithInlineObjectsAndAsyncReturn(self):
  485. namespace = self._GetNamespace(fake_json, 'fake_api.json', False)
  486. self.assertMultiLineEqual(fake_json_expected,
  487. JsExternsGenerator().Generate(namespace).Render())
  488. if __name__ == '__main__':
  489. unittest.main()