diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..74c037f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "spec"] + path = spec + url = https://github.com/multiformats/multibase.git diff --git a/.gxignore b/.gxignore new file mode 100644 index 0000000..c1d28ba --- /dev/null +++ b/.gxignore @@ -0,0 +1,2 @@ +/spec/ +*_test.go diff --git a/.travis.yml b/.travis.yml index 7462824..303daab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,25 +1,23 @@ -os: - - linux - language: go +sudo: false + +os: + - linux go: - - 1.10.2 + - 1.11.x install: - - go get -u github.com/whyrusleeping/gx - - go get -u github.com/whyrusleeping/gx-go - - gx install - + - make deps script: - - gx-go rewrite - - go test -race -coverprofile=unittest.coverprofile -covermode=atomic . - - -after_success: - - bash <(curl -s https://codecov.io/bash) -f unittest.coverprofile -F unittest + - bash <(curl -s https://raw.githubusercontent.com/ipfs/ci-helpers/master/travis-ci/run-standard-tests.sh) cache: - directories: - - $GOPATH/src/gx + directories: + - $GOPATH/src/gx + +notifications: + email: false + +env: GOTFLAGS="-race" diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..411b4a8 --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +test: deps + go test -race -v ./... + +export IPFS_API ?= v04x.ipfs.io + +gx: + go get -u github.com/whyrusleeping/gx + go get -u github.com/whyrusleeping/gx-go + +deps: gx + gx --verbose install --global + gx-go rewrite + go get -t ./... diff --git a/spec b/spec new file mode 160000 index 0000000..f0792f7 --- /dev/null +++ b/spec @@ -0,0 +1 @@ +Subproject commit f0792f703031bb01251aa99fcea6a5fe945b6297 diff --git a/spec_test.go b/spec_test.go new file mode 100644 index 0000000..6e56898 --- /dev/null +++ b/spec_test.go @@ -0,0 +1,147 @@ +package multibase + +import ( + "encoding/csv" + "os" + "path/filepath" + "strconv" + "strings" + "testing" + "unicode/utf8" +) + +func TestSpec(t *testing.T) { + file, err := os.Open("spec/multibase.csv") + if err != nil { + t.Fatal(err) + } + defer file.Close() + + reader := csv.NewReader(file) + reader.LazyQuotes = false + reader.FieldsPerRecord = 3 + reader.TrimLeadingSpace = true + + values, err := reader.ReadAll() + if err != nil { + t.Error(err) + } + expectedEncodings := make(map[Encoding]string, len(values)-1) + for _, v := range values[1:] { + encoding := v[0] + codeStr := v[1] + + var code Encoding + if strings.HasPrefix(codeStr, "0x") { + i, err := strconv.ParseUint(codeStr[2:], 16, 64) + if err != nil { + t.Errorf("invalid multibase byte %q", codeStr) + continue + } + code = Encoding(i) + } else { + codeRune, length := utf8.DecodeRuneInString(codeStr) + if code == utf8.RuneError { + t.Errorf("multibase %q wasn't valid utf8", codeStr) + continue + } + if length != len(codeStr) { + t.Errorf("multibase %q wasn't a single character", codeStr) + continue + } + code = Encoding(codeRune) + } + expectedEncodings[code] = encoding + } + + for name, enc := range Encodings { + expectedName, ok := expectedEncodings[enc] + if !ok { + t.Errorf("encoding %q (%c) not defined in the spec", name, enc) + continue + } + if expectedName != name { + t.Errorf("encoding %q (%c) has unexpected name %q", expectedName, enc, name) + } + } +} +func TestSpecVectors(t *testing.T) { + files, err := filepath.Glob("spec/tests/test[0-9]*.csv") + if err != nil { + t.Fatal(err) + } + for _, fname := range files { + t.Run(fname, func(t *testing.T) { + file, err := os.Open(fname) + if err != nil { + t.Error(err) + return + } + defer file.Close() + reader := csv.NewReader(file) + reader.LazyQuotes = false + reader.FieldsPerRecord = 2 + reader.TrimLeadingSpace = true + + values, err := reader.ReadAll() + if err != nil { + t.Error(err) + } + if len(values) == 0 { + t.Error("no test values") + return + } + header := values[0] + + var decodeOnly bool + switch header[0] { + case "encoding": + case "non-canonical encoding": + decodeOnly = true + default: + t.Errorf("invalid test spec %q", fname) + return + } + + testValue, err := strconv.Unquote("\"" + header[1] + "\"") + if err != nil { + t.Error("failed to unquote testcase:", err) + return + } + + for _, testCase := range values[1:] { + encodingName := testCase[0] + expected := testCase[1] + + t.Run(encodingName, func(t *testing.T) { + encoder, err := EncoderByName(encodingName) + if err != nil { + t.Skipf("skipping %s: not supported", encodingName) + return + } + if !decodeOnly { + t.Logf("encoding %q with %s", testValue, encodingName) + actual := encoder.Encode([]byte(testValue)) + if expected != actual { + t.Errorf("expected %q, got %q", expected, actual) + } + } + t.Logf("decoding %q", expected) + encoding, decoded, err := Decode(expected) + if err != nil { + t.Error("failed to decode:", err) + return + } + expectedEncoding := Encodings[encodingName] + if encoding != expectedEncoding { + t.Errorf("expected encoding to be %c, got %c", expectedEncoding, encoding) + } + if string(decoded) != testValue { + t.Errorf("failed to decode %q to %q, got %q", expected, testValue, string(decoded)) + } + }) + + } + }) + } +}