Skip to content

Commit 0e90c9d

Browse files
Improve lab testing (#673)
* Improve lab testing Improve how labs are tested. Change how the "examples" are used in the hint section so that people provide full examples up to the tested index, and *document* this (before it wasn't clear how do this). By using this approach we will *consistently* test hints. In the longer term, we might remove the constraint on which hints are returned during the testing, but that requires determining which forms cover which indexes, which is more complex. By asking for the fields now, we make it easier to make that change later. This makes changes in labs to *use* the new testing mechanism in hint examples. This also adds the script "mass-test", which automatically tests all labs. It does this by opening them all in the web browser, triggering the self-test in each lab. You have to manually look at each page, which is a little annoying. That said, that takes very little time. Automatically opening every page is a big improvement in terms of automated testing. Signed-off-by: David A. Wheeler <[email protected]> * Remove trailing space Signed-off-by: David A. Wheeler <[email protected]> --------- Signed-off-by: David A. Wheeler <[email protected]>
1 parent bc19c91 commit 0e90c9d

File tree

6 files changed

+111
-39
lines changed

6 files changed

+111
-39
lines changed

docs/labs/checker.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -425,12 +425,14 @@ function processHints(requestedHints) {
425425
newHint.text = hint.text;
426426
// Precompile all regular expressions & report any failures
427427
if (hint.present) {
428+
newHint.present = hint.present;
428429
newHint.presentRe = processRegex(hint.present,
429430
`hint[${i}].present`, false);
430431
};
431432
if (hint.absent) {
433+
newHint.absent = hint.absent;
432434
newHint.absentRe = processRegex(hint.absent,
433-
`hint[${i}].present`, false);
435+
`hint[${i}].absent`, false);
434436
};
435437
if (!hint.absent && !hint.present && (i != requestedHints.length - 1)) {
436438
showDebugOutput(
@@ -565,11 +567,11 @@ function runSelftest() {
565567
for (let hint of hints) {
566568
if (hint.examples) {
567569
for (let example of hint.examples) {
568-
// Create a testAttempt
569-
let testAttempt = expected.slice(); // shallow copy of expected
570-
testAttempt[hint.index] = example;
571-
// What hint does our new testAttempt give?
572-
actualHint = findHint(testAttempt, [hint.index]);
570+
// We directly pass our example.
571+
// This means that examples will need to contain multiple
572+
// values if the index > 0. We only return hints with the
573+
// given hint index.
574+
actualHint = findHint(example, [hint.index]);
573575
if (actualHint != hint.text) {
574576
alert(`Lab Error: Unexpected hint!\n\nExample:\n${example}\n\nExpected hint:\n${hint.text}\n\nProduced hint:\n${actualHint}\n\nExpected (passing example)=${JSON.stringify(expected)}\n\ntestAttempt=${JSON.stringify(testAttempt)}\nFailing hint=${JSON.stringify(hint)}`);
575577
};

docs/labs/create_checker.md

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -364,9 +364,17 @@ If you want to check an index other than `0`, add an `index` field and provide
364364
an integer.
365365

366366
A hint can include an `examples` field, which must then contain
367-
an array of examples (each example is an array of Strings).
367+
an array of examples which are used as tests.
368+
Each example is an array of Strings; each element
369+
corresponds to the indexes.
368370
On load the system will verify that each example will report the
369-
maatching hint (this helps ensure that the hint order is sensible).
371+
matching hint (this helps ensure that the hint order is sensible).
372+
373+
At the time of this writing, all examples are loaded and used as tests
374+
to ensure that the hint requested is actually the one reported.
375+
If your example is for a later index, provide test values that
376+
don't trigger earlier index values. Currently those values are ignored,
377+
but future versions will probably use them when checking the examples.
370378

371379
#### Examples of hints
372380

@@ -398,6 +406,21 @@ the hint.
398406
The second hint triggers when the user attempt *contains* the given
399407
pattern (note the term `present`).
400408

409+
The "examples" shown here are for a common case: the index is 0.
410+
Once you have multiple index, you'll need to use a longer form for
411+
examples with larger indexes:
412+
413+
~~~~yaml
414+
examples:
415+
-
416+
- " VALUE FOR INDEX0"
417+
- " VALUE FOR INDEX1"
418+
-
419+
- " VALUE FOR INDEX0"
420+
- " VALUE FOR INDEX1"
421+
~~~~yaml
422+
423+
401424
### Notes on YAML
402425

403426
The info section supports
@@ -648,7 +671,7 @@ Here is an example:
648671

649672
~~~~yaml
650673
preprocessing:
651-
-
674+
-
652675
- |-
653676
[\n\r]+
654677
- ""

docs/labs/deserialization.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,10 @@
115115
text: Begin the second section with `if ( data.username && ... `
116116
because you must check if data is even present before you can check
117117
various attributes of that data.
118-
# examples:
119-
# -
120-
# - "const data = JSON.parse(base64Decoded);"
121-
# - "if (typeof data.username == 'string' && data.username.length < 20 && data.username) {"
118+
examples:
119+
-
120+
- "const data = JSON.parse(base64Decoded);"
121+
- "if (typeof data.username == 'string' && data.username.length < 20 && data.username) {"
122122
successes:
123123
-
124124
- const data = JSON.parse(base64Decoded);

docs/labs/handling-errors.html

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -79,45 +79,67 @@
7979
present: "{ (.*?)} "
8080
text: Try simply returning the result of the division.
8181
examples:
82-
- - " return { success: true, result: a / b };"
83-
- - " return { result: a / b };"
82+
-
83+
- throw new Error("Division by zero is not allowed");
84+
- " return { success: true, result: a / b };"
85+
-
86+
- throw new Error("Division by zero is not allowed");
87+
- " return { result: a / b };"
8488
- index: 2
8589
absent: '\s*try\s*{\s* '
8690
text: >-
8791
Use a try block to catch any exceptions that might be thrown.
8892
It should look something like `try { ... } catch(err) {...}`
8993
(fill in the `...` sections).
9094
examples:
91-
- - " const result = divide(10, 2);"
95+
-
96+
- throw new Error("Division by zero is not allowed");
97+
- return a / b;
98+
- " const result = divide(10, 2);"
9299
- index: 2
93100
present: '\s* try \s* { .*? if \( result.success \) .*?'
94101
text: You may assume that the result is successful within the try block.
95102
examples:
96-
- - " try { const result = divide(10 ,2); if( result.success) { console.log ( \"Result:\", result ); "
103+
-
104+
- throw new Error("Division by zero is not allowed");
105+
- return a / b;
106+
- " try { const result = divide(10 ,2); if( result.success) { console.log ( \"Result:\", result ); "
97107
- index: 2
98108
present: '.*? result.result .*?'
99109
text: The result is not an object, it is a number.
100110
examples:
101-
- - " try { const result = divide(10 ,2); console.log ( \"Result:\", result.result ); "
111+
-
112+
- throw new Error("Division by zero is not allowed");
113+
- return a / b;
114+
- " try { const result = divide(10 ,2); console.log ( \"Result:\", result.result ); "
102115
- index: 2
103116
absent: '.*? catch .*? '
104117
text: >-
105118
Handle the error within the catch block. You need `catch(err) {...}`
106119
after `try {...}` to catch an error in the try block.
107120
examples:
108-
- - " try { const result = divide(10 ,2); console.log ( \"Result:\", result ); }"
121+
-
122+
- throw new Error("Division by zero is not allowed");
123+
- return a / b;
124+
- " try { const result = divide(10 ,2); console.log ( \"Result:\", result ); }"
109125
- index: 2
110126
absent: '\s* catch \s* \( .*? \) { \s* '
111127
text: Use 'catch (...) {...}' to catch an error object within the catch block.
112128
examples:
113-
- - " try { const result = divide(10 ,2); console.log ( \"Result:\", result ); } catch {}"
129+
-
130+
- throw new Error("Division by zero is not allowed");
131+
- return a / b;
132+
- " try { const result = divide(10 ,2); console.log ( \"Result:\", result ); } catch {}"
114133
- index: 2
115134
absent: |-
116135
catch \( err \)
117136
text: >-
118137
Please use `catch(err) {...}` for purposes of this lab.
119138
examples:
120-
- - " try { const result = divide(10 ,2); console.log ( \"Result:\", result ); } catch (foo) {"
139+
-
140+
- throw new Error("Division by zero is not allowed");
141+
- return a / b;
142+
- " try { const result = divide(10 ,2); console.log ( \"Result:\", result ); } catch (foo) {"
121143
- index: 2
122144
present: |-
123145
catch .* console \. error \( ["'][^"']*["'] , result
@@ -129,8 +151,14 @@
129151
the variable `result` is out of scope in the catch block anyway;
130152
it was declared in the try block.
131153
examples:
132-
- - " try { const result = divide(10 ,2); console.log ( \"Result:\", result ); } catch (err) { console.error('Error', result.message);"
133-
- - " try { const result = divide(10 ,2); console.log ( \"Result:\", result ); } catch (err) { console.error('Error', result );"
154+
-
155+
- throw new Error("Division by zero is not allowed");
156+
- return a / b;
157+
- " try { const result = divide(10 ,2); console.log ( \"Result:\", result ); } catch (err) { console.error('Error', result.message);"
158+
-
159+
- throw new Error("Division by zero is not allowed");
160+
- return a / b;
161+
- " try { const result = divide(10 ,2); console.log ( \"Result:\", result ); } catch (err) { console.error('Error', result );"
134162
# debug: true
135163
</script>
136164
</head>

docs/labs/mass-test

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/bin/sh
2+
3+
# Mass open all lab files. Do this by opening every lab in a web browser,
4+
# which will invoke each lab's built-in tests.
5+
6+
# Create a list of labs
7+
grep -o '[A-Za-z0-9_-]*\.html' README.md | sort |uniq > ,1
8+
9+
OPENER=xdg-open
10+
if ! which "$OPENER" >/dev/null; then
11+
OPENER=open
12+
fi
13+
14+
for file in $(cat ,1); do
15+
${OPENER} "$file"
16+
done
17+
18+
echo 'Check each lab file to ensure there are no error alerts and that'
19+
echo 'there is a yellow field for input.'

docs/labs/regex1.html

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -139,26 +139,26 @@
139139
^\^
140140
index: 1
141141
text: For input validation, start with '^' to indicate a full match.
142-
# examples:
143-
# -
144-
# - "^[YN]$"
145-
# - ""
142+
examples:
143+
-
144+
- "^[YN]$"
145+
- ""
146146
- absent: |-
147147
\$$
148148
index: 1
149149
text: For input validation, end with '$' to indicate a full match.
150-
# examples:
151-
# -
152-
# - "^[YN]$"
153-
# - "^"
150+
examples:
151+
-
152+
- "^[YN]$"
153+
- "^"
154154
- absent: |-
155155
\[A-Z\]
156156
index: 1
157157
text: You can use [A-Z] to match one uppercase Latin letter (A through Z).
158-
# examples:
159-
# -
160-
# - "^[YN]$"
161-
# - "^$"
158+
examples:
159+
-
160+
- "^[YN]$"
161+
- "^$"
162162
- present: |-
163163
\^\[A-Z\]\*
164164
index: 1
@@ -172,10 +172,10 @@
172172
\[A-Z\]\[A-Z\]\*)
173173
index: 1
174174
text: You can use [A-Z]+ to match one or more uppercase Latin letters.
175-
# examples:
176-
# -
177-
# - "^[YN]$"
178-
# - "^[A-Z]$"
175+
examples:
176+
-
177+
- "^[YN]$"
178+
- "^[A-Z]$"
179179
- present: "True"
180180
index: 2
181181
text: Regular expressions are case-sensitive by default; use "true".

0 commit comments

Comments
 (0)