Skip to content

Commit 07f092e

Browse files
adonovancopybara-github
authored andcommitted
starlark: improve expectation hygiene in ScriptTest
Prior to this change, ScriptTest inherited a fuzzy syntax for expectations from its Python-based predecessor: the content of a ### comment was interpreted either as a substring, or as a regular expression. This change causes it to interpret the content of the comment as a regular expression, always. Some expectations were updated. PiperOrigin-RevId: 338258986
1 parent f056dd1 commit 07f092e

File tree

8 files changed

+63
-49
lines changed

8 files changed

+63
-49
lines changed

src/test/java/net/starlark/java/eval/ScriptTest.java

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import java.util.HashMap;
2525
import java.util.List;
2626
import java.util.Map;
27+
import java.util.regex.Pattern;
28+
import java.util.regex.PatternSyntaxException;
2729
import net.starlark.java.annot.Param;
2830
import net.starlark.java.annot.StarlarkBuiltin;
2931
import net.starlark.java.annot.StarlarkMethod;
@@ -39,7 +41,11 @@ public final class ScriptTest {
3941
//
4042
// In each test file, chunks are separated by "\n---\n".
4143
// Each chunk is evaluated separately.
42-
// Use "###" to specify the expected error.
44+
// A comment containing
45+
// ### regular expression
46+
// specifies an expected error on that line.
47+
// The part after '###', with leading/trailing spaces removed,
48+
// must be a valid regular expression matching the error.
4349
// If there is no "###", the test will succeed iff there is no error.
4450
//
4551
// Within the file, the assert_ and assert_eq functions may be used to
@@ -48,11 +54,8 @@ public final class ScriptTest {
4854

4955
// TODO(adonovan): improve this test driver (following go.starlark.net):
5056
//
51-
// - use a proper quotation syntax (Starlark string literals) in '### "foo"' expectations.
5257
// - extract support for "chunked files" into a library
5358
// and reuse it for tests of lexer, parser, resolver.
54-
// - don't interpret the pattern as "either a substring or a regexp".
55-
// Be consistent: always use regexp.
5659
// - require that some frame of each EvalException match the file/line of the expectation.
5760

5861
interface Reporter {
@@ -120,19 +123,28 @@ public static void main(String[] args) throws Exception {
120123
System.err.printf("%s:%d: <<%s>>\n", file, linenum, buf);
121124
}
122125

123-
// extract "### string" expectations
124-
Map<String, Integer> expectations = new HashMap<>();
125-
for (int i = 0; true; i += "###".length()) {
126-
i = chunk.indexOf("###", i);
127-
if (i < 0) {
128-
break;
129-
}
126+
// extract expectations: ### "regular expression"
127+
Map<Pattern, Integer> expectations = new HashMap<>();
128+
for (int i = chunk.indexOf("###"); i >= 0; i = chunk.indexOf("###", i)) {
130129
int j = chunk.indexOf("\n", i);
131130
if (j < 0) {
132131
j = chunk.length();
133132
}
134-
String pattern = chunk.substring(i + 3, j).trim();
133+
135134
int line = linenum + newlines(chunk.substring(0, i));
135+
String comment = chunk.substring(i + 3, j);
136+
i = j;
137+
138+
// Compile regular expression in comment.
139+
Pattern pattern;
140+
try {
141+
pattern = Pattern.compile(comment.trim());
142+
} catch (PatternSyntaxException ex) {
143+
System.err.printf("%s:%d: invalid regexp: %s\n", file, line, ex.getMessage());
144+
ok = false;
145+
continue;
146+
}
147+
136148
if (false) {
137149
System.err.printf("%s:%d: expectation '%s'\n", file, line, pattern);
138150
}
@@ -172,6 +184,8 @@ public static void main(String[] args) throws Exception {
172184
// and expections match exactly. Furthermore, look only at errors
173185
// whose stack has a frame with a file/line that matches the expectation.
174186
// This requires inspecting EvalException stack.
187+
// (There can be at most one dynamic error per chunk.
188+
// Do we even need to allow multiple expectations?)
175189
if (!expected(expectations, ex.getMessage())) {
176190
System.err.println(ex.getMessageWithStack());
177191
ok = false;
@@ -187,7 +201,7 @@ public static void main(String[] args) throws Exception {
187201
}
188202

189203
// unmatched expectations
190-
for (Map.Entry<String, Integer> e : expectations.entrySet()) {
204+
for (Map.Entry<Pattern, Integer> e : expectations.entrySet()) {
191205
System.err.printf("%s:%d: unmatched expectation: %s\n", file, e.getValue(), e.getKey());
192206
ok = false;
193207
}
@@ -214,9 +228,9 @@ private static void reportError(StarlarkThread thread, String message) {
214228
ok = false;
215229
}
216230

217-
private static boolean expected(Map<String, Integer> expectations, String message) {
218-
for (String pattern : expectations.keySet()) {
219-
if (message.contains(pattern) || message.matches(".*" + pattern + ".*")) {
231+
private static boolean expected(Map<Pattern, Integer> expectations, String message) {
232+
for (Pattern pattern : expectations.keySet()) {
233+
if (pattern.matcher(message).find()) {
220234
expectations.remove(pattern);
221235
return true;
222236
}

src/test/java/net/starlark/java/eval/testdata/int.sky

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ assert_eq(~((~(0x1010 << 100)) >> 100), 0x1010)
206206
---
207207
1 & False ### unsupported binary operation: int & bool
208208
---
209-
"a" ^ 5 ### unsupported binary operation: string ^ int
209+
"a" ^ 5 ### unsupported binary operation: string \^ int
210210
---
211211
~False ### unsupported unary operation: ~bool
212212
---

src/test/java/net/starlark/java/eval/testdata/int_constructor.sky

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ int('123', 3) ### invalid base-3 literal: "123"
7272
---
7373
int('FF', 15) ### invalid base-15 literal: "FF"
7474
---
75-
int('123', -1) ### invalid base -1 (want 2 <= base <= 36)
75+
int('123', -1) ### invalid base -1 \(want 2 <= base <= 36\)
7676
---
7777
int('123', 1) ### invalid base 1 \(want 2 <= base <= 36\)
7878
---
@@ -94,7 +94,7 @@ int(' 42 ') ### invalid base-10 literal: " 42 "
9494
---
9595
int('-') ### invalid base-10 literal: "-"
9696
---
97-
int('+') ### invalid base-10 literal: "+"
97+
int('+') ### invalid base-10 literal: "\+"
9898
---
9999
int('0x') ### invalid base-10 literal: "0x"
100100
---

src/test/java/net/starlark/java/eval/testdata/json.sky

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,13 @@ json.decode('truefalse') ### at offset 4, unexpected character "f" after value
101101
---
102102
json.decode('"abc') ### unclosed string literal
103103
---
104-
json.decode('"ab\\gc"') ### invalid escape '\g'
104+
json.decode('"ab\\gc"') ### invalid escape '\\g'
105105
---
106106
json.decode("'abc'") ### unexpected character "'"
107107
---
108108
json.decode("1.2.3") ### invalid number: 1.2.3
109109
---
110-
json.decode("+1") ### unexpected character "+"
110+
json.decode("+1") ### unexpected character "\+"
111111
---
112112
json.decode("-abc") ### invalid number: -
113113
---
@@ -119,7 +119,7 @@ json.decode("00") ### invalid number: 00
119119
---
120120
json.decode("--1") ### invalid number: --1
121121
---
122-
json.decode("-+1") ### invalid number: -+1
122+
json.decode("-+1") ### invalid number: -\+1
123123
---
124124
json.decode("1e1e1") ### invalid number: 1e1e1
125125
---
@@ -157,7 +157,7 @@ json.decode('{"one": 1') ### unexpected end of file
157157
---
158158
json.decode('{"one" 1') ### after object key, got "1", want ':'
159159
---
160-
json.decode('{"one": 1 "two": 2') ### in object, got "\"", want ',' or '}'
160+
json.decode('{"one": 1 "two": 2') ### in object, got "\\"", want ',' or '}'
161161
---
162162
json.decode('{"x": 1, "x": 2}') ### object has duplicate key: "x"
163163
---
@@ -172,13 +172,13 @@ json.decode('{"one": 1]') ### in object, got "]", want ',' or '}'
172172
json.decode('[' * 10000) ### nesting depth limit exceeded
173173
---
174174
# Unescaped control codes (even tabs) are forbidden in strings.
175-
json.decode('"\t"') ### invalid character '\x09' in string literal
175+
json.decode('"\t"') ### invalid character '\\x09' in string literal
176176
---
177-
json.decode('"\\u123"') ### incomplete \uXXXX escape
177+
json.decode('"\\u123"') ### incomplete \\uXXXX escape
178178
---
179-
json.decode('"\\u123') ### incomplete \uXXXX escape
179+
json.decode('"\\u123') ### incomplete \\uXXXX escape
180180
---
181-
json.decode('"\\u1') ### incomplete \uXXXX escape
181+
json.decode('"\\u1') ### incomplete \\uXXXX escape
182182
---
183183

184184
def codec(x):

src/test/java/net/starlark/java/eval/testdata/list_mutation.sky

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ assert_eq(li3.pop(1), 3)
9191
assert_eq(li3, [2, 4])
9292

9393
---
94-
[1, 2].pop(3) ### index out of range (index is 3, but sequence has 2 elements)
94+
[1, 2].pop(3) ### index out of range \(index is 3, but sequence has 2 elements\)
9595
---
9696
(1, 2).pop() ### 'tuple' value has no field or method 'pop'
9797
---

src/test/java/net/starlark/java/eval/testdata/list_slices.sky

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,12 @@ bananas.index("d", -1000, 1000) ### not found in list
106106
---
107107
[[1], [2]]['a'] ### got string for sequence index, want int
108108
---
109-
[0, 1, 2][3] ### index out of range (index is 3, but sequence has 3 elements)
109+
[0, 1, 2][3] ### index out of range \(index is 3, but sequence has 3 elements\)
110110
---
111-
[0, 1, 2][-4] ### index out of range (index is -4, but sequence has 3 elements)
111+
[0, 1, 2][-4] ### index out of range \(index is -4, but sequence has 3 elements\)
112112
---
113-
[0][-2] ### index out of range (index is -2, but sequence has 1 elements)
113+
[0][-2] ### index out of range \(index is -2, but sequence has 1 elements\)
114114
---
115-
[0][1] ### index out of range (index is 1, but sequence has 1 elements)
115+
[0][1] ### index out of range \(index is 1, but sequence has 1 elements\)
116116
---
117-
[][1] ### index out of range (index is 1, but sequence has 0 elements)
117+
[][1] ### index out of range \(index is 1, but sequence has 0 elements\)

src/test/java/net/starlark/java/eval/testdata/loop.sky

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ x, y = 1 ### got 'int' in sequence assignment
1818
---
1919

2020
# too few
21-
x, y = () ### too few values to unpack (got 0, want 2)
21+
x, y = () ### too few values to unpack \(got 0, want 2\)
2222
---
23-
[x, y] = () ### too few values to unpack (got 0, want 2)
23+
[x, y] = () ### too few values to unpack \(got 0, want 2\)
2424
---
2525

2626
# just right
@@ -34,11 +34,11 @@ x, y = 1, 2
3434
# too many
3535
() = 1 ### got 'int' in sequence assignment
3636
---
37-
() = (1,) ### too many values to unpack (got 1, want 0)
37+
() = (1,) ### too many values to unpack \(got 1, want 0\)
3838
---
39-
x, y = 1, 2, 3 ### too many values to unpack (got 3, want 2)
39+
x, y = 1, 2, 3 ### too many values to unpack \(got 3, want 2\)
4040
---
41-
[x, y] = 1, 2, 3 ### too many values to unpack (got 3, want 2)
41+
[x, y] = 1, 2, 3 ### too many values to unpack \(got 3, want 2\)
4242
---
4343

4444
# Assignment to empty tuple is permitted.
@@ -47,11 +47,11 @@ assert_eq([1 for [] in [(), []]], [1, 1])
4747

4848
# Iterating over dict without .items() gives informative error.
4949
assert_eq([v for v in dict(a = "b")], ["a"])
50-
[None for () in dict(a = "b")] ### got 'string' in sequence assignment (want 0-element sequence)
50+
[None for () in dict(a = "b")] ### got 'string' in sequence assignment \(want 0-element sequence\)
5151
---
52-
[None for (v1,) in dict(a = "b")] ### got 'string' in sequence assignment (want 1-element sequence)
52+
[None for (v1,) in dict(a = "b")] ### got 'string' in sequence assignment \(want 1-element sequence\)
5353
---
54-
[None for v1, v2 in dict(a = "b")] ### got 'string' in sequence assignment (want 2-element sequence)
54+
[None for v1, v2 in dict(a = "b")] ### got 'string' in sequence assignment \(want 2-element sequence\)
5555
---
5656

5757
# --- list comprehensions ---
@@ -136,15 +136,15 @@ assert_eq([x + y for x, y in [(1, 2), (3, 4)]], [3, 7])
136136
assert_eq([z + t for (z, t) in [[1, 2], [3, 4]]], [3, 7])
137137

138138
# multiple variables, fail
139-
[x + y for x, y, z in [(1, 2), (3, 4)]] ### too few values to unpack (got 2, want 3)
139+
[x + y for x, y, z in [(1, 2), (3, 4)]] ### too few values to unpack \(got 2, want 3\)
140140
---
141-
[x + y for x, y in (1, 2)] ### got 'int' in sequence assignment (want 2-element sequence)
141+
[x + y for x, y in (1, 2)] ### got 'int' in sequence assignment \(want 2-element sequence\)
142142
---
143-
[x + y for x, y, z in [(1, 2), (3, 4)]] ### too few values to unpack (got 2, want 3)
143+
[x + y for x, y, z in [(1, 2), (3, 4)]] ### too few values to unpack \(got 2, want 3\)
144144
---
145-
[x + y for x, y in (1, 2)] ### got 'int' in sequence assignment (want 2-element sequence)
145+
[x + y for x, y in (1, 2)] ### got 'int' in sequence assignment \(want 2-element sequence\)
146146
---
147-
[x + y for x, y, z in [(1, 2), (3, 4)]] ### too few values to unpack (got 2, want 3)
147+
[x + y for x, y, z in [(1, 2), (3, 4)]] ### too few values to unpack \(got 2, want 3\)
148148
---
149-
[x2 + y2 for x2, y2 in (1, 2)] ### got 'int' in sequence assignment (want 2-element sequence)
149+
[x2 + y2 for x2, y2 in (1, 2)] ### got 'int' in sequence assignment \(want 2-element sequence\)
150150
---

src/test/java/net/starlark/java/eval/testdata/string_format.sky

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ assert_eq('{test} and {}'.format(2, test = 1), "1 and 2")
5454
assert_eq('{test} and {0}'.format(2, test = 1), "1 and 2")
5555

5656
---
57-
'{{}'.format(1) ### Found '}' without matching '{'
57+
'{{}'.format(1) ### Found '}' without matching '\{'
5858
---
59-
'{}}'.format(1) ### Found '}' without matching '{'
59+
'{}}'.format(1) ### Found '}' without matching '\{'
6060
---
6161
'{0}'.format() ### No replacement found for index 0
6262
---

0 commit comments

Comments
 (0)