@tensorflow/tfjs-core
Version:
Hardware-accelerated JavaScript library for machine intelligence
268 lines (234 loc) • 7.7 kB
text/typescript
/**
* @license
* Copyright 2017 Google Inc. All Rights Reserved.
* 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.
* =============================================================================
*/
// We use the pattern below (as opposed to require('jasmine') to create the
// jasmine module in order to avoid loading node specific modules which may
// be ignored in browser environments but cannot be ignored in react-native
// due to the pre-bundling of dependencies that it must do.
// tslint:disable-next-line:no-require-imports
const jasmineRequire = require('jasmine-core/lib/jasmine-core/jasmine.js');
const jasmineCore = jasmineRequire.core(jasmineRequire);
import {KernelBackend} from './backends/backend';
import {ENGINE} from './engine';
import {env, Environment, Flags} from './environment';
Error.stackTraceLimit = Infinity;
jasmineCore.DEFAULT_TIMEOUT_INTERVAL = 10000;
export type Constraints = {
flags?: Flags,
predicate?: (testEnv: TestEnv) => boolean,
};
export const NODE_ENVS: Constraints = {
predicate: () => env().platformName === 'node'
};
export const CHROME_ENVS: Constraints = {
flags: {'IS_CHROME': true}
};
export const BROWSER_ENVS: Constraints = {
predicate: () => env().platformName === 'browser'
};
export const SYNC_BACKEND_ENVS: Constraints = {
predicate: (testEnv: TestEnv) => testEnv.isDataSync === true
};
export const HAS_WORKER = {
predicate: () => typeof (Worker) !== 'undefined' &&
typeof (Blob) !== 'undefined' && typeof (URL) !== 'undefined'
};
export const HAS_NODE_WORKER = {
predicate: () => {
let hasWorker = true;
try {
require.resolve('worker_threads');
} catch {
hasWorker = false;
}
return typeof (process) !== 'undefined' && hasWorker;
}
};
export const ALL_ENVS: Constraints = {};
// Tests whether the current environment satisfies the set of constraints.
export function envSatisfiesConstraints(
env: Environment, testEnv: TestEnv, constraints: Constraints): boolean {
if (constraints == null) {
return true;
}
if (constraints.flags != null) {
for (const flagName in constraints.flags) {
const flagValue = constraints.flags[flagName];
if (env.get(flagName) !== flagValue) {
return false;
}
}
}
if (constraints.predicate != null && !constraints.predicate(testEnv)) {
return false;
}
return true;
}
export interface TestFilter {
include?: string;
startsWith?: string;
excludes?: string[];
}
export function setupTestFilters(
testFilters: TestFilter[], customInclude: (name: string) => boolean) {
const env = jasmine.getEnv();
// Account for --grep flag passed to karma by saving the existing specFilter.
const grepFilter = env.specFilter;
/**
* Filter method that returns boolean, if a given test should run or be
* ignored based on its name. The exclude list has priority over the
* include list. Thus, if a test matches both the exclude and the include
* list, it will be exluded.
*/
// tslint:disable-next-line: no-any
env.specFilter = (spec: any) => {
// Filter out tests if the --grep flag is passed.
if (!grepFilter(spec)) {
return false;
}
const name = spec.getFullName();
if (customInclude(name)) {
return true;
}
// Include a describeWithFlags() test from tfjs-core only if the test is
// in the include list.
for (let i = 0; i < testFilters.length; ++i) {
const testFilter = testFilters[i];
if ((testFilter.include != null &&
name.indexOf(testFilter.include) > -1) ||
(testFilter.startsWith != null &&
name.startsWith(testFilter.startsWith))) {
if (testFilter.excludes != null) {
for (let j = 0; j < testFilter.excludes.length; j++) {
if (name.indexOf(testFilter.excludes[j]) > -1) {
return false;
}
}
}
return true;
}
}
// Otherwise ignore the test.
return false;
};
}
export function parseTestEnvFromKarmaFlags(
args: string[], registeredTestEnvs: TestEnv[]): TestEnv {
let flags: Flags;
let testEnvName: string;
args.forEach((arg, i) => {
if (arg === '--flags') {
flags = JSON.parse(args[i + 1]);
} else if (arg === '--testEnv') {
testEnvName = args[i + 1];
}
});
const testEnvNames = registeredTestEnvs.map(env => env.name).join(', ');
if (flags != null && testEnvName == null) {
throw new Error(
'--testEnv flag is required when --flags is present. ' +
`Available values are [${testEnvNames}].`);
}
if (testEnvName == null) {
return null;
}
let testEnv: TestEnv;
registeredTestEnvs.forEach(env => {
if (env.name === testEnvName) {
testEnv = env;
}
});
if (testEnv == null) {
throw new Error(
`Test environment with name ${testEnvName} not ` +
`found. Available test environment names are ` +
`${testEnvNames}`);
}
if (flags != null) {
testEnv.flags = flags;
}
return testEnv;
}
export function describeWithFlags(
name: string, constraints: Constraints, tests: (env: TestEnv) => void) {
if (TEST_ENVS.length === 0) {
throw new Error(
`Found no test environments. This is likely due to test environment ` +
`registries never being imported or test environment registries ` +
`being registered too late.`);
}
TEST_ENVS.forEach(testEnv => {
env().setFlags(testEnv.flags);
if (envSatisfiesConstraints(env(), testEnv, constraints)) {
const testName =
name + ' ' + testEnv.name + ' ' + JSON.stringify(testEnv.flags || {});
executeTests(testName, tests, testEnv);
}
});
}
export interface TestEnv {
name: string;
backendName: string;
flags?: Flags;
isDataSync?: boolean;
}
export const TEST_ENVS: TestEnv[] = [];
// Whether a call to setTestEnvs has been called so we turn off
// registration. This allows command line overriding or programmatic
// overriding of the default registrations.
let testEnvSet = false;
export function setTestEnvs(testEnvs: TestEnv[]) {
testEnvSet = true;
TEST_ENVS.length = 0;
TEST_ENVS.push(...testEnvs);
}
export function registerTestEnv(testEnv: TestEnv) {
// When using an explicit call to setTestEnvs, turn off registration of
// test environments because the explicit call will set the test
// environments.
if (testEnvSet) {
return;
}
TEST_ENVS.push(testEnv);
}
function executeTests(
testName: string, tests: (env: TestEnv) => void, testEnv: TestEnv) {
describe(testName, () => {
beforeAll(async () => {
ENGINE.reset();
if (testEnv.flags != null) {
env().setFlags(testEnv.flags);
}
env().set('IS_TEST', true);
// Await setting the new backend since it can have async init.
await ENGINE.setBackend(testEnv.backendName);
});
beforeEach(() => {
ENGINE.startScope();
});
afterEach(() => {
ENGINE.endScope();
ENGINE.disposeVariables();
});
afterAll(() => {
ENGINE.reset();
});
tests(testEnv);
});
}
export class TestKernelBackend extends KernelBackend {
dispose(): void {}
}