Cross-runtime testing

This commit is contained in:
Timothy Warren 2023-11-03 11:59:58 -04:00
parent 7dcd42da13
commit 2fcfe4328c
12 changed files with 164 additions and 19 deletions

View File

@ -3,7 +3,8 @@
"lint": {
"include": ["src/"],
"rules": {
"tags": ["recommended"]
"tags": ["recommended"],
"exclude": ["no-explicit-any"]
}
},
"fmt": {

View File

@ -3,24 +3,51 @@ default:
@just --list
# Typescript checking
check:
check: lint
deno check --unstable --all -c deno.jsonc ./src/deno/*.ts ./src/common/*.ts
# Code linting
deno-lint:
lint:
deno lint
# Reformat the code
fmt:
deno fmt
# Run tests with all the runtimes
test: deno-test bun-test
# Clean up any generated files
clean:
rm -rf .deno-cover
rm -rf cover
rm -rf docs
########################################################################################################################
# Bun-specific commands
########################################################################################################################
# Test with bun
bun-test:
bun test --coverage
# Run with bun
bun-run:
bun run ./src/scroll.ts
########################################################################################################################
# Deno-specific commands
########################################################################################################################
# Test with deno
deno-test:
deno test --allow-all
# Create test coverage report with deno
deno-coverage:
deno test --allow-all --coverage=.deno-cover
deno coverage --lcov .deno-cover
# Run with deno
deno-run:
deno run --allow-all --allow-ffi --deny-net --deny-hrtime --unstable ./src/scroll.ts
deno-test:
deno test --allow-all

38
src/bun/test_base.ts Normal file
View File

@ -0,0 +1,38 @@
/**
* Adapt the bun test interface to the shared testing interface
*/
import {test as btest, expect } from 'bun:test';
import {ITestBase} from "../common/mod";
class TestBase implements ITestBase {
test(name: string, fn: () => void) {
return btest(name, fn);
}
assertEquals(actual: unknown, expected: unknown): void {
return expect(actual).toEqual(expected);
}
assertExists(actual: unknown): void {
return expect(actual).toBeDefined();
}
assertInstanceOf(actual: unknown, expectedType: any): void {
return expect(actual).toBeInstanceOf(expectedType);
}
assertNotEquals(actual: unknown, expected: unknown): void {
return expect(actual).not.toBe(expected);
}
assertFalse(actual: boolean): void {
return expect(actual).toBe(false);
}
assertTrue(actual: boolean): void {
return expect(actual).toBe(true);
}
}
const testBase = new TestBase();
export default testBase;

3
src/common/mod.ts Normal file
View File

@ -0,0 +1,3 @@
export * from './runtime.ts';
export * from './strings.ts';
export type { ITestBase } from './test_base.ts';

View File

@ -30,7 +30,7 @@ export const getRuntime = (): RunTime => {
/**
* Import a runtime-specific module
*
* eg. to load "src/bun/index.ts", if the runtime is bun,
* eg. to load "src/bun/mod.ts", if the runtime is bun,
* you can use like so `await importForRuntime('index')`;
*
* @param path - the path within the runtime module
@ -49,3 +49,18 @@ export const importForRuntime = async (path: string) => {
return await import(importPath);
};
/**
* Import the default export for a runtime-specific module
* (this is just a simple wrapper of `importForRuntime`)
*
* @param path - the path within the runtime module
*/
export const importDefaultForRuntime = async (path: string) => {
const pkg = await importForRuntime(path);
if ('default' in pkg) {
return pkg.default;
}
return null;
};

View File

@ -1,8 +1,12 @@
import { importForRuntime } from './index.ts';
import { chars } from './strings.ts';
import { chars, importDefaultForRuntime, is_ascii, ITestBase } from './mod.ts';
const { test, assertEquals } = await importForRuntime('test_base');
const t: ITestBase = await importDefaultForRuntime('test_base');
test('chars fn properly splits strings into unicode characters', () => {
assertEquals(chars('😺😸😹'), ['😺', '😸', '😹']);
t.test('chars fn properly splits strings into unicode characters', () => {
t.assertEquals(chars('😺😸😹'), ['😺', '😸', '😹']);
});
t.test('is_ascii properly descerns ascii chars', () => {
t.assertTrue(is_ascii('asjyverkjhsdf1928374'));
t.assertFalse(is_ascii('😺acalskjsdf'));
});

View File

@ -1,4 +1,4 @@
import { die, importForRuntime } from './index.ts';
import { die, importForRuntime } from './mod.ts';
export const STDIN_FILENO = 0;
export const STOUT_FILENO = 1;
@ -122,7 +122,7 @@ export const getTermios = async () => {
}
const oldTermiosPtr = getPointer(this.#cookedTermios);
let res = tcsetattr(STDIN_FILENO, TCSANOW, oldTermiosPtr);
const res = tcsetattr(STDIN_FILENO, TCSANOW, oldTermiosPtr);
if (res === -1) {
die('Failed to restore canonical mode.');
}

13
src/common/test_base.ts Normal file
View File

@ -0,0 +1,13 @@
/**
* The shared test interface, so tests can be run by both runtimes
*/
export interface ITestBase {
test(name: string, fn: () => void): void;
assertEquals(actual: unknown, expected: unknown): void;
assertNotEquals(actual: unknown, expected: unknown): void;
assertStrictEquals(actual: unknown, expected: unknown): void;
assertExists(actual: unknown): void;
assertInstanceOf(actual: unknown, expectedType: any): void;
assertTrue(actual: boolean): void;
assertFalse(actual: boolean): void;
}

View File

@ -1,7 +1,51 @@
export const test = Deno.test;
export {
assert,
import { ITestBase } from '../common/mod.ts';
import {
assertEquals,
assertExists,
assertInstanceOf,
AssertionError,
assertNotEquals,
assertStrictEquals,
} from 'https://deno.land/std/assert/mod.ts';
class TestBase implements ITestBase {
test(name: string, fn: () => void): void {
return Deno.test(name, fn);
}
assertEquals(actual: unknown, expected: unknown): void {
return assertEquals(actual, expected);
}
assertStrictEquals(actual: unknown, expected: unknown) {
return assertStrictEquals(actual, expected);
}
assertNotEquals(actual: unknown, expected: unknown): void {
return assertNotEquals(actual, expected);
}
assertExists(actual: unknown, msg?: string): void {
return assertExists(actual, msg);
}
assertInstanceOf(actual: unknown, expectedType: any): void {
return assertInstanceOf(actual, expectedType);
}
assertTrue(actual: boolean): void {
if (actual !== true) {
throw new AssertionError(`actual: "${actual}" expected to be true"`);
}
}
assertFalse(actual: boolean): void {
if (actual !== false) {
throw new AssertionError(`actual: "${actual}" expected to be false"`);
}
}
}
export const testBase = new TestBase();
export default testBase;

View File

@ -8,12 +8,12 @@ export enum RunTime {
Unknown = 'common',
}
import { importForRuntime } from './common/index.ts';
import { importForRuntime } from './common/mod.ts';
/**
* Determine the runtime strategy, and go!
*/
(async () => {
const { main } = await importForRuntime('./index.ts');
const { main } = await importForRuntime('./mod.ts');
await main();
})();