Skip to content

Commit 16dd115

Browse files
authored
Cover lexer and content-stream branches
* Cover lexer string/predicate and content-stream array branches - lex readLiteralString: \r\t\b\f escapes, CR/CRLF continuations, bare CR->LF, unknown escape, and the trailing-backslash/unterminated errors - lex IsDelimiter, hexDigit: spec §7.2.2 character-set classifiers - contentstream readArray: name/bool/null/nested element kinds + errors - contentstream Operand.Int: non-number and int64-overflow cases - xref indexEOL: no-EOL sentinel * Trim narration from new coverage-test comments Keep only comments that explain intent or a footgun a reader can't get from the test body, git, or the PR; delete or inline the rest. PR #20
1 parent fd74315 commit 16dd115

3 files changed

Lines changed: 127 additions & 0 deletions

File tree

contentstream/scanner_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,3 +345,47 @@ func TestReadDictValueErrors(t *testing.T) {
345345
})
346346
}
347347
}
348+
349+
// Every readArray element kind, including the ones TJ rarely uses (bool, null,
350+
// name, nested array/dict).
351+
func TestArrayMixedElementKinds(t *testing.T) {
352+
ops := collect(t, "[42 3.14 /Nm (s) <41> true false null [1 2] << /K 9 >>] TJ")
353+
if len(ops) != 1 || ops[0].Operator != "TJ" {
354+
t.Fatalf("want one TJ op, got %+v", ops)
355+
}
356+
got := ops[0].Operands[0].Array
357+
wantKinds := []contentstream.Kind{
358+
contentstream.KindNumber, contentstream.KindNumber, contentstream.KindName,
359+
contentstream.KindString, contentstream.KindString, contentstream.KindBool,
360+
contentstream.KindBool, contentstream.KindNull, contentstream.KindArray,
361+
contentstream.KindDict,
362+
}
363+
if len(got) != len(wantKinds) {
364+
t.Fatalf("array len = %d, want %d", len(got), len(wantKinds))
365+
}
366+
for i, want := range wantKinds {
367+
if got[i].Kind != want {
368+
t.Errorf("element %d Kind = %v, want %v", i, got[i].Kind, want)
369+
}
370+
}
371+
}
372+
373+
func TestArrayElementErrors(t *testing.T) {
374+
for _, src := range []string{"[ foo ] n", "[1 2"} { // bad keyword in array; unterminated
375+
t.Run(src, func(t *testing.T) {
376+
if _, err := contentstream.New([]byte(src)).Next(); err == nil {
377+
t.Fatal("expected an error, got nil")
378+
}
379+
})
380+
}
381+
}
382+
383+
// Int must report ok=false on int64 overflow (not silently wrap) and for non-numbers.
384+
func TestOperandIntEdgeCases(t *testing.T) {
385+
if _, ok := collect(t, "/Name n")[0].Operands[0].Int(); ok {
386+
t.Error("name operand yielded an int")
387+
}
388+
if _, ok := collect(t, "99999999999999999999999999 n")[0].Operands[0].Int(); ok {
389+
t.Error("overflowing integer literal yielded an int")
390+
}
391+
}

internal/lex/lex_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,3 +219,67 @@ func TestLexerAccessors(t *testing.T) {
219219
t.Errorf("Remaining = %q, want world", got)
220220
}
221221
}
222+
223+
// Literal-string escapes/newlines (§7.3.4.2) not already in TestLexerLiteralString.
224+
func TestLexerLiteralStringEscapes(t *testing.T) {
225+
cases := []struct {
226+
name, in, want string
227+
}{
228+
{"named_escapes", "(\\t\\b\\f\\r)", "\t\b\f\r"},
229+
{"cr_line_continuation", "(a\\\rb)", "ab"},
230+
{"crlf_line_continuation", "(a\\\r\nb)", "ab"},
231+
{"unknown_escape_keeps_char", "(\\q)", "q"},
232+
{"bare_cr_to_lf", "(a\rb)", "a\nb"},
233+
{"bare_crlf_to_lf", "(a\r\nb)", "a\nb"},
234+
}
235+
for _, c := range cases {
236+
t.Run(c.name, func(t *testing.T) {
237+
tok, err := New([]byte(c.in)).Next()
238+
if err != nil {
239+
t.Fatalf("Next: %v", err)
240+
}
241+
if tok.Kind != LitString {
242+
t.Fatalf("kind = %v, want LitString", tok.Kind)
243+
}
244+
if string(tok.Bytes) != c.want {
245+
t.Errorf("got %q, want %q", tok.Bytes, c.want)
246+
}
247+
})
248+
}
249+
}
250+
251+
func TestLexerLiteralStringErrors(t *testing.T) {
252+
for _, in := range []string{"(\\", "(abc"} { // trailing backslash; unterminated
253+
if _, err := New([]byte(in)).Next(); err == nil {
254+
t.Errorf("%q: expected an error, got nil", in)
255+
}
256+
}
257+
}
258+
259+
// A misclassified delimiter byte (§7.2.2) would break tokenisation, so pin the set.
260+
func TestIsDelimiter(t *testing.T) {
261+
for _, c := range []byte("()<>[]{}/%") {
262+
if !IsDelimiter(c) {
263+
t.Errorf("IsDelimiter(%q) = false, want true", c)
264+
}
265+
}
266+
for _, c := range []byte("aZ0 \t.\\") {
267+
if IsDelimiter(c) {
268+
t.Errorf("IsDelimiter(%q) = true, want false", c)
269+
}
270+
}
271+
}
272+
273+
func TestHexDigit(t *testing.T) {
274+
valid := map[byte]int{'0': 0, '9': 9, 'a': 10, 'f': 15, 'A': 10, 'F': 15}
275+
for c, want := range valid {
276+
if v, ok := hexDigit(c); !ok || v != want {
277+
t.Errorf("hexDigit(%q) = (%d, %v), want (%d, true)", c, v, ok, want)
278+
}
279+
}
280+
for _, c := range []byte("gG/ \x00") {
281+
if _, ok := hexDigit(c); ok {
282+
t.Errorf("hexDigit(%q) = ok, want not-ok", c)
283+
}
284+
}
285+
}

xref_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,3 +440,22 @@ func TestSkipEOL(t *testing.T) {
440440
})
441441
}
442442
}
443+
444+
func TestIndexEOL(t *testing.T) {
445+
cases := []struct {
446+
buf string
447+
pos int
448+
want int
449+
}{
450+
{"ab\ncd", 0, 2},
451+
{"ab\rcd", 0, 2},
452+
{"abcd", 0, -1},
453+
{"a\nb", 2, -1},
454+
{"", 0, -1},
455+
}
456+
for _, tc := range cases {
457+
if got := indexEOL([]byte(tc.buf), tc.pos); got != tc.want {
458+
t.Errorf("indexEOL(%q, %d) = %d, want %d", tc.buf, tc.pos, got, tc.want)
459+
}
460+
}
461+
}

0 commit comments

Comments
 (0)