// Copyright ©2020 The Gonum Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package rdf import ( "archive/tar" "compress/gzip" "fmt" "io" "os" "path/filepath" "reflect" "strings" "testing" ) func TestRDFWorkingGroupSuite(t *testing.T) { for _, file := range []string{ "ntriple_tests.tar.gz", "nquad_tests.tar.gz", } { suite, err := os.Open(filepath.Join("testdata", file)) if err != nil { t.Fatalf("Failed to open test suite in %q: %v", file, err) } defer suite.Close() r, err := gzip.NewReader(suite) if err != nil { t.Fatalf("Failed to uncompress test suite in %q: %v", file, err) } tr := tar.NewReader(r) for { h, err := tr.Next() if err != nil { if err == io.EOF { break } t.Fatalf("Unexpected error while reading suite archive: %v", err) } h.Name = filepath.Base(h.Name) if filepath.Ext(h.Name) != ".nt" && filepath.Ext(h.Name) != ".nq" { continue } if _, ok := testSuite[h.Name]; !ok { t.Errorf("Missing test suite item %q", h.Name) continue } isBad := strings.Contains(h.Name, "bad") var got []statement dec := NewDecoder(tr) for i := 0; ; i++ { s, err := dec.Unmarshal() if err == io.EOF { break } gotBad := err != nil if gotBad != isBad { t.Errorf("Unexpected error return for test suite item %q, got: %v", h.Name, err) } var subj, pred, obj, lab term if s != nil { subj.text, subj.qual, subj.kind, _ = s.Subject.Parts() pred.text, pred.qual, pred.kind, _ = s.Predicate.Parts() obj.text, obj.qual, obj.kind, _ = s.Object.Parts() lab.text, lab.qual, lab.kind, _ = s.Label.Parts() if lab.text == "" { lab = term{} } got = append(got, statement{testSuite[h.Name][i].input, subj, pred, obj, lab}) } if !gotBad { _, err = ParseNQuad(s.String()) if err != nil { t.Errorf("Unexpected error return for valid statement in test suite item %q (%#v) got: %v rendered as\n%[2]s", h.Name, s, err) } st, err := termFor(subj.text, subj.qual, subj.kind) if err != nil { t.Errorf("Unexpected error return for valid subject in test suite item %q (%#v) got: %v rendered as\n%[2]s", h.Name, s, err) } pt, err := termFor(pred.text, pred.qual, pred.kind) if err != nil { t.Errorf("Unexpected error return for valid predicate in test suite item %q (%#v) got: %v rendered as\n%[2]s", h.Name, s, err) } ot, err := termFor(obj.text, obj.qual, obj.kind) if err != nil { t.Errorf("Unexpected error return for valid object in test suite item %q (%#v) got: %v rendered as\n%[2]s", h.Name, s, err) } lt, err := termFor(lab.text, lab.qual, lab.kind) if err != nil { t.Errorf("Unexpected error return for valid label in test suite item %q (%#v) got: %v rendered as\n%[2]s", h.Name, s, err) } // We can't check that we recreate the original from the test suite // due to escaping, but we can check for a second pass through the // round-trip. c := &Statement{Subject: st, Predicate: pt, Object: ot, Label: lt} pc, err := ParseNQuad(c.String()) if err != nil { t.Errorf("Unexpected error return for reconstructed statement in test suite item %q (%#v) got: %v rendered as\n%[2]s", h.Name, s, err) } if !reflect.DeepEqual(c, pc) { t.Errorf("Unexpected reconstruction:\norig: %#v\ncons: %#v\nparsed:%#v", s, c, pc) } } } if !reflect.DeepEqual(testSuite[h.Name], got) { t.Errorf("Unexpected result for test suite item %q", h.Name) } } } } func termFor(text, qual string, kind Kind) (Term, error) { switch kind { case Invalid: return Term{}, nil case Blank: return NewBlankTerm(text) case IRI: return NewIRITerm(text) case Literal: return NewLiteralTerm(text, qual) default: panic(fmt.Sprintf("bad test kind=%d", kind)) } } var escapeSequenceTests = []struct { escaped string unEscaped string canRoundTrip bool }{ {escaped: `plain text!`, unEscaped: "plain text!", canRoundTrip: true}, {escaped: `\t`, unEscaped: "\t", canRoundTrip: false}, {escaped: `\b`, unEscaped: "\b", canRoundTrip: false}, {escaped: `\n`, unEscaped: "\n", canRoundTrip: true}, {escaped: `\r`, unEscaped: "\r", canRoundTrip: true}, {escaped: `\f`, unEscaped: "\f", canRoundTrip: false}, {escaped: `\\`, unEscaped: "\\", canRoundTrip: true}, {escaped: `\u0080`, unEscaped: "\u0080", canRoundTrip: true}, {escaped: `\U00000080`, unEscaped: "\u0080", canRoundTrip: false}, {escaped: `\t\b\n\r\f\"'\\`, unEscaped: "\t\b\n\r\f\"'\\", canRoundTrip: false}, {escaped: `\t\u0080`, unEscaped: "\t\u0080", canRoundTrip: false}, {escaped: `\b\U00000080`, unEscaped: "\b\u0080", canRoundTrip: false}, {escaped: `\u0080\n`, unEscaped: "\u0080\n", canRoundTrip: true}, {escaped: `\U00000080\r`, unEscaped: "\u0080\r", canRoundTrip: false}, {escaped: `\u00b7\f\U000000b7`, unEscaped: "·\f·", canRoundTrip: false}, {escaped: `\U000000b7\\\u00b7`, unEscaped: "·\\·", canRoundTrip: false}, {escaped: `\U00010105\\\U00010106`, unEscaped: "\U00010105\\\U00010106", canRoundTrip: true}, } func TestUnescape(t *testing.T) { for _, test := range escapeSequenceTests { got := unEscape([]rune(test.escaped)) if got != test.unEscaped { t.Errorf("Failed to properly unescape %q, got:%q want:%q", test.escaped, got, test.unEscaped) } if test.canRoundTrip { got = escape("", test.unEscaped, "") if got != test.escaped { t.Errorf("Failed to properly escape %q, got:%q want:%q", test.unEscaped, got, test.escaped) } got = escape(`"`, test.unEscaped, `"`) if got != `"`+test.escaped+`"` { t.Errorf("Failed to properly escape %q quoted, got:%q want:%q", test.unEscaped, got, `"`+test.escaped+`"`) } } } }