Skip to content

Commit

Permalink
Update toml-test
Browse files Browse the repository at this point in the history
Because toml-test now requires Go 1.18, also update the version here to
Go 1.18. Latest Debian has Go 1.19, and Go 1.18 is EOL now. People can
still continue to use older versions for older Go versions.
  • Loading branch information
arp242 committed Sep 23, 2023
1 parent cfb3ca9 commit 99c5690
Show file tree
Hide file tree
Showing 21 changed files with 181 additions and 110 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Expand Up @@ -5,7 +5,7 @@
"test": {
"strategy": {
"matrix": {
"go-version": ["1.16.x", "1.20.x"],
"go-version": ["1.18.x", "1.21.x"],
"os": ["ubuntu-latest", "macos-latest", "windows-latest"]
}
},
Expand Down
2 changes: 1 addition & 1 deletion go.mod
@@ -1,3 +1,3 @@
module github.com/BurntSushi/toml

go 1.16
go 1.18
83 changes: 54 additions & 29 deletions internal/toml-test/README.md
Expand Up @@ -8,8 +8,7 @@ JSON, described below.

Both encoders and decoders share valid tests, except an encoder accepts JSON and
outputs TOML rather than the reverse. The TOML representations are read with a
blessed decoder and is compared. Encoders have their own set of invalid tests in
the invalid-encoder directory. The JSON given to a TOML encoder is in the same
blessed decoder and is compared. The JSON given to a TOML encoder is in the same
format as the JSON that a TOML decoder should output.

Compatible with TOML version [v1.0.0][v1].
Expand All @@ -24,16 +23,13 @@ should run in most environments. It's recommended you use a binary, or a tagged
release if you build from source especially in CI environments. This prevents
your tests from breaking on changes to tests in this tool.

To compile from source you will need Go 1.16 or newer (older versions will *not*
work):
To compile from source you will need Go 1.18 or newer:

$ git clone https://github.com/BurntSushi/toml-test.git
$ cd toml-test
$ go build ./cmd/toml-test
$ go install github.com/toml-lang/toml-test/cmd/toml-test@latest

This will build a `./toml-test` binary.

[r]: https://github.com/BurntSushi/toml-test/releases
[r]: https://github.com/toml-lang/toml-test/releases

Usage
-----
Expand Down Expand Up @@ -69,11 +65,11 @@ See `toml-test -help` for detailed usage.
For your decoder to be compatible with `toml-test` it **must** satisfy the
expected interface:

- Your decoder **must** accept TOML data on `stdin` until EOF.
- Your decoder **must** accept TOML data on `stdin`.
- If the TOML data is invalid, your decoder **must** return with a non-zero
exit, code indicating an error.
exit code, indicating an error.
- If the TOML data is valid, your decoder **must** output a JSON encoding of
that data on `stdout` and return with a zero exit code indicating success.
that data on `stdout` and return with a zero exit code, indicating success.

An example in pseudocode:

Expand All @@ -94,12 +90,12 @@ Details on the tagged JSON is explained below in "JSON encoding".
For your encoder to be compatible with `toml-test`, it **must** satisfy the
expected interface:

- Your encoder **must** accept JSON data on `stdin` until EOF.
- Your encoder **must** accept JSON data on `stdin`.
- If the JSON data cannot be converted to a valid TOML representation, your
encoder **must** return with a non-zero exit code indicating an error.
encoder **must** return with a non-zero exit code, indicating an error.
- If the JSON data can be converted to a valid TOML representation, your encoder
**must** output a TOML encoding of that data on `stdout` and return with a
zero exit code indicating success.
zero exit code, indicating success.

An example in pseudocode:

Expand All @@ -121,9 +117,9 @@ The following JSON encoding applies equally to both encoders and decoders:
- TOML tables correspond to JSON objects.
- TOML table arrays correspond to JSON arrays.
- TOML values correspond to a special JSON object of the form:
`{"type": "{TTYPE}", "value": {TVALUE}}`
`{"type": "{TOML_TYPE}", "value": "{TOML_VALUE}"}`

In the above, `TTYPE` may be one of:
In the above, `TOML_TYPE` may be one of:

- string
- integer
Expand All @@ -134,7 +130,7 @@ In the above, `TTYPE` may be one of:
- date-local
- time-local

`TVALUE` is always a JSON string.
`TOML_VALUE` is always a JSON string.

Empty hashes correspond to empty JSON objects (`{}`) and empty arrays correspond
to empty JSON arrays (`[]`).
Expand All @@ -147,19 +143,19 @@ Examples:

TOML JSON

a = 42 {"type": "integer": "value": "42}
a = 42 {"type": "integer": "value": "42"}

---
<!-- -->

[tbl] {"tbl": {
a = 42 "a": {"type": "integer": "value": "42}
a = 42 "a": {"type": "integer": "value": "42"}
}}

---
<!-- -->

a = ["a", 2] {"a": [
{"type": "string", "value": "1"},
{"type: "integer": "value": "2"}
{"type": "string", "value": "1"},
{"type": "integer": "value": "2"}
]}

Or a more complex example:
Expand Down Expand Up @@ -200,14 +196,13 @@ Implementation-defined behaviour
This only tests behaviour that's should be true for every encoder implementing
TOML; a few things are left up to implementations, and are not tested here.

- Millisecond precision (4 digits) is required for datetimes and times, and
- Millisecond precision (3 digits) is required for datetimes and times, and
further precision is implementation-specific, and any greater precision than
is supported must be truncated (not rounded).

This tests only millisecond precision, and not any further precision or the
truncation of it.


Assumptions of Truth
--------------------
The following are taken as ground truths by `toml-test`:
Expand All @@ -216,9 +211,9 @@ The following are taken as ground truths by `toml-test`:
- All tests classified as `valid` **are** valid.
- All expected outputs in `valid/test-name.json` are exactly correct.
- The Go standard library package `encoding/json` decodes JSON correctly.
- When testing encoders, the TOML decoder at
[BurntSushi/toml](https://github.com/BurntSushi/toml) is assumed to be
correct. (Note that this assumption is not made when testing decoders!)
- When testing encoders the
[BurntSushi/toml](https://github.com/BurntSushi/toml) TOML decoder is assumed
to be correct. (Note that this assumption is not made when testing decoders!)

Of particular note is that **no TOML decoder** is taken as ground truth when
testing decoders. This means that most changes to the spec will only require an
Expand All @@ -228,10 +223,40 @@ added.) Obviously, this advantage does not apply to testing TOML encoders since
there must exist a TOML decoder that conforms to the specification in order to
read the output of a TOML encoder.

Usage without `toml-test` binary
--------------------------------
While the `toml-test` tool is a convenient way to run the tests, you can also
re-implement its behaviour in your own language's test-suite, which may be an
easier way to run the tests.

Because multiple TOML versions are supported, not all tests are valid for every
version of TOML; for example the 1.0.0 tests contain a test that trailing commas
in tables are invalid, but in 1.1.0 this should be considered valid.

In short: you can't "just" copy all .toml and .json files from the tests/
directory. The [tests/files-toml-1.0.0] and [tests/files-toml-1.1.0] files
contain a list of files you should run for that TOML version.

You can use those lists to determine which tests to run, or include only those
tests in your library by copying them with something like:

<files-toml-1.0.0 while read l; do
mkdir -p ~/my-test/"$(dirname "$l")"
cp -r "$l" ~/my-test/"$l"
done

These files are generated with `toml-test -list-files`:

% toml-test -list-files -toml=1.0.0
% toml-test -list-files -toml=1.1.0

[tests/files-toml-1.0.0]: tests/files-toml-1.0.0
[tests/files-toml-1.1.0]: tests/files-toml-1.1.0

Adding tests
------------
`toml-test` was designed so that tests can be easily added and removed. As
mentioned above, tests are split into two groups: invalid and valid tests.
mentioned above, tests are split into two groups: invalid and valid tests.

Invalid tests **only check if a decoder rejects invalid TOML data**. Or, in the
case of testing encoders, invalid tests **only check if an encoder rejects an
Expand Down
44 changes: 22 additions & 22 deletions internal/toml-test/json.go
Expand Up @@ -14,21 +14,21 @@ import (
//
// reflect.DeepEqual could work here, but it won't tell us how the two
// structures are different.
func (r Test) CompareJSON(want, have interface{}) Test {
func (r Test) CompareJSON(want, have any) Test {
switch w := want.(type) {
case map[string]interface{}:
case map[string]any:
return r.cmpJSONMaps(w, have)
case []interface{}:
case []any:
return r.cmpJSONArrays(w, have)
default:
return r.fail(
"Key '%s' in expected output should be a map or a list of maps, but it's a %T",
r.Key, want)
"Key '%s' in expected output should be a map or a list of maps, but it's a %s",
r.Key, fmtType(want))
}
}

func (r Test) cmpJSONMaps(want map[string]interface{}, have interface{}) Test {
haveMap, ok := have.(map[string]interface{})
func (r Test) cmpJSONMaps(want map[string]any, have any) Test {
haveMap, ok := have.(map[string]any)
if !ok {
return r.mismatch("table", want, haveMap)
}
Expand Down Expand Up @@ -73,16 +73,16 @@ func (r Test) cmpJSONMaps(want map[string]interface{}, have interface{}) Test {
return r
}

func (r Test) cmpJSONArrays(want, have interface{}) Test {
wantSlice, ok := want.([]interface{})
func (r Test) cmpJSONArrays(want, have any) Test {
wantSlice, ok := want.([]any)
if !ok {
return r.bug("'value' should be a JSON array when 'type=array', but it is a %T", want)
return r.bug("'value' should be a JSON array when 'type=array', but it is a %s", fmtType(want))
}

haveSlice, ok := have.([]interface{})
haveSlice, ok := have.([]any)
if !ok {
return r.fail(
"Malformed output from your encoder: 'value' is not a JSON array: %T", have)
"Malformed output from your encoder: 'value' is not a JSON array: %s", fmtType(have))
}

if len(wantSlice) != len(haveSlice) {
Expand All @@ -99,15 +99,15 @@ func (r Test) cmpJSONArrays(want, have interface{}) Test {
return r
}

func (r Test) cmpJSONValues(want, have map[string]interface{}) Test {
func (r Test) cmpJSONValues(want, have map[string]any) Test {
wantType, ok := want["type"].(string)
if !ok {
return r.bug("'type' should be a string, but it is a %T", want["type"])
return r.bug("'type' should be a string, but it is a %s", fmtType(want["type"]))
}

haveType, ok := have["type"].(string)
if !ok {
return r.fail("Malformed output from your encoder: 'type' is not a string: %T", have["type"])
return r.fail("Malformed output from your encoder: 'type' is not a string: %s", fmtType(have["type"]))
}

if wantType != haveType {
Expand All @@ -122,12 +122,12 @@ func (r Test) cmpJSONValues(want, have map[string]interface{}) Test {
// Atomic values are always strings
wantVal, ok := want["value"].(string)
if !ok {
return r.bug("'value' %v should be a string, but it is a %[1]T", want["value"])
return r.bug("'value' %v should be a string, but it is a %s", want["value"], fmtType(want["value"]))
}

haveVal, ok := have["value"].(string)
if !ok {
return r.fail("Malformed output from your encoder: %T is not a string", have["value"])
return r.fail("Malformed output from your encoder: %s is not a string", fmtType(have["value"]))
}

// Excepting floats and datetimes, other values can be compared as strings.
Expand Down Expand Up @@ -227,7 +227,7 @@ func (r Test) kjoin(key string) Test {
return r
}

func isValue(m map[string]interface{}) bool {
func isValue(m map[string]any) bool {
if len(m) != 2 {
return false
}
Expand All @@ -240,14 +240,14 @@ func isValue(m map[string]interface{}) bool {
return true
}

func (r Test) mismatch(wantType string, want, have interface{}) Test {
return r.fail("Key '%s' is not an %s but %[4]T:\n"+
func (r Test) mismatch(wantType string, want, have any) Test {
return r.fail("Key '%[1]s' is not an %[2]s but %[5]s:\n"+
" Expected: %#[3]v\n"+
" Your encoder: %#[4]v",
r.Key, wantType, want, have)
r.Key, wantType, want, have, fmtType(have))
}

func (r Test) valMismatch(wantType, haveType string, want, have interface{}) Test {
func (r Test) valMismatch(wantType, haveType string, want, have any) Test {
return r.fail("Key '%s' is not an %s but %s:\n"+
" Expected: %#[3]v\n"+
" Your encoder: %#[4]v",
Expand Down
14 changes: 7 additions & 7 deletions internal/toml-test/runner.go
@@ -1,4 +1,4 @@
//go:generate ./gen-multi.py
//go:generate ./gen.py

package tomltest

Expand Down Expand Up @@ -335,7 +335,7 @@ func (t Test) runValid(p Parser, fsys fs.FS) Test {
if err != nil {
return t.bug(err.Error())
}
var have interface{}
var have any
if _, err := toml.Decode(t.Output, &have); err != nil {
//return t.fail("decode TOML from encoder %q:\n %s", cmd, err)
return t.fail("decode TOML from encoder:\n %s", err)
Expand All @@ -349,7 +349,7 @@ func (t Test) runValid(p Parser, fsys fs.FS) Test {
return t.fail(err.Error())
}

var have interface{}
var have any
if err := json.Unmarshal([]byte(t.Output), &have); err != nil {
return t.fail("decode JSON output from parser:\n %s", err)
}
Expand Down Expand Up @@ -380,7 +380,7 @@ func (t Test) ReadWant(fsys fs.FS) (path, data string, err error) {
return path, string(d), nil
}

func (t *Test) ReadWantJSON(fsys fs.FS) (v interface{}, err error) {
func (t *Test) ReadWantJSON(fsys fs.FS) (v any, err error) {
var path string
path, t.Want, err = t.ReadWant(fsys)
if err != nil {
Expand All @@ -392,7 +392,7 @@ func (t *Test) ReadWantJSON(fsys fs.FS) (v interface{}, err error) {
}
return v, nil
}
func (t *Test) ReadWantTOML(fsys fs.FS) (v interface{}, err error) {
func (t *Test) ReadWantTOML(fsys fs.FS) (v any, err error) {
var path string
path, t.Want, err = t.ReadWant(fsys)
if err != nil {
Expand All @@ -413,11 +413,11 @@ func (t Test) Type() testType {
return TypeValid
}

func (t Test) fail(format string, v ...interface{}) Test {
func (t Test) fail(format string, v ...any) Test {
t.Failure = fmt.Sprintf(format, v...)
return t
}
func (t Test) bug(format string, v ...interface{}) Test {
func (t Test) bug(format string, v ...any) Test {
return t.fail("BUG IN TEST CASE: "+format, v...)
}

Expand Down
@@ -1 +1 @@
exp-trailing-us = 1e_23_
exp-trailing-us = 1e23_
5 changes: 4 additions & 1 deletion internal/toml-test/tests/invalid/float/float.multi
Expand Up @@ -25,7 +25,7 @@ exp-double-e-1 = 1ee2
exp-double-e-2 = 1e2e3

exp-leading-us = 1e_23
exp-trailing-us = 1e_23_
exp-trailing-us = 1e23_
exp-double-us = 1e__23

inf-incomplete-1 = in
Expand All @@ -38,3 +38,6 @@ nan-incomplete-3 = -na

nan_underscore = na_n
inf_underscore = in_f

trailing-us-exp-1 = 1_e2
trailing-us-exp-2 = 1.2_e2
@@ -0,0 +1 @@
trailing-us-exp-1 = 1_e2
@@ -0,0 +1 @@
trailing-us-exp-2 = 1.2_e2
4 changes: 0 additions & 4 deletions internal/toml-test/tests/invalid/float/trailing-us-exp.toml

This file was deleted.

0 comments on commit 99c5690

Please sign in to comment.