Skip to content

Commit 565a7c9

Browse files
aveladclaude
andauthored
build: Use generated shaka namespace typedef in tests (#10288)
Follow-up to #9596, which added a generated typedef for the `shaka` namespace to the externs but left the hand-maintained `shakaNamespaceType` typedef in place in the tests. This generates a standalone externs file containing only the `shaka` namespace typedef and includes it when type-checking the tests, so the tests can refer to the whole library namespace in a type-safe way. The hand-maintained `shakaNamespaceType` typedef is then removed in favor of the generated `shaka` type. Closes #6762 Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent af30b90 commit 565a7c9

9 files changed

Lines changed: 103 additions & 63 deletions

File tree

build/check.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -194,17 +194,33 @@ def check_tests(args):
194194
closure_base_js = shakaBuildHelpers.get_closure_base_js_path()
195195
get = shakaBuildHelpers.get_all_js_files
196196

197+
localizations = compiler.GenerateLocalizations(None)
198+
localizations.generate(args.force)
199+
200+
# Generate a standalone externs file with the shaka namespace typedef, so the
201+
# tests can refer to the whole library namespace in a type-safe way without a
202+
# hand-maintained typedef. We use the complete build's sources (plus the
203+
# generated localizations they depend on) so the typedef covers every
204+
# top-level namespace that the tests might reference. This is done before the
205+
# test and externs files are added below, since those are not part of the
206+
# library namespace.
207+
node_modules_path = os.path.join(base, 'node_modules')
208+
namespace_sources = [f for f in complete_build if node_modules_path not in f]
209+
namespace_sources.append(localizations.output)
210+
namespace_externs = compiler.NamespaceExternGenerator(
211+
namespace_sources, 'shaka-namespace')
212+
if not namespace_externs.generate(args.force):
213+
return False
214+
197215
files = complete_build
198216
files.update(set(
199217
get('externs') +
200218
get('test') +
201219
[closure_base_js]))
202220
files.add(os.path.join(base, 'demo', 'common', 'asset.js'))
203221
files.add(os.path.join(base, 'demo', 'common', 'assets.js'))
204-
205-
localizations = compiler.GenerateLocalizations(None)
206-
localizations.generate(args.force)
207222
files.add(localizations.output)
223+
files.add(namespace_externs.output)
208224

209225
closure_opts = build.common_closure_opts + build.common_closure_defines
210226
closure_opts += build.debug_closure_opts + build.debug_closure_defines

build/compiler.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,41 @@ def generate(self, force=False):
288288
return True
289289

290290

291+
class NamespaceExternGenerator(object):
292+
"""Generates a standalone externs file with only the shaka namespace typedef.
293+
294+
Unlike ExternGenerator, this does not re-declare the library API, so the
295+
output can be type-checked alongside the uncompiled source (e.g. the tests).
296+
"""
297+
298+
def __init__(self, source_files, output_name):
299+
self.source_files = _canonicalize_source_files(source_files)
300+
self.output = _get_source_path('dist/' + output_name + '.externs.js')
301+
302+
def generate(self, force=False):
303+
"""Generates the shaka namespace typedef for |self.source_files|.
304+
305+
Args:
306+
force: Generate the output even if the inputs have not changed.
307+
308+
Returns:
309+
True on success; False on failure.
310+
"""
311+
if not force and not _must_build(self.output, self.source_files):
312+
return True
313+
314+
extern_generator = _get_source_path('build/generateExterns.js')
315+
316+
cmd_line = ['node', extern_generator, '--namespace-typedef', self.output]
317+
cmd_line += self.source_files
318+
319+
if shakaBuildHelpers.execute_get_code(cmd_line) != 0:
320+
logging.error('Namespace typedef generation failed')
321+
return False
322+
323+
return True
324+
325+
291326
class TsDefGenerator(object):
292327
def __init__(self, source_files, build_name):
293328
self.source_files = _canonicalize_source_files(source_files)

build/generateExterns.js

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -908,16 +908,22 @@ function generateShakaNamespaceTypedef(names) {
908908
function main(args) {
909909
const inputPaths = [];
910910
let outputPath;
911+
let namespaceTypedefPath;
911912

912913
for (let i = 0; i < args.length; ++i) {
913914
if (args[i] == '--output') {
914915
outputPath = args[i + 1];
915916
++i;
917+
} else if (args[i] == '--namespace-typedef') {
918+
namespaceTypedefPath = args[i + 1];
919+
++i;
916920
} else {
917921
inputPaths.push(args[i]);
918922
}
919923
}
920-
assert(outputPath, 'You must specify output file with --output <EXTERNS>');
924+
assert(outputPath || namespaceTypedefPath,
925+
'You must specify an output file with --output <EXTERNS> and/or ' +
926+
'--namespace-typedef <EXTERNS>');
921927
assert(inputPaths.length, 'You must specify at least one input file.');
922928

923929
// Generate externs for all input paths.
@@ -978,16 +984,33 @@ function main(args) {
978984
const shakaTypedef = generateShakaNamespaceTypedef(names);
979985

980986
// Output generated externs, with an appropriate header.
981-
fs.writeFileSync(outputPath,
982-
licenseHeader +
983-
'/**\n' +
984-
' * @fileoverview Generated externs. DO NOT EDIT!\n' +
985-
' * @externs\n' +
986-
' * @suppress {constantProperty, duplicate} To prevent compiler\n' +
987-
' * errors with the namespace being declared both here and by\n' +
988-
' * goog.provide in the library.\n' +
989-
' */\n\n' +
990-
namespaceDeclarations.join('') + '\n' + shakaTypedef + externs);
987+
if (outputPath) {
988+
fs.writeFileSync(outputPath,
989+
licenseHeader +
990+
'/**\n' +
991+
' * @fileoverview Generated externs. DO NOT EDIT!\n' +
992+
' * @externs\n' +
993+
' * @suppress {constantProperty, duplicate} To prevent compiler\n' +
994+
' * errors with the namespace being declared both here and by\n' +
995+
' * goog.provide in the library.\n' +
996+
' */\n\n' +
997+
namespaceDeclarations.join('') + '\n' + shakaTypedef + externs);
998+
}
999+
1000+
// Output a standalone externs file with only the shaka namespace typedef.
1001+
// Unlike the full externs above, this does not re-declare the library API, so
1002+
// it can be type-checked alongside the uncompiled source (e.g. in the tests)
1003+
// without conflicting with the goog.provide declarations.
1004+
if (namespaceTypedefPath) {
1005+
fs.writeFileSync(namespaceTypedefPath,
1006+
licenseHeader +
1007+
'/**\n' +
1008+
' * @fileoverview Generated externs for the shaka namespace type.\n' +
1009+
' * DO NOT EDIT!\n' +
1010+
' * @externs\n' +
1011+
' */\n\n' +
1012+
shakaTypedef);
1013+
}
9911014
}
9921015

9931016

test/offline/storage_integration.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1849,14 +1849,14 @@ filterDescribe('Storage', storageSupport, () => {
18491849
* @param {string} uri
18501850
* @param {number} startTime
18511851
* @param {number} endTime
1852-
* @param {shakaNamespaceType=} compiledShaka
1852+
* @param {shaka=} compiledShaka
18531853
* @return {shaka.media.SegmentReference}
18541854
*/
18551855
function makeReference(uri, startTime, endTime, compiledShaka) {
1856-
/** @type {shakaNamespaceType} */
1857-
const shaka = compiledShaka || window['shaka'];
1856+
/** @type {shaka} */
1857+
const shakaNamespace = compiledShaka || window['shaka'];
18581858

1859-
return new shaka.media.SegmentReference(
1859+
return new shakaNamespace.media.SegmentReference(
18601860
startTime,
18611861
endTime,
18621862
/* getUris= */ () => [uri],

test/test/util/loader.js

Lines changed: 2 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,51 +4,15 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7-
/**
8-
* A stand-in type for the "shaka" namespace. Used when loading the compiled
9-
* library or when referencing it in ManifestGenerator or TestScheme.
10-
*
11-
* The new compiler has a "typeof" annotation for classes, but it warns of an
12-
* incomplete type when used on the entire library namespace. So instead, we
13-
* use this type, which maps out parts of the compiled namespace used in
14-
* top-level integration tests.
15-
*
16-
* @typedef {{
17-
* Player: typeof shaka.Player,
18-
* media: {
19-
* SegmentReference: typeof shaka.media.SegmentReference,
20-
* InitSegmentReference: typeof shaka.media.InitSegmentReference,
21-
* SegmentIndex: typeof shaka.media.SegmentIndex,
22-
* PresentationTimeline: typeof shaka.media.PresentationTimeline
23-
* },
24-
* net: {
25-
* NetworkingEngine: typeof shaka.net.NetworkingEngine
26-
* },
27-
* offline: {
28-
* Storage: typeof shaka.offline.Storage
29-
* },
30-
* ui: {
31-
* Overlay: typeof shaka.ui.Overlay,
32-
* Controls: typeof shaka.ui.Controls,
33-
* Element: typeof shaka.ui.Element
34-
* },
35-
* util: {
36-
* StringUtils: typeof shaka.util.StringUtils
37-
* }
38-
* }}
39-
*/
40-
let shakaNamespaceType;
41-
42-
437
shaka.test.Loader = class {
448
/**
459
* @param {boolean} loadUncompiled
46-
* @return {!Promise<shakaNamespaceType>}
10+
* @return {!Promise<shaka>}
4711
*/
4812
static async loadShaka(loadUncompiled) {
4913
/** @type {!Promise.PromiseWithResolvers} */
5014
const loaded = Promise.withResolvers();
51-
/** @type {shakaNamespaceType} */
15+
/** @type {shaka} */
5216
let compiledShaka;
5317

5418
if (loadUncompiled) {

test/test/util/manifest_generator.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
shaka.test.ManifestGenerator = class {
1717
/**
1818
* @param {function(!shaka.test.ManifestGenerator.Manifest)=} func
19-
* @param {shakaNamespaceType=} compiledShaka
19+
* @param {shaka=} compiledShaka
2020
* @return {shaka.extern.Manifest}
2121
*/
2222
static generate(func, compiledShaka) {
@@ -71,9 +71,9 @@ shaka.test.ManifestGenerator = class {
7171
};
7272

7373
shaka.test.ManifestGenerator.Manifest = class {
74-
/** @param {shakaNamespaceType=} compiledShaka */
74+
/** @param {shaka=} compiledShaka */
7575
constructor(compiledShaka) {
76-
/** @private {shakaNamespaceType} */
76+
/** @private {shaka} */
7777
this.shaka_ = compiledShaka || window['shaka'];
7878

7979
/** @type {!Array<shaka.extern.Variant>} */

test/test/util/test_scheme.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ shaka.test.TestScheme = class {
170170

171171
/**
172172
* Creates the manifests and generators.
173-
* @param {shakaNamespaceType} compiledShaka
173+
* @param {shaka} compiledShaka
174174
* @param {string} suffix
175175
* @return {!Promise}
176176
*/

test/ui/ui_integration.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ describe('UI', () => {
2727
let ui;
2828
/** @type {!shaka.ui.Controls} */
2929
let controls;
30-
/** @type {shakaNamespaceType} */
30+
/** @type {shaka} */
3131
let compiledShaka;
3232
/** @type {!Array<string>|undefined} */
3333
let savedLanguages;
@@ -759,8 +759,10 @@ describe('UI', () => {
759759
videoContainer, fakeControls);
760760
uncompiledElement.release();
761761

762+
/** @type {typeof shaka.ui.Element} */
763+
const CompiledElement = compiledShaka.ui.Element;
762764
/** @extends {shaka.ui.Element} */
763-
const TestElement = class extends compiledShaka.ui.Element {
765+
const TestElement = class extends CompiledElement {
764766
/**
765767
* @param {!HTMLElement} parent
766768
* @param {!shaka.ui.Controls} controls

test/ui/vr_ui_integration.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ describe('VR UI', () => {
2929
let ui;
3030
/** @type {!shaka.ui.Controls} */
3131
let controls;
32-
/** @type {shakaNamespaceType} */
32+
/** @type {shaka} */
3333
let compiledShaka;
3434
/** @type {boolean} */
3535
let canPlayVR;

0 commit comments

Comments
 (0)