forked from swiftwasm/JavaScriptKit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvalue-smith.mjs
More file actions
124 lines (102 loc) · 3.01 KB
/
Copy pathvalue-smith.mjs
File metadata and controls
124 lines (102 loc) · 3.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// @ts-check
/**
* @typedef {import('../types.mjs').BridgeType} BridgeType
*/
import { Random } from './random.mjs';
/** @type {number[]} */
const INT_EDGE_CASES = [0, 1, -1, 42, -42, 2147483647, -2147483648];
/** @type {number[]} */
const FLOAT_VALUES = [0, 1.5, -1.5, 3.14159, 0.5, -0.25, 100.0, 2.718281828];
/** @type {string[]} */
const STRING_VALUES = ['', 'a', 'hello', 'café', '🎉', 'foo bar', 'test', '42'];
/** @type {string[]} */
const DICT_KEYS = ['a', 'b', 'c', 'x', 'y', 'key', 'name', 'val'];
/**
* Generates random JS value source strings for a given BridgeType.
*/
export class ValueSmith {
/** @type {Random} */
#rng;
/**
* @param {Random} rng
*/
constructor(rng) {
this.#rng = rng;
}
/**
* Returns a JS source string for a random value of the given type.
* @param {BridgeType} type
* @returns {string}
*/
randomValue(type) {
switch (type.kind) {
case 'int':
return String(this.#rng.pick(INT_EDGE_CASES));
case 'float':
case 'double':
return String(this.#rng.pick(FLOAT_VALUES));
case 'string': {
const s = this.#rng.pick(STRING_VALUES);
return JSON.stringify(s);
}
case 'bool':
return this.#rng.bool() ? 'true' : 'false';
case 'nullable': {
if (this.#rng.next() < 0.3) {
return 'null';
}
return this.randomValue(type.wrapped);
}
case 'array': {
const len = this.#rng.nextInt(0, 5);
const elems = [];
for (let i = 0; i < len; i++) {
elems.push(this.randomValue(type.element));
}
return `[${elems.join(', ')}]`;
}
case 'dictionary': {
const len = this.#rng.nextInt(0, 3);
const usedKeys = new Set();
const entries = [];
for (let i = 0; i < len; i++) {
let key;
do {
key = this.#rng.pick(DICT_KEYS);
} while (usedKeys.has(key));
usedKeys.add(key);
entries.push(`${JSON.stringify(key)}: ${this.randomValue(type.value)}`);
}
return `{${entries.join(', ')}}`;
}
case 'jsObject':
return '{}';
case 'void':
return 'undefined';
case 'importedClass':
// Can't generate imported classes as literals
return 'undefined /* cannot literal importedClass */';
default:
return 'undefined';
}
}
/**
* Returns a JS source string for an assertion comparison.
* @param {string} varName — the variable to assert against
* @param {string} expectedValue — the expected value source string
* @param {BridgeType} type
* @returns {string}
*/
assertionCode(varName, expectedValue, type) {
switch (type.kind) {
case 'array':
case 'dictionary':
case 'nullable':
return `assert.deepStrictEqual(${varName}, ${expectedValue})`;
case 'void':
return `assert.strictEqual(${varName}, undefined)`;
default:
return `assert.strictEqual(${varName}, ${expectedValue})`;
}
}
}