Skip to content

Commit

Permalink
Add ability to unit test by permissions.
Browse files Browse the repository at this point in the history
  • Loading branch information
ry committed Aug 27, 2018
1 parent 722c7e4 commit e13f3c1
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 25 deletions.
2 changes: 1 addition & 1 deletion js/compiler_test.ts
@@ -1,5 +1,5 @@
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
import { test, assert, assertEqual } from "./testing/testing.ts";
import { test, assert, assertEqual } from "./test_util.ts";
import * as compiler from "compiler";
import * as ts from "typescript";

Expand Down
2 changes: 1 addition & 1 deletion js/console_test.ts
@@ -1,6 +1,6 @@
// Copyright 2018 the Deno authors. All rights reserved. MIT license.

import { test, assert, assertEqual } from "./testing/testing.ts";
import { test, assert, assertEqual } from "./test_util.ts";
import { stringifyArgs } from "./console.ts";

// tslint:disable-next-line:no-any
Expand Down
70 changes: 70 additions & 0 deletions js/test_util.ts
@@ -0,0 +1,70 @@
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
//
// We want to test many ops in deno which have different behavior depending on
// the permissions set. These tests can specify which permissions they expect,
// which appends a special string like "permW1N0" to the end of the test name.
// Here we run several copies of deno with different permissions, filtering the
// tests by the special string. permW0N0 means allow-write but not allow-net.
// See tools/unit_tests.py for more details.

import * as deno from "deno";
import * as testing from "./testing/testing.ts";
export { assert, assertEqual } from "./testing/testing.ts";

// testing.setFilter must be run before any tests are defined.
const permFilter = deno.argv[1];
permFromString(permFilter);
testing.setFilter(permFilter);

interface DenoPermissions {
write?: boolean;
net?: boolean;
}

function permToString(perms: DenoPermissions): string {
const w = perms.write ? 1 : 0;
const n = perms.net ? 1 : 0;
return `permW${w}N${n}`;
}

function permFromString(s: string): DenoPermissions {
const re = /^permW([01])N([01])$/;
const found = s.match(re);
if (!found) {
throw Error("Not a permission string");
}
return {
write: Boolean(Number(found[1])),
net: Boolean(Number(found[2]))
};
}

export function testPerm(perms: DenoPermissions, fn: testing.TestFunction) {
const name = `${fn.name}_${permToString(perms)}`;
testing.test({ fn, name });
}

export function test(fn: testing.TestFunction) {
testPerm({ write: false, net: false }, fn);
}

test(function permSerialization() {
for (let write of [true, false]) {
for (let net of [true, false]) {
let perms: DenoPermissions = { write, net };
testing.assertEqual(perms, permFromString(permToString(perms)));
}
}
});

// To better catch internal errors, permFromString should throw if it gets an
// invalid permission string.
test(function permFromStringThrows() {
let threw = false;
try {
permFromString("bad");
} catch (e) {
threw = true;
}
testing.assert(threw);
});
20 changes: 6 additions & 14 deletions js/testing/testing.ts
Expand Up @@ -24,22 +24,14 @@ export interface TestDefinition {

export const exitOnFail = true;

/* A subset of the tests can be ran by providing a filter expression.
* In Node.js the filter is specified on the command line:
*
* ts-node test_node log # all tests with 'log' in the name
* ts-node test_node ^util # tests starting with 'util'
*
* In the browser, the filter is specified as part of the url:
*
* http://localhost:9876/test.html#script=some/script.js&filter=log
* http://localhost:9876/test.html#script=some/script.js&filter=^util
*/
let filterExpr: string = null;

const filterRegExp = filterExpr ? new RegExp(filterExpr, "i") : null;
let filterRegExp: RegExp | null;
const tests: TestDefinition[] = [];

// Must be called before any test() that needs to be filtered.
export function setFilter(s: string): void {
filterRegExp = new RegExp(s, "i");
}

export function test(t: TestDefinition | TestFunction): void {
const fn: TestFunction = typeof t === "function" ? t : t.fn;
const name: string = t.name;
Expand Down
15 changes: 7 additions & 8 deletions js/unit_tests.ts
Expand Up @@ -3,8 +3,7 @@
// But it can also be run manually:
// ./deno tests.ts

import { test, assert, assertEqual } from "./testing/testing.ts";
import { readFileSync } from "deno";
import { test, testPerm, assert, assertEqual } from "./test_util.ts";
import * as deno from "deno";

import "./compiler_test.ts";
Expand All @@ -15,7 +14,7 @@ test(async function tests_test() {
});

test(async function tests_readFileSync() {
const data = readFileSync("package.json");
const data = deno.readFileSync("package.json");
if (!data.byteLength) {
throw Error(
`Expected positive value for data.byteLength ${data.byteLength}`
Expand All @@ -32,7 +31,7 @@ test(function tests_readFileSync_NotFound() {
let caughtError = false;
let data;
try {
data = readFileSync("bad_filename");
data = deno.readFileSync("bad_filename");
} catch (e) {
caughtError = true;
assert(e instanceof deno.NotFound);
Expand All @@ -48,14 +47,14 @@ test(function writeFileSyncSuccess() {
const dataWritten = enc.encode("Hello");
const filename = "TEMPDIR/test.txt";
deno.writeFileSync(filename, dataWritten, 0o666);
const dataRead = readFileSync(filename);
const dataRead = deno.readFileSync(filename);
assertEqual(dataRead, dataWritten);
});
*/

// For this test to pass we need --allow-write permission.
// Otherwise it will fail with deno.PermissionDenied instead of deno.NotFound.
test(function writeFileSyncFail() {
testPerm({ write: true }, function writeFileSyncFail() {
const enc = new TextEncoder();
const data = enc.encode("Hello");
const filename = "/baddir/test.txt";
Expand All @@ -71,7 +70,7 @@ test(function writeFileSyncFail() {
assert(caughtError);
});

test(async function tests_fetch() {
testPerm({ net: true }, async function tests_fetch() {
const response = await fetch("http://localhost:4545/package.json");
const json = await response.json();
assertEqual(json.name, "deno");
Expand All @@ -84,7 +83,7 @@ test(async function tests_writeFileSync() {
// TODO need ability to get tmp dir.
const fn = "/tmp/test.txt";
writeFileSync("/tmp/test.txt", data, 0o666);
const dataRead = readFileSync("/tmp/test.txt");
const dataRead = deno.readFileSync("/tmp/test.txt");
const dec = new TextDecoder("utf-8");
const actual = dec.decode(dataRead);
assertEqual("Hello", actual);
Expand Down
3 changes: 2 additions & 1 deletion tools/test.py
Expand Up @@ -5,6 +5,7 @@
import sys
from check_output_test import check_output_test
from util import executable_suffix, run, build_path
from unit_tests import unit_tests
from util_test import util_test
import subprocess
import http_server
Expand Down Expand Up @@ -41,7 +42,7 @@ def main(argv):

deno_exe = os.path.join(build_dir, "deno" + executable_suffix)
check_exists(deno_exe)
run([deno_exe, "js/unit_tests.ts", "--allow-write"])
unit_tests(deno_exe)

check_exists(deno_exe)
check_output_test(deno_exe)
Expand Down
26 changes: 26 additions & 0 deletions tools/unit_tests.py
@@ -0,0 +1,26 @@
#!/usr/bin/env python
from util import run
import sys


# We want to test many ops in deno which have different behavior depending on
# the permissions set. These tests can specify which permissions they expect,
# which appends a special string like "permW1N0" to the end of the test name.
# Here we run several copies of deno with different permissions, filtering the
# tests by the special string. permW0N0 means allow-write but not allow-net.
# See js/test_util.ts for more details.
def unit_tests(deno_exe):
run([deno_exe, "js/unit_tests.ts", "permW0N0"])
run([deno_exe, "js/unit_tests.ts", "permW1N0", "--allow-write"])
run([deno_exe, "js/unit_tests.ts", "permW0N1", "--allow-net"])
run([
deno_exe, "js/unit_tests.ts", "permW1N1", "--allow-write",
"--allow-net"
])


if __name__ == '__main__':
if len(sys.argv) < 2:
print "Usage ./tools/unit_tests.py out/debug/deno"
sys.exit(1)
unit_tests(sys.argv[1])

0 comments on commit e13f3c1

Please sign in to comment.