From 4c3649e718b45f17855da3851817ea14badd0f19 Mon Sep 17 00:00:00 2001 From: Volker Schukai <volker.schukai@schukai.com> Date: Sat, 16 Sep 2023 11:24:52 +0200 Subject: [PATCH] chore: add standard devenv.nix #5 --- .devenv.flake.nix | 81 + .devenv/devenv.json | 1 + .devenv/flake.json | 1 + .devenv/gc/shell | 1 + .devenv/gc/shell-2-link | 1 + .devenv/imports.txt | 0 .devenv/profile | 1 + .../github.com/agnivade/levenshtein/@v/list | 1 + .../agnivade/levenshtein/@v/v1.1.1.mod | 8 + .../github.com/arbovm/levenshtein/@v/list | 1 + .../@v/v0.0.0-20160628152529-48b4e1c0c4d0.mod | 1 + .../github.com/davecgh/go-spew/@v/list | 2 + .../github.com/davecgh/go-spew/@v/v1.1.0.mod | 1 + .../github.com/davecgh/go-spew/@v/v1.1.1.info | 1 + .../github.com/davecgh/go-spew/@v/v1.1.1.lock | 0 .../github.com/davecgh/go-spew/@v/v1.1.1.mod | 1 + .../github.com/davecgh/go-spew/@v/v1.1.1.zip | Bin 0 -> 60320 bytes .../davecgh/go-spew/@v/v1.1.1.ziphash | 1 + .../github.com/dgryski/trifles/@v/list | 1 + .../@v/v0.0.0-20200323201526-dd97f9abfb48.mod | 1 + .../github.com/pmezard/go-difflib/@v/list | 1 + .../pmezard/go-difflib/@v/v1.0.0.info | 1 + .../pmezard/go-difflib/@v/v1.0.0.lock | 0 .../pmezard/go-difflib/@v/v1.0.0.mod | 1 + .../pmezard/go-difflib/@v/v1.0.0.zip | Bin 0 -> 12433 bytes .../pmezard/go-difflib/@v/v1.0.0.ziphash | 1 + .../download/github.com/stretchr/objx/@v/list | 3 + .../github.com/stretchr/objx/@v/v0.1.0.mod | 1 + .../github.com/stretchr/objx/@v/v0.4.0.mod | 8 + .../github.com/stretchr/objx/@v/v0.5.0.mod | 5 + .../github.com/stretchr/testify/@v/list | 3 + .../github.com/stretchr/testify/@v/v1.7.1.mod | 10 + .../github.com/stretchr/testify/@v/v1.8.0.mod | 10 + .../stretchr/testify/@v/v1.8.2.info | 1 + .../stretchr/testify/@v/v1.8.2.lock | 0 .../github.com/stretchr/testify/@v/v1.8.2.mod | 10 + .../github.com/stretchr/testify/@v/v1.8.2.zip | Bin 0 -> 114188 bytes .../stretchr/testify/@v/v1.8.2.ziphash | 1 + .../libraries/go/utilities/pathfinder/@v/list | 2 + .../go/utilities/pathfinder/@v/v0.5.2.lock | 0 .../go/utilities/pathfinder/@v/v0.5.2.mod | 10 + .../go/utilities/pathfinder/@v/v0.5.2.zip | Bin 0 -> 25981 bytes .../go/utilities/pathfinder/@v/v0.5.2.ziphash | 1 + .../go/utilities/pathfinder/@v/v0.8.1.info | 1 + .../go/utilities/pathfinder/@v/v0.8.1.lock | 0 .../go/utilities/pathfinder/@v/v0.8.1.mod | 11 + .../go/utilities/pathfinder/@v/v0.8.1.zip | Bin 0 -> 39099 bytes .../go/utilities/pathfinder/@v/v0.8.1.ziphash | 1 + .../cache/download/gopkg.in/check.v1/@v/list | 1 + .../@v/v0.0.0-20161208181325-20d25e280405.mod | 1 + .../cache/download/gopkg.in/yaml.v3/@v/list | 2 + .../@v/v3.0.0-20200313102051-9f266ea9e77c.mod | 5 + .../download/gopkg.in/yaml.v3/@v/v3.0.1.info | 1 + .../download/gopkg.in/yaml.v3/@v/v3.0.1.lock | 0 .../download/gopkg.in/yaml.v3/@v/v3.0.1.mod | 5 + .../download/gopkg.in/yaml.v3/@v/v3.0.1.zip | Bin 0 -> 104623 bytes .../gopkg.in/yaml.v3/@v/v3.0.1.ziphash | 1 + .../libraries/go/utilities/pathfinder@v0.8.1 | 9 + .../sum.golang.org/tile/8/0/x076/690.p/73 | Bin 0 -> 2336 bytes .../sumdb/sum.golang.org/tile/8/1/299.p/146 | Bin 0 -> 4672 bytes .../sumdb/sum.golang.org/tile/8/2/001.p/43 | Bin 0 -> 1376 bytes .../sumdb/sum.golang.org/tile/8/3/000.p/1 | 2 + .devenv/state/go/pkg/mod/cache/lock | 0 .../davecgh/go-spew@v1.1.1/.gitignore | 22 + .../davecgh/go-spew@v1.1.1/.travis.yml | 28 + .../github.com/davecgh/go-spew@v1.1.1/LICENSE | 15 + .../davecgh/go-spew@v1.1.1/README.md | 201 ++ .../davecgh/go-spew@v1.1.1/cov_report.sh | 22 + .../davecgh/go-spew@v1.1.1/spew/bypass.go | 145 + .../davecgh/go-spew@v1.1.1/spew/bypasssafe.go | 38 + .../davecgh/go-spew@v1.1.1/spew/common.go | 341 ++ .../go-spew@v1.1.1/spew/common_test.go | 298 ++ .../davecgh/go-spew@v1.1.1/spew/config.go | 306 ++ .../davecgh/go-spew@v1.1.1/spew/doc.go | 211 ++ .../davecgh/go-spew@v1.1.1/spew/dump.go | 509 +++ .../davecgh/go-spew@v1.1.1/spew/dump_test.go | 1042 ++++++ .../go-spew@v1.1.1/spew/dumpcgo_test.go | 101 + .../go-spew@v1.1.1/spew/dumpnocgo_test.go | 26 + .../go-spew@v1.1.1/spew/example_test.go | 226 ++ .../davecgh/go-spew@v1.1.1/spew/format.go | 419 +++ .../go-spew@v1.1.1/spew/format_test.go | 1558 +++++++++ .../go-spew@v1.1.1/spew/internal_test.go | 84 + .../spew/internalunsafe_test.go | 101 + .../davecgh/go-spew@v1.1.1/spew/spew.go | 148 + .../davecgh/go-spew@v1.1.1/spew/spew_test.go | 320 ++ .../davecgh/go-spew@v1.1.1/test_coverage.txt | 61 + .../pmezard/go-difflib@v1.0.0/.travis.yml | 5 + .../pmezard/go-difflib@v1.0.0/LICENSE | 27 + .../pmezard/go-difflib@v1.0.0/README.md | 50 + .../go-difflib@v1.0.0/difflib/difflib.go | 772 +++++ .../go-difflib@v1.0.0/difflib/difflib_test.go | 426 +++ .../stretchr/testify@v1.8.2/.ci.gofmt.sh | 14 + .../stretchr/testify@v1.8.2/.ci.gogenerate.sh | 16 + .../stretchr/testify@v1.8.2/.ci.govet.sh | 5 + .../testify@v1.8.2/.github/dependabot.yml | 10 + .../.github/pull_request_template.md | 15 + .../testify@v1.8.2/.github/workflows/main.yml | 19 + .../stretchr/testify@v1.8.2/.gitignore | 24 + .../stretchr/testify@v1.8.2/CONTRIBUTING.md | 50 + .../stretchr/testify@v1.8.2/LICENSE | 21 + .../stretchr/testify@v1.8.2/MAINTAINERS.md | 9 + .../stretchr/testify@v1.8.2/README.md | 371 ++ .../assert/assertion_compare.go | 458 +++ .../assert/assertion_compare_can_convert.go | 16 + .../assert/assertion_compare_go1.17_test.go | 182 + .../assert/assertion_compare_legacy.go | 16 + .../assert/assertion_compare_test.go | 449 +++ .../testify@v1.8.2/assert/assertion_format.go | 763 +++++ .../assert/assertion_format.go.tmpl | 5 + .../assert/assertion_forward.go | 1514 ++++++++ .../assert/assertion_forward.go.tmpl | 5 + .../testify@v1.8.2/assert/assertion_order.go | 81 + .../assert/assertion_order_test.go | 203 ++ .../testify@v1.8.2/assert/assertions.go | 1856 ++++++++++ .../testify@v1.8.2/assert/assertions_test.go | 2582 ++++++++++++++ .../stretchr/testify@v1.8.2/assert/doc.go | 45 + .../stretchr/testify@v1.8.2/assert/errors.go | 10 + .../assert/forward_assertions.go | 16 + .../assert/forward_assertions_test.go | 752 ++++ .../testify@v1.8.2/assert/http_assertions.go | 162 + .../assert/http_assertions_test.go | 182 + .../github.com/stretchr/testify@v1.8.2/doc.go | 23 + .../github.com/stretchr/testify@v1.8.2/go.mod | 10 + .../github.com/stretchr/testify@v1.8.2/go.sum | 16 + .../stretchr/testify@v1.8.2/http/doc.go | 2 + .../http/test_response_writer.go | 49 + .../testify@v1.8.2/http/test_round_tripper.go | 18 + .../stretchr/testify@v1.8.2/mock/doc.go | 44 + .../stretchr/testify@v1.8.2/mock/mock.go | 1104 ++++++ .../stretchr/testify@v1.8.2/mock/mock_test.go | 2084 +++++++++++ .../stretchr/testify@v1.8.2/package_test.go | 13 + .../stretchr/testify@v1.8.2/require/doc.go | 28 + .../require/forward_requirements.go | 16 + .../require/forward_requirements_test.go | 523 +++ .../testify@v1.8.2/require/require.go | 1935 +++++++++++ .../testify@v1.8.2/require/require.go.tmpl | 6 + .../testify@v1.8.2/require/require_forward.go | 1515 ++++++++ .../require/require_forward.go.tmpl | 5 + .../testify@v1.8.2/require/requirements.go | 29 + .../require/requirements_test.go | 683 ++++ .../stretchr/testify@v1.8.2/suite/doc.go | 65 + .../testify@v1.8.2/suite/interfaces.go | 66 + .../stretchr/testify@v1.8.2/suite/stats.go | 46 + .../testify@v1.8.2/suite/stats_test.go | 29 + .../stretchr/testify@v1.8.2/suite/suite.go | 248 ++ .../testify@v1.8.2/suite/suite_test.go | 619 ++++ .../.chglog/CHANGELOG.tpl.md | 27 + .../pathfinder@v0.5.2/.chglog/config.yml | 57 + .../go/utilities/pathfinder@v0.5.2/.gitignore | 149 + .../utilities/pathfinder@v0.5.2/CHANGELOG.md | 37 + .../go/utilities/pathfinder@v0.5.2/LICENSE | 662 ++++ .../go/utilities/pathfinder@v0.5.2/Makefile | 157 + .../go/utilities/pathfinder@v0.5.2/README.md | 69 + .../go/utilities/pathfinder@v0.5.2/error.go | 39 + .../utilities/pathfinder@v0.5.2/error_test.go | 40 + .../go/utilities/pathfinder@v0.5.2/get.go | 58 + .../utilities/pathfinder@v0.5.2/get_test.go | 100 + .../go/utilities/pathfinder@v0.5.2/go.mod | 10 + .../go/utilities/pathfinder@v0.5.2/go.sum | 14 + .../pathfinder@v0.5.2/pathfinder.iml | 10 + .../utilities/pathfinder@v0.5.2/release.json | 1 + .../go/utilities/pathfinder@v0.5.2/set.go | 154 + .../utilities/pathfinder@v0.5.2/set_test.go | 278 ++ .../.chglog/CHANGELOG.tpl.md | 27 + .../pathfinder@v0.8.1/.chglog/config.yml | 57 + .../go/utilities/pathfinder@v0.8.1/.envrc | 3 + .../go/utilities/pathfinder@v0.8.1/.gitignore | 155 + .../pathfinder@v0.8.1/.gitlab-ci.yml | 69 + .../pathfinder@v0.8.1/.idea/.gitignore | 8 + .../pathfinder@v0.8.1/.idea/markdown.xml | 9 + .../pathfinder@v0.8.1/.idea/misc.xml | 6 + .../pathfinder@v0.8.1/.idea/modules.xml | 8 + .../pathfinder@v0.8.1/.idea/pathfinder.iml | 10 + .../utilities/pathfinder@v0.8.1/.idea/vcs.xml | 6 + .../go/utilities/pathfinder@v0.8.1/LICENSE | 662 ++++ .../go/utilities/pathfinder@v0.8.1/README.md | 69 + .../utilities/pathfinder@v0.8.1/Taskfile.yml | 59 + .../utilities/pathfinder@v0.8.1/devenv.lock | 190 ++ .../go/utilities/pathfinder@v0.8.1/devenv.nix | 638 ++++ .../utilities/pathfinder@v0.8.1/devenv.yaml | 7 + .../go/utilities/pathfinder@v0.8.1/error.go | 39 + .../utilities/pathfinder@v0.8.1/error_test.go | 40 + .../go/utilities/pathfinder@v0.8.1/get.go | 74 + .../utilities/pathfinder@v0.8.1/get_test.go | 100 + .../go/utilities/pathfinder@v0.8.1/go.mod | 11 + .../go/utilities/pathfinder@v0.8.1/go.sum | 16 + .../pathfinder@v0.8.1/issue_2_test.go | 151 + .../pathfinder@v0.8.1/issue_7_test.go | 126 + .../pathfinder@v0.8.1/pathfinder.iml | 10 + .../utilities/pathfinder@v0.8.1/release.json | 1 + .../go/utilities/pathfinder@v0.8.1/set.go | 332 ++ .../utilities/pathfinder@v0.8.1/set_test.go | 278 ++ .../yaml.v3@v3.0.1/.github/workflows/go.yaml | 61 + .../pkg/mod/gopkg.in/yaml.v3@v3.0.1/LICENSE | 50 + .../go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/NOTICE | 13 + .../pkg/mod/gopkg.in/yaml.v3@v3.0.1/README.md | 150 + .../pkg/mod/gopkg.in/yaml.v3@v3.0.1/apic.go | 747 ++++ .../pkg/mod/gopkg.in/yaml.v3@v3.0.1/decode.go | 1000 ++++++ .../gopkg.in/yaml.v3@v3.0.1/decode_test.go | 1771 ++++++++++ .../mod/gopkg.in/yaml.v3@v3.0.1/emitterc.go | 2020 +++++++++++ .../pkg/mod/gopkg.in/yaml.v3@v3.0.1/encode.go | 577 ++++ .../gopkg.in/yaml.v3@v3.0.1/encode_test.go | 736 ++++ .../yaml.v3@v3.0.1/example_embedded_test.go | 56 + .../go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/go.mod | 5 + .../mod/gopkg.in/yaml.v3@v3.0.1/limit_test.go | 128 + .../mod/gopkg.in/yaml.v3@v3.0.1/node_test.go | 2886 ++++++++++++++++ .../mod/gopkg.in/yaml.v3@v3.0.1/parserc.go | 1258 +++++++ .../mod/gopkg.in/yaml.v3@v3.0.1/readerc.go | 434 +++ .../mod/gopkg.in/yaml.v3@v3.0.1/resolve.go | 326 ++ .../mod/gopkg.in/yaml.v3@v3.0.1/scannerc.go | 3038 +++++++++++++++++ .../pkg/mod/gopkg.in/yaml.v3@v3.0.1/sorter.go | 134 + .../mod/gopkg.in/yaml.v3@v3.0.1/suite_test.go | 27 + .../mod/gopkg.in/yaml.v3@v3.0.1/writerc.go | 48 + .../pkg/mod/gopkg.in/yaml.v3@v3.0.1/yaml.go | 698 ++++ .../pkg/mod/gopkg.in/yaml.v3@v3.0.1/yamlh.go | 807 +++++ .../gopkg.in/yaml.v3@v3.0.1/yamlprivateh.go | 198 ++ .../state/go/pkg/sumdb/sum.golang.org/latest | 5 + ...22f25393f8e218fc5cf98d5b0468ada972afd9e.rc | 258 ++ .gitlab-ci.yml | 9 +- Taskfile.yml | 39 +- devenv.nix | 751 +++- go.mod | 2 +- go.sum | 2 + 223 files changed, 49852 insertions(+), 148 deletions(-) create mode 100644 .devenv.flake.nix create mode 100644 .devenv/devenv.json create mode 100644 .devenv/flake.json create mode 120000 .devenv/gc/shell create mode 120000 .devenv/gc/shell-2-link create mode 100644 .devenv/imports.txt create mode 120000 .devenv/profile create mode 100644 .devenv/state/go/pkg/mod/cache/download/github.com/agnivade/levenshtein/@v/list create mode 100644 .devenv/state/go/pkg/mod/cache/download/github.com/agnivade/levenshtein/@v/v1.1.1.mod create mode 100644 .devenv/state/go/pkg/mod/cache/download/github.com/arbovm/levenshtein/@v/list create mode 100644 .devenv/state/go/pkg/mod/cache/download/github.com/arbovm/levenshtein/@v/v0.0.0-20160628152529-48b4e1c0c4d0.mod create mode 100644 .devenv/state/go/pkg/mod/cache/download/github.com/davecgh/go-spew/@v/list create mode 100644 .devenv/state/go/pkg/mod/cache/download/github.com/davecgh/go-spew/@v/v1.1.0.mod create mode 100644 .devenv/state/go/pkg/mod/cache/download/github.com/davecgh/go-spew/@v/v1.1.1.info create mode 100644 .devenv/state/go/pkg/mod/cache/download/github.com/davecgh/go-spew/@v/v1.1.1.lock create mode 100644 .devenv/state/go/pkg/mod/cache/download/github.com/davecgh/go-spew/@v/v1.1.1.mod create mode 100644 .devenv/state/go/pkg/mod/cache/download/github.com/davecgh/go-spew/@v/v1.1.1.zip create mode 100644 .devenv/state/go/pkg/mod/cache/download/github.com/davecgh/go-spew/@v/v1.1.1.ziphash create mode 100644 .devenv/state/go/pkg/mod/cache/download/github.com/dgryski/trifles/@v/list create mode 100644 .devenv/state/go/pkg/mod/cache/download/github.com/dgryski/trifles/@v/v0.0.0-20200323201526-dd97f9abfb48.mod create mode 100644 .devenv/state/go/pkg/mod/cache/download/github.com/pmezard/go-difflib/@v/list create mode 100644 .devenv/state/go/pkg/mod/cache/download/github.com/pmezard/go-difflib/@v/v1.0.0.info create mode 100644 .devenv/state/go/pkg/mod/cache/download/github.com/pmezard/go-difflib/@v/v1.0.0.lock create mode 100644 .devenv/state/go/pkg/mod/cache/download/github.com/pmezard/go-difflib/@v/v1.0.0.mod create mode 100644 .devenv/state/go/pkg/mod/cache/download/github.com/pmezard/go-difflib/@v/v1.0.0.zip create mode 100644 .devenv/state/go/pkg/mod/cache/download/github.com/pmezard/go-difflib/@v/v1.0.0.ziphash create mode 100644 .devenv/state/go/pkg/mod/cache/download/github.com/stretchr/objx/@v/list create mode 100644 .devenv/state/go/pkg/mod/cache/download/github.com/stretchr/objx/@v/v0.1.0.mod create mode 100644 .devenv/state/go/pkg/mod/cache/download/github.com/stretchr/objx/@v/v0.4.0.mod create mode 100644 .devenv/state/go/pkg/mod/cache/download/github.com/stretchr/objx/@v/v0.5.0.mod create mode 100644 .devenv/state/go/pkg/mod/cache/download/github.com/stretchr/testify/@v/list create mode 100644 .devenv/state/go/pkg/mod/cache/download/github.com/stretchr/testify/@v/v1.7.1.mod create mode 100644 .devenv/state/go/pkg/mod/cache/download/github.com/stretchr/testify/@v/v1.8.0.mod create mode 100644 .devenv/state/go/pkg/mod/cache/download/github.com/stretchr/testify/@v/v1.8.2.info create mode 100644 .devenv/state/go/pkg/mod/cache/download/github.com/stretchr/testify/@v/v1.8.2.lock create mode 100644 .devenv/state/go/pkg/mod/cache/download/github.com/stretchr/testify/@v/v1.8.2.mod create mode 100644 .devenv/state/go/pkg/mod/cache/download/github.com/stretchr/testify/@v/v1.8.2.zip create mode 100644 .devenv/state/go/pkg/mod/cache/download/github.com/stretchr/testify/@v/v1.8.2.ziphash create mode 100644 .devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/list create mode 100644 .devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.5.2.lock create mode 100644 .devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.5.2.mod create mode 100644 .devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.5.2.zip create mode 100644 .devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.5.2.ziphash create mode 100644 .devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.8.1.info create mode 100644 .devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.8.1.lock create mode 100644 .devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.8.1.mod create mode 100644 .devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.8.1.zip create mode 100644 .devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.8.1.ziphash create mode 100644 .devenv/state/go/pkg/mod/cache/download/gopkg.in/check.v1/@v/list create mode 100644 .devenv/state/go/pkg/mod/cache/download/gopkg.in/check.v1/@v/v0.0.0-20161208181325-20d25e280405.mod create mode 100644 .devenv/state/go/pkg/mod/cache/download/gopkg.in/yaml.v3/@v/list create mode 100644 .devenv/state/go/pkg/mod/cache/download/gopkg.in/yaml.v3/@v/v3.0.0-20200313102051-9f266ea9e77c.mod create mode 100644 .devenv/state/go/pkg/mod/cache/download/gopkg.in/yaml.v3/@v/v3.0.1.info create mode 100644 .devenv/state/go/pkg/mod/cache/download/gopkg.in/yaml.v3/@v/v3.0.1.lock create mode 100644 .devenv/state/go/pkg/mod/cache/download/gopkg.in/yaml.v3/@v/v3.0.1.mod create mode 100644 .devenv/state/go/pkg/mod/cache/download/gopkg.in/yaml.v3/@v/v3.0.1.zip create mode 100644 .devenv/state/go/pkg/mod/cache/download/gopkg.in/yaml.v3/@v/v3.0.1.ziphash create mode 100644 .devenv/state/go/pkg/mod/cache/download/sumdb/sum.golang.org/lookup/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1 create mode 100644 .devenv/state/go/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x076/690.p/73 create mode 100644 .devenv/state/go/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/1/299.p/146 create mode 100644 .devenv/state/go/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/2/001.p/43 create mode 100644 .devenv/state/go/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/3/000.p/1 create mode 100644 .devenv/state/go/pkg/mod/cache/lock create mode 100644 .devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/.gitignore create mode 100644 .devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/.travis.yml create mode 100644 .devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/LICENSE create mode 100644 .devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/README.md create mode 100644 .devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/cov_report.sh create mode 100644 .devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/bypass.go create mode 100644 .devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/bypasssafe.go create mode 100644 .devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/common.go create mode 100644 .devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/common_test.go create mode 100644 .devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/config.go create mode 100644 .devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/doc.go create mode 100644 .devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/dump.go create mode 100644 .devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/dump_test.go create mode 100644 .devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/dumpcgo_test.go create mode 100644 .devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/dumpnocgo_test.go create mode 100644 .devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/example_test.go create mode 100644 .devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/format.go create mode 100644 .devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/format_test.go create mode 100644 .devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/internal_test.go create mode 100644 .devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/internalunsafe_test.go create mode 100644 .devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/spew.go create mode 100644 .devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/spew_test.go create mode 100644 .devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/test_coverage.txt create mode 100644 .devenv/state/go/pkg/mod/github.com/pmezard/go-difflib@v1.0.0/.travis.yml create mode 100644 .devenv/state/go/pkg/mod/github.com/pmezard/go-difflib@v1.0.0/LICENSE create mode 100644 .devenv/state/go/pkg/mod/github.com/pmezard/go-difflib@v1.0.0/README.md create mode 100644 .devenv/state/go/pkg/mod/github.com/pmezard/go-difflib@v1.0.0/difflib/difflib.go create mode 100644 .devenv/state/go/pkg/mod/github.com/pmezard/go-difflib@v1.0.0/difflib/difflib_test.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/.ci.gofmt.sh create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/.ci.gogenerate.sh create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/.ci.govet.sh create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/.github/dependabot.yml create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/.github/pull_request_template.md create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/.github/workflows/main.yml create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/.gitignore create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/CONTRIBUTING.md create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/LICENSE create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/MAINTAINERS.md create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/README.md create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_compare.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_compare_can_convert.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_compare_go1.17_test.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_compare_legacy.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_compare_test.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_format.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_format.go.tmpl create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_forward.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_forward.go.tmpl create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_order.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_order_test.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertions.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertions_test.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/doc.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/errors.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/forward_assertions.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/forward_assertions_test.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/http_assertions.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/http_assertions_test.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/doc.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/go.mod create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/go.sum create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/http/doc.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/http/test_response_writer.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/http/test_round_tripper.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/mock/doc.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/mock/mock.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/mock/mock_test.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/package_test.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/doc.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/forward_requirements.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/forward_requirements_test.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/require.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/require.go.tmpl create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/require_forward.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/require_forward.go.tmpl create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/requirements.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/requirements_test.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/suite/doc.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/suite/interfaces.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/suite/stats.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/suite/stats_test.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/suite/suite.go create mode 100644 .devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/suite/suite_test.go create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/.chglog/CHANGELOG.tpl.md create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/.chglog/config.yml create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/.gitignore create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/CHANGELOG.md create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/LICENSE create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/Makefile create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/README.md create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/error.go create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/error_test.go create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/get.go create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/get_test.go create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/go.mod create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/go.sum create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/pathfinder.iml create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/release.json create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/set.go create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/set_test.go create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.chglog/CHANGELOG.tpl.md create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.chglog/config.yml create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.envrc create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.gitignore create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.gitlab-ci.yml create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.idea/.gitignore create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.idea/markdown.xml create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.idea/misc.xml create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.idea/modules.xml create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.idea/pathfinder.iml create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.idea/vcs.xml create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/LICENSE create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/README.md create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/Taskfile.yml create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/devenv.lock create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/devenv.nix create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/devenv.yaml create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/error.go create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/error_test.go create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/get.go create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/get_test.go create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/go.mod create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/go.sum create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/issue_2_test.go create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/issue_7_test.go create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/pathfinder.iml create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/release.json create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/set.go create mode 100644 .devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/set_test.go create mode 100644 .devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/.github/workflows/go.yaml create mode 100644 .devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/LICENSE create mode 100644 .devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/NOTICE create mode 100644 .devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/README.md create mode 100644 .devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/apic.go create mode 100644 .devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/decode.go create mode 100644 .devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/decode_test.go create mode 100644 .devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/emitterc.go create mode 100644 .devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/encode.go create mode 100644 .devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/encode_test.go create mode 100644 .devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/example_embedded_test.go create mode 100644 .devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/go.mod create mode 100644 .devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/limit_test.go create mode 100644 .devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/node_test.go create mode 100644 .devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/parserc.go create mode 100644 .devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/readerc.go create mode 100644 .devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/resolve.go create mode 100644 .devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/scannerc.go create mode 100644 .devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/sorter.go create mode 100644 .devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/suite_test.go create mode 100644 .devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/writerc.go create mode 100644 .devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/yaml.go create mode 100644 .devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/yamlh.go create mode 100644 .devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/yamlprivateh.go create mode 100644 .devenv/state/go/pkg/sumdb/sum.golang.org/latest create mode 100644 .direnv/devenv-profile-722f25393f8e218fc5cf98d5b0468ada972afd9e.rc diff --git a/.devenv.flake.nix b/.devenv.flake.nix new file mode 100644 index 0000000..8b866ef --- /dev/null +++ b/.devenv.flake.nix @@ -0,0 +1,81 @@ +{ + inputs = { + pre-commit-hooks.url = "github:cachix/pre-commit-hooks.nix"; + pre-commit-hooks.inputs.nixpkgs.follows = "nixpkgs"; + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + devenv.url = "github:cachix/devenv?dir=src/modules"; + } // (if builtins.pathExists ./.devenv/flake.json + then builtins.fromJSON (builtins.readFile ./.devenv/flake.json) + else {}); + + outputs = { nixpkgs, ... }@inputs: + let + devenv = if builtins.pathExists ./.devenv/devenv.json + then builtins.fromJSON (builtins.readFile ./.devenv/devenv.json) + else {}; + getOverlays = inputName: inputAttrs: + map (overlay: let + input = inputs.${inputName} or (throw "No such input `${inputName}` while trying to configure overlays."); + in input.overlays.${overlay} or (throw "Input `${inputName}` has no overlay called `${overlay}`. Supported overlays: ${nixpkgs.lib.concatStringsSep ", " (builtins.attrNames input.overlays)}")) + inputAttrs.overlays or []; + overlays = nixpkgs.lib.flatten (nixpkgs.lib.mapAttrsToList getOverlays (devenv.inputs or {})); + pkgs = import nixpkgs { + system = "x86_64-linux"; + config = { + allowUnfree = devenv.allowUnfree or false; + permittedInsecurePackages = devenv.permittedInsecurePackages or []; + }; + inherit overlays; + }; + lib = pkgs.lib; + importModule = path: + if lib.hasPrefix "./" path + then ./. + (builtins.substring 1 255 path) + "/devenv.nix" + else if lib.hasPrefix "../" path + then throw "devenv: ../ is not supported for imports" + else let + paths = lib.splitString "/" path; + name = builtins.head paths; + input = inputs.${name} or (throw "Unknown input ${name}"); + subpath = "/${lib.concatStringsSep "/" (builtins.tail paths)}"; + devenvpath = "${input}" + subpath + "/devenv.nix"; + in if builtins.pathExists devenvpath + then devenvpath + else throw (devenvpath + " file does not exist for input ${name}."); + project = pkgs.lib.evalModules { + specialArgs = inputs // { inherit inputs pkgs; }; + modules = [ + (inputs.devenv.modules + /top-level.nix) + { devenv.cliVersion = "0.6.3"; } + ] ++ (map importModule (devenv.imports or [])) ++ [ + ./devenv.nix + (devenv.devenv or {}) + (if builtins.pathExists ./devenv.local.nix then ./devenv.local.nix else {}) + ]; + }; + config = project.config; + + options = pkgs.nixosOptionsDoc { + options = builtins.removeAttrs project.options [ "_module" ]; + # Unpack Nix types, e.g. literalExpression, mDoc. + transformOptions = + let isDocType = v: builtins.elem v [ "literalDocBook" "literalExpression" "literalMD" "mdDoc" ]; + in lib.attrsets.mapAttrs (_: v: + if v ? _type && isDocType v._type then + v.text + else if v ? _type && v._type == "derivation" then + v.name + else + v + ); + }; + in { + packages."x86_64-linux" = { + optionsJSON = options.optionsJSON; + inherit (config) info procfileScript procfileEnv procfile; + ci = config.ciDerivation; + }; + devenv.containers = config.containers; + devShell."x86_64-linux" = config.shell; + }; +} diff --git a/.devenv/devenv.json b/.devenv/devenv.json new file mode 100644 index 0000000..7af2219 --- /dev/null +++ b/.devenv/devenv.json @@ -0,0 +1 @@ +{"inputs": {"nixpkgs": {"url": "github:nixos/nixpkgs/nixos-23.05"}, "version": {"url": "git+https://gitlab.schukai.com/oss/utilities/version.git", "flake": true}}, "allowUnfree": false} \ No newline at end of file diff --git a/.devenv/flake.json b/.devenv/flake.json new file mode 100644 index 0000000..5d482a5 --- /dev/null +++ b/.devenv/flake.json @@ -0,0 +1 @@ +{"nixpkgs": {"url": "github:nixos/nixpkgs/nixos-23.05"}, "version": {"url": "git+https://gitlab.schukai.com/oss/utilities/version.git", "flake": true}} \ No newline at end of file diff --git a/.devenv/gc/shell b/.devenv/gc/shell new file mode 120000 index 0000000..f0d48df --- /dev/null +++ b/.devenv/gc/shell @@ -0,0 +1 @@ +shell-2-link \ No newline at end of file diff --git a/.devenv/gc/shell-2-link b/.devenv/gc/shell-2-link new file mode 120000 index 0000000..982769c --- /dev/null +++ b/.devenv/gc/shell-2-link @@ -0,0 +1 @@ +/nix/store/1ppns0fipd84msivyw8ypd4804zn8jli-devenv-shell-env \ No newline at end of file diff --git a/.devenv/imports.txt b/.devenv/imports.txt new file mode 100644 index 0000000..e69de29 diff --git a/.devenv/profile b/.devenv/profile new file mode 120000 index 0000000..c21b37d --- /dev/null +++ b/.devenv/profile @@ -0,0 +1 @@ +/nix/store/ishxkjwm6m1nq2dzailw41xw5rczf6hi-devenv-profile \ No newline at end of file diff --git a/.devenv/state/go/pkg/mod/cache/download/github.com/agnivade/levenshtein/@v/list b/.devenv/state/go/pkg/mod/cache/download/github.com/agnivade/levenshtein/@v/list new file mode 100644 index 0000000..56130fb --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/github.com/agnivade/levenshtein/@v/list @@ -0,0 +1 @@ +v1.1.1 diff --git a/.devenv/state/go/pkg/mod/cache/download/github.com/agnivade/levenshtein/@v/v1.1.1.mod b/.devenv/state/go/pkg/mod/cache/download/github.com/agnivade/levenshtein/@v/v1.1.1.mod new file mode 100644 index 0000000..4fcfe43 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/github.com/agnivade/levenshtein/@v/v1.1.1.mod @@ -0,0 +1,8 @@ +module github.com/agnivade/levenshtein + +go 1.13 + +require ( + github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 + github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 +) diff --git a/.devenv/state/go/pkg/mod/cache/download/github.com/arbovm/levenshtein/@v/list b/.devenv/state/go/pkg/mod/cache/download/github.com/arbovm/levenshtein/@v/list new file mode 100644 index 0000000..d2ece77 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/github.com/arbovm/levenshtein/@v/list @@ -0,0 +1 @@ +v0.0.0-20160628152529-48b4e1c0c4d0 diff --git a/.devenv/state/go/pkg/mod/cache/download/github.com/arbovm/levenshtein/@v/v0.0.0-20160628152529-48b4e1c0c4d0.mod b/.devenv/state/go/pkg/mod/cache/download/github.com/arbovm/levenshtein/@v/v0.0.0-20160628152529-48b4e1c0c4d0.mod new file mode 100644 index 0000000..0381ddb --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/github.com/arbovm/levenshtein/@v/v0.0.0-20160628152529-48b4e1c0c4d0.mod @@ -0,0 +1 @@ +module github.com/arbovm/levenshtein diff --git a/.devenv/state/go/pkg/mod/cache/download/github.com/davecgh/go-spew/@v/list b/.devenv/state/go/pkg/mod/cache/download/github.com/davecgh/go-spew/@v/list new file mode 100644 index 0000000..1af0c48 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/github.com/davecgh/go-spew/@v/list @@ -0,0 +1,2 @@ +v1.1.0 +v1.1.1 diff --git a/.devenv/state/go/pkg/mod/cache/download/github.com/davecgh/go-spew/@v/v1.1.0.mod b/.devenv/state/go/pkg/mod/cache/download/github.com/davecgh/go-spew/@v/v1.1.0.mod new file mode 100644 index 0000000..5e4b0f5 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/github.com/davecgh/go-spew/@v/v1.1.0.mod @@ -0,0 +1 @@ +module github.com/davecgh/go-spew diff --git a/.devenv/state/go/pkg/mod/cache/download/github.com/davecgh/go-spew/@v/v1.1.1.info b/.devenv/state/go/pkg/mod/cache/download/github.com/davecgh/go-spew/@v/v1.1.1.info new file mode 100644 index 0000000..0644503 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/github.com/davecgh/go-spew/@v/v1.1.1.info @@ -0,0 +1 @@ +{"Version":"v1.1.1","Time":"2018-02-21T23:26:28Z"} \ No newline at end of file diff --git a/.devenv/state/go/pkg/mod/cache/download/github.com/davecgh/go-spew/@v/v1.1.1.lock b/.devenv/state/go/pkg/mod/cache/download/github.com/davecgh/go-spew/@v/v1.1.1.lock new file mode 100644 index 0000000..e69de29 diff --git a/.devenv/state/go/pkg/mod/cache/download/github.com/davecgh/go-spew/@v/v1.1.1.mod b/.devenv/state/go/pkg/mod/cache/download/github.com/davecgh/go-spew/@v/v1.1.1.mod new file mode 100644 index 0000000..5e4b0f5 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/github.com/davecgh/go-spew/@v/v1.1.1.mod @@ -0,0 +1 @@ +module github.com/davecgh/go-spew diff --git a/.devenv/state/go/pkg/mod/cache/download/github.com/davecgh/go-spew/@v/v1.1.1.zip b/.devenv/state/go/pkg/mod/cache/download/github.com/davecgh/go-spew/@v/v1.1.1.zip new file mode 100644 index 0000000000000000000000000000000000000000..826238ef3eedd456c73cfdc4745f803b2ceadf1d GIT binary patch literal 60320 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	_-z-Vl8Aj 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 literal 0 HcmV?d00001 diff --git a/.devenv/state/go/pkg/mod/cache/download/github.com/davecgh/go-spew/@v/v1.1.1.ziphash b/.devenv/state/go/pkg/mod/cache/download/github.com/davecgh/go-spew/@v/v1.1.1.ziphash new file mode 100644 index 0000000..b36e08e --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/github.com/davecgh/go-spew/@v/v1.1.1.ziphash @@ -0,0 +1 @@ +h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= \ No newline at end of file diff --git a/.devenv/state/go/pkg/mod/cache/download/github.com/dgryski/trifles/@v/list b/.devenv/state/go/pkg/mod/cache/download/github.com/dgryski/trifles/@v/list new file mode 100644 index 0000000..262cda8 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/github.com/dgryski/trifles/@v/list @@ -0,0 +1 @@ +v0.0.0-20200323201526-dd97f9abfb48 diff --git a/.devenv/state/go/pkg/mod/cache/download/github.com/dgryski/trifles/@v/v0.0.0-20200323201526-dd97f9abfb48.mod b/.devenv/state/go/pkg/mod/cache/download/github.com/dgryski/trifles/@v/v0.0.0-20200323201526-dd97f9abfb48.mod new file mode 100644 index 0000000..ac53d95 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/github.com/dgryski/trifles/@v/v0.0.0-20200323201526-dd97f9abfb48.mod @@ -0,0 +1 @@ +module github.com/dgryski/trifles diff --git a/.devenv/state/go/pkg/mod/cache/download/github.com/pmezard/go-difflib/@v/list b/.devenv/state/go/pkg/mod/cache/download/github.com/pmezard/go-difflib/@v/list new file mode 100644 index 0000000..0ec25f7 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/github.com/pmezard/go-difflib/@v/list @@ -0,0 +1 @@ +v1.0.0 diff --git a/.devenv/state/go/pkg/mod/cache/download/github.com/pmezard/go-difflib/@v/v1.0.0.info b/.devenv/state/go/pkg/mod/cache/download/github.com/pmezard/go-difflib/@v/v1.0.0.info new file mode 100644 index 0000000..79e89a9 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/github.com/pmezard/go-difflib/@v/v1.0.0.info @@ -0,0 +1 @@ +{"Version":"v1.0.0","Time":"2016-01-10T10:55:54Z"} \ No newline at end of file diff --git a/.devenv/state/go/pkg/mod/cache/download/github.com/pmezard/go-difflib/@v/v1.0.0.lock b/.devenv/state/go/pkg/mod/cache/download/github.com/pmezard/go-difflib/@v/v1.0.0.lock new file mode 100644 index 0000000..e69de29 diff --git a/.devenv/state/go/pkg/mod/cache/download/github.com/pmezard/go-difflib/@v/v1.0.0.mod b/.devenv/state/go/pkg/mod/cache/download/github.com/pmezard/go-difflib/@v/v1.0.0.mod new file mode 100644 index 0000000..a03051f --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/github.com/pmezard/go-difflib/@v/v1.0.0.mod @@ -0,0 +1 @@ +module github.com/pmezard/go-difflib diff --git a/.devenv/state/go/pkg/mod/cache/download/github.com/pmezard/go-difflib/@v/v1.0.0.zip b/.devenv/state/go/pkg/mod/cache/download/github.com/pmezard/go-difflib/@v/v1.0.0.zip new file mode 100644 index 0000000000000000000000000000000000000000..5ad7d64ca4bbb8488f50c1f7c72a5a5f46473740 GIT binary patch literal 12433 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 R5cuDA@D~Y9`+wfg{{eVYE1CcR literal 0 HcmV?d00001 diff --git a/.devenv/state/go/pkg/mod/cache/download/github.com/pmezard/go-difflib/@v/v1.0.0.ziphash b/.devenv/state/go/pkg/mod/cache/download/github.com/pmezard/go-difflib/@v/v1.0.0.ziphash new file mode 100644 index 0000000..6ae1402 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/github.com/pmezard/go-difflib/@v/v1.0.0.ziphash @@ -0,0 +1 @@ +h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= \ No newline at end of file diff --git a/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/objx/@v/list b/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/objx/@v/list new file mode 100644 index 0000000..008f082 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/objx/@v/list @@ -0,0 +1,3 @@ +v0.1.0 +v0.4.0 +v0.5.0 diff --git a/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/objx/@v/v0.1.0.mod b/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/objx/@v/v0.1.0.mod new file mode 100644 index 0000000..cbaa421 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/objx/@v/v0.1.0.mod @@ -0,0 +1 @@ +module github.com/stretchr/objx diff --git a/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/objx/@v/v0.4.0.mod b/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/objx/@v/v0.4.0.mod new file mode 100644 index 0000000..45a55d2 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/objx/@v/v0.4.0.mod @@ -0,0 +1,8 @@ +module github.com/stretchr/objx + +go 1.12 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/stretchr/testify v1.7.1 +) diff --git a/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/objx/@v/v0.5.0.mod b/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/objx/@v/v0.5.0.mod new file mode 100644 index 0000000..eeb2cb3 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/objx/@v/v0.5.0.mod @@ -0,0 +1,5 @@ +module github.com/stretchr/objx + +go 1.12 + +require github.com/stretchr/testify v1.8.0 diff --git a/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/testify/@v/list b/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/testify/@v/list new file mode 100644 index 0000000..b7cdaf4 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/testify/@v/list @@ -0,0 +1,3 @@ +v1.7.1 +v1.8.0 +v1.8.2 diff --git a/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/testify/@v/v1.7.1.mod b/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/testify/@v/v1.7.1.mod new file mode 100644 index 0000000..ed0b50b --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/testify/@v/v1.7.1.mod @@ -0,0 +1,10 @@ +module github.com/stretchr/testify + +go 1.13 + +require ( + github.com/davecgh/go-spew v1.1.0 + github.com/pmezard/go-difflib v1.0.0 + github.com/stretchr/objx v0.1.0 + gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c +) diff --git a/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/testify/@v/v1.8.0.mod b/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/testify/@v/v1.8.0.mod new file mode 100644 index 0000000..c5d3e88 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/testify/@v/v1.8.0.mod @@ -0,0 +1,10 @@ +module github.com/stretchr/testify + +go 1.13 + +require ( + github.com/davecgh/go-spew v1.1.1 + github.com/pmezard/go-difflib v1.0.0 + github.com/stretchr/objx v0.4.0 + gopkg.in/yaml.v3 v3.0.1 +) diff --git a/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/testify/@v/v1.8.2.info b/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/testify/@v/v1.8.2.info new file mode 100644 index 0000000..e1271a7 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/testify/@v/v1.8.2.info @@ -0,0 +1 @@ +{"Version":"v1.8.2","Time":"2023-02-25T12:46:30Z","Origin":{"VCS":"git","URL":"https://github.com/stretchr/testify","Ref":"refs/tags/v1.8.2","Hash":"f36bfe3c337aa95c86f04c721acdbafb5ffb1611"}} \ No newline at end of file diff --git a/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/testify/@v/v1.8.2.lock b/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/testify/@v/v1.8.2.lock new file mode 100644 index 0000000..e69de29 diff --git a/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/testify/@v/v1.8.2.mod b/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/testify/@v/v1.8.2.mod new file mode 100644 index 0000000..3fe9bab --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/testify/@v/v1.8.2.mod @@ -0,0 +1,10 @@ +module github.com/stretchr/testify + +go 1.13 + +require ( + github.com/davecgh/go-spew v1.1.1 + github.com/pmezard/go-difflib v1.0.0 + github.com/stretchr/objx v0.5.0 + gopkg.in/yaml.v3 v3.0.1 +) diff --git a/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/testify/@v/v1.8.2.zip b/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/testify/@v/v1.8.2.zip new file mode 100644 index 0000000000000000000000000000000000000000..3ac4e13886c8a3f35ac7849fdf8c5a47adcdd33a GIT binary patch literal 114188 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 literal 0 HcmV?d00001 diff --git a/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/testify/@v/v1.8.2.ziphash b/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/testify/@v/v1.8.2.ziphash new file mode 100644 index 0000000..c8b1155 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/github.com/stretchr/testify/@v/v1.8.2.ziphash @@ -0,0 +1 @@ +h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= \ No newline at end of file diff --git a/.devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/list b/.devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/list new file mode 100644 index 0000000..4cf6351 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/list @@ -0,0 +1,2 @@ +v0.5.2 +v0.8.1 diff --git a/.devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.5.2.lock b/.devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.5.2.lock new file mode 100644 index 0000000..e69de29 diff --git a/.devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.5.2.mod b/.devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.5.2.mod new file mode 100644 index 0000000..2d2e207 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.5.2.mod @@ -0,0 +1,10 @@ +module gitlab.schukai.com/oss/libraries/go/utilities/pathfinder + +go 1.19 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/testify v1.8.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/.devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.5.2.zip b/.devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.5.2.zip new file mode 100644 index 0000000000000000000000000000000000000000..b6a41fad614b0f44456f67c3204ee93445dc5722 GIT binary patch literal 25981 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�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 literal 0 HcmV?d00001 diff --git a/.devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.5.2.ziphash b/.devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.5.2.ziphash new file mode 100644 index 0000000..cb5dd94 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.5.2.ziphash @@ -0,0 +1 @@ +h1:R+dL2NJCM+AQNPK4DPDmfvx1eomi1Xb1dl0XKEFj7Ek= \ No newline at end of file diff --git a/.devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.8.1.info b/.devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.8.1.info new file mode 100644 index 0000000..c8b194c --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.8.1.info @@ -0,0 +1 @@ +{"Version":"v0.8.1","Time":"2023-09-16T08:48:21Z","Origin":{"VCS":"git","URL":"https://gitlab.schukai.com/oss/libraries/go/utilities/pathfinder.git","Ref":"refs/tags/v0.8.1","Hash":"aa99843ec5a11ae8ac4af99f6ce2052ad410d716"}} \ No newline at end of file diff --git a/.devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.8.1.lock b/.devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.8.1.lock new file mode 100644 index 0000000..e69de29 diff --git a/.devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.8.1.mod b/.devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.8.1.mod new file mode 100644 index 0000000..f1f0841 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.8.1.mod @@ -0,0 +1,11 @@ +module gitlab.schukai.com/oss/libraries/go/utilities/pathfinder + +go 1.20 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/testify v1.8.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/.devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.8.1.zip b/.devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.8.1.zip new file mode 100644 index 0000000000000000000000000000000000000000..50b322712529a1e100d1cacb90f35c6a41fc555b GIT binary patch literal 39099 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 literal 0 HcmV?d00001 diff --git a/.devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.8.1.ziphash b/.devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.8.1.ziphash new file mode 100644 index 0000000..6eb6044 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/@v/v0.8.1.ziphash @@ -0,0 +1 @@ +h1:A3KvLvu4rV3OstgEn6xHulhQaXawVvzFzbafYHWHUfs= \ No newline at end of file diff --git a/.devenv/state/go/pkg/mod/cache/download/gopkg.in/check.v1/@v/list b/.devenv/state/go/pkg/mod/cache/download/gopkg.in/check.v1/@v/list new file mode 100644 index 0000000..d4c3e43 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/gopkg.in/check.v1/@v/list @@ -0,0 +1 @@ +v0.0.0-20161208181325-20d25e280405 diff --git a/.devenv/state/go/pkg/mod/cache/download/gopkg.in/check.v1/@v/v0.0.0-20161208181325-20d25e280405.mod b/.devenv/state/go/pkg/mod/cache/download/gopkg.in/check.v1/@v/v0.0.0-20161208181325-20d25e280405.mod new file mode 100644 index 0000000..7cec6a0 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/gopkg.in/check.v1/@v/v0.0.0-20161208181325-20d25e280405.mod @@ -0,0 +1 @@ +module gopkg.in/check.v1 diff --git a/.devenv/state/go/pkg/mod/cache/download/gopkg.in/yaml.v3/@v/list b/.devenv/state/go/pkg/mod/cache/download/gopkg.in/yaml.v3/@v/list new file mode 100644 index 0000000..2c0cc33 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/gopkg.in/yaml.v3/@v/list @@ -0,0 +1,2 @@ +v3.0.0-20200313102051-9f266ea9e77c +v3.0.1 diff --git a/.devenv/state/go/pkg/mod/cache/download/gopkg.in/yaml.v3/@v/v3.0.0-20200313102051-9f266ea9e77c.mod b/.devenv/state/go/pkg/mod/cache/download/gopkg.in/yaml.v3/@v/v3.0.0-20200313102051-9f266ea9e77c.mod new file mode 100644 index 0000000..f407ea3 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/gopkg.in/yaml.v3/@v/v3.0.0-20200313102051-9f266ea9e77c.mod @@ -0,0 +1,5 @@ +module "gopkg.in/yaml.v3" + +require ( + "gopkg.in/check.v1" v0.0.0-20161208181325-20d25e280405 +) diff --git a/.devenv/state/go/pkg/mod/cache/download/gopkg.in/yaml.v3/@v/v3.0.1.info b/.devenv/state/go/pkg/mod/cache/download/gopkg.in/yaml.v3/@v/v3.0.1.info new file mode 100644 index 0000000..590bbd3 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/gopkg.in/yaml.v3/@v/v3.0.1.info @@ -0,0 +1 @@ +{"Version":"v3.0.1","Time":"2022-05-27T08:35:30Z"} \ No newline at end of file diff --git a/.devenv/state/go/pkg/mod/cache/download/gopkg.in/yaml.v3/@v/v3.0.1.lock b/.devenv/state/go/pkg/mod/cache/download/gopkg.in/yaml.v3/@v/v3.0.1.lock new file mode 100644 index 0000000..e69de29 diff --git a/.devenv/state/go/pkg/mod/cache/download/gopkg.in/yaml.v3/@v/v3.0.1.mod b/.devenv/state/go/pkg/mod/cache/download/gopkg.in/yaml.v3/@v/v3.0.1.mod new file mode 100644 index 0000000..f407ea3 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/gopkg.in/yaml.v3/@v/v3.0.1.mod @@ -0,0 +1,5 @@ +module "gopkg.in/yaml.v3" + +require ( + "gopkg.in/check.v1" v0.0.0-20161208181325-20d25e280405 +) diff --git a/.devenv/state/go/pkg/mod/cache/download/gopkg.in/yaml.v3/@v/v3.0.1.zip b/.devenv/state/go/pkg/mod/cache/download/gopkg.in/yaml.v3/@v/v3.0.1.zip new file mode 100644 index 0000000000000000000000000000000000000000..14e5f8611f725d670dd1d408ced2c4f47e4869c7 GIT binary patch literal 104623 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 AZ2$lO literal 0 HcmV?d00001 diff --git a/.devenv/state/go/pkg/mod/cache/download/gopkg.in/yaml.v3/@v/v3.0.1.ziphash b/.devenv/state/go/pkg/mod/cache/download/gopkg.in/yaml.v3/@v/v3.0.1.ziphash new file mode 100644 index 0000000..1220d15 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/gopkg.in/yaml.v3/@v/v3.0.1.ziphash @@ -0,0 +1 @@ +h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= \ No newline at end of file diff --git a/.devenv/state/go/pkg/mod/cache/download/sumdb/sum.golang.org/lookup/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1 b/.devenv/state/go/pkg/mod/cache/download/sumdb/sum.golang.org/lookup/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1 new file mode 100644 index 0000000..66c1c22 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/sumdb/sum.golang.org/lookup/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1 @@ -0,0 +1,9 @@ +19632712 +gitlab.schukai.com/oss/libraries/go/utilities/pathfinder v0.8.1 h1:A3KvLvu4rV3OstgEn6xHulhQaXawVvzFzbafYHWHUfs= +gitlab.schukai.com/oss/libraries/go/utilities/pathfinder v0.8.1/go.mod h1:MqCBFv7DXKoBE2rZDc51LGvl2QI7Kz0D+XkQ0izj+ws= + +go.sum database tree +19632713 ++dFqIiCep9PFS2o8VBuGFtkBBGfUWWNR5ZdeHkgmu9k= + +— sum.golang.org Az3griAQL5WUYUZsThtGZu3lOLHLrxWK0YS6TDTBr7TJUTmogfpY1Hyrv21yIzVzvlSSsoq4A1vGmP1Se1EmBw8IcgQ= diff --git a/.devenv/state/go/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x076/690.p/73 b/.devenv/state/go/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x076/690.p/73 new file mode 100644 index 0000000000000000000000000000000000000000..d26c0100c2798b8ac5723cbd3c2e40773132972e GIT binary patch literal 2336 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 GrVLl_3VO)^ literal 0 HcmV?d00001 diff --git a/.devenv/state/go/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/1/299.p/146 b/.devenv/state/go/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/1/299.p/146 new file mode 100644 index 0000000000000000000000000000000000000000..fc9c844ae605cf14f394812ddfd08db01c537e8b GIT binary patch literal 4672 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 C@E{2Q literal 0 HcmV?d00001 diff --git a/.devenv/state/go/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/2/001.p/43 b/.devenv/state/go/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/2/001.p/43 new file mode 100644 index 0000000000000000000000000000000000000000..eb5d9393463e6672ac460bab415ac1fce97cfb05 GIT binary patch literal 1376 zcmV-m1)uskv-Yx*9{#C?jDn?s$&6O>d0KkNRDRwfJwR#>(7jzN46kKoT(rLMg|lg< zqWyUoO*BIh%rLVgP+KFJcxqi>!L0lc6E2WY=qtAsBm-VzO9$3Wzh&)bAEmie4wxne z+KD^#CBC_|(#B9e+uHA|(Z7CWY9knY$O9?=l5QEN&Bb-kIn=LcA3z3y%=`Da6wTuX zIvVQ%w~L{E>(^OWWVO&KU1v!k%<caPd0vwvw&{R2RQCpGX$FFMJ&6$6mDSrdiayx9 z+NFXsNVz*X<8?GYJtzH#sn9pSJ*4@1@0uzGiPEh}epQ6Qs;!Mr(6O!~{1|U`PFz*~ zbuD-i{tMU%S;qQQhb^pR7~;-E;MN>1no};EdhFDEk+m73%z-HaLjdveu{{obuQ`Kl zVXdsX@d<)W{BeHL{D=bb@fb6^d8HI~h|~IN7ckc_Z`+735D(=Mu}C2mL_GUinAKLQ zM$EawB)nnNGv_6!!Vl3o(zgz`*Y2<f;&i;<-sI}kW$Tl(0@Rt{JRy>c-T;D7bG+qE zwG@I8tfDtN?e{Uf@sEkyGn({AlR<AZ&H4Mf-(N2KA{(if4I>6)gV-Y=KJ2kYBAd1E zPMy7Fdh@)eIZHEnzK^nptiz-~E2LS&LhPU0$I?gO&gK)n+?QQcJ9eUh^t!=a6Vcx8 z#&(ysLT-or^I%Reexx0)*ldeap?Ex7ksJr0v3NZ8x3xl>pT*o)&amj63G=GicwgeQ zxsj5#9nx9C{4(RyM2+F`)+n6}nl%W1OBd6FVkv!iXb>s_{7O9)LmUwN9d_7WUTFi} z^{nUgB2gq3v`x5>U<X!d$hFhr0Ea7obj8)=Kw`;}E+=A(lj>4|*g7UPz*^2*j3!n6 z1)~75U}*V++_)NkLltON)J!C^nQ7^$Kycoxe2mFJ0%CI&d)(N^ISjv2fndyak9E!; zpuNAF$+oB8TLeKZA_N0`QIHh6s)q|d;}gzu)z}y`JsGy&t~f1Fs!{yWdD*={VHVyu zkX0UR2)(W;;Ck~7LPB<x>yla&C9k6ubOd-4$kv62Hz(?!(9PKFx;qCpJHEV|QpD~@ z)2^1{t{@mrGt{L|`*=ttsN)>dy)LcwuQf=pDD`IJ+eqqZm^{Eu*PDrA#c0p%7bDqV z<v&zY+S8gB3r4>)<H!|4YjiwwOi!pd9szci7L1J%2J@h4pH_txbe6PbhL}4s+D_P` z&%CH{tsS%Pg~QDHaYQ`FK2PEZuzCc3$0!zRBQS{*sX+WIZg6Bn=4DRo6cEwqZJ}+V z7rY?-e=$=pIIw^PJ(u!eNwo&`m^5VHTUcE(uh*@a6kmT<3^OAwXk_o>>Nbxs)i6q; zf=kBqu4Qb99sY^s6QBa&Qyi$T@)`sceY!fUaV^GDyB*K`8bB51q*;<mLg67fr4S66 z$+bJIJ*bSVkR@+D7@fXi7=5<~;0l3%^V}vwV*thbJDTtp%IVLQw6`wQPwwg(M6A7- zPy*VV-_ruL@)<3qKz;Lqr`-euifg+yila2(K0IyCu1hEwSO<bXOwAenv!w_WZVxa_ zSOrBqsA^F0B0N+=eIxRhfQ9!yko}tkgkSMtu$)a5K!CBGvkm>XM!+M851hVAtE5Mm zCtW*Rzk8X~Gv6oYq3m}Nu+pYh>LFhpSiohVXo8Q!w?D!`eIuCTaY`@-Y<{=?FFgMF zN$(peyvh6Wwl_K47jKLc2L@&zvW#MYDp^^|@oKfTBaAL5>529o?Itk2ZBizfFJ3tq ip~HLFr(=rt0?pW>Hr};W9OUT{OLVCDi?e8rUUiV`qp11- literal 0 HcmV?d00001 diff --git a/.devenv/state/go/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/3/000.p/1 b/.devenv/state/go/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/3/000.p/1 new file mode 100644 index 0000000..1664933 --- /dev/null +++ b/.devenv/state/go/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/3/000.p/1 @@ -0,0 +1,2 @@ +o +”@m°%q±£T`…ow!^ñ¾Z—{§ËéÐv \ No newline at end of file diff --git a/.devenv/state/go/pkg/mod/cache/lock b/.devenv/state/go/pkg/mod/cache/lock new file mode 100644 index 0000000..e69de29 diff --git a/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/.gitignore b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/.gitignore new file mode 100644 index 0000000..0026861 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/.gitignore @@ -0,0 +1,22 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe diff --git a/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/.travis.yml b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/.travis.yml new file mode 100644 index 0000000..1f4cbf5 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/.travis.yml @@ -0,0 +1,28 @@ +language: go +go_import_path: github.com/davecgh/go-spew +go: + - 1.6.x + - 1.7.x + - 1.8.x + - 1.9.x + - 1.10.x + - tip +sudo: false +install: + - go get -v github.com/alecthomas/gometalinter + - gometalinter --install +script: + - export PATH=$PATH:$HOME/gopath/bin + - export GORACE="halt_on_error=1" + - test -z "$(gometalinter --disable-all + --enable=gofmt + --enable=golint + --enable=vet + --enable=gosimple + --enable=unconvert + --deadline=4m ./spew | tee /dev/stderr)" + - go test -v -race -tags safe ./spew + - go test -v -race -tags testcgo ./spew -covermode=atomic -coverprofile=profile.cov +after_success: + - go get -v github.com/mattn/goveralls + - goveralls -coverprofile=profile.cov -service=travis-ci diff --git a/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/LICENSE b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/LICENSE new file mode 100644 index 0000000..bc52e96 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/LICENSE @@ -0,0 +1,15 @@ +ISC License + +Copyright (c) 2012-2016 Dave Collins <dave@davec.name> + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/README.md b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/README.md new file mode 100644 index 0000000..f6ed02c --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/README.md @@ -0,0 +1,201 @@ +go-spew +======= + +[](https://travis-ci.org/davecgh/go-spew) +[](http://copyfree.org) +[](https://coveralls.io/r/davecgh/go-spew?branch=master) + +Go-spew implements a deep pretty printer for Go data structures to aid in +debugging. A comprehensive suite of tests with 100% test coverage is provided +to ensure proper functionality. See `test_coverage.txt` for the gocov coverage +report. Go-spew is licensed under the liberal ISC license, so it may be used in +open source or commercial projects. + +If you're interested in reading about how this package came to life and some +of the challenges involved in providing a deep pretty printer, there is a blog +post about it +[here](https://web.archive.org/web/20160304013555/https://blog.cyphertite.com/go-spew-a-journey-into-dumping-go-data-structures/). + +## Documentation + +[](http://godoc.org/github.com/davecgh/go-spew/spew) + +Full `go doc` style documentation for the project can be viewed online without +installing this package by using the excellent GoDoc site here: +http://godoc.org/github.com/davecgh/go-spew/spew + +You can also view the documentation locally once the package is installed with +the `godoc` tool by running `godoc -http=":6060"` and pointing your browser to +http://localhost:6060/pkg/github.com/davecgh/go-spew/spew + +## Installation + +```bash +$ go get -u github.com/davecgh/go-spew/spew +``` + +## Quick Start + +Add this import line to the file you're working in: + +```Go +import "github.com/davecgh/go-spew/spew" +``` + +To dump a variable with full newlines, indentation, type, and pointer +information use Dump, Fdump, or Sdump: + +```Go +spew.Dump(myVar1, myVar2, ...) +spew.Fdump(someWriter, myVar1, myVar2, ...) +str := spew.Sdump(myVar1, myVar2, ...) +``` + +Alternatively, if you would prefer to use format strings with a compacted inline +printing style, use the convenience wrappers Printf, Fprintf, etc with %v (most +compact), %+v (adds pointer addresses), %#v (adds types), or %#+v (adds types +and pointer addresses): + +```Go +spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) +spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) +spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) +spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) +``` + +## Debugging a Web Application Example + +Here is an example of how you can use `spew.Sdump()` to help debug a web application. Please be sure to wrap your output using the `html.EscapeString()` function for safety reasons. You should also only use this debugging technique in a development environment, never in production. + +```Go +package main + +import ( + "fmt" + "html" + "net/http" + + "github.com/davecgh/go-spew/spew" +) + +func handler(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/html") + fmt.Fprintf(w, "Hi there, %s!", r.URL.Path[1:]) + fmt.Fprintf(w, "<!--\n" + html.EscapeString(spew.Sdump(w)) + "\n-->") +} + +func main() { + http.HandleFunc("/", handler) + http.ListenAndServe(":8080", nil) +} +``` + +## Sample Dump Output + +``` +(main.Foo) { + unexportedField: (*main.Bar)(0xf84002e210)({ + flag: (main.Flag) flagTwo, + data: (uintptr) <nil> + }), + ExportedField: (map[interface {}]interface {}) { + (string) "one": (bool) true + } +} +([]uint8) { + 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... | + 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0| + 00000020 31 32 |12| +} +``` + +## Sample Formatter Output + +Double pointer to a uint8: +``` + %v: <**>5 + %+v: <**>(0xf8400420d0->0xf8400420c8)5 + %#v: (**uint8)5 + %#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5 +``` + +Pointer to circular struct with a uint8 field and a pointer to itself: +``` + %v: <*>{1 <*><shown>} + %+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)<shown>} + %#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)<shown>} + %#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)<shown>} +``` + +## Configuration Options + +Configuration of spew is handled by fields in the ConfigState type. For +convenience, all of the top-level functions use a global state available via the +spew.Config global. + +It is also possible to create a ConfigState instance that provides methods +equivalent to the top-level functions. This allows concurrent configuration +options. See the ConfigState documentation for more details. + +``` +* Indent + String to use for each indentation level for Dump functions. + It is a single space by default. A popular alternative is "\t". + +* MaxDepth + Maximum number of levels to descend into nested data structures. + There is no limit by default. + +* DisableMethods + Disables invocation of error and Stringer interface methods. + Method invocation is enabled by default. + +* DisablePointerMethods + Disables invocation of error and Stringer interface methods on types + which only accept pointer receivers from non-pointer variables. This option + relies on access to the unsafe package, so it will not have any effect when + running in environments without access to the unsafe package such as Google + App Engine or with the "safe" build tag specified. + Pointer method invocation is enabled by default. + +* DisablePointerAddresses + DisablePointerAddresses specifies whether to disable the printing of + pointer addresses. This is useful when diffing data structures in tests. + +* DisableCapacities + DisableCapacities specifies whether to disable the printing of capacities + for arrays, slices, maps and channels. This is useful when diffing data + structures in tests. + +* ContinueOnMethod + Enables recursion into types after invoking error and Stringer interface + methods. Recursion after method invocation is disabled by default. + +* SortKeys + Specifies map keys should be sorted before being printed. Use + this to have a more deterministic, diffable output. Note that + only native types (bool, int, uint, floats, uintptr and string) + and types which implement error or Stringer interfaces are supported, + with other types sorted according to the reflect.Value.String() output + which guarantees display stability. Natural map order is used by + default. + +* SpewKeys + SpewKeys specifies that, as a last resort attempt, map keys should be + spewed to strings and sorted by those strings. This is only considered + if SortKeys is true. + +``` + +## Unsafe Package Dependency + +This package relies on the unsafe package to perform some of the more advanced +features, however it also supports a "limited" mode which allows it to work in +environments where the unsafe package is not available. By default, it will +operate in this mode on Google App Engine and when compiled with GopherJS. The +"safe" build tag may also be specified to force the package to build without +using the unsafe package. + +## License + +Go-spew is licensed under the [copyfree](http://copyfree.org) ISC License. diff --git a/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/cov_report.sh b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/cov_report.sh new file mode 100644 index 0000000..9579497 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/cov_report.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +# This script uses gocov to generate a test coverage report. +# The gocov tool my be obtained with the following command: +# go get github.com/axw/gocov/gocov +# +# It will be installed to $GOPATH/bin, so ensure that location is in your $PATH. + +# Check for gocov. +if ! type gocov >/dev/null 2>&1; then + echo >&2 "This script requires the gocov tool." + echo >&2 "You may obtain it with the following command:" + echo >&2 "go get github.com/axw/gocov/gocov" + exit 1 +fi + +# Only run the cgo tests if gcc is installed. +if type gcc >/dev/null 2>&1; then + (cd spew && gocov test -tags testcgo | gocov report) +else + (cd spew && gocov test | gocov report) +fi diff --git a/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/bypass.go b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/bypass.go new file mode 100644 index 0000000..7929947 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/bypass.go @@ -0,0 +1,145 @@ +// Copyright (c) 2015-2016 Dave Collins <dave@davec.name> +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +// NOTE: Due to the following build constraints, this file will only be compiled +// when the code is not running on Google App Engine, compiled by GopherJS, and +// "-tags safe" is not added to the go build command line. The "disableunsafe" +// tag is deprecated and thus should not be used. +// Go versions prior to 1.4 are disabled because they use a different layout +// for interfaces which make the implementation of unsafeReflectValue more complex. +// +build !js,!appengine,!safe,!disableunsafe,go1.4 + +package spew + +import ( + "reflect" + "unsafe" +) + +const ( + // UnsafeDisabled is a build-time constant which specifies whether or + // not access to the unsafe package is available. + UnsafeDisabled = false + + // ptrSize is the size of a pointer on the current arch. + ptrSize = unsafe.Sizeof((*byte)(nil)) +) + +type flag uintptr + +var ( + // flagRO indicates whether the value field of a reflect.Value + // is read-only. + flagRO flag + + // flagAddr indicates whether the address of the reflect.Value's + // value may be taken. + flagAddr flag +) + +// flagKindMask holds the bits that make up the kind +// part of the flags field. In all the supported versions, +// it is in the lower 5 bits. +const flagKindMask = flag(0x1f) + +// Different versions of Go have used different +// bit layouts for the flags type. This table +// records the known combinations. +var okFlags = []struct { + ro, addr flag +}{{ + // From Go 1.4 to 1.5 + ro: 1 << 5, + addr: 1 << 7, +}, { + // Up to Go tip. + ro: 1<<5 | 1<<6, + addr: 1 << 8, +}} + +var flagValOffset = func() uintptr { + field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") + if !ok { + panic("reflect.Value has no flag field") + } + return field.Offset +}() + +// flagField returns a pointer to the flag field of a reflect.Value. +func flagField(v *reflect.Value) *flag { + return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset)) +} + +// unsafeReflectValue converts the passed reflect.Value into a one that bypasses +// the typical safety restrictions preventing access to unaddressable and +// unexported data. It works by digging the raw pointer to the underlying +// value out of the protected value and generating a new unprotected (unsafe) +// reflect.Value to it. +// +// This allows us to check for implementations of the Stringer and error +// interfaces to be used for pretty printing ordinarily unaddressable and +// inaccessible values such as unexported struct fields. +func unsafeReflectValue(v reflect.Value) reflect.Value { + if !v.IsValid() || (v.CanInterface() && v.CanAddr()) { + return v + } + flagFieldPtr := flagField(&v) + *flagFieldPtr &^= flagRO + *flagFieldPtr |= flagAddr + return v +} + +// Sanity checks against future reflect package changes +// to the type or semantics of the Value.flag field. +func init() { + field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") + if !ok { + panic("reflect.Value has no flag field") + } + if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() { + panic("reflect.Value flag field has changed kind") + } + type t0 int + var t struct { + A t0 + // t0 will have flagEmbedRO set. + t0 + // a will have flagStickyRO set + a t0 + } + vA := reflect.ValueOf(t).FieldByName("A") + va := reflect.ValueOf(t).FieldByName("a") + vt0 := reflect.ValueOf(t).FieldByName("t0") + + // Infer flagRO from the difference between the flags + // for the (otherwise identical) fields in t. + flagPublic := *flagField(&vA) + flagWithRO := *flagField(&va) | *flagField(&vt0) + flagRO = flagPublic ^ flagWithRO + + // Infer flagAddr from the difference between a value + // taken from a pointer and not. + vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A") + flagNoPtr := *flagField(&vA) + flagPtr := *flagField(&vPtrA) + flagAddr = flagNoPtr ^ flagPtr + + // Check that the inferred flags tally with one of the known versions. + for _, f := range okFlags { + if flagRO == f.ro && flagAddr == f.addr { + return + } + } + panic("reflect.Value read-only flag has changed semantics") +} diff --git a/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/bypasssafe.go b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/bypasssafe.go new file mode 100644 index 0000000..205c28d --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/bypasssafe.go @@ -0,0 +1,38 @@ +// Copyright (c) 2015-2016 Dave Collins <dave@davec.name> +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +// NOTE: Due to the following build constraints, this file will only be compiled +// when the code is running on Google App Engine, compiled by GopherJS, or +// "-tags safe" is added to the go build command line. The "disableunsafe" +// tag is deprecated and thus should not be used. +// +build js appengine safe disableunsafe !go1.4 + +package spew + +import "reflect" + +const ( + // UnsafeDisabled is a build-time constant which specifies whether or + // not access to the unsafe package is available. + UnsafeDisabled = true +) + +// unsafeReflectValue typically converts the passed reflect.Value into a one +// that bypasses the typical safety restrictions preventing access to +// unaddressable and unexported data. However, doing this relies on access to +// the unsafe package. This is a stub version which simply returns the passed +// reflect.Value when the unsafe package is not available. +func unsafeReflectValue(v reflect.Value) reflect.Value { + return v +} diff --git a/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/common.go b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/common.go new file mode 100644 index 0000000..1be8ce9 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/common.go @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2013-2016 Dave Collins <dave@davec.name> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "bytes" + "fmt" + "io" + "reflect" + "sort" + "strconv" +) + +// Some constants in the form of bytes to avoid string overhead. This mirrors +// the technique used in the fmt package. +var ( + panicBytes = []byte("(PANIC=") + plusBytes = []byte("+") + iBytes = []byte("i") + trueBytes = []byte("true") + falseBytes = []byte("false") + interfaceBytes = []byte("(interface {})") + commaNewlineBytes = []byte(",\n") + newlineBytes = []byte("\n") + openBraceBytes = []byte("{") + openBraceNewlineBytes = []byte("{\n") + closeBraceBytes = []byte("}") + asteriskBytes = []byte("*") + colonBytes = []byte(":") + colonSpaceBytes = []byte(": ") + openParenBytes = []byte("(") + closeParenBytes = []byte(")") + spaceBytes = []byte(" ") + pointerChainBytes = []byte("->") + nilAngleBytes = []byte("<nil>") + maxNewlineBytes = []byte("<max depth reached>\n") + maxShortBytes = []byte("<max>") + circularBytes = []byte("<already shown>") + circularShortBytes = []byte("<shown>") + invalidAngleBytes = []byte("<invalid>") + openBracketBytes = []byte("[") + closeBracketBytes = []byte("]") + percentBytes = []byte("%") + precisionBytes = []byte(".") + openAngleBytes = []byte("<") + closeAngleBytes = []byte(">") + openMapBytes = []byte("map[") + closeMapBytes = []byte("]") + lenEqualsBytes = []byte("len=") + capEqualsBytes = []byte("cap=") +) + +// hexDigits is used to map a decimal value to a hex digit. +var hexDigits = "0123456789abcdef" + +// catchPanic handles any panics that might occur during the handleMethods +// calls. +func catchPanic(w io.Writer, v reflect.Value) { + if err := recover(); err != nil { + w.Write(panicBytes) + fmt.Fprintf(w, "%v", err) + w.Write(closeParenBytes) + } +} + +// handleMethods attempts to call the Error and String methods on the underlying +// type the passed reflect.Value represents and outputes the result to Writer w. +// +// It handles panics in any called methods by catching and displaying the error +// as the formatted value. +func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) { + // We need an interface to check if the type implements the error or + // Stringer interface. However, the reflect package won't give us an + // interface on certain things like unexported struct fields in order + // to enforce visibility rules. We use unsafe, when it's available, + // to bypass these restrictions since this package does not mutate the + // values. + if !v.CanInterface() { + if UnsafeDisabled { + return false + } + + v = unsafeReflectValue(v) + } + + // Choose whether or not to do error and Stringer interface lookups against + // the base type or a pointer to the base type depending on settings. + // Technically calling one of these methods with a pointer receiver can + // mutate the value, however, types which choose to satisify an error or + // Stringer interface with a pointer receiver should not be mutating their + // state inside these interface methods. + if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() { + v = unsafeReflectValue(v) + } + if v.CanAddr() { + v = v.Addr() + } + + // Is it an error or Stringer? + switch iface := v.Interface().(type) { + case error: + defer catchPanic(w, v) + if cs.ContinueOnMethod { + w.Write(openParenBytes) + w.Write([]byte(iface.Error())) + w.Write(closeParenBytes) + w.Write(spaceBytes) + return false + } + + w.Write([]byte(iface.Error())) + return true + + case fmt.Stringer: + defer catchPanic(w, v) + if cs.ContinueOnMethod { + w.Write(openParenBytes) + w.Write([]byte(iface.String())) + w.Write(closeParenBytes) + w.Write(spaceBytes) + return false + } + w.Write([]byte(iface.String())) + return true + } + return false +} + +// printBool outputs a boolean value as true or false to Writer w. +func printBool(w io.Writer, val bool) { + if val { + w.Write(trueBytes) + } else { + w.Write(falseBytes) + } +} + +// printInt outputs a signed integer value to Writer w. +func printInt(w io.Writer, val int64, base int) { + w.Write([]byte(strconv.FormatInt(val, base))) +} + +// printUint outputs an unsigned integer value to Writer w. +func printUint(w io.Writer, val uint64, base int) { + w.Write([]byte(strconv.FormatUint(val, base))) +} + +// printFloat outputs a floating point value using the specified precision, +// which is expected to be 32 or 64bit, to Writer w. +func printFloat(w io.Writer, val float64, precision int) { + w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision))) +} + +// printComplex outputs a complex value using the specified float precision +// for the real and imaginary parts to Writer w. +func printComplex(w io.Writer, c complex128, floatPrecision int) { + r := real(c) + w.Write(openParenBytes) + w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision))) + i := imag(c) + if i >= 0 { + w.Write(plusBytes) + } + w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision))) + w.Write(iBytes) + w.Write(closeParenBytes) +} + +// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x' +// prefix to Writer w. +func printHexPtr(w io.Writer, p uintptr) { + // Null pointer. + num := uint64(p) + if num == 0 { + w.Write(nilAngleBytes) + return + } + + // Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix + buf := make([]byte, 18) + + // It's simpler to construct the hex string right to left. + base := uint64(16) + i := len(buf) - 1 + for num >= base { + buf[i] = hexDigits[num%base] + num /= base + i-- + } + buf[i] = hexDigits[num] + + // Add '0x' prefix. + i-- + buf[i] = 'x' + i-- + buf[i] = '0' + + // Strip unused leading bytes. + buf = buf[i:] + w.Write(buf) +} + +// valuesSorter implements sort.Interface to allow a slice of reflect.Value +// elements to be sorted. +type valuesSorter struct { + values []reflect.Value + strings []string // either nil or same len and values + cs *ConfigState +} + +// newValuesSorter initializes a valuesSorter instance, which holds a set of +// surrogate keys on which the data should be sorted. It uses flags in +// ConfigState to decide if and how to populate those surrogate keys. +func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface { + vs := &valuesSorter{values: values, cs: cs} + if canSortSimply(vs.values[0].Kind()) { + return vs + } + if !cs.DisableMethods { + vs.strings = make([]string, len(values)) + for i := range vs.values { + b := bytes.Buffer{} + if !handleMethods(cs, &b, vs.values[i]) { + vs.strings = nil + break + } + vs.strings[i] = b.String() + } + } + if vs.strings == nil && cs.SpewKeys { + vs.strings = make([]string, len(values)) + for i := range vs.values { + vs.strings[i] = Sprintf("%#v", vs.values[i].Interface()) + } + } + return vs +} + +// canSortSimply tests whether a reflect.Kind is a primitive that can be sorted +// directly, or whether it should be considered for sorting by surrogate keys +// (if the ConfigState allows it). +func canSortSimply(kind reflect.Kind) bool { + // This switch parallels valueSortLess, except for the default case. + switch kind { + case reflect.Bool: + return true + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + return true + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + return true + case reflect.Float32, reflect.Float64: + return true + case reflect.String: + return true + case reflect.Uintptr: + return true + case reflect.Array: + return true + } + return false +} + +// Len returns the number of values in the slice. It is part of the +// sort.Interface implementation. +func (s *valuesSorter) Len() int { + return len(s.values) +} + +// Swap swaps the values at the passed indices. It is part of the +// sort.Interface implementation. +func (s *valuesSorter) Swap(i, j int) { + s.values[i], s.values[j] = s.values[j], s.values[i] + if s.strings != nil { + s.strings[i], s.strings[j] = s.strings[j], s.strings[i] + } +} + +// valueSortLess returns whether the first value should sort before the second +// value. It is used by valueSorter.Less as part of the sort.Interface +// implementation. +func valueSortLess(a, b reflect.Value) bool { + switch a.Kind() { + case reflect.Bool: + return !a.Bool() && b.Bool() + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + return a.Int() < b.Int() + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + return a.Uint() < b.Uint() + case reflect.Float32, reflect.Float64: + return a.Float() < b.Float() + case reflect.String: + return a.String() < b.String() + case reflect.Uintptr: + return a.Uint() < b.Uint() + case reflect.Array: + // Compare the contents of both arrays. + l := a.Len() + for i := 0; i < l; i++ { + av := a.Index(i) + bv := b.Index(i) + if av.Interface() == bv.Interface() { + continue + } + return valueSortLess(av, bv) + } + } + return a.String() < b.String() +} + +// Less returns whether the value at index i should sort before the +// value at index j. It is part of the sort.Interface implementation. +func (s *valuesSorter) Less(i, j int) bool { + if s.strings == nil { + return valueSortLess(s.values[i], s.values[j]) + } + return s.strings[i] < s.strings[j] +} + +// sortValues is a sort function that handles both native types and any type that +// can be converted to error or Stringer. Other inputs are sorted according to +// their Value.String() value to ensure display stability. +func sortValues(values []reflect.Value, cs *ConfigState) { + if len(values) == 0 { + return + } + sort.Sort(newValuesSorter(values, cs)) +} diff --git a/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/common_test.go b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/common_test.go new file mode 100644 index 0000000..0f5ce47 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/common_test.go @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2013-2016 Dave Collins <dave@davec.name> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew_test + +import ( + "fmt" + "reflect" + "testing" + + "github.com/davecgh/go-spew/spew" +) + +// custom type to test Stinger interface on non-pointer receiver. +type stringer string + +// String implements the Stringer interface for testing invocation of custom +// stringers on types with non-pointer receivers. +func (s stringer) String() string { + return "stringer " + string(s) +} + +// custom type to test Stinger interface on pointer receiver. +type pstringer string + +// String implements the Stringer interface for testing invocation of custom +// stringers on types with only pointer receivers. +func (s *pstringer) String() string { + return "stringer " + string(*s) +} + +// xref1 and xref2 are cross referencing structs for testing circular reference +// detection. +type xref1 struct { + ps2 *xref2 +} +type xref2 struct { + ps1 *xref1 +} + +// indirCir1, indirCir2, and indirCir3 are used to generate an indirect circular +// reference for testing detection. +type indirCir1 struct { + ps2 *indirCir2 +} +type indirCir2 struct { + ps3 *indirCir3 +} +type indirCir3 struct { + ps1 *indirCir1 +} + +// embed is used to test embedded structures. +type embed struct { + a string +} + +// embedwrap is used to test embedded structures. +type embedwrap struct { + *embed + e *embed +} + +// panicer is used to intentionally cause a panic for testing spew properly +// handles them +type panicer int + +func (p panicer) String() string { + panic("test panic") +} + +// customError is used to test custom error interface invocation. +type customError int + +func (e customError) Error() string { + return fmt.Sprintf("error: %d", int(e)) +} + +// stringizeWants converts a slice of wanted test output into a format suitable +// for a test error message. +func stringizeWants(wants []string) string { + s := "" + for i, want := range wants { + if i > 0 { + s += fmt.Sprintf("want%d: %s", i+1, want) + } else { + s += "want: " + want + } + } + return s +} + +// testFailed returns whether or not a test failed by checking if the result +// of the test is in the slice of wanted strings. +func testFailed(result string, wants []string) bool { + for _, want := range wants { + if result == want { + return false + } + } + return true +} + +type sortableStruct struct { + x int +} + +func (ss sortableStruct) String() string { + return fmt.Sprintf("ss.%d", ss.x) +} + +type unsortableStruct struct { + x int +} + +type sortTestCase struct { + input []reflect.Value + expected []reflect.Value +} + +func helpTestSortValues(tests []sortTestCase, cs *spew.ConfigState, t *testing.T) { + getInterfaces := func(values []reflect.Value) []interface{} { + interfaces := []interface{}{} + for _, v := range values { + interfaces = append(interfaces, v.Interface()) + } + return interfaces + } + + for _, test := range tests { + spew.SortValues(test.input, cs) + // reflect.DeepEqual cannot really make sense of reflect.Value, + // probably because of all the pointer tricks. For instance, + // v(2.0) != v(2.0) on a 32-bits system. Turn them into interface{} + // instead. + input := getInterfaces(test.input) + expected := getInterfaces(test.expected) + if !reflect.DeepEqual(input, expected) { + t.Errorf("Sort mismatch:\n %v != %v", input, expected) + } + } +} + +// TestSortValues ensures the sort functionality for relect.Value based sorting +// works as intended. +func TestSortValues(t *testing.T) { + v := reflect.ValueOf + + a := v("a") + b := v("b") + c := v("c") + embedA := v(embed{"a"}) + embedB := v(embed{"b"}) + embedC := v(embed{"c"}) + tests := []sortTestCase{ + // No values. + { + []reflect.Value{}, + []reflect.Value{}, + }, + // Bools. + { + []reflect.Value{v(false), v(true), v(false)}, + []reflect.Value{v(false), v(false), v(true)}, + }, + // Ints. + { + []reflect.Value{v(2), v(1), v(3)}, + []reflect.Value{v(1), v(2), v(3)}, + }, + // Uints. + { + []reflect.Value{v(uint8(2)), v(uint8(1)), v(uint8(3))}, + []reflect.Value{v(uint8(1)), v(uint8(2)), v(uint8(3))}, + }, + // Floats. + { + []reflect.Value{v(2.0), v(1.0), v(3.0)}, + []reflect.Value{v(1.0), v(2.0), v(3.0)}, + }, + // Strings. + { + []reflect.Value{b, a, c}, + []reflect.Value{a, b, c}, + }, + // Array + { + []reflect.Value{v([3]int{3, 2, 1}), v([3]int{1, 3, 2}), v([3]int{1, 2, 3})}, + []reflect.Value{v([3]int{1, 2, 3}), v([3]int{1, 3, 2}), v([3]int{3, 2, 1})}, + }, + // Uintptrs. + { + []reflect.Value{v(uintptr(2)), v(uintptr(1)), v(uintptr(3))}, + []reflect.Value{v(uintptr(1)), v(uintptr(2)), v(uintptr(3))}, + }, + // SortableStructs. + { + // Note: not sorted - DisableMethods is set. + []reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})}, + []reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})}, + }, + // UnsortableStructs. + { + // Note: not sorted - SpewKeys is false. + []reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})}, + []reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})}, + }, + // Invalid. + { + []reflect.Value{embedB, embedA, embedC}, + []reflect.Value{embedB, embedA, embedC}, + }, + } + cs := spew.ConfigState{DisableMethods: true, SpewKeys: false} + helpTestSortValues(tests, &cs, t) +} + +// TestSortValuesWithMethods ensures the sort functionality for relect.Value +// based sorting works as intended when using string methods. +func TestSortValuesWithMethods(t *testing.T) { + v := reflect.ValueOf + + a := v("a") + b := v("b") + c := v("c") + tests := []sortTestCase{ + // Ints. + { + []reflect.Value{v(2), v(1), v(3)}, + []reflect.Value{v(1), v(2), v(3)}, + }, + // Strings. + { + []reflect.Value{b, a, c}, + []reflect.Value{a, b, c}, + }, + // SortableStructs. + { + []reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})}, + []reflect.Value{v(sortableStruct{1}), v(sortableStruct{2}), v(sortableStruct{3})}, + }, + // UnsortableStructs. + { + // Note: not sorted - SpewKeys is false. + []reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})}, + []reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})}, + }, + } + cs := spew.ConfigState{DisableMethods: false, SpewKeys: false} + helpTestSortValues(tests, &cs, t) +} + +// TestSortValuesWithSpew ensures the sort functionality for relect.Value +// based sorting works as intended when using spew to stringify keys. +func TestSortValuesWithSpew(t *testing.T) { + v := reflect.ValueOf + + a := v("a") + b := v("b") + c := v("c") + tests := []sortTestCase{ + // Ints. + { + []reflect.Value{v(2), v(1), v(3)}, + []reflect.Value{v(1), v(2), v(3)}, + }, + // Strings. + { + []reflect.Value{b, a, c}, + []reflect.Value{a, b, c}, + }, + // SortableStructs. + { + []reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})}, + []reflect.Value{v(sortableStruct{1}), v(sortableStruct{2}), v(sortableStruct{3})}, + }, + // UnsortableStructs. + { + []reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})}, + []reflect.Value{v(unsortableStruct{1}), v(unsortableStruct{2}), v(unsortableStruct{3})}, + }, + } + cs := spew.ConfigState{DisableMethods: true, SpewKeys: true} + helpTestSortValues(tests, &cs, t) +} diff --git a/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/config.go b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/config.go new file mode 100644 index 0000000..2e3d22f --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/config.go @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2013-2016 Dave Collins <dave@davec.name> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "bytes" + "fmt" + "io" + "os" +) + +// ConfigState houses the configuration options used by spew to format and +// display values. There is a global instance, Config, that is used to control +// all top-level Formatter and Dump functionality. Each ConfigState instance +// provides methods equivalent to the top-level functions. +// +// The zero value for ConfigState provides no indentation. You would typically +// want to set it to a space or a tab. +// +// Alternatively, you can use NewDefaultConfig to get a ConfigState instance +// with default settings. See the documentation of NewDefaultConfig for default +// values. +type ConfigState struct { + // Indent specifies the string to use for each indentation level. The + // global config instance that all top-level functions use set this to a + // single space by default. If you would like more indentation, you might + // set this to a tab with "\t" or perhaps two spaces with " ". + Indent string + + // MaxDepth controls the maximum number of levels to descend into nested + // data structures. The default, 0, means there is no limit. + // + // NOTE: Circular data structures are properly detected, so it is not + // necessary to set this value unless you specifically want to limit deeply + // nested data structures. + MaxDepth int + + // DisableMethods specifies whether or not error and Stringer interfaces are + // invoked for types that implement them. + DisableMethods bool + + // DisablePointerMethods specifies whether or not to check for and invoke + // error and Stringer interfaces on types which only accept a pointer + // receiver when the current type is not a pointer. + // + // NOTE: This might be an unsafe action since calling one of these methods + // with a pointer receiver could technically mutate the value, however, + // in practice, types which choose to satisify an error or Stringer + // interface with a pointer receiver should not be mutating their state + // inside these interface methods. As a result, this option relies on + // access to the unsafe package, so it will not have any effect when + // running in environments without access to the unsafe package such as + // Google App Engine or with the "safe" build tag specified. + DisablePointerMethods bool + + // DisablePointerAddresses specifies whether to disable the printing of + // pointer addresses. This is useful when diffing data structures in tests. + DisablePointerAddresses bool + + // DisableCapacities specifies whether to disable the printing of capacities + // for arrays, slices, maps and channels. This is useful when diffing + // data structures in tests. + DisableCapacities bool + + // ContinueOnMethod specifies whether or not recursion should continue once + // a custom error or Stringer interface is invoked. The default, false, + // means it will print the results of invoking the custom error or Stringer + // interface and return immediately instead of continuing to recurse into + // the internals of the data type. + // + // NOTE: This flag does not have any effect if method invocation is disabled + // via the DisableMethods or DisablePointerMethods options. + ContinueOnMethod bool + + // SortKeys specifies map keys should be sorted before being printed. Use + // this to have a more deterministic, diffable output. Note that only + // native types (bool, int, uint, floats, uintptr and string) and types + // that support the error or Stringer interfaces (if methods are + // enabled) are supported, with other types sorted according to the + // reflect.Value.String() output which guarantees display stability. + SortKeys bool + + // SpewKeys specifies that, as a last resort attempt, map keys should + // be spewed to strings and sorted by those strings. This is only + // considered if SortKeys is true. + SpewKeys bool +} + +// Config is the active configuration of the top-level functions. +// The configuration can be changed by modifying the contents of spew.Config. +var Config = ConfigState{Indent: " "} + +// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the formatted string as a value that satisfies error. See NewFormatter +// for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Errorf(format, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Errorf(format string, a ...interface{}) (err error) { + return fmt.Errorf(format, c.convertArgs(a)...) +} + +// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprint(w, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Fprint(w io.Writer, a ...interface{}) (n int, err error) { + return fmt.Fprint(w, c.convertArgs(a)...) +} + +// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprintf(w, format, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { + return fmt.Fprintf(w, format, c.convertArgs(a)...) +} + +// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it +// passed with a Formatter interface returned by c.NewFormatter. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprintln(w, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) { + return fmt.Fprintln(w, c.convertArgs(a)...) +} + +// Print is a wrapper for fmt.Print that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Print(c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Print(a ...interface{}) (n int, err error) { + return fmt.Print(c.convertArgs(a)...) +} + +// Printf is a wrapper for fmt.Printf that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Printf(format, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Printf(format string, a ...interface{}) (n int, err error) { + return fmt.Printf(format, c.convertArgs(a)...) +} + +// Println is a wrapper for fmt.Println that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Println(c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Println(a ...interface{}) (n int, err error) { + return fmt.Println(c.convertArgs(a)...) +} + +// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprint(c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Sprint(a ...interface{}) string { + return fmt.Sprint(c.convertArgs(a)...) +} + +// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprintf(format, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Sprintf(format string, a ...interface{}) string { + return fmt.Sprintf(format, c.convertArgs(a)...) +} + +// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it +// were passed with a Formatter interface returned by c.NewFormatter. It +// returns the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprintln(c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Sprintln(a ...interface{}) string { + return fmt.Sprintln(c.convertArgs(a)...) +} + +/* +NewFormatter returns a custom formatter that satisfies the fmt.Formatter +interface. As a result, it integrates cleanly with standard fmt package +printing functions. The formatter is useful for inline printing of smaller data +types similar to the standard %v format specifier. + +The custom formatter only responds to the %v (most compact), %+v (adds pointer +addresses), %#v (adds types), and %#+v (adds types and pointer addresses) verb +combinations. Any other verbs such as %x and %q will be sent to the the +standard fmt package for formatting. In addition, the custom formatter ignores +the width and precision arguments (however they will still work on the format +specifiers not handled by the custom formatter). + +Typically this function shouldn't be called directly. It is much easier to make +use of the custom formatter by calling one of the convenience functions such as +c.Printf, c.Println, or c.Printf. +*/ +func (c *ConfigState) NewFormatter(v interface{}) fmt.Formatter { + return newFormatter(c, v) +} + +// Fdump formats and displays the passed arguments to io.Writer w. It formats +// exactly the same as Dump. +func (c *ConfigState) Fdump(w io.Writer, a ...interface{}) { + fdump(c, w, a...) +} + +/* +Dump displays the passed parameters to standard out with newlines, customizable +indentation, and additional debug information such as complete types and all +pointer addresses used to indirect to the final value. It provides the +following features over the built-in printing facilities provided by the fmt +package: + + * Pointers are dereferenced and followed + * Circular data structures are detected and handled properly + * Custom Stringer/error interfaces are optionally invoked, including + on unexported types + * Custom types which only implement the Stringer/error interfaces via + a pointer receiver are optionally invoked when passing non-pointer + variables + * Byte arrays and slices are dumped like the hexdump -C command which + includes offsets, byte values in hex, and ASCII output + +The configuration options are controlled by modifying the public members +of c. See ConfigState for options documentation. + +See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to +get the formatted result as a string. +*/ +func (c *ConfigState) Dump(a ...interface{}) { + fdump(c, os.Stdout, a...) +} + +// Sdump returns a string with the passed arguments formatted exactly the same +// as Dump. +func (c *ConfigState) Sdump(a ...interface{}) string { + var buf bytes.Buffer + fdump(c, &buf, a...) + return buf.String() +} + +// convertArgs accepts a slice of arguments and returns a slice of the same +// length with each argument converted to a spew Formatter interface using +// the ConfigState associated with s. +func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) { + formatters = make([]interface{}, len(args)) + for index, arg := range args { + formatters[index] = newFormatter(c, arg) + } + return formatters +} + +// NewDefaultConfig returns a ConfigState with the following default settings. +// +// Indent: " " +// MaxDepth: 0 +// DisableMethods: false +// DisablePointerMethods: false +// ContinueOnMethod: false +// SortKeys: false +func NewDefaultConfig() *ConfigState { + return &ConfigState{Indent: " "} +} diff --git a/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/doc.go b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/doc.go new file mode 100644 index 0000000..aacaac6 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/doc.go @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2013-2016 Dave Collins <dave@davec.name> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* +Package spew implements a deep pretty printer for Go data structures to aid in +debugging. + +A quick overview of the additional features spew provides over the built-in +printing facilities for Go data types are as follows: + + * Pointers are dereferenced and followed + * Circular data structures are detected and handled properly + * Custom Stringer/error interfaces are optionally invoked, including + on unexported types + * Custom types which only implement the Stringer/error interfaces via + a pointer receiver are optionally invoked when passing non-pointer + variables + * Byte arrays and slices are dumped like the hexdump -C command which + includes offsets, byte values in hex, and ASCII output (only when using + Dump style) + +There are two different approaches spew allows for dumping Go data structures: + + * Dump style which prints with newlines, customizable indentation, + and additional debug information such as types and all pointer addresses + used to indirect to the final value + * A custom Formatter interface that integrates cleanly with the standard fmt + package and replaces %v, %+v, %#v, and %#+v to provide inline printing + similar to the default %v while providing the additional functionality + outlined above and passing unsupported format verbs such as %x and %q + along to fmt + +Quick Start + +This section demonstrates how to quickly get started with spew. See the +sections below for further details on formatting and configuration options. + +To dump a variable with full newlines, indentation, type, and pointer +information use Dump, Fdump, or Sdump: + spew.Dump(myVar1, myVar2, ...) + spew.Fdump(someWriter, myVar1, myVar2, ...) + str := spew.Sdump(myVar1, myVar2, ...) + +Alternatively, if you would prefer to use format strings with a compacted inline +printing style, use the convenience wrappers Printf, Fprintf, etc with +%v (most compact), %+v (adds pointer addresses), %#v (adds types), or +%#+v (adds types and pointer addresses): + spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) + spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) + spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) + spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) + +Configuration Options + +Configuration of spew is handled by fields in the ConfigState type. For +convenience, all of the top-level functions use a global state available +via the spew.Config global. + +It is also possible to create a ConfigState instance that provides methods +equivalent to the top-level functions. This allows concurrent configuration +options. See the ConfigState documentation for more details. + +The following configuration options are available: + * Indent + String to use for each indentation level for Dump functions. + It is a single space by default. A popular alternative is "\t". + + * MaxDepth + Maximum number of levels to descend into nested data structures. + There is no limit by default. + + * DisableMethods + Disables invocation of error and Stringer interface methods. + Method invocation is enabled by default. + + * DisablePointerMethods + Disables invocation of error and Stringer interface methods on types + which only accept pointer receivers from non-pointer variables. + Pointer method invocation is enabled by default. + + * DisablePointerAddresses + DisablePointerAddresses specifies whether to disable the printing of + pointer addresses. This is useful when diffing data structures in tests. + + * DisableCapacities + DisableCapacities specifies whether to disable the printing of + capacities for arrays, slices, maps and channels. This is useful when + diffing data structures in tests. + + * ContinueOnMethod + Enables recursion into types after invoking error and Stringer interface + methods. Recursion after method invocation is disabled by default. + + * SortKeys + Specifies map keys should be sorted before being printed. Use + this to have a more deterministic, diffable output. Note that + only native types (bool, int, uint, floats, uintptr and string) + and types which implement error or Stringer interfaces are + supported with other types sorted according to the + reflect.Value.String() output which guarantees display + stability. Natural map order is used by default. + + * SpewKeys + Specifies that, as a last resort attempt, map keys should be + spewed to strings and sorted by those strings. This is only + considered if SortKeys is true. + +Dump Usage + +Simply call spew.Dump with a list of variables you want to dump: + + spew.Dump(myVar1, myVar2, ...) + +You may also call spew.Fdump if you would prefer to output to an arbitrary +io.Writer. For example, to dump to standard error: + + spew.Fdump(os.Stderr, myVar1, myVar2, ...) + +A third option is to call spew.Sdump to get the formatted output as a string: + + str := spew.Sdump(myVar1, myVar2, ...) + +Sample Dump Output + +See the Dump example for details on the setup of the types and variables being +shown here. + + (main.Foo) { + unexportedField: (*main.Bar)(0xf84002e210)({ + flag: (main.Flag) flagTwo, + data: (uintptr) <nil> + }), + ExportedField: (map[interface {}]interface {}) (len=1) { + (string) (len=3) "one": (bool) true + } + } + +Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C +command as shown. + ([]uint8) (len=32 cap=32) { + 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... | + 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0| + 00000020 31 32 |12| + } + +Custom Formatter + +Spew provides a custom formatter that implements the fmt.Formatter interface +so that it integrates cleanly with standard fmt package printing functions. The +formatter is useful for inline printing of smaller data types similar to the +standard %v format specifier. + +The custom formatter only responds to the %v (most compact), %+v (adds pointer +addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb +combinations. Any other verbs such as %x and %q will be sent to the the +standard fmt package for formatting. In addition, the custom formatter ignores +the width and precision arguments (however they will still work on the format +specifiers not handled by the custom formatter). + +Custom Formatter Usage + +The simplest way to make use of the spew custom formatter is to call one of the +convenience functions such as spew.Printf, spew.Println, or spew.Printf. The +functions have syntax you are most likely already familiar with: + + spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) + spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) + spew.Println(myVar, myVar2) + spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) + spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) + +See the Index for the full list convenience functions. + +Sample Formatter Output + +Double pointer to a uint8: + %v: <**>5 + %+v: <**>(0xf8400420d0->0xf8400420c8)5 + %#v: (**uint8)5 + %#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5 + +Pointer to circular struct with a uint8 field and a pointer to itself: + %v: <*>{1 <*><shown>} + %+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)<shown>} + %#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)<shown>} + %#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)<shown>} + +See the Printf example for details on the setup of variables being shown +here. + +Errors + +Since it is possible for custom Stringer/error interfaces to panic, spew +detects them and handles them internally by printing the panic information +inline with the output. Since spew is intended to provide deep pretty printing +capabilities on structures, it intentionally does not return any errors. +*/ +package spew diff --git a/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/dump.go b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/dump.go new file mode 100644 index 0000000..f78d89f --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/dump.go @@ -0,0 +1,509 @@ +/* + * Copyright (c) 2013-2016 Dave Collins <dave@davec.name> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "bytes" + "encoding/hex" + "fmt" + "io" + "os" + "reflect" + "regexp" + "strconv" + "strings" +) + +var ( + // uint8Type is a reflect.Type representing a uint8. It is used to + // convert cgo types to uint8 slices for hexdumping. + uint8Type = reflect.TypeOf(uint8(0)) + + // cCharRE is a regular expression that matches a cgo char. + // It is used to detect character arrays to hexdump them. + cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`) + + // cUnsignedCharRE is a regular expression that matches a cgo unsigned + // char. It is used to detect unsigned character arrays to hexdump + // them. + cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`) + + // cUint8tCharRE is a regular expression that matches a cgo uint8_t. + // It is used to detect uint8_t arrays to hexdump them. + cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`) +) + +// dumpState contains information about the state of a dump operation. +type dumpState struct { + w io.Writer + depth int + pointers map[uintptr]int + ignoreNextType bool + ignoreNextIndent bool + cs *ConfigState +} + +// indent performs indentation according to the depth level and cs.Indent +// option. +func (d *dumpState) indent() { + if d.ignoreNextIndent { + d.ignoreNextIndent = false + return + } + d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth)) +} + +// unpackValue returns values inside of non-nil interfaces when possible. +// This is useful for data types like structs, arrays, slices, and maps which +// can contain varying types packed inside an interface. +func (d *dumpState) unpackValue(v reflect.Value) reflect.Value { + if v.Kind() == reflect.Interface && !v.IsNil() { + v = v.Elem() + } + return v +} + +// dumpPtr handles formatting of pointers by indirecting them as necessary. +func (d *dumpState) dumpPtr(v reflect.Value) { + // Remove pointers at or below the current depth from map used to detect + // circular refs. + for k, depth := range d.pointers { + if depth >= d.depth { + delete(d.pointers, k) + } + } + + // Keep list of all dereferenced pointers to show later. + pointerChain := make([]uintptr, 0) + + // Figure out how many levels of indirection there are by dereferencing + // pointers and unpacking interfaces down the chain while detecting circular + // references. + nilFound := false + cycleFound := false + indirects := 0 + ve := v + for ve.Kind() == reflect.Ptr { + if ve.IsNil() { + nilFound = true + break + } + indirects++ + addr := ve.Pointer() + pointerChain = append(pointerChain, addr) + if pd, ok := d.pointers[addr]; ok && pd < d.depth { + cycleFound = true + indirects-- + break + } + d.pointers[addr] = d.depth + + ve = ve.Elem() + if ve.Kind() == reflect.Interface { + if ve.IsNil() { + nilFound = true + break + } + ve = ve.Elem() + } + } + + // Display type information. + d.w.Write(openParenBytes) + d.w.Write(bytes.Repeat(asteriskBytes, indirects)) + d.w.Write([]byte(ve.Type().String())) + d.w.Write(closeParenBytes) + + // Display pointer information. + if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 { + d.w.Write(openParenBytes) + for i, addr := range pointerChain { + if i > 0 { + d.w.Write(pointerChainBytes) + } + printHexPtr(d.w, addr) + } + d.w.Write(closeParenBytes) + } + + // Display dereferenced value. + d.w.Write(openParenBytes) + switch { + case nilFound: + d.w.Write(nilAngleBytes) + + case cycleFound: + d.w.Write(circularBytes) + + default: + d.ignoreNextType = true + d.dump(ve) + } + d.w.Write(closeParenBytes) +} + +// dumpSlice handles formatting of arrays and slices. Byte (uint8 under +// reflection) arrays and slices are dumped in hexdump -C fashion. +func (d *dumpState) dumpSlice(v reflect.Value) { + // Determine whether this type should be hex dumped or not. Also, + // for types which should be hexdumped, try to use the underlying data + // first, then fall back to trying to convert them to a uint8 slice. + var buf []uint8 + doConvert := false + doHexDump := false + numEntries := v.Len() + if numEntries > 0 { + vt := v.Index(0).Type() + vts := vt.String() + switch { + // C types that need to be converted. + case cCharRE.MatchString(vts): + fallthrough + case cUnsignedCharRE.MatchString(vts): + fallthrough + case cUint8tCharRE.MatchString(vts): + doConvert = true + + // Try to use existing uint8 slices and fall back to converting + // and copying if that fails. + case vt.Kind() == reflect.Uint8: + // We need an addressable interface to convert the type + // to a byte slice. However, the reflect package won't + // give us an interface on certain things like + // unexported struct fields in order to enforce + // visibility rules. We use unsafe, when available, to + // bypass these restrictions since this package does not + // mutate the values. + vs := v + if !vs.CanInterface() || !vs.CanAddr() { + vs = unsafeReflectValue(vs) + } + if !UnsafeDisabled { + vs = vs.Slice(0, numEntries) + + // Use the existing uint8 slice if it can be + // type asserted. + iface := vs.Interface() + if slice, ok := iface.([]uint8); ok { + buf = slice + doHexDump = true + break + } + } + + // The underlying data needs to be converted if it can't + // be type asserted to a uint8 slice. + doConvert = true + } + + // Copy and convert the underlying type if needed. + if doConvert && vt.ConvertibleTo(uint8Type) { + // Convert and copy each element into a uint8 byte + // slice. + buf = make([]uint8, numEntries) + for i := 0; i < numEntries; i++ { + vv := v.Index(i) + buf[i] = uint8(vv.Convert(uint8Type).Uint()) + } + doHexDump = true + } + } + + // Hexdump the entire slice as needed. + if doHexDump { + indent := strings.Repeat(d.cs.Indent, d.depth) + str := indent + hex.Dump(buf) + str = strings.Replace(str, "\n", "\n"+indent, -1) + str = strings.TrimRight(str, d.cs.Indent) + d.w.Write([]byte(str)) + return + } + + // Recursively call dump for each item. + for i := 0; i < numEntries; i++ { + d.dump(d.unpackValue(v.Index(i))) + if i < (numEntries - 1) { + d.w.Write(commaNewlineBytes) + } else { + d.w.Write(newlineBytes) + } + } +} + +// dump is the main workhorse for dumping a value. It uses the passed reflect +// value to figure out what kind of object we are dealing with and formats it +// appropriately. It is a recursive function, however circular data structures +// are detected and handled properly. +func (d *dumpState) dump(v reflect.Value) { + // Handle invalid reflect values immediately. + kind := v.Kind() + if kind == reflect.Invalid { + d.w.Write(invalidAngleBytes) + return + } + + // Handle pointers specially. + if kind == reflect.Ptr { + d.indent() + d.dumpPtr(v) + return + } + + // Print type information unless already handled elsewhere. + if !d.ignoreNextType { + d.indent() + d.w.Write(openParenBytes) + d.w.Write([]byte(v.Type().String())) + d.w.Write(closeParenBytes) + d.w.Write(spaceBytes) + } + d.ignoreNextType = false + + // Display length and capacity if the built-in len and cap functions + // work with the value's kind and the len/cap itself is non-zero. + valueLen, valueCap := 0, 0 + switch v.Kind() { + case reflect.Array, reflect.Slice, reflect.Chan: + valueLen, valueCap = v.Len(), v.Cap() + case reflect.Map, reflect.String: + valueLen = v.Len() + } + if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 { + d.w.Write(openParenBytes) + if valueLen != 0 { + d.w.Write(lenEqualsBytes) + printInt(d.w, int64(valueLen), 10) + } + if !d.cs.DisableCapacities && valueCap != 0 { + if valueLen != 0 { + d.w.Write(spaceBytes) + } + d.w.Write(capEqualsBytes) + printInt(d.w, int64(valueCap), 10) + } + d.w.Write(closeParenBytes) + d.w.Write(spaceBytes) + } + + // Call Stringer/error interfaces if they exist and the handle methods flag + // is enabled + if !d.cs.DisableMethods { + if (kind != reflect.Invalid) && (kind != reflect.Interface) { + if handled := handleMethods(d.cs, d.w, v); handled { + return + } + } + } + + switch kind { + case reflect.Invalid: + // Do nothing. We should never get here since invalid has already + // been handled above. + + case reflect.Bool: + printBool(d.w, v.Bool()) + + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + printInt(d.w, v.Int(), 10) + + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + printUint(d.w, v.Uint(), 10) + + case reflect.Float32: + printFloat(d.w, v.Float(), 32) + + case reflect.Float64: + printFloat(d.w, v.Float(), 64) + + case reflect.Complex64: + printComplex(d.w, v.Complex(), 32) + + case reflect.Complex128: + printComplex(d.w, v.Complex(), 64) + + case reflect.Slice: + if v.IsNil() { + d.w.Write(nilAngleBytes) + break + } + fallthrough + + case reflect.Array: + d.w.Write(openBraceNewlineBytes) + d.depth++ + if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { + d.indent() + d.w.Write(maxNewlineBytes) + } else { + d.dumpSlice(v) + } + d.depth-- + d.indent() + d.w.Write(closeBraceBytes) + + case reflect.String: + d.w.Write([]byte(strconv.Quote(v.String()))) + + case reflect.Interface: + // The only time we should get here is for nil interfaces due to + // unpackValue calls. + if v.IsNil() { + d.w.Write(nilAngleBytes) + } + + case reflect.Ptr: + // Do nothing. We should never get here since pointers have already + // been handled above. + + case reflect.Map: + // nil maps should be indicated as different than empty maps + if v.IsNil() { + d.w.Write(nilAngleBytes) + break + } + + d.w.Write(openBraceNewlineBytes) + d.depth++ + if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { + d.indent() + d.w.Write(maxNewlineBytes) + } else { + numEntries := v.Len() + keys := v.MapKeys() + if d.cs.SortKeys { + sortValues(keys, d.cs) + } + for i, key := range keys { + d.dump(d.unpackValue(key)) + d.w.Write(colonSpaceBytes) + d.ignoreNextIndent = true + d.dump(d.unpackValue(v.MapIndex(key))) + if i < (numEntries - 1) { + d.w.Write(commaNewlineBytes) + } else { + d.w.Write(newlineBytes) + } + } + } + d.depth-- + d.indent() + d.w.Write(closeBraceBytes) + + case reflect.Struct: + d.w.Write(openBraceNewlineBytes) + d.depth++ + if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { + d.indent() + d.w.Write(maxNewlineBytes) + } else { + vt := v.Type() + numFields := v.NumField() + for i := 0; i < numFields; i++ { + d.indent() + vtf := vt.Field(i) + d.w.Write([]byte(vtf.Name)) + d.w.Write(colonSpaceBytes) + d.ignoreNextIndent = true + d.dump(d.unpackValue(v.Field(i))) + if i < (numFields - 1) { + d.w.Write(commaNewlineBytes) + } else { + d.w.Write(newlineBytes) + } + } + } + d.depth-- + d.indent() + d.w.Write(closeBraceBytes) + + case reflect.Uintptr: + printHexPtr(d.w, uintptr(v.Uint())) + + case reflect.UnsafePointer, reflect.Chan, reflect.Func: + printHexPtr(d.w, v.Pointer()) + + // There were not any other types at the time this code was written, but + // fall back to letting the default fmt package handle it in case any new + // types are added. + default: + if v.CanInterface() { + fmt.Fprintf(d.w, "%v", v.Interface()) + } else { + fmt.Fprintf(d.w, "%v", v.String()) + } + } +} + +// fdump is a helper function to consolidate the logic from the various public +// methods which take varying writers and config states. +func fdump(cs *ConfigState, w io.Writer, a ...interface{}) { + for _, arg := range a { + if arg == nil { + w.Write(interfaceBytes) + w.Write(spaceBytes) + w.Write(nilAngleBytes) + w.Write(newlineBytes) + continue + } + + d := dumpState{w: w, cs: cs} + d.pointers = make(map[uintptr]int) + d.dump(reflect.ValueOf(arg)) + d.w.Write(newlineBytes) + } +} + +// Fdump formats and displays the passed arguments to io.Writer w. It formats +// exactly the same as Dump. +func Fdump(w io.Writer, a ...interface{}) { + fdump(&Config, w, a...) +} + +// Sdump returns a string with the passed arguments formatted exactly the same +// as Dump. +func Sdump(a ...interface{}) string { + var buf bytes.Buffer + fdump(&Config, &buf, a...) + return buf.String() +} + +/* +Dump displays the passed parameters to standard out with newlines, customizable +indentation, and additional debug information such as complete types and all +pointer addresses used to indirect to the final value. It provides the +following features over the built-in printing facilities provided by the fmt +package: + + * Pointers are dereferenced and followed + * Circular data structures are detected and handled properly + * Custom Stringer/error interfaces are optionally invoked, including + on unexported types + * Custom types which only implement the Stringer/error interfaces via + a pointer receiver are optionally invoked when passing non-pointer + variables + * Byte arrays and slices are dumped like the hexdump -C command which + includes offsets, byte values in hex, and ASCII output + +The configuration options are controlled by an exported package global, +spew.Config. See ConfigState for options documentation. + +See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to +get the formatted result as a string. +*/ +func Dump(a ...interface{}) { + fdump(&Config, os.Stdout, a...) +} diff --git a/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/dump_test.go b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/dump_test.go new file mode 100644 index 0000000..4a31a2e --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/dump_test.go @@ -0,0 +1,1042 @@ +/* + * Copyright (c) 2013-2016 Dave Collins <dave@davec.name> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* +Test Summary: +NOTE: For each test, a nil pointer, a single pointer and double pointer to the +base test element are also tested to ensure proper indirection across all types. + +- Max int8, int16, int32, int64, int +- Max uint8, uint16, uint32, uint64, uint +- Boolean true and false +- Standard complex64 and complex128 +- Array containing standard ints +- Array containing type with custom formatter on pointer receiver only +- Array containing interfaces +- Array containing bytes +- Slice containing standard float32 values +- Slice containing type with custom formatter on pointer receiver only +- Slice containing interfaces +- Slice containing bytes +- Nil slice +- Standard string +- Nil interface +- Sub-interface +- Map with string keys and int vals +- Map with custom formatter type on pointer receiver only keys and vals +- Map with interface keys and values +- Map with nil interface value +- Struct with primitives +- Struct that contains another struct +- Struct that contains custom type with Stringer pointer interface via both + exported and unexported fields +- Struct that contains embedded struct and field to same struct +- Uintptr to 0 (null pointer) +- Uintptr address of real variable +- Unsafe.Pointer to 0 (null pointer) +- Unsafe.Pointer to address of real variable +- Nil channel +- Standard int channel +- Function with no params and no returns +- Function with param and no returns +- Function with multiple params and multiple returns +- Struct that is circular through self referencing +- Structs that are circular through cross referencing +- Structs that are indirectly circular +- Type that panics in its Stringer interface +*/ + +package spew_test + +import ( + "bytes" + "fmt" + "testing" + "unsafe" + + "github.com/davecgh/go-spew/spew" +) + +// dumpTest is used to describe a test to be performed against the Dump method. +type dumpTest struct { + in interface{} + wants []string +} + +// dumpTests houses all of the tests to be performed against the Dump method. +var dumpTests = make([]dumpTest, 0) + +// addDumpTest is a helper method to append the passed input and desired result +// to dumpTests +func addDumpTest(in interface{}, wants ...string) { + test := dumpTest{in, wants} + dumpTests = append(dumpTests, test) +} + +func addIntDumpTests() { + // Max int8. + v := int8(127) + nv := (*int8)(nil) + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "int8" + vs := "127" + addDumpTest(v, "("+vt+") "+vs+"\n") + addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") + addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + addDumpTest(nv, "(*"+vt+")(<nil>)\n") + + // Max int16. + v2 := int16(32767) + nv2 := (*int16)(nil) + pv2 := &v2 + v2Addr := fmt.Sprintf("%p", pv2) + pv2Addr := fmt.Sprintf("%p", &pv2) + v2t := "int16" + v2s := "32767" + addDumpTest(v2, "("+v2t+") "+v2s+"\n") + addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") + addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") + addDumpTest(nv2, "(*"+v2t+")(<nil>)\n") + + // Max int32. + v3 := int32(2147483647) + nv3 := (*int32)(nil) + pv3 := &v3 + v3Addr := fmt.Sprintf("%p", pv3) + pv3Addr := fmt.Sprintf("%p", &pv3) + v3t := "int32" + v3s := "2147483647" + addDumpTest(v3, "("+v3t+") "+v3s+"\n") + addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n") + addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n") + addDumpTest(nv3, "(*"+v3t+")(<nil>)\n") + + // Max int64. + v4 := int64(9223372036854775807) + nv4 := (*int64)(nil) + pv4 := &v4 + v4Addr := fmt.Sprintf("%p", pv4) + pv4Addr := fmt.Sprintf("%p", &pv4) + v4t := "int64" + v4s := "9223372036854775807" + addDumpTest(v4, "("+v4t+") "+v4s+"\n") + addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n") + addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n") + addDumpTest(nv4, "(*"+v4t+")(<nil>)\n") + + // Max int. + v5 := int(2147483647) + nv5 := (*int)(nil) + pv5 := &v5 + v5Addr := fmt.Sprintf("%p", pv5) + pv5Addr := fmt.Sprintf("%p", &pv5) + v5t := "int" + v5s := "2147483647" + addDumpTest(v5, "("+v5t+") "+v5s+"\n") + addDumpTest(pv5, "(*"+v5t+")("+v5Addr+")("+v5s+")\n") + addDumpTest(&pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")("+v5s+")\n") + addDumpTest(nv5, "(*"+v5t+")(<nil>)\n") +} + +func addUintDumpTests() { + // Max uint8. + v := uint8(255) + nv := (*uint8)(nil) + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "uint8" + vs := "255" + addDumpTest(v, "("+vt+") "+vs+"\n") + addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") + addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + addDumpTest(nv, "(*"+vt+")(<nil>)\n") + + // Max uint16. + v2 := uint16(65535) + nv2 := (*uint16)(nil) + pv2 := &v2 + v2Addr := fmt.Sprintf("%p", pv2) + pv2Addr := fmt.Sprintf("%p", &pv2) + v2t := "uint16" + v2s := "65535" + addDumpTest(v2, "("+v2t+") "+v2s+"\n") + addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") + addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") + addDumpTest(nv2, "(*"+v2t+")(<nil>)\n") + + // Max uint32. + v3 := uint32(4294967295) + nv3 := (*uint32)(nil) + pv3 := &v3 + v3Addr := fmt.Sprintf("%p", pv3) + pv3Addr := fmt.Sprintf("%p", &pv3) + v3t := "uint32" + v3s := "4294967295" + addDumpTest(v3, "("+v3t+") "+v3s+"\n") + addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n") + addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n") + addDumpTest(nv3, "(*"+v3t+")(<nil>)\n") + + // Max uint64. + v4 := uint64(18446744073709551615) + nv4 := (*uint64)(nil) + pv4 := &v4 + v4Addr := fmt.Sprintf("%p", pv4) + pv4Addr := fmt.Sprintf("%p", &pv4) + v4t := "uint64" + v4s := "18446744073709551615" + addDumpTest(v4, "("+v4t+") "+v4s+"\n") + addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n") + addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n") + addDumpTest(nv4, "(*"+v4t+")(<nil>)\n") + + // Max uint. + v5 := uint(4294967295) + nv5 := (*uint)(nil) + pv5 := &v5 + v5Addr := fmt.Sprintf("%p", pv5) + pv5Addr := fmt.Sprintf("%p", &pv5) + v5t := "uint" + v5s := "4294967295" + addDumpTest(v5, "("+v5t+") "+v5s+"\n") + addDumpTest(pv5, "(*"+v5t+")("+v5Addr+")("+v5s+")\n") + addDumpTest(&pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")("+v5s+")\n") + addDumpTest(nv5, "(*"+v5t+")(<nil>)\n") +} + +func addBoolDumpTests() { + // Boolean true. + v := bool(true) + nv := (*bool)(nil) + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "bool" + vs := "true" + addDumpTest(v, "("+vt+") "+vs+"\n") + addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") + addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + addDumpTest(nv, "(*"+vt+")(<nil>)\n") + + // Boolean false. + v2 := bool(false) + pv2 := &v2 + v2Addr := fmt.Sprintf("%p", pv2) + pv2Addr := fmt.Sprintf("%p", &pv2) + v2t := "bool" + v2s := "false" + addDumpTest(v2, "("+v2t+") "+v2s+"\n") + addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") + addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") +} + +func addFloatDumpTests() { + // Standard float32. + v := float32(3.1415) + nv := (*float32)(nil) + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "float32" + vs := "3.1415" + addDumpTest(v, "("+vt+") "+vs+"\n") + addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") + addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + addDumpTest(nv, "(*"+vt+")(<nil>)\n") + + // Standard float64. + v2 := float64(3.1415926) + nv2 := (*float64)(nil) + pv2 := &v2 + v2Addr := fmt.Sprintf("%p", pv2) + pv2Addr := fmt.Sprintf("%p", &pv2) + v2t := "float64" + v2s := "3.1415926" + addDumpTest(v2, "("+v2t+") "+v2s+"\n") + addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") + addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") + addDumpTest(nv2, "(*"+v2t+")(<nil>)\n") +} + +func addComplexDumpTests() { + // Standard complex64. + v := complex(float32(6), -2) + nv := (*complex64)(nil) + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "complex64" + vs := "(6-2i)" + addDumpTest(v, "("+vt+") "+vs+"\n") + addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") + addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + addDumpTest(nv, "(*"+vt+")(<nil>)\n") + + // Standard complex128. + v2 := complex(float64(-6), 2) + nv2 := (*complex128)(nil) + pv2 := &v2 + v2Addr := fmt.Sprintf("%p", pv2) + pv2Addr := fmt.Sprintf("%p", &pv2) + v2t := "complex128" + v2s := "(-6+2i)" + addDumpTest(v2, "("+v2t+") "+v2s+"\n") + addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") + addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") + addDumpTest(nv2, "(*"+v2t+")(<nil>)\n") +} + +func addArrayDumpTests() { + // Array containing standard ints. + v := [3]int{1, 2, 3} + vLen := fmt.Sprintf("%d", len(v)) + vCap := fmt.Sprintf("%d", cap(v)) + nv := (*[3]int)(nil) + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "int" + vs := "(len=" + vLen + " cap=" + vCap + ") {\n (" + vt + ") 1,\n (" + + vt + ") 2,\n (" + vt + ") 3\n}" + addDumpTest(v, "([3]"+vt+") "+vs+"\n") + addDumpTest(pv, "(*[3]"+vt+")("+vAddr+")("+vs+")\n") + addDumpTest(&pv, "(**[3]"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + addDumpTest(nv, "(*[3]"+vt+")(<nil>)\n") + + // Array containing type with custom formatter on pointer receiver only. + v2i0 := pstringer("1") + v2i1 := pstringer("2") + v2i2 := pstringer("3") + v2 := [3]pstringer{v2i0, v2i1, v2i2} + v2i0Len := fmt.Sprintf("%d", len(v2i0)) + v2i1Len := fmt.Sprintf("%d", len(v2i1)) + v2i2Len := fmt.Sprintf("%d", len(v2i2)) + v2Len := fmt.Sprintf("%d", len(v2)) + v2Cap := fmt.Sprintf("%d", cap(v2)) + nv2 := (*[3]pstringer)(nil) + pv2 := &v2 + v2Addr := fmt.Sprintf("%p", pv2) + pv2Addr := fmt.Sprintf("%p", &pv2) + v2t := "spew_test.pstringer" + v2sp := "(len=" + v2Len + " cap=" + v2Cap + ") {\n (" + v2t + + ") (len=" + v2i0Len + ") stringer 1,\n (" + v2t + + ") (len=" + v2i1Len + ") stringer 2,\n (" + v2t + + ") (len=" + v2i2Len + ") " + "stringer 3\n}" + v2s := v2sp + if spew.UnsafeDisabled { + v2s = "(len=" + v2Len + " cap=" + v2Cap + ") {\n (" + v2t + + ") (len=" + v2i0Len + ") \"1\",\n (" + v2t + ") (len=" + + v2i1Len + ") \"2\",\n (" + v2t + ") (len=" + v2i2Len + + ") " + "\"3\"\n}" + } + addDumpTest(v2, "([3]"+v2t+") "+v2s+"\n") + addDumpTest(pv2, "(*[3]"+v2t+")("+v2Addr+")("+v2sp+")\n") + addDumpTest(&pv2, "(**[3]"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2sp+")\n") + addDumpTest(nv2, "(*[3]"+v2t+")(<nil>)\n") + + // Array containing interfaces. + v3i0 := "one" + v3 := [3]interface{}{v3i0, int(2), uint(3)} + v3i0Len := fmt.Sprintf("%d", len(v3i0)) + v3Len := fmt.Sprintf("%d", len(v3)) + v3Cap := fmt.Sprintf("%d", cap(v3)) + nv3 := (*[3]interface{})(nil) + pv3 := &v3 + v3Addr := fmt.Sprintf("%p", pv3) + pv3Addr := fmt.Sprintf("%p", &pv3) + v3t := "[3]interface {}" + v3t2 := "string" + v3t3 := "int" + v3t4 := "uint" + v3s := "(len=" + v3Len + " cap=" + v3Cap + ") {\n (" + v3t2 + ") " + + "(len=" + v3i0Len + ") \"one\",\n (" + v3t3 + ") 2,\n (" + + v3t4 + ") 3\n}" + addDumpTest(v3, "("+v3t+") "+v3s+"\n") + addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n") + addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n") + addDumpTest(nv3, "(*"+v3t+")(<nil>)\n") + + // Array containing bytes. + v4 := [34]byte{ + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, + 0x31, 0x32, + } + v4Len := fmt.Sprintf("%d", len(v4)) + v4Cap := fmt.Sprintf("%d", cap(v4)) + nv4 := (*[34]byte)(nil) + pv4 := &v4 + v4Addr := fmt.Sprintf("%p", pv4) + pv4Addr := fmt.Sprintf("%p", &pv4) + v4t := "[34]uint8" + v4s := "(len=" + v4Len + " cap=" + v4Cap + ") " + + "{\n 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20" + + " |............... |\n" + + " 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30" + + " |!\"#$%&'()*+,-./0|\n" + + " 00000020 31 32 " + + " |12|\n}" + addDumpTest(v4, "("+v4t+") "+v4s+"\n") + addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n") + addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n") + addDumpTest(nv4, "(*"+v4t+")(<nil>)\n") +} + +func addSliceDumpTests() { + // Slice containing standard float32 values. + v := []float32{3.14, 6.28, 12.56} + vLen := fmt.Sprintf("%d", len(v)) + vCap := fmt.Sprintf("%d", cap(v)) + nv := (*[]float32)(nil) + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "float32" + vs := "(len=" + vLen + " cap=" + vCap + ") {\n (" + vt + ") 3.14,\n (" + + vt + ") 6.28,\n (" + vt + ") 12.56\n}" + addDumpTest(v, "([]"+vt+") "+vs+"\n") + addDumpTest(pv, "(*[]"+vt+")("+vAddr+")("+vs+")\n") + addDumpTest(&pv, "(**[]"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + addDumpTest(nv, "(*[]"+vt+")(<nil>)\n") + + // Slice containing type with custom formatter on pointer receiver only. + v2i0 := pstringer("1") + v2i1 := pstringer("2") + v2i2 := pstringer("3") + v2 := []pstringer{v2i0, v2i1, v2i2} + v2i0Len := fmt.Sprintf("%d", len(v2i0)) + v2i1Len := fmt.Sprintf("%d", len(v2i1)) + v2i2Len := fmt.Sprintf("%d", len(v2i2)) + v2Len := fmt.Sprintf("%d", len(v2)) + v2Cap := fmt.Sprintf("%d", cap(v2)) + nv2 := (*[]pstringer)(nil) + pv2 := &v2 + v2Addr := fmt.Sprintf("%p", pv2) + pv2Addr := fmt.Sprintf("%p", &pv2) + v2t := "spew_test.pstringer" + v2s := "(len=" + v2Len + " cap=" + v2Cap + ") {\n (" + v2t + ") (len=" + + v2i0Len + ") stringer 1,\n (" + v2t + ") (len=" + v2i1Len + + ") stringer 2,\n (" + v2t + ") (len=" + v2i2Len + ") " + + "stringer 3\n}" + addDumpTest(v2, "([]"+v2t+") "+v2s+"\n") + addDumpTest(pv2, "(*[]"+v2t+")("+v2Addr+")("+v2s+")\n") + addDumpTest(&pv2, "(**[]"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") + addDumpTest(nv2, "(*[]"+v2t+")(<nil>)\n") + + // Slice containing interfaces. + v3i0 := "one" + v3 := []interface{}{v3i0, int(2), uint(3), nil} + v3i0Len := fmt.Sprintf("%d", len(v3i0)) + v3Len := fmt.Sprintf("%d", len(v3)) + v3Cap := fmt.Sprintf("%d", cap(v3)) + nv3 := (*[]interface{})(nil) + pv3 := &v3 + v3Addr := fmt.Sprintf("%p", pv3) + pv3Addr := fmt.Sprintf("%p", &pv3) + v3t := "[]interface {}" + v3t2 := "string" + v3t3 := "int" + v3t4 := "uint" + v3t5 := "interface {}" + v3s := "(len=" + v3Len + " cap=" + v3Cap + ") {\n (" + v3t2 + ") " + + "(len=" + v3i0Len + ") \"one\",\n (" + v3t3 + ") 2,\n (" + + v3t4 + ") 3,\n (" + v3t5 + ") <nil>\n}" + addDumpTest(v3, "("+v3t+") "+v3s+"\n") + addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n") + addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n") + addDumpTest(nv3, "(*"+v3t+")(<nil>)\n") + + // Slice containing bytes. + v4 := []byte{ + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, + 0x31, 0x32, + } + v4Len := fmt.Sprintf("%d", len(v4)) + v4Cap := fmt.Sprintf("%d", cap(v4)) + nv4 := (*[]byte)(nil) + pv4 := &v4 + v4Addr := fmt.Sprintf("%p", pv4) + pv4Addr := fmt.Sprintf("%p", &pv4) + v4t := "[]uint8" + v4s := "(len=" + v4Len + " cap=" + v4Cap + ") " + + "{\n 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20" + + " |............... |\n" + + " 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30" + + " |!\"#$%&'()*+,-./0|\n" + + " 00000020 31 32 " + + " |12|\n}" + addDumpTest(v4, "("+v4t+") "+v4s+"\n") + addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n") + addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n") + addDumpTest(nv4, "(*"+v4t+")(<nil>)\n") + + // Nil slice. + v5 := []int(nil) + nv5 := (*[]int)(nil) + pv5 := &v5 + v5Addr := fmt.Sprintf("%p", pv5) + pv5Addr := fmt.Sprintf("%p", &pv5) + v5t := "[]int" + v5s := "<nil>" + addDumpTest(v5, "("+v5t+") "+v5s+"\n") + addDumpTest(pv5, "(*"+v5t+")("+v5Addr+")("+v5s+")\n") + addDumpTest(&pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")("+v5s+")\n") + addDumpTest(nv5, "(*"+v5t+")(<nil>)\n") +} + +func addStringDumpTests() { + // Standard string. + v := "test" + vLen := fmt.Sprintf("%d", len(v)) + nv := (*string)(nil) + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "string" + vs := "(len=" + vLen + ") \"test\"" + addDumpTest(v, "("+vt+") "+vs+"\n") + addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") + addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + addDumpTest(nv, "(*"+vt+")(<nil>)\n") +} + +func addInterfaceDumpTests() { + // Nil interface. + var v interface{} + nv := (*interface{})(nil) + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "interface {}" + vs := "<nil>" + addDumpTest(v, "("+vt+") "+vs+"\n") + addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") + addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + addDumpTest(nv, "(*"+vt+")(<nil>)\n") + + // Sub-interface. + v2 := interface{}(uint16(65535)) + pv2 := &v2 + v2Addr := fmt.Sprintf("%p", pv2) + pv2Addr := fmt.Sprintf("%p", &pv2) + v2t := "uint16" + v2s := "65535" + addDumpTest(v2, "("+v2t+") "+v2s+"\n") + addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") + addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") +} + +func addMapDumpTests() { + // Map with string keys and int vals. + k := "one" + kk := "two" + m := map[string]int{k: 1, kk: 2} + klen := fmt.Sprintf("%d", len(k)) // not kLen to shut golint up + kkLen := fmt.Sprintf("%d", len(kk)) + mLen := fmt.Sprintf("%d", len(m)) + nilMap := map[string]int(nil) + nm := (*map[string]int)(nil) + pm := &m + mAddr := fmt.Sprintf("%p", pm) + pmAddr := fmt.Sprintf("%p", &pm) + mt := "map[string]int" + mt1 := "string" + mt2 := "int" + ms := "(len=" + mLen + ") {\n (" + mt1 + ") (len=" + klen + ") " + + "\"one\": (" + mt2 + ") 1,\n (" + mt1 + ") (len=" + kkLen + + ") \"two\": (" + mt2 + ") 2\n}" + ms2 := "(len=" + mLen + ") {\n (" + mt1 + ") (len=" + kkLen + ") " + + "\"two\": (" + mt2 + ") 2,\n (" + mt1 + ") (len=" + klen + + ") \"one\": (" + mt2 + ") 1\n}" + addDumpTest(m, "("+mt+") "+ms+"\n", "("+mt+") "+ms2+"\n") + addDumpTest(pm, "(*"+mt+")("+mAddr+")("+ms+")\n", + "(*"+mt+")("+mAddr+")("+ms2+")\n") + addDumpTest(&pm, "(**"+mt+")("+pmAddr+"->"+mAddr+")("+ms+")\n", + "(**"+mt+")("+pmAddr+"->"+mAddr+")("+ms2+")\n") + addDumpTest(nm, "(*"+mt+")(<nil>)\n") + addDumpTest(nilMap, "("+mt+") <nil>\n") + + // Map with custom formatter type on pointer receiver only keys and vals. + k2 := pstringer("one") + v2 := pstringer("1") + m2 := map[pstringer]pstringer{k2: v2} + k2Len := fmt.Sprintf("%d", len(k2)) + v2Len := fmt.Sprintf("%d", len(v2)) + m2Len := fmt.Sprintf("%d", len(m2)) + nilMap2 := map[pstringer]pstringer(nil) + nm2 := (*map[pstringer]pstringer)(nil) + pm2 := &m2 + m2Addr := fmt.Sprintf("%p", pm2) + pm2Addr := fmt.Sprintf("%p", &pm2) + m2t := "map[spew_test.pstringer]spew_test.pstringer" + m2t1 := "spew_test.pstringer" + m2t2 := "spew_test.pstringer" + m2s := "(len=" + m2Len + ") {\n (" + m2t1 + ") (len=" + k2Len + ") " + + "stringer one: (" + m2t2 + ") (len=" + v2Len + ") stringer 1\n}" + if spew.UnsafeDisabled { + m2s = "(len=" + m2Len + ") {\n (" + m2t1 + ") (len=" + k2Len + + ") " + "\"one\": (" + m2t2 + ") (len=" + v2Len + + ") \"1\"\n}" + } + addDumpTest(m2, "("+m2t+") "+m2s+"\n") + addDumpTest(pm2, "(*"+m2t+")("+m2Addr+")("+m2s+")\n") + addDumpTest(&pm2, "(**"+m2t+")("+pm2Addr+"->"+m2Addr+")("+m2s+")\n") + addDumpTest(nm2, "(*"+m2t+")(<nil>)\n") + addDumpTest(nilMap2, "("+m2t+") <nil>\n") + + // Map with interface keys and values. + k3 := "one" + k3Len := fmt.Sprintf("%d", len(k3)) + m3 := map[interface{}]interface{}{k3: 1} + m3Len := fmt.Sprintf("%d", len(m3)) + nilMap3 := map[interface{}]interface{}(nil) + nm3 := (*map[interface{}]interface{})(nil) + pm3 := &m3 + m3Addr := fmt.Sprintf("%p", pm3) + pm3Addr := fmt.Sprintf("%p", &pm3) + m3t := "map[interface {}]interface {}" + m3t1 := "string" + m3t2 := "int" + m3s := "(len=" + m3Len + ") {\n (" + m3t1 + ") (len=" + k3Len + ") " + + "\"one\": (" + m3t2 + ") 1\n}" + addDumpTest(m3, "("+m3t+") "+m3s+"\n") + addDumpTest(pm3, "(*"+m3t+")("+m3Addr+")("+m3s+")\n") + addDumpTest(&pm3, "(**"+m3t+")("+pm3Addr+"->"+m3Addr+")("+m3s+")\n") + addDumpTest(nm3, "(*"+m3t+")(<nil>)\n") + addDumpTest(nilMap3, "("+m3t+") <nil>\n") + + // Map with nil interface value. + k4 := "nil" + k4Len := fmt.Sprintf("%d", len(k4)) + m4 := map[string]interface{}{k4: nil} + m4Len := fmt.Sprintf("%d", len(m4)) + nilMap4 := map[string]interface{}(nil) + nm4 := (*map[string]interface{})(nil) + pm4 := &m4 + m4Addr := fmt.Sprintf("%p", pm4) + pm4Addr := fmt.Sprintf("%p", &pm4) + m4t := "map[string]interface {}" + m4t1 := "string" + m4t2 := "interface {}" + m4s := "(len=" + m4Len + ") {\n (" + m4t1 + ") (len=" + k4Len + ")" + + " \"nil\": (" + m4t2 + ") <nil>\n}" + addDumpTest(m4, "("+m4t+") "+m4s+"\n") + addDumpTest(pm4, "(*"+m4t+")("+m4Addr+")("+m4s+")\n") + addDumpTest(&pm4, "(**"+m4t+")("+pm4Addr+"->"+m4Addr+")("+m4s+")\n") + addDumpTest(nm4, "(*"+m4t+")(<nil>)\n") + addDumpTest(nilMap4, "("+m4t+") <nil>\n") +} + +func addStructDumpTests() { + // Struct with primitives. + type s1 struct { + a int8 + b uint8 + } + v := s1{127, 255} + nv := (*s1)(nil) + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "spew_test.s1" + vt2 := "int8" + vt3 := "uint8" + vs := "{\n a: (" + vt2 + ") 127,\n b: (" + vt3 + ") 255\n}" + addDumpTest(v, "("+vt+") "+vs+"\n") + addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") + addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + addDumpTest(nv, "(*"+vt+")(<nil>)\n") + + // Struct that contains another struct. + type s2 struct { + s1 s1 + b bool + } + v2 := s2{s1{127, 255}, true} + nv2 := (*s2)(nil) + pv2 := &v2 + v2Addr := fmt.Sprintf("%p", pv2) + pv2Addr := fmt.Sprintf("%p", &pv2) + v2t := "spew_test.s2" + v2t2 := "spew_test.s1" + v2t3 := "int8" + v2t4 := "uint8" + v2t5 := "bool" + v2s := "{\n s1: (" + v2t2 + ") {\n a: (" + v2t3 + ") 127,\n b: (" + + v2t4 + ") 255\n },\n b: (" + v2t5 + ") true\n}" + addDumpTest(v2, "("+v2t+") "+v2s+"\n") + addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") + addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") + addDumpTest(nv2, "(*"+v2t+")(<nil>)\n") + + // Struct that contains custom type with Stringer pointer interface via both + // exported and unexported fields. + type s3 struct { + s pstringer + S pstringer + } + v3 := s3{"test", "test2"} + nv3 := (*s3)(nil) + pv3 := &v3 + v3Addr := fmt.Sprintf("%p", pv3) + pv3Addr := fmt.Sprintf("%p", &pv3) + v3t := "spew_test.s3" + v3t2 := "spew_test.pstringer" + v3s := "{\n s: (" + v3t2 + ") (len=4) stringer test,\n S: (" + v3t2 + + ") (len=5) stringer test2\n}" + v3sp := v3s + if spew.UnsafeDisabled { + v3s = "{\n s: (" + v3t2 + ") (len=4) \"test\",\n S: (" + + v3t2 + ") (len=5) \"test2\"\n}" + v3sp = "{\n s: (" + v3t2 + ") (len=4) \"test\",\n S: (" + + v3t2 + ") (len=5) stringer test2\n}" + } + addDumpTest(v3, "("+v3t+") "+v3s+"\n") + addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3sp+")\n") + addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3sp+")\n") + addDumpTest(nv3, "(*"+v3t+")(<nil>)\n") + + // Struct that contains embedded struct and field to same struct. + e := embed{"embedstr"} + eLen := fmt.Sprintf("%d", len("embedstr")) + v4 := embedwrap{embed: &e, e: &e} + nv4 := (*embedwrap)(nil) + pv4 := &v4 + eAddr := fmt.Sprintf("%p", &e) + v4Addr := fmt.Sprintf("%p", pv4) + pv4Addr := fmt.Sprintf("%p", &pv4) + v4t := "spew_test.embedwrap" + v4t2 := "spew_test.embed" + v4t3 := "string" + v4s := "{\n embed: (*" + v4t2 + ")(" + eAddr + ")({\n a: (" + v4t3 + + ") (len=" + eLen + ") \"embedstr\"\n }),\n e: (*" + v4t2 + + ")(" + eAddr + ")({\n a: (" + v4t3 + ") (len=" + eLen + ")" + + " \"embedstr\"\n })\n}" + addDumpTest(v4, "("+v4t+") "+v4s+"\n") + addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n") + addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n") + addDumpTest(nv4, "(*"+v4t+")(<nil>)\n") +} + +func addUintptrDumpTests() { + // Null pointer. + v := uintptr(0) + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "uintptr" + vs := "<nil>" + addDumpTest(v, "("+vt+") "+vs+"\n") + addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") + addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + + // Address of real variable. + i := 1 + v2 := uintptr(unsafe.Pointer(&i)) + nv2 := (*uintptr)(nil) + pv2 := &v2 + v2Addr := fmt.Sprintf("%p", pv2) + pv2Addr := fmt.Sprintf("%p", &pv2) + v2t := "uintptr" + v2s := fmt.Sprintf("%p", &i) + addDumpTest(v2, "("+v2t+") "+v2s+"\n") + addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") + addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") + addDumpTest(nv2, "(*"+v2t+")(<nil>)\n") +} + +func addUnsafePointerDumpTests() { + // Null pointer. + v := unsafe.Pointer(nil) + nv := (*unsafe.Pointer)(nil) + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "unsafe.Pointer" + vs := "<nil>" + addDumpTest(v, "("+vt+") "+vs+"\n") + addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") + addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + addDumpTest(nv, "(*"+vt+")(<nil>)\n") + + // Address of real variable. + i := 1 + v2 := unsafe.Pointer(&i) + pv2 := &v2 + v2Addr := fmt.Sprintf("%p", pv2) + pv2Addr := fmt.Sprintf("%p", &pv2) + v2t := "unsafe.Pointer" + v2s := fmt.Sprintf("%p", &i) + addDumpTest(v2, "("+v2t+") "+v2s+"\n") + addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") + addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") + addDumpTest(nv, "(*"+vt+")(<nil>)\n") +} + +func addChanDumpTests() { + // Nil channel. + var v chan int + pv := &v + nv := (*chan int)(nil) + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "chan int" + vs := "<nil>" + addDumpTest(v, "("+vt+") "+vs+"\n") + addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") + addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + addDumpTest(nv, "(*"+vt+")(<nil>)\n") + + // Real channel. + v2 := make(chan int) + pv2 := &v2 + v2Addr := fmt.Sprintf("%p", pv2) + pv2Addr := fmt.Sprintf("%p", &pv2) + v2t := "chan int" + v2s := fmt.Sprintf("%p", v2) + addDumpTest(v2, "("+v2t+") "+v2s+"\n") + addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") + addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") +} + +func addFuncDumpTests() { + // Function with no params and no returns. + v := addIntDumpTests + nv := (*func())(nil) + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "func()" + vs := fmt.Sprintf("%p", v) + addDumpTest(v, "("+vt+") "+vs+"\n") + addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") + addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + addDumpTest(nv, "(*"+vt+")(<nil>)\n") + + // Function with param and no returns. + v2 := TestDump + nv2 := (*func(*testing.T))(nil) + pv2 := &v2 + v2Addr := fmt.Sprintf("%p", pv2) + pv2Addr := fmt.Sprintf("%p", &pv2) + v2t := "func(*testing.T)" + v2s := fmt.Sprintf("%p", v2) + addDumpTest(v2, "("+v2t+") "+v2s+"\n") + addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") + addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") + addDumpTest(nv2, "(*"+v2t+")(<nil>)\n") + + // Function with multiple params and multiple returns. + var v3 = func(i int, s string) (b bool, err error) { + return true, nil + } + nv3 := (*func(int, string) (bool, error))(nil) + pv3 := &v3 + v3Addr := fmt.Sprintf("%p", pv3) + pv3Addr := fmt.Sprintf("%p", &pv3) + v3t := "func(int, string) (bool, error)" + v3s := fmt.Sprintf("%p", v3) + addDumpTest(v3, "("+v3t+") "+v3s+"\n") + addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n") + addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n") + addDumpTest(nv3, "(*"+v3t+")(<nil>)\n") +} + +func addCircularDumpTests() { + // Struct that is circular through self referencing. + type circular struct { + c *circular + } + v := circular{nil} + v.c = &v + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "spew_test.circular" + vs := "{\n c: (*" + vt + ")(" + vAddr + ")({\n c: (*" + vt + ")(" + + vAddr + ")(<already shown>)\n })\n}" + vs2 := "{\n c: (*" + vt + ")(" + vAddr + ")(<already shown>)\n}" + addDumpTest(v, "("+vt+") "+vs+"\n") + addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs2+")\n") + addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs2+")\n") + + // Structs that are circular through cross referencing. + v2 := xref1{nil} + ts2 := xref2{&v2} + v2.ps2 = &ts2 + pv2 := &v2 + ts2Addr := fmt.Sprintf("%p", &ts2) + v2Addr := fmt.Sprintf("%p", pv2) + pv2Addr := fmt.Sprintf("%p", &pv2) + v2t := "spew_test.xref1" + v2t2 := "spew_test.xref2" + v2s := "{\n ps2: (*" + v2t2 + ")(" + ts2Addr + ")({\n ps1: (*" + v2t + + ")(" + v2Addr + ")({\n ps2: (*" + v2t2 + ")(" + ts2Addr + + ")(<already shown>)\n })\n })\n}" + v2s2 := "{\n ps2: (*" + v2t2 + ")(" + ts2Addr + ")({\n ps1: (*" + v2t + + ")(" + v2Addr + ")(<already shown>)\n })\n}" + addDumpTest(v2, "("+v2t+") "+v2s+"\n") + addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s2+")\n") + addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s2+")\n") + + // Structs that are indirectly circular. + v3 := indirCir1{nil} + tic2 := indirCir2{nil} + tic3 := indirCir3{&v3} + tic2.ps3 = &tic3 + v3.ps2 = &tic2 + pv3 := &v3 + tic2Addr := fmt.Sprintf("%p", &tic2) + tic3Addr := fmt.Sprintf("%p", &tic3) + v3Addr := fmt.Sprintf("%p", pv3) + pv3Addr := fmt.Sprintf("%p", &pv3) + v3t := "spew_test.indirCir1" + v3t2 := "spew_test.indirCir2" + v3t3 := "spew_test.indirCir3" + v3s := "{\n ps2: (*" + v3t2 + ")(" + tic2Addr + ")({\n ps3: (*" + v3t3 + + ")(" + tic3Addr + ")({\n ps1: (*" + v3t + ")(" + v3Addr + + ")({\n ps2: (*" + v3t2 + ")(" + tic2Addr + + ")(<already shown>)\n })\n })\n })\n}" + v3s2 := "{\n ps2: (*" + v3t2 + ")(" + tic2Addr + ")({\n ps3: (*" + v3t3 + + ")(" + tic3Addr + ")({\n ps1: (*" + v3t + ")(" + v3Addr + + ")(<already shown>)\n })\n })\n}" + addDumpTest(v3, "("+v3t+") "+v3s+"\n") + addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s2+")\n") + addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s2+")\n") +} + +func addPanicDumpTests() { + // Type that panics in its Stringer interface. + v := panicer(127) + nv := (*panicer)(nil) + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "spew_test.panicer" + vs := "(PANIC=test panic)127" + addDumpTest(v, "("+vt+") "+vs+"\n") + addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") + addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + addDumpTest(nv, "(*"+vt+")(<nil>)\n") +} + +func addErrorDumpTests() { + // Type that has a custom Error interface. + v := customError(127) + nv := (*customError)(nil) + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "spew_test.customError" + vs := "error: 127" + addDumpTest(v, "("+vt+") "+vs+"\n") + addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") + addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + addDumpTest(nv, "(*"+vt+")(<nil>)\n") +} + +// TestDump executes all of the tests described by dumpTests. +func TestDump(t *testing.T) { + // Setup tests. + addIntDumpTests() + addUintDumpTests() + addBoolDumpTests() + addFloatDumpTests() + addComplexDumpTests() + addArrayDumpTests() + addSliceDumpTests() + addStringDumpTests() + addInterfaceDumpTests() + addMapDumpTests() + addStructDumpTests() + addUintptrDumpTests() + addUnsafePointerDumpTests() + addChanDumpTests() + addFuncDumpTests() + addCircularDumpTests() + addPanicDumpTests() + addErrorDumpTests() + addCgoDumpTests() + + t.Logf("Running %d tests", len(dumpTests)) + for i, test := range dumpTests { + buf := new(bytes.Buffer) + spew.Fdump(buf, test.in) + s := buf.String() + if testFailed(s, test.wants) { + t.Errorf("Dump #%d\n got: %s %s", i, s, stringizeWants(test.wants)) + continue + } + } +} + +func TestDumpSortedKeys(t *testing.T) { + cfg := spew.ConfigState{SortKeys: true} + s := cfg.Sdump(map[int]string{1: "1", 3: "3", 2: "2"}) + expected := "(map[int]string) (len=3) {\n(int) 1: (string) (len=1) " + + "\"1\",\n(int) 2: (string) (len=1) \"2\",\n(int) 3: (string) " + + "(len=1) \"3\"\n" + + "}\n" + if s != expected { + t.Errorf("Sorted keys mismatch:\n %v %v", s, expected) + } + + s = cfg.Sdump(map[stringer]int{"1": 1, "3": 3, "2": 2}) + expected = "(map[spew_test.stringer]int) (len=3) {\n" + + "(spew_test.stringer) (len=1) stringer 1: (int) 1,\n" + + "(spew_test.stringer) (len=1) stringer 2: (int) 2,\n" + + "(spew_test.stringer) (len=1) stringer 3: (int) 3\n" + + "}\n" + if s != expected { + t.Errorf("Sorted keys mismatch:\n %v %v", s, expected) + } + + s = cfg.Sdump(map[pstringer]int{pstringer("1"): 1, pstringer("3"): 3, pstringer("2"): 2}) + expected = "(map[spew_test.pstringer]int) (len=3) {\n" + + "(spew_test.pstringer) (len=1) stringer 1: (int) 1,\n" + + "(spew_test.pstringer) (len=1) stringer 2: (int) 2,\n" + + "(spew_test.pstringer) (len=1) stringer 3: (int) 3\n" + + "}\n" + if spew.UnsafeDisabled { + expected = "(map[spew_test.pstringer]int) (len=3) {\n" + + "(spew_test.pstringer) (len=1) \"1\": (int) 1,\n" + + "(spew_test.pstringer) (len=1) \"2\": (int) 2,\n" + + "(spew_test.pstringer) (len=1) \"3\": (int) 3\n" + + "}\n" + } + if s != expected { + t.Errorf("Sorted keys mismatch:\n %v %v", s, expected) + } + + s = cfg.Sdump(map[customError]int{customError(1): 1, customError(3): 3, customError(2): 2}) + expected = "(map[spew_test.customError]int) (len=3) {\n" + + "(spew_test.customError) error: 1: (int) 1,\n" + + "(spew_test.customError) error: 2: (int) 2,\n" + + "(spew_test.customError) error: 3: (int) 3\n" + + "}\n" + if s != expected { + t.Errorf("Sorted keys mismatch:\n %v %v", s, expected) + } + +} diff --git a/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/dumpcgo_test.go b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/dumpcgo_test.go new file mode 100644 index 0000000..108baa5 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/dumpcgo_test.go @@ -0,0 +1,101 @@ +// Copyright (c) 2013-2016 Dave Collins <dave@davec.name> +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +// NOTE: Due to the following build constraints, this file will only be compiled +// when both cgo is supported and "-tags testcgo" is added to the go test +// command line. This means the cgo tests are only added (and hence run) when +// specifially requested. This configuration is used because spew itself +// does not require cgo to run even though it does handle certain cgo types +// specially. Rather than forcing all clients to require cgo and an external +// C compiler just to run the tests, this scheme makes them optional. +// +build cgo,testcgo + +package spew_test + +import ( + "fmt" + + "github.com/davecgh/go-spew/spew/testdata" +) + +func addCgoDumpTests() { + // C char pointer. + v := testdata.GetCgoCharPointer() + nv := testdata.GetCgoNullCharPointer() + pv := &v + vcAddr := fmt.Sprintf("%p", v) + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "*testdata._Ctype_char" + vs := "116" + addDumpTest(v, "("+vt+")("+vcAddr+")("+vs+")\n") + addDumpTest(pv, "(*"+vt+")("+vAddr+"->"+vcAddr+")("+vs+")\n") + addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+"->"+vcAddr+")("+vs+")\n") + addDumpTest(nv, "("+vt+")(<nil>)\n") + + // C char array. + v2, v2l, v2c := testdata.GetCgoCharArray() + v2Len := fmt.Sprintf("%d", v2l) + v2Cap := fmt.Sprintf("%d", v2c) + v2t := "[6]testdata._Ctype_char" + v2s := "(len=" + v2Len + " cap=" + v2Cap + ") " + + "{\n 00000000 74 65 73 74 32 00 " + + " |test2.|\n}" + addDumpTest(v2, "("+v2t+") "+v2s+"\n") + + // C unsigned char array. + v3, v3l, v3c := testdata.GetCgoUnsignedCharArray() + v3Len := fmt.Sprintf("%d", v3l) + v3Cap := fmt.Sprintf("%d", v3c) + v3t := "[6]testdata._Ctype_unsignedchar" + v3t2 := "[6]testdata._Ctype_uchar" + v3s := "(len=" + v3Len + " cap=" + v3Cap + ") " + + "{\n 00000000 74 65 73 74 33 00 " + + " |test3.|\n}" + addDumpTest(v3, "("+v3t+") "+v3s+"\n", "("+v3t2+") "+v3s+"\n") + + // C signed char array. + v4, v4l, v4c := testdata.GetCgoSignedCharArray() + v4Len := fmt.Sprintf("%d", v4l) + v4Cap := fmt.Sprintf("%d", v4c) + v4t := "[6]testdata._Ctype_schar" + v4t2 := "testdata._Ctype_schar" + v4s := "(len=" + v4Len + " cap=" + v4Cap + ") " + + "{\n (" + v4t2 + ") 116,\n (" + v4t2 + ") 101,\n (" + v4t2 + + ") 115,\n (" + v4t2 + ") 116,\n (" + v4t2 + ") 52,\n (" + v4t2 + + ") 0\n}" + addDumpTest(v4, "("+v4t+") "+v4s+"\n") + + // C uint8_t array. + v5, v5l, v5c := testdata.GetCgoUint8tArray() + v5Len := fmt.Sprintf("%d", v5l) + v5Cap := fmt.Sprintf("%d", v5c) + v5t := "[6]testdata._Ctype_uint8_t" + v5t2 := "[6]testdata._Ctype_uchar" + v5s := "(len=" + v5Len + " cap=" + v5Cap + ") " + + "{\n 00000000 74 65 73 74 35 00 " + + " |test5.|\n}" + addDumpTest(v5, "("+v5t+") "+v5s+"\n", "("+v5t2+") "+v5s+"\n") + + // C typedefed unsigned char array. + v6, v6l, v6c := testdata.GetCgoTypdefedUnsignedCharArray() + v6Len := fmt.Sprintf("%d", v6l) + v6Cap := fmt.Sprintf("%d", v6c) + v6t := "[6]testdata._Ctype_custom_uchar_t" + v6t2 := "[6]testdata._Ctype_uchar" + v6s := "(len=" + v6Len + " cap=" + v6Cap + ") " + + "{\n 00000000 74 65 73 74 36 00 " + + " |test6.|\n}" + addDumpTest(v6, "("+v6t+") "+v6s+"\n", "("+v6t2+") "+v6s+"\n") +} diff --git a/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/dumpnocgo_test.go b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/dumpnocgo_test.go new file mode 100644 index 0000000..52a0971 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/dumpnocgo_test.go @@ -0,0 +1,26 @@ +// Copyright (c) 2013 Dave Collins <dave@davec.name> +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +// NOTE: Due to the following build constraints, this file will only be compiled +// when either cgo is not supported or "-tags testcgo" is not added to the go +// test command line. This file intentionally does not setup any cgo tests in +// this scenario. +// +build !cgo !testcgo + +package spew_test + +func addCgoDumpTests() { + // Don't add any tests for cgo since this file is only compiled when + // there should not be any cgo tests. +} diff --git a/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/example_test.go b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/example_test.go new file mode 100644 index 0000000..c6ec8c6 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/example_test.go @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2013-2016 Dave Collins <dave@davec.name> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew_test + +import ( + "fmt" + + "github.com/davecgh/go-spew/spew" +) + +type Flag int + +const ( + flagOne Flag = iota + flagTwo +) + +var flagStrings = map[Flag]string{ + flagOne: "flagOne", + flagTwo: "flagTwo", +} + +func (f Flag) String() string { + if s, ok := flagStrings[f]; ok { + return s + } + return fmt.Sprintf("Unknown flag (%d)", int(f)) +} + +type Bar struct { + data uintptr +} + +type Foo struct { + unexportedField Bar + ExportedField map[interface{}]interface{} +} + +// This example demonstrates how to use Dump to dump variables to stdout. +func ExampleDump() { + // The following package level declarations are assumed for this example: + /* + type Flag int + + const ( + flagOne Flag = iota + flagTwo + ) + + var flagStrings = map[Flag]string{ + flagOne: "flagOne", + flagTwo: "flagTwo", + } + + func (f Flag) String() string { + if s, ok := flagStrings[f]; ok { + return s + } + return fmt.Sprintf("Unknown flag (%d)", int(f)) + } + + type Bar struct { + data uintptr + } + + type Foo struct { + unexportedField Bar + ExportedField map[interface{}]interface{} + } + */ + + // Setup some sample data structures for the example. + bar := Bar{uintptr(0)} + s1 := Foo{bar, map[interface{}]interface{}{"one": true}} + f := Flag(5) + b := []byte{ + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, + 0x31, 0x32, + } + + // Dump! + spew.Dump(s1, f, b) + + // Output: + // (spew_test.Foo) { + // unexportedField: (spew_test.Bar) { + // data: (uintptr) <nil> + // }, + // ExportedField: (map[interface {}]interface {}) (len=1) { + // (string) (len=3) "one": (bool) true + // } + // } + // (spew_test.Flag) Unknown flag (5) + // ([]uint8) (len=34 cap=34) { + // 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... | + // 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0| + // 00000020 31 32 |12| + // } + // +} + +// This example demonstrates how to use Printf to display a variable with a +// format string and inline formatting. +func ExamplePrintf() { + // Create a double pointer to a uint 8. + ui8 := uint8(5) + pui8 := &ui8 + ppui8 := &pui8 + + // Create a circular data type. + type circular struct { + ui8 uint8 + c *circular + } + c := circular{ui8: 1} + c.c = &c + + // Print! + spew.Printf("ppui8: %v\n", ppui8) + spew.Printf("circular: %v\n", c) + + // Output: + // ppui8: <**>5 + // circular: {1 <*>{1 <*><shown>}} +} + +// This example demonstrates how to use a ConfigState. +func ExampleConfigState() { + // Modify the indent level of the ConfigState only. The global + // configuration is not modified. + scs := spew.ConfigState{Indent: "\t"} + + // Output using the ConfigState instance. + v := map[string]int{"one": 1} + scs.Printf("v: %v\n", v) + scs.Dump(v) + + // Output: + // v: map[one:1] + // (map[string]int) (len=1) { + // (string) (len=3) "one": (int) 1 + // } +} + +// This example demonstrates how to use ConfigState.Dump to dump variables to +// stdout +func ExampleConfigState_Dump() { + // See the top-level Dump example for details on the types used in this + // example. + + // Create two ConfigState instances with different indentation. + scs := spew.ConfigState{Indent: "\t"} + scs2 := spew.ConfigState{Indent: " "} + + // Setup some sample data structures for the example. + bar := Bar{uintptr(0)} + s1 := Foo{bar, map[interface{}]interface{}{"one": true}} + + // Dump using the ConfigState instances. + scs.Dump(s1) + scs2.Dump(s1) + + // Output: + // (spew_test.Foo) { + // unexportedField: (spew_test.Bar) { + // data: (uintptr) <nil> + // }, + // ExportedField: (map[interface {}]interface {}) (len=1) { + // (string) (len=3) "one": (bool) true + // } + // } + // (spew_test.Foo) { + // unexportedField: (spew_test.Bar) { + // data: (uintptr) <nil> + // }, + // ExportedField: (map[interface {}]interface {}) (len=1) { + // (string) (len=3) "one": (bool) true + // } + // } + // +} + +// This example demonstrates how to use ConfigState.Printf to display a variable +// with a format string and inline formatting. +func ExampleConfigState_Printf() { + // See the top-level Dump example for details on the types used in this + // example. + + // Create two ConfigState instances and modify the method handling of the + // first ConfigState only. + scs := spew.NewDefaultConfig() + scs2 := spew.NewDefaultConfig() + scs.DisableMethods = true + + // Alternatively + // scs := spew.ConfigState{Indent: " ", DisableMethods: true} + // scs2 := spew.ConfigState{Indent: " "} + + // This is of type Flag which implements a Stringer and has raw value 1. + f := flagTwo + + // Dump using the ConfigState instances. + scs.Printf("f: %v\n", f) + scs2.Printf("f: %v\n", f) + + // Output: + // f: 1 + // f: flagTwo +} diff --git a/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/format.go b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/format.go new file mode 100644 index 0000000..b04edb7 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/format.go @@ -0,0 +1,419 @@ +/* + * Copyright (c) 2013-2016 Dave Collins <dave@davec.name> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "bytes" + "fmt" + "reflect" + "strconv" + "strings" +) + +// supportedFlags is a list of all the character flags supported by fmt package. +const supportedFlags = "0-+# " + +// formatState implements the fmt.Formatter interface and contains information +// about the state of a formatting operation. The NewFormatter function can +// be used to get a new Formatter which can be used directly as arguments +// in standard fmt package printing calls. +type formatState struct { + value interface{} + fs fmt.State + depth int + pointers map[uintptr]int + ignoreNextType bool + cs *ConfigState +} + +// buildDefaultFormat recreates the original format string without precision +// and width information to pass in to fmt.Sprintf in the case of an +// unrecognized type. Unless new types are added to the language, this +// function won't ever be called. +func (f *formatState) buildDefaultFormat() (format string) { + buf := bytes.NewBuffer(percentBytes) + + for _, flag := range supportedFlags { + if f.fs.Flag(int(flag)) { + buf.WriteRune(flag) + } + } + + buf.WriteRune('v') + + format = buf.String() + return format +} + +// constructOrigFormat recreates the original format string including precision +// and width information to pass along to the standard fmt package. This allows +// automatic deferral of all format strings this package doesn't support. +func (f *formatState) constructOrigFormat(verb rune) (format string) { + buf := bytes.NewBuffer(percentBytes) + + for _, flag := range supportedFlags { + if f.fs.Flag(int(flag)) { + buf.WriteRune(flag) + } + } + + if width, ok := f.fs.Width(); ok { + buf.WriteString(strconv.Itoa(width)) + } + + if precision, ok := f.fs.Precision(); ok { + buf.Write(precisionBytes) + buf.WriteString(strconv.Itoa(precision)) + } + + buf.WriteRune(verb) + + format = buf.String() + return format +} + +// unpackValue returns values inside of non-nil interfaces when possible and +// ensures that types for values which have been unpacked from an interface +// are displayed when the show types flag is also set. +// This is useful for data types like structs, arrays, slices, and maps which +// can contain varying types packed inside an interface. +func (f *formatState) unpackValue(v reflect.Value) reflect.Value { + if v.Kind() == reflect.Interface { + f.ignoreNextType = false + if !v.IsNil() { + v = v.Elem() + } + } + return v +} + +// formatPtr handles formatting of pointers by indirecting them as necessary. +func (f *formatState) formatPtr(v reflect.Value) { + // Display nil if top level pointer is nil. + showTypes := f.fs.Flag('#') + if v.IsNil() && (!showTypes || f.ignoreNextType) { + f.fs.Write(nilAngleBytes) + return + } + + // Remove pointers at or below the current depth from map used to detect + // circular refs. + for k, depth := range f.pointers { + if depth >= f.depth { + delete(f.pointers, k) + } + } + + // Keep list of all dereferenced pointers to possibly show later. + pointerChain := make([]uintptr, 0) + + // Figure out how many levels of indirection there are by derferencing + // pointers and unpacking interfaces down the chain while detecting circular + // references. + nilFound := false + cycleFound := false + indirects := 0 + ve := v + for ve.Kind() == reflect.Ptr { + if ve.IsNil() { + nilFound = true + break + } + indirects++ + addr := ve.Pointer() + pointerChain = append(pointerChain, addr) + if pd, ok := f.pointers[addr]; ok && pd < f.depth { + cycleFound = true + indirects-- + break + } + f.pointers[addr] = f.depth + + ve = ve.Elem() + if ve.Kind() == reflect.Interface { + if ve.IsNil() { + nilFound = true + break + } + ve = ve.Elem() + } + } + + // Display type or indirection level depending on flags. + if showTypes && !f.ignoreNextType { + f.fs.Write(openParenBytes) + f.fs.Write(bytes.Repeat(asteriskBytes, indirects)) + f.fs.Write([]byte(ve.Type().String())) + f.fs.Write(closeParenBytes) + } else { + if nilFound || cycleFound { + indirects += strings.Count(ve.Type().String(), "*") + } + f.fs.Write(openAngleBytes) + f.fs.Write([]byte(strings.Repeat("*", indirects))) + f.fs.Write(closeAngleBytes) + } + + // Display pointer information depending on flags. + if f.fs.Flag('+') && (len(pointerChain) > 0) { + f.fs.Write(openParenBytes) + for i, addr := range pointerChain { + if i > 0 { + f.fs.Write(pointerChainBytes) + } + printHexPtr(f.fs, addr) + } + f.fs.Write(closeParenBytes) + } + + // Display dereferenced value. + switch { + case nilFound: + f.fs.Write(nilAngleBytes) + + case cycleFound: + f.fs.Write(circularShortBytes) + + default: + f.ignoreNextType = true + f.format(ve) + } +} + +// format is the main workhorse for providing the Formatter interface. It +// uses the passed reflect value to figure out what kind of object we are +// dealing with and formats it appropriately. It is a recursive function, +// however circular data structures are detected and handled properly. +func (f *formatState) format(v reflect.Value) { + // Handle invalid reflect values immediately. + kind := v.Kind() + if kind == reflect.Invalid { + f.fs.Write(invalidAngleBytes) + return + } + + // Handle pointers specially. + if kind == reflect.Ptr { + f.formatPtr(v) + return + } + + // Print type information unless already handled elsewhere. + if !f.ignoreNextType && f.fs.Flag('#') { + f.fs.Write(openParenBytes) + f.fs.Write([]byte(v.Type().String())) + f.fs.Write(closeParenBytes) + } + f.ignoreNextType = false + + // Call Stringer/error interfaces if they exist and the handle methods + // flag is enabled. + if !f.cs.DisableMethods { + if (kind != reflect.Invalid) && (kind != reflect.Interface) { + if handled := handleMethods(f.cs, f.fs, v); handled { + return + } + } + } + + switch kind { + case reflect.Invalid: + // Do nothing. We should never get here since invalid has already + // been handled above. + + case reflect.Bool: + printBool(f.fs, v.Bool()) + + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + printInt(f.fs, v.Int(), 10) + + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + printUint(f.fs, v.Uint(), 10) + + case reflect.Float32: + printFloat(f.fs, v.Float(), 32) + + case reflect.Float64: + printFloat(f.fs, v.Float(), 64) + + case reflect.Complex64: + printComplex(f.fs, v.Complex(), 32) + + case reflect.Complex128: + printComplex(f.fs, v.Complex(), 64) + + case reflect.Slice: + if v.IsNil() { + f.fs.Write(nilAngleBytes) + break + } + fallthrough + + case reflect.Array: + f.fs.Write(openBracketBytes) + f.depth++ + if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { + f.fs.Write(maxShortBytes) + } else { + numEntries := v.Len() + for i := 0; i < numEntries; i++ { + if i > 0 { + f.fs.Write(spaceBytes) + } + f.ignoreNextType = true + f.format(f.unpackValue(v.Index(i))) + } + } + f.depth-- + f.fs.Write(closeBracketBytes) + + case reflect.String: + f.fs.Write([]byte(v.String())) + + case reflect.Interface: + // The only time we should get here is for nil interfaces due to + // unpackValue calls. + if v.IsNil() { + f.fs.Write(nilAngleBytes) + } + + case reflect.Ptr: + // Do nothing. We should never get here since pointers have already + // been handled above. + + case reflect.Map: + // nil maps should be indicated as different than empty maps + if v.IsNil() { + f.fs.Write(nilAngleBytes) + break + } + + f.fs.Write(openMapBytes) + f.depth++ + if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { + f.fs.Write(maxShortBytes) + } else { + keys := v.MapKeys() + if f.cs.SortKeys { + sortValues(keys, f.cs) + } + for i, key := range keys { + if i > 0 { + f.fs.Write(spaceBytes) + } + f.ignoreNextType = true + f.format(f.unpackValue(key)) + f.fs.Write(colonBytes) + f.ignoreNextType = true + f.format(f.unpackValue(v.MapIndex(key))) + } + } + f.depth-- + f.fs.Write(closeMapBytes) + + case reflect.Struct: + numFields := v.NumField() + f.fs.Write(openBraceBytes) + f.depth++ + if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { + f.fs.Write(maxShortBytes) + } else { + vt := v.Type() + for i := 0; i < numFields; i++ { + if i > 0 { + f.fs.Write(spaceBytes) + } + vtf := vt.Field(i) + if f.fs.Flag('+') || f.fs.Flag('#') { + f.fs.Write([]byte(vtf.Name)) + f.fs.Write(colonBytes) + } + f.format(f.unpackValue(v.Field(i))) + } + } + f.depth-- + f.fs.Write(closeBraceBytes) + + case reflect.Uintptr: + printHexPtr(f.fs, uintptr(v.Uint())) + + case reflect.UnsafePointer, reflect.Chan, reflect.Func: + printHexPtr(f.fs, v.Pointer()) + + // There were not any other types at the time this code was written, but + // fall back to letting the default fmt package handle it if any get added. + default: + format := f.buildDefaultFormat() + if v.CanInterface() { + fmt.Fprintf(f.fs, format, v.Interface()) + } else { + fmt.Fprintf(f.fs, format, v.String()) + } + } +} + +// Format satisfies the fmt.Formatter interface. See NewFormatter for usage +// details. +func (f *formatState) Format(fs fmt.State, verb rune) { + f.fs = fs + + // Use standard formatting for verbs that are not v. + if verb != 'v' { + format := f.constructOrigFormat(verb) + fmt.Fprintf(fs, format, f.value) + return + } + + if f.value == nil { + if fs.Flag('#') { + fs.Write(interfaceBytes) + } + fs.Write(nilAngleBytes) + return + } + + f.format(reflect.ValueOf(f.value)) +} + +// newFormatter is a helper function to consolidate the logic from the various +// public methods which take varying config states. +func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter { + fs := &formatState{value: v, cs: cs} + fs.pointers = make(map[uintptr]int) + return fs +} + +/* +NewFormatter returns a custom formatter that satisfies the fmt.Formatter +interface. As a result, it integrates cleanly with standard fmt package +printing functions. The formatter is useful for inline printing of smaller data +types similar to the standard %v format specifier. + +The custom formatter only responds to the %v (most compact), %+v (adds pointer +addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb +combinations. Any other verbs such as %x and %q will be sent to the the +standard fmt package for formatting. In addition, the custom formatter ignores +the width and precision arguments (however they will still work on the format +specifiers not handled by the custom formatter). + +Typically this function shouldn't be called directly. It is much easier to make +use of the custom formatter by calling one of the convenience functions such as +Printf, Println, or Fprintf. +*/ +func NewFormatter(v interface{}) fmt.Formatter { + return newFormatter(&Config, v) +} diff --git a/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/format_test.go b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/format_test.go new file mode 100644 index 0000000..87ee965 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/format_test.go @@ -0,0 +1,1558 @@ +/* + * Copyright (c) 2013-2016 Dave Collins <dave@davec.name> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* +Test Summary: +NOTE: For each test, a nil pointer, a single pointer and double pointer to the +base test element are also tested to ensure proper indirection across all types. + +- Max int8, int16, int32, int64, int +- Max uint8, uint16, uint32, uint64, uint +- Boolean true and false +- Standard complex64 and complex128 +- Array containing standard ints +- Array containing type with custom formatter on pointer receiver only +- Array containing interfaces +- Slice containing standard float32 values +- Slice containing type with custom formatter on pointer receiver only +- Slice containing interfaces +- Nil slice +- Standard string +- Nil interface +- Sub-interface +- Map with string keys and int vals +- Map with custom formatter type on pointer receiver only keys and vals +- Map with interface keys and values +- Map with nil interface value +- Struct with primitives +- Struct that contains another struct +- Struct that contains custom type with Stringer pointer interface via both + exported and unexported fields +- Struct that contains embedded struct and field to same struct +- Uintptr to 0 (null pointer) +- Uintptr address of real variable +- Unsafe.Pointer to 0 (null pointer) +- Unsafe.Pointer to address of real variable +- Nil channel +- Standard int channel +- Function with no params and no returns +- Function with param and no returns +- Function with multiple params and multiple returns +- Struct that is circular through self referencing +- Structs that are circular through cross referencing +- Structs that are indirectly circular +- Type that panics in its Stringer interface +- Type that has a custom Error interface +- %x passthrough with uint +- %#x passthrough with uint +- %f passthrough with precision +- %f passthrough with width and precision +- %d passthrough with width +- %q passthrough with string +*/ + +package spew_test + +import ( + "bytes" + "fmt" + "testing" + "unsafe" + + "github.com/davecgh/go-spew/spew" +) + +// formatterTest is used to describe a test to be performed against NewFormatter. +type formatterTest struct { + format string + in interface{} + wants []string +} + +// formatterTests houses all of the tests to be performed against NewFormatter. +var formatterTests = make([]formatterTest, 0) + +// addFormatterTest is a helper method to append the passed input and desired +// result to formatterTests. +func addFormatterTest(format string, in interface{}, wants ...string) { + test := formatterTest{format, in, wants} + formatterTests = append(formatterTests, test) +} + +func addIntFormatterTests() { + // Max int8. + v := int8(127) + nv := (*int8)(nil) + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "int8" + vs := "127" + addFormatterTest("%v", v, vs) + addFormatterTest("%v", pv, "<*>"+vs) + addFormatterTest("%v", &pv, "<**>"+vs) + addFormatterTest("%v", nv, "<nil>") + addFormatterTest("%+v", v, vs) + addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) + addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) + addFormatterTest("%+v", nv, "<nil>") + addFormatterTest("%#v", v, "("+vt+")"+vs) + addFormatterTest("%#v", pv, "(*"+vt+")"+vs) + addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) + addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>") + addFormatterTest("%#+v", v, "("+vt+")"+vs) + addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) + addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) + addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>") + + // Max int16. + v2 := int16(32767) + nv2 := (*int16)(nil) + pv2 := &v2 + v2Addr := fmt.Sprintf("%p", pv2) + pv2Addr := fmt.Sprintf("%p", &pv2) + v2t := "int16" + v2s := "32767" + addFormatterTest("%v", v2, v2s) + addFormatterTest("%v", pv2, "<*>"+v2s) + addFormatterTest("%v", &pv2, "<**>"+v2s) + addFormatterTest("%v", nv2, "<nil>") + addFormatterTest("%+v", v2, v2s) + addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) + addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) + addFormatterTest("%+v", nv2, "<nil>") + addFormatterTest("%#v", v2, "("+v2t+")"+v2s) + addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) + addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) + addFormatterTest("%#v", nv2, "(*"+v2t+")"+"<nil>") + addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) + addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) + addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) + addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"<nil>") + + // Max int32. + v3 := int32(2147483647) + nv3 := (*int32)(nil) + pv3 := &v3 + v3Addr := fmt.Sprintf("%p", pv3) + pv3Addr := fmt.Sprintf("%p", &pv3) + v3t := "int32" + v3s := "2147483647" + addFormatterTest("%v", v3, v3s) + addFormatterTest("%v", pv3, "<*>"+v3s) + addFormatterTest("%v", &pv3, "<**>"+v3s) + addFormatterTest("%v", nv3, "<nil>") + addFormatterTest("%+v", v3, v3s) + addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s) + addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s) + addFormatterTest("%+v", nv3, "<nil>") + addFormatterTest("%#v", v3, "("+v3t+")"+v3s) + addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s) + addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s) + addFormatterTest("%#v", nv3, "(*"+v3t+")"+"<nil>") + addFormatterTest("%#+v", v3, "("+v3t+")"+v3s) + addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s) + addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s) + addFormatterTest("%#v", nv3, "(*"+v3t+")"+"<nil>") + + // Max int64. + v4 := int64(9223372036854775807) + nv4 := (*int64)(nil) + pv4 := &v4 + v4Addr := fmt.Sprintf("%p", pv4) + pv4Addr := fmt.Sprintf("%p", &pv4) + v4t := "int64" + v4s := "9223372036854775807" + addFormatterTest("%v", v4, v4s) + addFormatterTest("%v", pv4, "<*>"+v4s) + addFormatterTest("%v", &pv4, "<**>"+v4s) + addFormatterTest("%v", nv4, "<nil>") + addFormatterTest("%+v", v4, v4s) + addFormatterTest("%+v", pv4, "<*>("+v4Addr+")"+v4s) + addFormatterTest("%+v", &pv4, "<**>("+pv4Addr+"->"+v4Addr+")"+v4s) + addFormatterTest("%+v", nv4, "<nil>") + addFormatterTest("%#v", v4, "("+v4t+")"+v4s) + addFormatterTest("%#v", pv4, "(*"+v4t+")"+v4s) + addFormatterTest("%#v", &pv4, "(**"+v4t+")"+v4s) + addFormatterTest("%#v", nv4, "(*"+v4t+")"+"<nil>") + addFormatterTest("%#+v", v4, "("+v4t+")"+v4s) + addFormatterTest("%#+v", pv4, "(*"+v4t+")("+v4Addr+")"+v4s) + addFormatterTest("%#+v", &pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")"+v4s) + addFormatterTest("%#+v", nv4, "(*"+v4t+")"+"<nil>") + + // Max int. + v5 := int(2147483647) + nv5 := (*int)(nil) + pv5 := &v5 + v5Addr := fmt.Sprintf("%p", pv5) + pv5Addr := fmt.Sprintf("%p", &pv5) + v5t := "int" + v5s := "2147483647" + addFormatterTest("%v", v5, v5s) + addFormatterTest("%v", pv5, "<*>"+v5s) + addFormatterTest("%v", &pv5, "<**>"+v5s) + addFormatterTest("%v", nv5, "<nil>") + addFormatterTest("%+v", v5, v5s) + addFormatterTest("%+v", pv5, "<*>("+v5Addr+")"+v5s) + addFormatterTest("%+v", &pv5, "<**>("+pv5Addr+"->"+v5Addr+")"+v5s) + addFormatterTest("%+v", nv5, "<nil>") + addFormatterTest("%#v", v5, "("+v5t+")"+v5s) + addFormatterTest("%#v", pv5, "(*"+v5t+")"+v5s) + addFormatterTest("%#v", &pv5, "(**"+v5t+")"+v5s) + addFormatterTest("%#v", nv5, "(*"+v5t+")"+"<nil>") + addFormatterTest("%#+v", v5, "("+v5t+")"+v5s) + addFormatterTest("%#+v", pv5, "(*"+v5t+")("+v5Addr+")"+v5s) + addFormatterTest("%#+v", &pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")"+v5s) + addFormatterTest("%#+v", nv5, "(*"+v5t+")"+"<nil>") +} + +func addUintFormatterTests() { + // Max uint8. + v := uint8(255) + nv := (*uint8)(nil) + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "uint8" + vs := "255" + addFormatterTest("%v", v, vs) + addFormatterTest("%v", pv, "<*>"+vs) + addFormatterTest("%v", &pv, "<**>"+vs) + addFormatterTest("%v", nv, "<nil>") + addFormatterTest("%+v", v, vs) + addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) + addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) + addFormatterTest("%+v", nv, "<nil>") + addFormatterTest("%#v", v, "("+vt+")"+vs) + addFormatterTest("%#v", pv, "(*"+vt+")"+vs) + addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) + addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>") + addFormatterTest("%#+v", v, "("+vt+")"+vs) + addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) + addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) + addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>") + + // Max uint16. + v2 := uint16(65535) + nv2 := (*uint16)(nil) + pv2 := &v2 + v2Addr := fmt.Sprintf("%p", pv2) + pv2Addr := fmt.Sprintf("%p", &pv2) + v2t := "uint16" + v2s := "65535" + addFormatterTest("%v", v2, v2s) + addFormatterTest("%v", pv2, "<*>"+v2s) + addFormatterTest("%v", &pv2, "<**>"+v2s) + addFormatterTest("%v", nv2, "<nil>") + addFormatterTest("%+v", v2, v2s) + addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) + addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) + addFormatterTest("%+v", nv2, "<nil>") + addFormatterTest("%#v", v2, "("+v2t+")"+v2s) + addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) + addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) + addFormatterTest("%#v", nv2, "(*"+v2t+")"+"<nil>") + addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) + addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) + addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) + addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"<nil>") + + // Max uint32. + v3 := uint32(4294967295) + nv3 := (*uint32)(nil) + pv3 := &v3 + v3Addr := fmt.Sprintf("%p", pv3) + pv3Addr := fmt.Sprintf("%p", &pv3) + v3t := "uint32" + v3s := "4294967295" + addFormatterTest("%v", v3, v3s) + addFormatterTest("%v", pv3, "<*>"+v3s) + addFormatterTest("%v", &pv3, "<**>"+v3s) + addFormatterTest("%v", nv3, "<nil>") + addFormatterTest("%+v", v3, v3s) + addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s) + addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s) + addFormatterTest("%+v", nv3, "<nil>") + addFormatterTest("%#v", v3, "("+v3t+")"+v3s) + addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s) + addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s) + addFormatterTest("%#v", nv3, "(*"+v3t+")"+"<nil>") + addFormatterTest("%#+v", v3, "("+v3t+")"+v3s) + addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s) + addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s) + addFormatterTest("%#v", nv3, "(*"+v3t+")"+"<nil>") + + // Max uint64. + v4 := uint64(18446744073709551615) + nv4 := (*uint64)(nil) + pv4 := &v4 + v4Addr := fmt.Sprintf("%p", pv4) + pv4Addr := fmt.Sprintf("%p", &pv4) + v4t := "uint64" + v4s := "18446744073709551615" + addFormatterTest("%v", v4, v4s) + addFormatterTest("%v", pv4, "<*>"+v4s) + addFormatterTest("%v", &pv4, "<**>"+v4s) + addFormatterTest("%v", nv4, "<nil>") + addFormatterTest("%+v", v4, v4s) + addFormatterTest("%+v", pv4, "<*>("+v4Addr+")"+v4s) + addFormatterTest("%+v", &pv4, "<**>("+pv4Addr+"->"+v4Addr+")"+v4s) + addFormatterTest("%+v", nv4, "<nil>") + addFormatterTest("%#v", v4, "("+v4t+")"+v4s) + addFormatterTest("%#v", pv4, "(*"+v4t+")"+v4s) + addFormatterTest("%#v", &pv4, "(**"+v4t+")"+v4s) + addFormatterTest("%#v", nv4, "(*"+v4t+")"+"<nil>") + addFormatterTest("%#+v", v4, "("+v4t+")"+v4s) + addFormatterTest("%#+v", pv4, "(*"+v4t+")("+v4Addr+")"+v4s) + addFormatterTest("%#+v", &pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")"+v4s) + addFormatterTest("%#+v", nv4, "(*"+v4t+")"+"<nil>") + + // Max uint. + v5 := uint(4294967295) + nv5 := (*uint)(nil) + pv5 := &v5 + v5Addr := fmt.Sprintf("%p", pv5) + pv5Addr := fmt.Sprintf("%p", &pv5) + v5t := "uint" + v5s := "4294967295" + addFormatterTest("%v", v5, v5s) + addFormatterTest("%v", pv5, "<*>"+v5s) + addFormatterTest("%v", &pv5, "<**>"+v5s) + addFormatterTest("%v", nv5, "<nil>") + addFormatterTest("%+v", v5, v5s) + addFormatterTest("%+v", pv5, "<*>("+v5Addr+")"+v5s) + addFormatterTest("%+v", &pv5, "<**>("+pv5Addr+"->"+v5Addr+")"+v5s) + addFormatterTest("%+v", nv5, "<nil>") + addFormatterTest("%#v", v5, "("+v5t+")"+v5s) + addFormatterTest("%#v", pv5, "(*"+v5t+")"+v5s) + addFormatterTest("%#v", &pv5, "(**"+v5t+")"+v5s) + addFormatterTest("%#v", nv5, "(*"+v5t+")"+"<nil>") + addFormatterTest("%#+v", v5, "("+v5t+")"+v5s) + addFormatterTest("%#+v", pv5, "(*"+v5t+")("+v5Addr+")"+v5s) + addFormatterTest("%#+v", &pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")"+v5s) + addFormatterTest("%#v", nv5, "(*"+v5t+")"+"<nil>") +} + +func addBoolFormatterTests() { + // Boolean true. + v := bool(true) + nv := (*bool)(nil) + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "bool" + vs := "true" + addFormatterTest("%v", v, vs) + addFormatterTest("%v", pv, "<*>"+vs) + addFormatterTest("%v", &pv, "<**>"+vs) + addFormatterTest("%v", nv, "<nil>") + addFormatterTest("%+v", v, vs) + addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) + addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) + addFormatterTest("%+v", nv, "<nil>") + addFormatterTest("%#v", v, "("+vt+")"+vs) + addFormatterTest("%#v", pv, "(*"+vt+")"+vs) + addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) + addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>") + addFormatterTest("%#+v", v, "("+vt+")"+vs) + addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) + addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) + addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>") + + // Boolean false. + v2 := bool(false) + pv2 := &v2 + v2Addr := fmt.Sprintf("%p", pv2) + pv2Addr := fmt.Sprintf("%p", &pv2) + v2t := "bool" + v2s := "false" + addFormatterTest("%v", v2, v2s) + addFormatterTest("%v", pv2, "<*>"+v2s) + addFormatterTest("%v", &pv2, "<**>"+v2s) + addFormatterTest("%+v", v2, v2s) + addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) + addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) + addFormatterTest("%#v", v2, "("+v2t+")"+v2s) + addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) + addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) + addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) + addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) + addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) +} + +func addFloatFormatterTests() { + // Standard float32. + v := float32(3.1415) + nv := (*float32)(nil) + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "float32" + vs := "3.1415" + addFormatterTest("%v", v, vs) + addFormatterTest("%v", pv, "<*>"+vs) + addFormatterTest("%v", &pv, "<**>"+vs) + addFormatterTest("%v", nv, "<nil>") + addFormatterTest("%+v", v, vs) + addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) + addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) + addFormatterTest("%+v", nv, "<nil>") + addFormatterTest("%#v", v, "("+vt+")"+vs) + addFormatterTest("%#v", pv, "(*"+vt+")"+vs) + addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) + addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>") + addFormatterTest("%#+v", v, "("+vt+")"+vs) + addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) + addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) + addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>") + + // Standard float64. + v2 := float64(3.1415926) + nv2 := (*float64)(nil) + pv2 := &v2 + v2Addr := fmt.Sprintf("%p", pv2) + pv2Addr := fmt.Sprintf("%p", &pv2) + v2t := "float64" + v2s := "3.1415926" + addFormatterTest("%v", v2, v2s) + addFormatterTest("%v", pv2, "<*>"+v2s) + addFormatterTest("%v", &pv2, "<**>"+v2s) + addFormatterTest("%+v", nv2, "<nil>") + addFormatterTest("%+v", v2, v2s) + addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) + addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) + addFormatterTest("%+v", nv2, "<nil>") + addFormatterTest("%#v", v2, "("+v2t+")"+v2s) + addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) + addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) + addFormatterTest("%#v", nv2, "(*"+v2t+")"+"<nil>") + addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) + addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) + addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) + addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"<nil>") +} + +func addComplexFormatterTests() { + // Standard complex64. + v := complex(float32(6), -2) + nv := (*complex64)(nil) + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "complex64" + vs := "(6-2i)" + addFormatterTest("%v", v, vs) + addFormatterTest("%v", pv, "<*>"+vs) + addFormatterTest("%v", &pv, "<**>"+vs) + addFormatterTest("%+v", nv, "<nil>") + addFormatterTest("%+v", v, vs) + addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) + addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) + addFormatterTest("%+v", nv, "<nil>") + addFormatterTest("%#v", v, "("+vt+")"+vs) + addFormatterTest("%#v", pv, "(*"+vt+")"+vs) + addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) + addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>") + addFormatterTest("%#+v", v, "("+vt+")"+vs) + addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) + addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) + addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>") + + // Standard complex128. + v2 := complex(float64(-6), 2) + nv2 := (*complex128)(nil) + pv2 := &v2 + v2Addr := fmt.Sprintf("%p", pv2) + pv2Addr := fmt.Sprintf("%p", &pv2) + v2t := "complex128" + v2s := "(-6+2i)" + addFormatterTest("%v", v2, v2s) + addFormatterTest("%v", pv2, "<*>"+v2s) + addFormatterTest("%v", &pv2, "<**>"+v2s) + addFormatterTest("%+v", nv2, "<nil>") + addFormatterTest("%+v", v2, v2s) + addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) + addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) + addFormatterTest("%+v", nv2, "<nil>") + addFormatterTest("%#v", v2, "("+v2t+")"+v2s) + addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) + addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) + addFormatterTest("%#v", nv2, "(*"+v2t+")"+"<nil>") + addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) + addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) + addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) + addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"<nil>") +} + +func addArrayFormatterTests() { + // Array containing standard ints. + v := [3]int{1, 2, 3} + nv := (*[3]int)(nil) + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "[3]int" + vs := "[1 2 3]" + addFormatterTest("%v", v, vs) + addFormatterTest("%v", pv, "<*>"+vs) + addFormatterTest("%v", &pv, "<**>"+vs) + addFormatterTest("%+v", nv, "<nil>") + addFormatterTest("%+v", v, vs) + addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) + addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) + addFormatterTest("%+v", nv, "<nil>") + addFormatterTest("%#v", v, "("+vt+")"+vs) + addFormatterTest("%#v", pv, "(*"+vt+")"+vs) + addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) + addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>") + addFormatterTest("%#+v", v, "("+vt+")"+vs) + addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) + addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) + addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>") + + // Array containing type with custom formatter on pointer receiver only. + v2 := [3]pstringer{"1", "2", "3"} + nv2 := (*[3]pstringer)(nil) + pv2 := &v2 + v2Addr := fmt.Sprintf("%p", pv2) + pv2Addr := fmt.Sprintf("%p", &pv2) + v2t := "[3]spew_test.pstringer" + v2sp := "[stringer 1 stringer 2 stringer 3]" + v2s := v2sp + if spew.UnsafeDisabled { + v2s = "[1 2 3]" + } + addFormatterTest("%v", v2, v2s) + addFormatterTest("%v", pv2, "<*>"+v2sp) + addFormatterTest("%v", &pv2, "<**>"+v2sp) + addFormatterTest("%+v", nv2, "<nil>") + addFormatterTest("%+v", v2, v2s) + addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2sp) + addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2sp) + addFormatterTest("%+v", nv2, "<nil>") + addFormatterTest("%#v", v2, "("+v2t+")"+v2s) + addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2sp) + addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2sp) + addFormatterTest("%#v", nv2, "(*"+v2t+")"+"<nil>") + addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) + addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2sp) + addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2sp) + addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"<nil>") + + // Array containing interfaces. + v3 := [3]interface{}{"one", int(2), uint(3)} + nv3 := (*[3]interface{})(nil) + pv3 := &v3 + v3Addr := fmt.Sprintf("%p", pv3) + pv3Addr := fmt.Sprintf("%p", &pv3) + v3t := "[3]interface {}" + v3t2 := "string" + v3t3 := "int" + v3t4 := "uint" + v3s := "[one 2 3]" + v3s2 := "[(" + v3t2 + ")one (" + v3t3 + ")2 (" + v3t4 + ")3]" + addFormatterTest("%v", v3, v3s) + addFormatterTest("%v", pv3, "<*>"+v3s) + addFormatterTest("%v", &pv3, "<**>"+v3s) + addFormatterTest("%+v", nv3, "<nil>") + addFormatterTest("%+v", v3, v3s) + addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s) + addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s) + addFormatterTest("%+v", nv3, "<nil>") + addFormatterTest("%#v", v3, "("+v3t+")"+v3s2) + addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s2) + addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s2) + addFormatterTest("%#v", nv3, "(*"+v3t+")"+"<nil>") + addFormatterTest("%#+v", v3, "("+v3t+")"+v3s2) + addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s2) + addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s2) + addFormatterTest("%#+v", nv3, "(*"+v3t+")"+"<nil>") +} + +func addSliceFormatterTests() { + // Slice containing standard float32 values. + v := []float32{3.14, 6.28, 12.56} + nv := (*[]float32)(nil) + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "[]float32" + vs := "[3.14 6.28 12.56]" + addFormatterTest("%v", v, vs) + addFormatterTest("%v", pv, "<*>"+vs) + addFormatterTest("%v", &pv, "<**>"+vs) + addFormatterTest("%+v", nv, "<nil>") + addFormatterTest("%+v", v, vs) + addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) + addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) + addFormatterTest("%+v", nv, "<nil>") + addFormatterTest("%#v", v, "("+vt+")"+vs) + addFormatterTest("%#v", pv, "(*"+vt+")"+vs) + addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) + addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>") + addFormatterTest("%#+v", v, "("+vt+")"+vs) + addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) + addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) + addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>") + + // Slice containing type with custom formatter on pointer receiver only. + v2 := []pstringer{"1", "2", "3"} + nv2 := (*[]pstringer)(nil) + pv2 := &v2 + v2Addr := fmt.Sprintf("%p", pv2) + pv2Addr := fmt.Sprintf("%p", &pv2) + v2t := "[]spew_test.pstringer" + v2s := "[stringer 1 stringer 2 stringer 3]" + addFormatterTest("%v", v2, v2s) + addFormatterTest("%v", pv2, "<*>"+v2s) + addFormatterTest("%v", &pv2, "<**>"+v2s) + addFormatterTest("%+v", nv2, "<nil>") + addFormatterTest("%+v", v2, v2s) + addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) + addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) + addFormatterTest("%+v", nv2, "<nil>") + addFormatterTest("%#v", v2, "("+v2t+")"+v2s) + addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) + addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) + addFormatterTest("%#v", nv2, "(*"+v2t+")"+"<nil>") + addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) + addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) + addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) + addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"<nil>") + + // Slice containing interfaces. + v3 := []interface{}{"one", int(2), uint(3), nil} + nv3 := (*[]interface{})(nil) + pv3 := &v3 + v3Addr := fmt.Sprintf("%p", pv3) + pv3Addr := fmt.Sprintf("%p", &pv3) + v3t := "[]interface {}" + v3t2 := "string" + v3t3 := "int" + v3t4 := "uint" + v3t5 := "interface {}" + v3s := "[one 2 3 <nil>]" + v3s2 := "[(" + v3t2 + ")one (" + v3t3 + ")2 (" + v3t4 + ")3 (" + v3t5 + + ")<nil>]" + addFormatterTest("%v", v3, v3s) + addFormatterTest("%v", pv3, "<*>"+v3s) + addFormatterTest("%v", &pv3, "<**>"+v3s) + addFormatterTest("%+v", nv3, "<nil>") + addFormatterTest("%+v", v3, v3s) + addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s) + addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s) + addFormatterTest("%+v", nv3, "<nil>") + addFormatterTest("%#v", v3, "("+v3t+")"+v3s2) + addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s2) + addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s2) + addFormatterTest("%#v", nv3, "(*"+v3t+")"+"<nil>") + addFormatterTest("%#+v", v3, "("+v3t+")"+v3s2) + addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s2) + addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s2) + addFormatterTest("%#+v", nv3, "(*"+v3t+")"+"<nil>") + + // Nil slice. + var v4 []int + nv4 := (*[]int)(nil) + pv4 := &v4 + v4Addr := fmt.Sprintf("%p", pv4) + pv4Addr := fmt.Sprintf("%p", &pv4) + v4t := "[]int" + v4s := "<nil>" + addFormatterTest("%v", v4, v4s) + addFormatterTest("%v", pv4, "<*>"+v4s) + addFormatterTest("%v", &pv4, "<**>"+v4s) + addFormatterTest("%+v", nv4, "<nil>") + addFormatterTest("%+v", v4, v4s) + addFormatterTest("%+v", pv4, "<*>("+v4Addr+")"+v4s) + addFormatterTest("%+v", &pv4, "<**>("+pv4Addr+"->"+v4Addr+")"+v4s) + addFormatterTest("%+v", nv4, "<nil>") + addFormatterTest("%#v", v4, "("+v4t+")"+v4s) + addFormatterTest("%#v", pv4, "(*"+v4t+")"+v4s) + addFormatterTest("%#v", &pv4, "(**"+v4t+")"+v4s) + addFormatterTest("%#v", nv4, "(*"+v4t+")"+"<nil>") + addFormatterTest("%#+v", v4, "("+v4t+")"+v4s) + addFormatterTest("%#+v", pv4, "(*"+v4t+")("+v4Addr+")"+v4s) + addFormatterTest("%#+v", &pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")"+v4s) + addFormatterTest("%#+v", nv4, "(*"+v4t+")"+"<nil>") +} + +func addStringFormatterTests() { + // Standard string. + v := "test" + nv := (*string)(nil) + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "string" + vs := "test" + addFormatterTest("%v", v, vs) + addFormatterTest("%v", pv, "<*>"+vs) + addFormatterTest("%v", &pv, "<**>"+vs) + addFormatterTest("%+v", nv, "<nil>") + addFormatterTest("%+v", v, vs) + addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) + addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) + addFormatterTest("%+v", nv, "<nil>") + addFormatterTest("%#v", v, "("+vt+")"+vs) + addFormatterTest("%#v", pv, "(*"+vt+")"+vs) + addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) + addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>") + addFormatterTest("%#+v", v, "("+vt+")"+vs) + addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) + addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) + addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>") +} + +func addInterfaceFormatterTests() { + // Nil interface. + var v interface{} + nv := (*interface{})(nil) + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "interface {}" + vs := "<nil>" + addFormatterTest("%v", v, vs) + addFormatterTest("%v", pv, "<*>"+vs) + addFormatterTest("%v", &pv, "<**>"+vs) + addFormatterTest("%+v", nv, "<nil>") + addFormatterTest("%+v", v, vs) + addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) + addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) + addFormatterTest("%+v", nv, "<nil>") + addFormatterTest("%#v", v, "("+vt+")"+vs) + addFormatterTest("%#v", pv, "(*"+vt+")"+vs) + addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) + addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>") + addFormatterTest("%#+v", v, "("+vt+")"+vs) + addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) + addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) + addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>") + + // Sub-interface. + v2 := interface{}(uint16(65535)) + pv2 := &v2 + v2Addr := fmt.Sprintf("%p", pv2) + pv2Addr := fmt.Sprintf("%p", &pv2) + v2t := "uint16" + v2s := "65535" + addFormatterTest("%v", v2, v2s) + addFormatterTest("%v", pv2, "<*>"+v2s) + addFormatterTest("%v", &pv2, "<**>"+v2s) + addFormatterTest("%+v", v2, v2s) + addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) + addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) + addFormatterTest("%#v", v2, "("+v2t+")"+v2s) + addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) + addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) + addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) + addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) + addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) +} + +func addMapFormatterTests() { + // Map with string keys and int vals. + v := map[string]int{"one": 1, "two": 2} + nilMap := map[string]int(nil) + nv := (*map[string]int)(nil) + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "map[string]int" + vs := "map[one:1 two:2]" + vs2 := "map[two:2 one:1]" + addFormatterTest("%v", v, vs, vs2) + addFormatterTest("%v", pv, "<*>"+vs, "<*>"+vs2) + addFormatterTest("%v", &pv, "<**>"+vs, "<**>"+vs2) + addFormatterTest("%+v", nilMap, "<nil>") + addFormatterTest("%+v", nv, "<nil>") + addFormatterTest("%+v", v, vs, vs2) + addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs, "<*>("+vAddr+")"+vs2) + addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs, + "<**>("+pvAddr+"->"+vAddr+")"+vs2) + addFormatterTest("%+v", nilMap, "<nil>") + addFormatterTest("%+v", nv, "<nil>") + addFormatterTest("%#v", v, "("+vt+")"+vs, "("+vt+")"+vs2) + addFormatterTest("%#v", pv, "(*"+vt+")"+vs, "(*"+vt+")"+vs2) + addFormatterTest("%#v", &pv, "(**"+vt+")"+vs, "(**"+vt+")"+vs2) + addFormatterTest("%#v", nilMap, "("+vt+")"+"<nil>") + addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>") + addFormatterTest("%#+v", v, "("+vt+")"+vs, "("+vt+")"+vs2) + addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs, + "(*"+vt+")("+vAddr+")"+vs2) + addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs, + "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs2) + addFormatterTest("%#+v", nilMap, "("+vt+")"+"<nil>") + addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>") + + // Map with custom formatter type on pointer receiver only keys and vals. + v2 := map[pstringer]pstringer{"one": "1"} + nv2 := (*map[pstringer]pstringer)(nil) + pv2 := &v2 + v2Addr := fmt.Sprintf("%p", pv2) + pv2Addr := fmt.Sprintf("%p", &pv2) + v2t := "map[spew_test.pstringer]spew_test.pstringer" + v2s := "map[stringer one:stringer 1]" + if spew.UnsafeDisabled { + v2s = "map[one:1]" + } + addFormatterTest("%v", v2, v2s) + addFormatterTest("%v", pv2, "<*>"+v2s) + addFormatterTest("%v", &pv2, "<**>"+v2s) + addFormatterTest("%+v", nv2, "<nil>") + addFormatterTest("%+v", v2, v2s) + addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) + addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) + addFormatterTest("%+v", nv2, "<nil>") + addFormatterTest("%#v", v2, "("+v2t+")"+v2s) + addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) + addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) + addFormatterTest("%#v", nv2, "(*"+v2t+")"+"<nil>") + addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) + addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) + addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) + addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"<nil>") + + // Map with interface keys and values. + v3 := map[interface{}]interface{}{"one": 1} + nv3 := (*map[interface{}]interface{})(nil) + pv3 := &v3 + v3Addr := fmt.Sprintf("%p", pv3) + pv3Addr := fmt.Sprintf("%p", &pv3) + v3t := "map[interface {}]interface {}" + v3t1 := "string" + v3t2 := "int" + v3s := "map[one:1]" + v3s2 := "map[(" + v3t1 + ")one:(" + v3t2 + ")1]" + addFormatterTest("%v", v3, v3s) + addFormatterTest("%v", pv3, "<*>"+v3s) + addFormatterTest("%v", &pv3, "<**>"+v3s) + addFormatterTest("%+v", nv3, "<nil>") + addFormatterTest("%+v", v3, v3s) + addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s) + addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s) + addFormatterTest("%+v", nv3, "<nil>") + addFormatterTest("%#v", v3, "("+v3t+")"+v3s2) + addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s2) + addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s2) + addFormatterTest("%#v", nv3, "(*"+v3t+")"+"<nil>") + addFormatterTest("%#+v", v3, "("+v3t+")"+v3s2) + addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s2) + addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s2) + addFormatterTest("%#+v", nv3, "(*"+v3t+")"+"<nil>") + + // Map with nil interface value + v4 := map[string]interface{}{"nil": nil} + nv4 := (*map[string]interface{})(nil) + pv4 := &v4 + v4Addr := fmt.Sprintf("%p", pv4) + pv4Addr := fmt.Sprintf("%p", &pv4) + v4t := "map[string]interface {}" + v4t1 := "interface {}" + v4s := "map[nil:<nil>]" + v4s2 := "map[nil:(" + v4t1 + ")<nil>]" + addFormatterTest("%v", v4, v4s) + addFormatterTest("%v", pv4, "<*>"+v4s) + addFormatterTest("%v", &pv4, "<**>"+v4s) + addFormatterTest("%+v", nv4, "<nil>") + addFormatterTest("%+v", v4, v4s) + addFormatterTest("%+v", pv4, "<*>("+v4Addr+")"+v4s) + addFormatterTest("%+v", &pv4, "<**>("+pv4Addr+"->"+v4Addr+")"+v4s) + addFormatterTest("%+v", nv4, "<nil>") + addFormatterTest("%#v", v4, "("+v4t+")"+v4s2) + addFormatterTest("%#v", pv4, "(*"+v4t+")"+v4s2) + addFormatterTest("%#v", &pv4, "(**"+v4t+")"+v4s2) + addFormatterTest("%#v", nv4, "(*"+v4t+")"+"<nil>") + addFormatterTest("%#+v", v4, "("+v4t+")"+v4s2) + addFormatterTest("%#+v", pv4, "(*"+v4t+")("+v4Addr+")"+v4s2) + addFormatterTest("%#+v", &pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")"+v4s2) + addFormatterTest("%#+v", nv4, "(*"+v4t+")"+"<nil>") +} + +func addStructFormatterTests() { + // Struct with primitives. + type s1 struct { + a int8 + b uint8 + } + v := s1{127, 255} + nv := (*s1)(nil) + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "spew_test.s1" + vt2 := "int8" + vt3 := "uint8" + vs := "{127 255}" + vs2 := "{a:127 b:255}" + vs3 := "{a:(" + vt2 + ")127 b:(" + vt3 + ")255}" + addFormatterTest("%v", v, vs) + addFormatterTest("%v", pv, "<*>"+vs) + addFormatterTest("%v", &pv, "<**>"+vs) + addFormatterTest("%+v", nv, "<nil>") + addFormatterTest("%+v", v, vs2) + addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs2) + addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs2) + addFormatterTest("%+v", nv, "<nil>") + addFormatterTest("%#v", v, "("+vt+")"+vs3) + addFormatterTest("%#v", pv, "(*"+vt+")"+vs3) + addFormatterTest("%#v", &pv, "(**"+vt+")"+vs3) + addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>") + addFormatterTest("%#+v", v, "("+vt+")"+vs3) + addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs3) + addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs3) + addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>") + + // Struct that contains another struct. + type s2 struct { + s1 s1 + b bool + } + v2 := s2{s1{127, 255}, true} + nv2 := (*s2)(nil) + pv2 := &v2 + v2Addr := fmt.Sprintf("%p", pv2) + pv2Addr := fmt.Sprintf("%p", &pv2) + v2t := "spew_test.s2" + v2t2 := "spew_test.s1" + v2t3 := "int8" + v2t4 := "uint8" + v2t5 := "bool" + v2s := "{{127 255} true}" + v2s2 := "{s1:{a:127 b:255} b:true}" + v2s3 := "{s1:(" + v2t2 + "){a:(" + v2t3 + ")127 b:(" + v2t4 + ")255} b:(" + + v2t5 + ")true}" + addFormatterTest("%v", v2, v2s) + addFormatterTest("%v", pv2, "<*>"+v2s) + addFormatterTest("%v", &pv2, "<**>"+v2s) + addFormatterTest("%+v", nv2, "<nil>") + addFormatterTest("%+v", v2, v2s2) + addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s2) + addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s2) + addFormatterTest("%+v", nv2, "<nil>") + addFormatterTest("%#v", v2, "("+v2t+")"+v2s3) + addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s3) + addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s3) + addFormatterTest("%#v", nv2, "(*"+v2t+")"+"<nil>") + addFormatterTest("%#+v", v2, "("+v2t+")"+v2s3) + addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s3) + addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s3) + addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"<nil>") + + // Struct that contains custom type with Stringer pointer interface via both + // exported and unexported fields. + type s3 struct { + s pstringer + S pstringer + } + v3 := s3{"test", "test2"} + nv3 := (*s3)(nil) + pv3 := &v3 + v3Addr := fmt.Sprintf("%p", pv3) + pv3Addr := fmt.Sprintf("%p", &pv3) + v3t := "spew_test.s3" + v3t2 := "spew_test.pstringer" + v3s := "{stringer test stringer test2}" + v3sp := v3s + v3s2 := "{s:stringer test S:stringer test2}" + v3s2p := v3s2 + v3s3 := "{s:(" + v3t2 + ")stringer test S:(" + v3t2 + ")stringer test2}" + v3s3p := v3s3 + if spew.UnsafeDisabled { + v3s = "{test test2}" + v3sp = "{test stringer test2}" + v3s2 = "{s:test S:test2}" + v3s2p = "{s:test S:stringer test2}" + v3s3 = "{s:(" + v3t2 + ")test S:(" + v3t2 + ")test2}" + v3s3p = "{s:(" + v3t2 + ")test S:(" + v3t2 + ")stringer test2}" + } + addFormatterTest("%v", v3, v3s) + addFormatterTest("%v", pv3, "<*>"+v3sp) + addFormatterTest("%v", &pv3, "<**>"+v3sp) + addFormatterTest("%+v", nv3, "<nil>") + addFormatterTest("%+v", v3, v3s2) + addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s2p) + addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s2p) + addFormatterTest("%+v", nv3, "<nil>") + addFormatterTest("%#v", v3, "("+v3t+")"+v3s3) + addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s3p) + addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s3p) + addFormatterTest("%#v", nv3, "(*"+v3t+")"+"<nil>") + addFormatterTest("%#+v", v3, "("+v3t+")"+v3s3) + addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s3p) + addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s3p) + addFormatterTest("%#+v", nv3, "(*"+v3t+")"+"<nil>") + + // Struct that contains embedded struct and field to same struct. + e := embed{"embedstr"} + v4 := embedwrap{embed: &e, e: &e} + nv4 := (*embedwrap)(nil) + pv4 := &v4 + eAddr := fmt.Sprintf("%p", &e) + v4Addr := fmt.Sprintf("%p", pv4) + pv4Addr := fmt.Sprintf("%p", &pv4) + v4t := "spew_test.embedwrap" + v4t2 := "spew_test.embed" + v4t3 := "string" + v4s := "{<*>{embedstr} <*>{embedstr}}" + v4s2 := "{embed:<*>(" + eAddr + "){a:embedstr} e:<*>(" + eAddr + + "){a:embedstr}}" + v4s3 := "{embed:(*" + v4t2 + "){a:(" + v4t3 + ")embedstr} e:(*" + v4t2 + + "){a:(" + v4t3 + ")embedstr}}" + v4s4 := "{embed:(*" + v4t2 + ")(" + eAddr + "){a:(" + v4t3 + + ")embedstr} e:(*" + v4t2 + ")(" + eAddr + "){a:(" + v4t3 + ")embedstr}}" + addFormatterTest("%v", v4, v4s) + addFormatterTest("%v", pv4, "<*>"+v4s) + addFormatterTest("%v", &pv4, "<**>"+v4s) + addFormatterTest("%+v", nv4, "<nil>") + addFormatterTest("%+v", v4, v4s2) + addFormatterTest("%+v", pv4, "<*>("+v4Addr+")"+v4s2) + addFormatterTest("%+v", &pv4, "<**>("+pv4Addr+"->"+v4Addr+")"+v4s2) + addFormatterTest("%+v", nv4, "<nil>") + addFormatterTest("%#v", v4, "("+v4t+")"+v4s3) + addFormatterTest("%#v", pv4, "(*"+v4t+")"+v4s3) + addFormatterTest("%#v", &pv4, "(**"+v4t+")"+v4s3) + addFormatterTest("%#v", nv4, "(*"+v4t+")"+"<nil>") + addFormatterTest("%#+v", v4, "("+v4t+")"+v4s4) + addFormatterTest("%#+v", pv4, "(*"+v4t+")("+v4Addr+")"+v4s4) + addFormatterTest("%#+v", &pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")"+v4s4) + addFormatterTest("%#+v", nv4, "(*"+v4t+")"+"<nil>") +} + +func addUintptrFormatterTests() { + // Null pointer. + v := uintptr(0) + nv := (*uintptr)(nil) + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "uintptr" + vs := "<nil>" + addFormatterTest("%v", v, vs) + addFormatterTest("%v", pv, "<*>"+vs) + addFormatterTest("%v", &pv, "<**>"+vs) + addFormatterTest("%+v", nv, "<nil>") + addFormatterTest("%+v", v, vs) + addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) + addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) + addFormatterTest("%+v", nv, "<nil>") + addFormatterTest("%#v", v, "("+vt+")"+vs) + addFormatterTest("%#v", pv, "(*"+vt+")"+vs) + addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) + addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>") + addFormatterTest("%#+v", v, "("+vt+")"+vs) + addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) + addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) + addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>") + + // Address of real variable. + i := 1 + v2 := uintptr(unsafe.Pointer(&i)) + pv2 := &v2 + v2Addr := fmt.Sprintf("%p", pv2) + pv2Addr := fmt.Sprintf("%p", &pv2) + v2t := "uintptr" + v2s := fmt.Sprintf("%p", &i) + addFormatterTest("%v", v2, v2s) + addFormatterTest("%v", pv2, "<*>"+v2s) + addFormatterTest("%v", &pv2, "<**>"+v2s) + addFormatterTest("%+v", v2, v2s) + addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) + addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) + addFormatterTest("%#v", v2, "("+v2t+")"+v2s) + addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) + addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) + addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) + addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) + addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) +} + +func addUnsafePointerFormatterTests() { + // Null pointer. + v := unsafe.Pointer(nil) + nv := (*unsafe.Pointer)(nil) + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "unsafe.Pointer" + vs := "<nil>" + addFormatterTest("%v", v, vs) + addFormatterTest("%v", pv, "<*>"+vs) + addFormatterTest("%v", &pv, "<**>"+vs) + addFormatterTest("%+v", nv, "<nil>") + addFormatterTest("%+v", v, vs) + addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) + addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) + addFormatterTest("%+v", nv, "<nil>") + addFormatterTest("%#v", v, "("+vt+")"+vs) + addFormatterTest("%#v", pv, "(*"+vt+")"+vs) + addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) + addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>") + addFormatterTest("%#+v", v, "("+vt+")"+vs) + addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) + addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) + addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>") + + // Address of real variable. + i := 1 + v2 := unsafe.Pointer(&i) + pv2 := &v2 + v2Addr := fmt.Sprintf("%p", pv2) + pv2Addr := fmt.Sprintf("%p", &pv2) + v2t := "unsafe.Pointer" + v2s := fmt.Sprintf("%p", &i) + addFormatterTest("%v", v2, v2s) + addFormatterTest("%v", pv2, "<*>"+v2s) + addFormatterTest("%v", &pv2, "<**>"+v2s) + addFormatterTest("%+v", v2, v2s) + addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) + addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) + addFormatterTest("%#v", v2, "("+v2t+")"+v2s) + addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) + addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) + addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) + addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) + addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) +} + +func addChanFormatterTests() { + // Nil channel. + var v chan int + pv := &v + nv := (*chan int)(nil) + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "chan int" + vs := "<nil>" + addFormatterTest("%v", v, vs) + addFormatterTest("%v", pv, "<*>"+vs) + addFormatterTest("%v", &pv, "<**>"+vs) + addFormatterTest("%+v", nv, "<nil>") + addFormatterTest("%+v", v, vs) + addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) + addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) + addFormatterTest("%+v", nv, "<nil>") + addFormatterTest("%#v", v, "("+vt+")"+vs) + addFormatterTest("%#v", pv, "(*"+vt+")"+vs) + addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) + addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>") + addFormatterTest("%#+v", v, "("+vt+")"+vs) + addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) + addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) + addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>") + + // Real channel. + v2 := make(chan int) + pv2 := &v2 + v2Addr := fmt.Sprintf("%p", pv2) + pv2Addr := fmt.Sprintf("%p", &pv2) + v2t := "chan int" + v2s := fmt.Sprintf("%p", v2) + addFormatterTest("%v", v2, v2s) + addFormatterTest("%v", pv2, "<*>"+v2s) + addFormatterTest("%v", &pv2, "<**>"+v2s) + addFormatterTest("%+v", v2, v2s) + addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) + addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) + addFormatterTest("%#v", v2, "("+v2t+")"+v2s) + addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) + addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) + addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) + addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) + addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) +} + +func addFuncFormatterTests() { + // Function with no params and no returns. + v := addIntFormatterTests + nv := (*func())(nil) + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "func()" + vs := fmt.Sprintf("%p", v) + addFormatterTest("%v", v, vs) + addFormatterTest("%v", pv, "<*>"+vs) + addFormatterTest("%v", &pv, "<**>"+vs) + addFormatterTest("%+v", nv, "<nil>") + addFormatterTest("%+v", v, vs) + addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) + addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) + addFormatterTest("%+v", nv, "<nil>") + addFormatterTest("%#v", v, "("+vt+")"+vs) + addFormatterTest("%#v", pv, "(*"+vt+")"+vs) + addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) + addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>") + addFormatterTest("%#+v", v, "("+vt+")"+vs) + addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) + addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) + addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>") + + // Function with param and no returns. + v2 := TestFormatter + nv2 := (*func(*testing.T))(nil) + pv2 := &v2 + v2Addr := fmt.Sprintf("%p", pv2) + pv2Addr := fmt.Sprintf("%p", &pv2) + v2t := "func(*testing.T)" + v2s := fmt.Sprintf("%p", v2) + addFormatterTest("%v", v2, v2s) + addFormatterTest("%v", pv2, "<*>"+v2s) + addFormatterTest("%v", &pv2, "<**>"+v2s) + addFormatterTest("%+v", nv2, "<nil>") + addFormatterTest("%+v", v2, v2s) + addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) + addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) + addFormatterTest("%+v", nv2, "<nil>") + addFormatterTest("%#v", v2, "("+v2t+")"+v2s) + addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) + addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) + addFormatterTest("%#v", nv2, "(*"+v2t+")"+"<nil>") + addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) + addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) + addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) + addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"<nil>") + + // Function with multiple params and multiple returns. + var v3 = func(i int, s string) (b bool, err error) { + return true, nil + } + nv3 := (*func(int, string) (bool, error))(nil) + pv3 := &v3 + v3Addr := fmt.Sprintf("%p", pv3) + pv3Addr := fmt.Sprintf("%p", &pv3) + v3t := "func(int, string) (bool, error)" + v3s := fmt.Sprintf("%p", v3) + addFormatterTest("%v", v3, v3s) + addFormatterTest("%v", pv3, "<*>"+v3s) + addFormatterTest("%v", &pv3, "<**>"+v3s) + addFormatterTest("%+v", nv3, "<nil>") + addFormatterTest("%+v", v3, v3s) + addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s) + addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s) + addFormatterTest("%+v", nv3, "<nil>") + addFormatterTest("%#v", v3, "("+v3t+")"+v3s) + addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s) + addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s) + addFormatterTest("%#v", nv3, "(*"+v3t+")"+"<nil>") + addFormatterTest("%#+v", v3, "("+v3t+")"+v3s) + addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s) + addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s) + addFormatterTest("%#+v", nv3, "(*"+v3t+")"+"<nil>") +} + +func addCircularFormatterTests() { + // Struct that is circular through self referencing. + type circular struct { + c *circular + } + v := circular{nil} + v.c = &v + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "spew_test.circular" + vs := "{<*>{<*><shown>}}" + vs2 := "{<*><shown>}" + vs3 := "{c:<*>(" + vAddr + "){c:<*>(" + vAddr + ")<shown>}}" + vs4 := "{c:<*>(" + vAddr + ")<shown>}" + vs5 := "{c:(*" + vt + "){c:(*" + vt + ")<shown>}}" + vs6 := "{c:(*" + vt + ")<shown>}" + vs7 := "{c:(*" + vt + ")(" + vAddr + "){c:(*" + vt + ")(" + vAddr + + ")<shown>}}" + vs8 := "{c:(*" + vt + ")(" + vAddr + ")<shown>}" + addFormatterTest("%v", v, vs) + addFormatterTest("%v", pv, "<*>"+vs2) + addFormatterTest("%v", &pv, "<**>"+vs2) + addFormatterTest("%+v", v, vs3) + addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs4) + addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs4) + addFormatterTest("%#v", v, "("+vt+")"+vs5) + addFormatterTest("%#v", pv, "(*"+vt+")"+vs6) + addFormatterTest("%#v", &pv, "(**"+vt+")"+vs6) + addFormatterTest("%#+v", v, "("+vt+")"+vs7) + addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs8) + addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs8) + + // Structs that are circular through cross referencing. + v2 := xref1{nil} + ts2 := xref2{&v2} + v2.ps2 = &ts2 + pv2 := &v2 + ts2Addr := fmt.Sprintf("%p", &ts2) + v2Addr := fmt.Sprintf("%p", pv2) + pv2Addr := fmt.Sprintf("%p", &pv2) + v2t := "spew_test.xref1" + v2t2 := "spew_test.xref2" + v2s := "{<*>{<*>{<*><shown>}}}" + v2s2 := "{<*>{<*><shown>}}" + v2s3 := "{ps2:<*>(" + ts2Addr + "){ps1:<*>(" + v2Addr + "){ps2:<*>(" + + ts2Addr + ")<shown>}}}" + v2s4 := "{ps2:<*>(" + ts2Addr + "){ps1:<*>(" + v2Addr + ")<shown>}}" + v2s5 := "{ps2:(*" + v2t2 + "){ps1:(*" + v2t + "){ps2:(*" + v2t2 + + ")<shown>}}}" + v2s6 := "{ps2:(*" + v2t2 + "){ps1:(*" + v2t + ")<shown>}}" + v2s7 := "{ps2:(*" + v2t2 + ")(" + ts2Addr + "){ps1:(*" + v2t + + ")(" + v2Addr + "){ps2:(*" + v2t2 + ")(" + ts2Addr + + ")<shown>}}}" + v2s8 := "{ps2:(*" + v2t2 + ")(" + ts2Addr + "){ps1:(*" + v2t + + ")(" + v2Addr + ")<shown>}}" + addFormatterTest("%v", v2, v2s) + addFormatterTest("%v", pv2, "<*>"+v2s2) + addFormatterTest("%v", &pv2, "<**>"+v2s2) + addFormatterTest("%+v", v2, v2s3) + addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s4) + addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s4) + addFormatterTest("%#v", v2, "("+v2t+")"+v2s5) + addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s6) + addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s6) + addFormatterTest("%#+v", v2, "("+v2t+")"+v2s7) + addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s8) + addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s8) + + // Structs that are indirectly circular. + v3 := indirCir1{nil} + tic2 := indirCir2{nil} + tic3 := indirCir3{&v3} + tic2.ps3 = &tic3 + v3.ps2 = &tic2 + pv3 := &v3 + tic2Addr := fmt.Sprintf("%p", &tic2) + tic3Addr := fmt.Sprintf("%p", &tic3) + v3Addr := fmt.Sprintf("%p", pv3) + pv3Addr := fmt.Sprintf("%p", &pv3) + v3t := "spew_test.indirCir1" + v3t2 := "spew_test.indirCir2" + v3t3 := "spew_test.indirCir3" + v3s := "{<*>{<*>{<*>{<*><shown>}}}}" + v3s2 := "{<*>{<*>{<*><shown>}}}" + v3s3 := "{ps2:<*>(" + tic2Addr + "){ps3:<*>(" + tic3Addr + "){ps1:<*>(" + + v3Addr + "){ps2:<*>(" + tic2Addr + ")<shown>}}}}" + v3s4 := "{ps2:<*>(" + tic2Addr + "){ps3:<*>(" + tic3Addr + "){ps1:<*>(" + + v3Addr + ")<shown>}}}" + v3s5 := "{ps2:(*" + v3t2 + "){ps3:(*" + v3t3 + "){ps1:(*" + v3t + + "){ps2:(*" + v3t2 + ")<shown>}}}}" + v3s6 := "{ps2:(*" + v3t2 + "){ps3:(*" + v3t3 + "){ps1:(*" + v3t + + ")<shown>}}}" + v3s7 := "{ps2:(*" + v3t2 + ")(" + tic2Addr + "){ps3:(*" + v3t3 + ")(" + + tic3Addr + "){ps1:(*" + v3t + ")(" + v3Addr + "){ps2:(*" + v3t2 + + ")(" + tic2Addr + ")<shown>}}}}" + v3s8 := "{ps2:(*" + v3t2 + ")(" + tic2Addr + "){ps3:(*" + v3t3 + ")(" + + tic3Addr + "){ps1:(*" + v3t + ")(" + v3Addr + ")<shown>}}}" + addFormatterTest("%v", v3, v3s) + addFormatterTest("%v", pv3, "<*>"+v3s2) + addFormatterTest("%v", &pv3, "<**>"+v3s2) + addFormatterTest("%+v", v3, v3s3) + addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s4) + addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s4) + addFormatterTest("%#v", v3, "("+v3t+")"+v3s5) + addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s6) + addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s6) + addFormatterTest("%#+v", v3, "("+v3t+")"+v3s7) + addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s8) + addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s8) +} + +func addPanicFormatterTests() { + // Type that panics in its Stringer interface. + v := panicer(127) + nv := (*panicer)(nil) + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "spew_test.panicer" + vs := "(PANIC=test panic)127" + addFormatterTest("%v", v, vs) + addFormatterTest("%v", pv, "<*>"+vs) + addFormatterTest("%v", &pv, "<**>"+vs) + addFormatterTest("%v", nv, "<nil>") + addFormatterTest("%+v", v, vs) + addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) + addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) + addFormatterTest("%+v", nv, "<nil>") + addFormatterTest("%#v", v, "("+vt+")"+vs) + addFormatterTest("%#v", pv, "(*"+vt+")"+vs) + addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) + addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>") + addFormatterTest("%#+v", v, "("+vt+")"+vs) + addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) + addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) + addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>") +} + +func addErrorFormatterTests() { + // Type that has a custom Error interface. + v := customError(127) + nv := (*customError)(nil) + pv := &v + vAddr := fmt.Sprintf("%p", pv) + pvAddr := fmt.Sprintf("%p", &pv) + vt := "spew_test.customError" + vs := "error: 127" + addFormatterTest("%v", v, vs) + addFormatterTest("%v", pv, "<*>"+vs) + addFormatterTest("%v", &pv, "<**>"+vs) + addFormatterTest("%v", nv, "<nil>") + addFormatterTest("%+v", v, vs) + addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) + addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) + addFormatterTest("%+v", nv, "<nil>") + addFormatterTest("%#v", v, "("+vt+")"+vs) + addFormatterTest("%#v", pv, "(*"+vt+")"+vs) + addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) + addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>") + addFormatterTest("%#+v", v, "("+vt+")"+vs) + addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) + addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) + addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>") +} + +func addPassthroughFormatterTests() { + // %x passthrough with uint. + v := uint(4294967295) + pv := &v + vAddr := fmt.Sprintf("%x", pv) + pvAddr := fmt.Sprintf("%x", &pv) + vs := "ffffffff" + addFormatterTest("%x", v, vs) + addFormatterTest("%x", pv, vAddr) + addFormatterTest("%x", &pv, pvAddr) + + // %#x passthrough with uint. + v2 := int(2147483647) + pv2 := &v2 + v2Addr := fmt.Sprintf("%#x", pv2) + pv2Addr := fmt.Sprintf("%#x", &pv2) + v2s := "0x7fffffff" + addFormatterTest("%#x", v2, v2s) + addFormatterTest("%#x", pv2, v2Addr) + addFormatterTest("%#x", &pv2, pv2Addr) + + // %f passthrough with precision. + addFormatterTest("%.2f", 3.1415, "3.14") + addFormatterTest("%.3f", 3.1415, "3.142") + addFormatterTest("%.4f", 3.1415, "3.1415") + + // %f passthrough with width and precision. + addFormatterTest("%5.2f", 3.1415, " 3.14") + addFormatterTest("%6.3f", 3.1415, " 3.142") + addFormatterTest("%7.4f", 3.1415, " 3.1415") + + // %d passthrough with width. + addFormatterTest("%3d", 127, "127") + addFormatterTest("%4d", 127, " 127") + addFormatterTest("%5d", 127, " 127") + + // %q passthrough with string. + addFormatterTest("%q", "test", "\"test\"") +} + +// TestFormatter executes all of the tests described by formatterTests. +func TestFormatter(t *testing.T) { + // Setup tests. + addIntFormatterTests() + addUintFormatterTests() + addBoolFormatterTests() + addFloatFormatterTests() + addComplexFormatterTests() + addArrayFormatterTests() + addSliceFormatterTests() + addStringFormatterTests() + addInterfaceFormatterTests() + addMapFormatterTests() + addStructFormatterTests() + addUintptrFormatterTests() + addUnsafePointerFormatterTests() + addChanFormatterTests() + addFuncFormatterTests() + addCircularFormatterTests() + addPanicFormatterTests() + addErrorFormatterTests() + addPassthroughFormatterTests() + + t.Logf("Running %d tests", len(formatterTests)) + for i, test := range formatterTests { + buf := new(bytes.Buffer) + spew.Fprintf(buf, test.format, test.in) + s := buf.String() + if testFailed(s, test.wants) { + t.Errorf("Formatter #%d format: %s got: %s %s", i, test.format, s, + stringizeWants(test.wants)) + continue + } + } +} + +type testStruct struct { + x int +} + +func (ts testStruct) String() string { + return fmt.Sprintf("ts.%d", ts.x) +} + +type testStructP struct { + x int +} + +func (ts *testStructP) String() string { + return fmt.Sprintf("ts.%d", ts.x) +} + +func TestPrintSortedKeys(t *testing.T) { + cfg := spew.ConfigState{SortKeys: true} + s := cfg.Sprint(map[int]string{1: "1", 3: "3", 2: "2"}) + expected := "map[1:1 2:2 3:3]" + if s != expected { + t.Errorf("Sorted keys mismatch 1:\n %v %v", s, expected) + } + + s = cfg.Sprint(map[stringer]int{"1": 1, "3": 3, "2": 2}) + expected = "map[stringer 1:1 stringer 2:2 stringer 3:3]" + if s != expected { + t.Errorf("Sorted keys mismatch 2:\n %v %v", s, expected) + } + + s = cfg.Sprint(map[pstringer]int{pstringer("1"): 1, pstringer("3"): 3, pstringer("2"): 2}) + expected = "map[stringer 1:1 stringer 2:2 stringer 3:3]" + if spew.UnsafeDisabled { + expected = "map[1:1 2:2 3:3]" + } + if s != expected { + t.Errorf("Sorted keys mismatch 3:\n %v %v", s, expected) + } + + s = cfg.Sprint(map[testStruct]int{{1}: 1, {3}: 3, {2}: 2}) + expected = "map[ts.1:1 ts.2:2 ts.3:3]" + if s != expected { + t.Errorf("Sorted keys mismatch 4:\n %v %v", s, expected) + } + + if !spew.UnsafeDisabled { + s = cfg.Sprint(map[testStructP]int{{1}: 1, {3}: 3, {2}: 2}) + expected = "map[ts.1:1 ts.2:2 ts.3:3]" + if s != expected { + t.Errorf("Sorted keys mismatch 5:\n %v %v", s, expected) + } + } + + s = cfg.Sprint(map[customError]int{customError(1): 1, customError(3): 3, customError(2): 2}) + expected = "map[error: 1:1 error: 2:2 error: 3:3]" + if s != expected { + t.Errorf("Sorted keys mismatch 6:\n %v %v", s, expected) + } +} diff --git a/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/internal_test.go b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/internal_test.go new file mode 100644 index 0000000..e312b4f --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/internal_test.go @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2013-2016 Dave Collins <dave@davec.name> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* +This test file is part of the spew package rather than than the spew_test +package because it needs access to internals to properly test certain cases +which are not possible via the public interface since they should never happen. +*/ + +package spew + +import ( + "bytes" + "reflect" + "testing" +) + +// dummyFmtState implements a fake fmt.State to use for testing invalid +// reflect.Value handling. This is necessary because the fmt package catches +// invalid values before invoking the formatter on them. +type dummyFmtState struct { + bytes.Buffer +} + +func (dfs *dummyFmtState) Flag(f int) bool { + return f == int('+') +} + +func (dfs *dummyFmtState) Precision() (int, bool) { + return 0, false +} + +func (dfs *dummyFmtState) Width() (int, bool) { + return 0, false +} + +// TestInvalidReflectValue ensures the dump and formatter code handles an +// invalid reflect value properly. This needs access to internal state since it +// should never happen in real code and therefore can't be tested via the public +// API. +func TestInvalidReflectValue(t *testing.T) { + i := 1 + + // Dump invalid reflect value. + v := new(reflect.Value) + buf := new(bytes.Buffer) + d := dumpState{w: buf, cs: &Config} + d.dump(*v) + s := buf.String() + want := "<invalid>" + if s != want { + t.Errorf("InvalidReflectValue #%d\n got: %s want: %s", i, s, want) + } + i++ + + // Formatter invalid reflect value. + buf2 := new(dummyFmtState) + f := formatState{value: *v, cs: &Config, fs: buf2} + f.format(*v) + s = buf2.String() + want = "<invalid>" + if s != want { + t.Errorf("InvalidReflectValue #%d got: %s want: %s", i, s, want) + } +} + +// SortValues makes the internal sortValues function available to the test +// package. +func SortValues(values []reflect.Value, cs *ConfigState) { + sortValues(values, cs) +} diff --git a/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/internalunsafe_test.go b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/internalunsafe_test.go new file mode 100644 index 0000000..80dc221 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/internalunsafe_test.go @@ -0,0 +1,101 @@ +// Copyright (c) 2013-2016 Dave Collins <dave@davec.name> + +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. + +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +// NOTE: Due to the following build constraints, this file will only be compiled +// when the code is not running on Google App Engine, compiled by GopherJS, and +// "-tags safe" is not added to the go build command line. The "disableunsafe" +// tag is deprecated and thus should not be used. +// +build !js,!appengine,!safe,!disableunsafe,go1.4 + +/* +This test file is part of the spew package rather than than the spew_test +package because it needs access to internals to properly test certain cases +which are not possible via the public interface since they should never happen. +*/ + +package spew + +import ( + "bytes" + "reflect" + "testing" +) + +// changeKind uses unsafe to intentionally change the kind of a reflect.Value to +// the maximum kind value which does not exist. This is needed to test the +// fallback code which punts to the standard fmt library for new types that +// might get added to the language. +func changeKind(v *reflect.Value, readOnly bool) { + flags := flagField(v) + if readOnly { + *flags |= flagRO + } else { + *flags &^= flagRO + } + *flags |= flagKindMask +} + +// TestAddedReflectValue tests functionaly of the dump and formatter code which +// falls back to the standard fmt library for new types that might get added to +// the language. +func TestAddedReflectValue(t *testing.T) { + i := 1 + + // Dump using a reflect.Value that is exported. + v := reflect.ValueOf(int8(5)) + changeKind(&v, false) + buf := new(bytes.Buffer) + d := dumpState{w: buf, cs: &Config} + d.dump(v) + s := buf.String() + want := "(int8) 5" + if s != want { + t.Errorf("TestAddedReflectValue #%d\n got: %s want: %s", i, s, want) + } + i++ + + // Dump using a reflect.Value that is not exported. + changeKind(&v, true) + buf.Reset() + d.dump(v) + s = buf.String() + want = "(int8) <int8 Value>" + if s != want { + t.Errorf("TestAddedReflectValue #%d\n got: %s want: %s", i, s, want) + } + i++ + + // Formatter using a reflect.Value that is exported. + changeKind(&v, false) + buf2 := new(dummyFmtState) + f := formatState{value: v, cs: &Config, fs: buf2} + f.format(v) + s = buf2.String() + want = "5" + if s != want { + t.Errorf("TestAddedReflectValue #%d got: %s want: %s", i, s, want) + } + i++ + + // Formatter using a reflect.Value that is not exported. + changeKind(&v, true) + buf2.Reset() + f = formatState{value: v, cs: &Config, fs: buf2} + f.format(v) + s = buf2.String() + want = "<int8 Value>" + if s != want { + t.Errorf("TestAddedReflectValue #%d got: %s want: %s", i, s, want) + } +} diff --git a/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/spew.go b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/spew.go new file mode 100644 index 0000000..32c0e33 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/spew.go @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2013-2016 Dave Collins <dave@davec.name> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "fmt" + "io" +) + +// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the formatted string as a value that satisfies error. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b)) +func Errorf(format string, a ...interface{}) (err error) { + return fmt.Errorf(format, convertArgs(a)...) +} + +// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b)) +func Fprint(w io.Writer, a ...interface{}) (n int, err error) { + return fmt.Fprint(w, convertArgs(a)...) +} + +// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b)) +func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { + return fmt.Fprintf(w, format, convertArgs(a)...) +} + +// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it +// passed with a default Formatter interface returned by NewFormatter. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b)) +func Fprintln(w io.Writer, a ...interface{}) (n int, err error) { + return fmt.Fprintln(w, convertArgs(a)...) +} + +// Print is a wrapper for fmt.Print that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b)) +func Print(a ...interface{}) (n int, err error) { + return fmt.Print(convertArgs(a)...) +} + +// Printf is a wrapper for fmt.Printf that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b)) +func Printf(format string, a ...interface{}) (n int, err error) { + return fmt.Printf(format, convertArgs(a)...) +} + +// Println is a wrapper for fmt.Println that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b)) +func Println(a ...interface{}) (n int, err error) { + return fmt.Println(convertArgs(a)...) +} + +// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b)) +func Sprint(a ...interface{}) string { + return fmt.Sprint(convertArgs(a)...) +} + +// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b)) +func Sprintf(format string, a ...interface{}) string { + return fmt.Sprintf(format, convertArgs(a)...) +} + +// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it +// were passed with a default Formatter interface returned by NewFormatter. It +// returns the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b)) +func Sprintln(a ...interface{}) string { + return fmt.Sprintln(convertArgs(a)...) +} + +// convertArgs accepts a slice of arguments and returns a slice of the same +// length with each argument converted to a default spew Formatter interface. +func convertArgs(args []interface{}) (formatters []interface{}) { + formatters = make([]interface{}, len(args)) + for index, arg := range args { + formatters[index] = NewFormatter(arg) + } + return formatters +} diff --git a/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/spew_test.go b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/spew_test.go new file mode 100644 index 0000000..b70466c --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/spew_test.go @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2013-2016 Dave Collins <dave@davec.name> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew_test + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "testing" + + "github.com/davecgh/go-spew/spew" +) + +// spewFunc is used to identify which public function of the spew package or +// ConfigState a test applies to. +type spewFunc int + +const ( + fCSFdump spewFunc = iota + fCSFprint + fCSFprintf + fCSFprintln + fCSPrint + fCSPrintln + fCSSdump + fCSSprint + fCSSprintf + fCSSprintln + fCSErrorf + fCSNewFormatter + fErrorf + fFprint + fFprintln + fPrint + fPrintln + fSdump + fSprint + fSprintf + fSprintln +) + +// Map of spewFunc values to names for pretty printing. +var spewFuncStrings = map[spewFunc]string{ + fCSFdump: "ConfigState.Fdump", + fCSFprint: "ConfigState.Fprint", + fCSFprintf: "ConfigState.Fprintf", + fCSFprintln: "ConfigState.Fprintln", + fCSSdump: "ConfigState.Sdump", + fCSPrint: "ConfigState.Print", + fCSPrintln: "ConfigState.Println", + fCSSprint: "ConfigState.Sprint", + fCSSprintf: "ConfigState.Sprintf", + fCSSprintln: "ConfigState.Sprintln", + fCSErrorf: "ConfigState.Errorf", + fCSNewFormatter: "ConfigState.NewFormatter", + fErrorf: "spew.Errorf", + fFprint: "spew.Fprint", + fFprintln: "spew.Fprintln", + fPrint: "spew.Print", + fPrintln: "spew.Println", + fSdump: "spew.Sdump", + fSprint: "spew.Sprint", + fSprintf: "spew.Sprintf", + fSprintln: "spew.Sprintln", +} + +func (f spewFunc) String() string { + if s, ok := spewFuncStrings[f]; ok { + return s + } + return fmt.Sprintf("Unknown spewFunc (%d)", int(f)) +} + +// spewTest is used to describe a test to be performed against the public +// functions of the spew package or ConfigState. +type spewTest struct { + cs *spew.ConfigState + f spewFunc + format string + in interface{} + want string +} + +// spewTests houses the tests to be performed against the public functions of +// the spew package and ConfigState. +// +// These tests are only intended to ensure the public functions are exercised +// and are intentionally not exhaustive of types. The exhaustive type +// tests are handled in the dump and format tests. +var spewTests []spewTest + +// redirStdout is a helper function to return the standard output from f as a +// byte slice. +func redirStdout(f func()) ([]byte, error) { + tempFile, err := ioutil.TempFile("", "ss-test") + if err != nil { + return nil, err + } + fileName := tempFile.Name() + defer os.Remove(fileName) // Ignore error + + origStdout := os.Stdout + os.Stdout = tempFile + f() + os.Stdout = origStdout + tempFile.Close() + + return ioutil.ReadFile(fileName) +} + +func initSpewTests() { + // Config states with various settings. + scsDefault := spew.NewDefaultConfig() + scsNoMethods := &spew.ConfigState{Indent: " ", DisableMethods: true} + scsNoPmethods := &spew.ConfigState{Indent: " ", DisablePointerMethods: true} + scsMaxDepth := &spew.ConfigState{Indent: " ", MaxDepth: 1} + scsContinue := &spew.ConfigState{Indent: " ", ContinueOnMethod: true} + scsNoPtrAddr := &spew.ConfigState{DisablePointerAddresses: true} + scsNoCap := &spew.ConfigState{DisableCapacities: true} + + // Variables for tests on types which implement Stringer interface with and + // without a pointer receiver. + ts := stringer("test") + tps := pstringer("test") + + type ptrTester struct { + s *struct{} + } + tptr := &ptrTester{s: &struct{}{}} + + // depthTester is used to test max depth handling for structs, array, slices + // and maps. + type depthTester struct { + ic indirCir1 + arr [1]string + slice []string + m map[string]int + } + dt := depthTester{indirCir1{nil}, [1]string{"arr"}, []string{"slice"}, + map[string]int{"one": 1}} + + // Variable for tests on types which implement error interface. + te := customError(10) + + spewTests = []spewTest{ + {scsDefault, fCSFdump, "", int8(127), "(int8) 127\n"}, + {scsDefault, fCSFprint, "", int16(32767), "32767"}, + {scsDefault, fCSFprintf, "%v", int32(2147483647), "2147483647"}, + {scsDefault, fCSFprintln, "", int(2147483647), "2147483647\n"}, + {scsDefault, fCSPrint, "", int64(9223372036854775807), "9223372036854775807"}, + {scsDefault, fCSPrintln, "", uint8(255), "255\n"}, + {scsDefault, fCSSdump, "", uint8(64), "(uint8) 64\n"}, + {scsDefault, fCSSprint, "", complex(1, 2), "(1+2i)"}, + {scsDefault, fCSSprintf, "%v", complex(float32(3), 4), "(3+4i)"}, + {scsDefault, fCSSprintln, "", complex(float64(5), 6), "(5+6i)\n"}, + {scsDefault, fCSErrorf, "%#v", uint16(65535), "(uint16)65535"}, + {scsDefault, fCSNewFormatter, "%v", uint32(4294967295), "4294967295"}, + {scsDefault, fErrorf, "%v", uint64(18446744073709551615), "18446744073709551615"}, + {scsDefault, fFprint, "", float32(3.14), "3.14"}, + {scsDefault, fFprintln, "", float64(6.28), "6.28\n"}, + {scsDefault, fPrint, "", true, "true"}, + {scsDefault, fPrintln, "", false, "false\n"}, + {scsDefault, fSdump, "", complex(-10, -20), "(complex128) (-10-20i)\n"}, + {scsDefault, fSprint, "", complex(-1, -2), "(-1-2i)"}, + {scsDefault, fSprintf, "%v", complex(float32(-3), -4), "(-3-4i)"}, + {scsDefault, fSprintln, "", complex(float64(-5), -6), "(-5-6i)\n"}, + {scsNoMethods, fCSFprint, "", ts, "test"}, + {scsNoMethods, fCSFprint, "", &ts, "<*>test"}, + {scsNoMethods, fCSFprint, "", tps, "test"}, + {scsNoMethods, fCSFprint, "", &tps, "<*>test"}, + {scsNoPmethods, fCSFprint, "", ts, "stringer test"}, + {scsNoPmethods, fCSFprint, "", &ts, "<*>stringer test"}, + {scsNoPmethods, fCSFprint, "", tps, "test"}, + {scsNoPmethods, fCSFprint, "", &tps, "<*>stringer test"}, + {scsMaxDepth, fCSFprint, "", dt, "{{<max>} [<max>] [<max>] map[<max>]}"}, + {scsMaxDepth, fCSFdump, "", dt, "(spew_test.depthTester) {\n" + + " ic: (spew_test.indirCir1) {\n <max depth reached>\n },\n" + + " arr: ([1]string) (len=1 cap=1) {\n <max depth reached>\n },\n" + + " slice: ([]string) (len=1 cap=1) {\n <max depth reached>\n },\n" + + " m: (map[string]int) (len=1) {\n <max depth reached>\n }\n}\n"}, + {scsContinue, fCSFprint, "", ts, "(stringer test) test"}, + {scsContinue, fCSFdump, "", ts, "(spew_test.stringer) " + + "(len=4) (stringer test) \"test\"\n"}, + {scsContinue, fCSFprint, "", te, "(error: 10) 10"}, + {scsContinue, fCSFdump, "", te, "(spew_test.customError) " + + "(error: 10) 10\n"}, + {scsNoPtrAddr, fCSFprint, "", tptr, "<*>{<*>{}}"}, + {scsNoPtrAddr, fCSSdump, "", tptr, "(*spew_test.ptrTester)({\ns: (*struct {})({\n})\n})\n"}, + {scsNoCap, fCSSdump, "", make([]string, 0, 10), "([]string) {\n}\n"}, + {scsNoCap, fCSSdump, "", make([]string, 1, 10), "([]string) (len=1) {\n(string) \"\"\n}\n"}, + } +} + +// TestSpew executes all of the tests described by spewTests. +func TestSpew(t *testing.T) { + initSpewTests() + + t.Logf("Running %d tests", len(spewTests)) + for i, test := range spewTests { + buf := new(bytes.Buffer) + switch test.f { + case fCSFdump: + test.cs.Fdump(buf, test.in) + + case fCSFprint: + test.cs.Fprint(buf, test.in) + + case fCSFprintf: + test.cs.Fprintf(buf, test.format, test.in) + + case fCSFprintln: + test.cs.Fprintln(buf, test.in) + + case fCSPrint: + b, err := redirStdout(func() { test.cs.Print(test.in) }) + if err != nil { + t.Errorf("%v #%d %v", test.f, i, err) + continue + } + buf.Write(b) + + case fCSPrintln: + b, err := redirStdout(func() { test.cs.Println(test.in) }) + if err != nil { + t.Errorf("%v #%d %v", test.f, i, err) + continue + } + buf.Write(b) + + case fCSSdump: + str := test.cs.Sdump(test.in) + buf.WriteString(str) + + case fCSSprint: + str := test.cs.Sprint(test.in) + buf.WriteString(str) + + case fCSSprintf: + str := test.cs.Sprintf(test.format, test.in) + buf.WriteString(str) + + case fCSSprintln: + str := test.cs.Sprintln(test.in) + buf.WriteString(str) + + case fCSErrorf: + err := test.cs.Errorf(test.format, test.in) + buf.WriteString(err.Error()) + + case fCSNewFormatter: + fmt.Fprintf(buf, test.format, test.cs.NewFormatter(test.in)) + + case fErrorf: + err := spew.Errorf(test.format, test.in) + buf.WriteString(err.Error()) + + case fFprint: + spew.Fprint(buf, test.in) + + case fFprintln: + spew.Fprintln(buf, test.in) + + case fPrint: + b, err := redirStdout(func() { spew.Print(test.in) }) + if err != nil { + t.Errorf("%v #%d %v", test.f, i, err) + continue + } + buf.Write(b) + + case fPrintln: + b, err := redirStdout(func() { spew.Println(test.in) }) + if err != nil { + t.Errorf("%v #%d %v", test.f, i, err) + continue + } + buf.Write(b) + + case fSdump: + str := spew.Sdump(test.in) + buf.WriteString(str) + + case fSprint: + str := spew.Sprint(test.in) + buf.WriteString(str) + + case fSprintf: + str := spew.Sprintf(test.format, test.in) + buf.WriteString(str) + + case fSprintln: + str := spew.Sprintln(test.in) + buf.WriteString(str) + + default: + t.Errorf("%v #%d unrecognized function", test.f, i) + continue + } + s := buf.String() + if test.want != s { + t.Errorf("ConfigState #%d\n got: %s want: %s", i, s, test.want) + continue + } + } +} diff --git a/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/test_coverage.txt b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/test_coverage.txt new file mode 100644 index 0000000..2cd087a --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/davecgh/go-spew@v1.1.1/test_coverage.txt @@ -0,0 +1,61 @@ + +github.com/davecgh/go-spew/spew/dump.go dumpState.dump 100.00% (88/88) +github.com/davecgh/go-spew/spew/format.go formatState.format 100.00% (82/82) +github.com/davecgh/go-spew/spew/format.go formatState.formatPtr 100.00% (52/52) +github.com/davecgh/go-spew/spew/dump.go dumpState.dumpPtr 100.00% (44/44) +github.com/davecgh/go-spew/spew/dump.go dumpState.dumpSlice 100.00% (39/39) +github.com/davecgh/go-spew/spew/common.go handleMethods 100.00% (30/30) +github.com/davecgh/go-spew/spew/common.go printHexPtr 100.00% (18/18) +github.com/davecgh/go-spew/spew/common.go unsafeReflectValue 100.00% (13/13) +github.com/davecgh/go-spew/spew/format.go formatState.constructOrigFormat 100.00% (12/12) +github.com/davecgh/go-spew/spew/dump.go fdump 100.00% (11/11) +github.com/davecgh/go-spew/spew/format.go formatState.Format 100.00% (11/11) +github.com/davecgh/go-spew/spew/common.go init 100.00% (10/10) +github.com/davecgh/go-spew/spew/common.go printComplex 100.00% (9/9) +github.com/davecgh/go-spew/spew/common.go valuesSorter.Less 100.00% (8/8) +github.com/davecgh/go-spew/spew/format.go formatState.buildDefaultFormat 100.00% (7/7) +github.com/davecgh/go-spew/spew/format.go formatState.unpackValue 100.00% (5/5) +github.com/davecgh/go-spew/spew/dump.go dumpState.indent 100.00% (4/4) +github.com/davecgh/go-spew/spew/common.go catchPanic 100.00% (4/4) +github.com/davecgh/go-spew/spew/config.go ConfigState.convertArgs 100.00% (4/4) +github.com/davecgh/go-spew/spew/spew.go convertArgs 100.00% (4/4) +github.com/davecgh/go-spew/spew/format.go newFormatter 100.00% (3/3) +github.com/davecgh/go-spew/spew/dump.go Sdump 100.00% (3/3) +github.com/davecgh/go-spew/spew/common.go printBool 100.00% (3/3) +github.com/davecgh/go-spew/spew/common.go sortValues 100.00% (3/3) +github.com/davecgh/go-spew/spew/config.go ConfigState.Sdump 100.00% (3/3) +github.com/davecgh/go-spew/spew/dump.go dumpState.unpackValue 100.00% (3/3) +github.com/davecgh/go-spew/spew/spew.go Printf 100.00% (1/1) +github.com/davecgh/go-spew/spew/spew.go Println 100.00% (1/1) +github.com/davecgh/go-spew/spew/spew.go Sprint 100.00% (1/1) +github.com/davecgh/go-spew/spew/spew.go Sprintf 100.00% (1/1) +github.com/davecgh/go-spew/spew/spew.go Sprintln 100.00% (1/1) +github.com/davecgh/go-spew/spew/common.go printFloat 100.00% (1/1) +github.com/davecgh/go-spew/spew/config.go NewDefaultConfig 100.00% (1/1) +github.com/davecgh/go-spew/spew/common.go printInt 100.00% (1/1) +github.com/davecgh/go-spew/spew/common.go printUint 100.00% (1/1) +github.com/davecgh/go-spew/spew/common.go valuesSorter.Len 100.00% (1/1) +github.com/davecgh/go-spew/spew/common.go valuesSorter.Swap 100.00% (1/1) +github.com/davecgh/go-spew/spew/config.go ConfigState.Errorf 100.00% (1/1) +github.com/davecgh/go-spew/spew/config.go ConfigState.Fprint 100.00% (1/1) +github.com/davecgh/go-spew/spew/config.go ConfigState.Fprintf 100.00% (1/1) +github.com/davecgh/go-spew/spew/config.go ConfigState.Fprintln 100.00% (1/1) +github.com/davecgh/go-spew/spew/config.go ConfigState.Print 100.00% (1/1) +github.com/davecgh/go-spew/spew/config.go ConfigState.Printf 100.00% (1/1) +github.com/davecgh/go-spew/spew/config.go ConfigState.Println 100.00% (1/1) +github.com/davecgh/go-spew/spew/config.go ConfigState.Sprint 100.00% (1/1) +github.com/davecgh/go-spew/spew/config.go ConfigState.Sprintf 100.00% (1/1) +github.com/davecgh/go-spew/spew/config.go ConfigState.Sprintln 100.00% (1/1) +github.com/davecgh/go-spew/spew/config.go ConfigState.NewFormatter 100.00% (1/1) +github.com/davecgh/go-spew/spew/config.go ConfigState.Fdump 100.00% (1/1) +github.com/davecgh/go-spew/spew/config.go ConfigState.Dump 100.00% (1/1) +github.com/davecgh/go-spew/spew/dump.go Fdump 100.00% (1/1) +github.com/davecgh/go-spew/spew/dump.go Dump 100.00% (1/1) +github.com/davecgh/go-spew/spew/spew.go Fprintln 100.00% (1/1) +github.com/davecgh/go-spew/spew/format.go NewFormatter 100.00% (1/1) +github.com/davecgh/go-spew/spew/spew.go Errorf 100.00% (1/1) +github.com/davecgh/go-spew/spew/spew.go Fprint 100.00% (1/1) +github.com/davecgh/go-spew/spew/spew.go Fprintf 100.00% (1/1) +github.com/davecgh/go-spew/spew/spew.go Print 100.00% (1/1) +github.com/davecgh/go-spew/spew ------------------------------- 100.00% (505/505) + diff --git a/.devenv/state/go/pkg/mod/github.com/pmezard/go-difflib@v1.0.0/.travis.yml b/.devenv/state/go/pkg/mod/github.com/pmezard/go-difflib@v1.0.0/.travis.yml new file mode 100644 index 0000000..90c9c6f --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/pmezard/go-difflib@v1.0.0/.travis.yml @@ -0,0 +1,5 @@ +language: go +go: + - 1.5 + - tip + diff --git a/.devenv/state/go/pkg/mod/github.com/pmezard/go-difflib@v1.0.0/LICENSE b/.devenv/state/go/pkg/mod/github.com/pmezard/go-difflib@v1.0.0/LICENSE new file mode 100644 index 0000000..c67dad6 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/pmezard/go-difflib@v1.0.0/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2013, Patrick Mezard +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + The names of its contributors may not be used to endorse or promote +products derived from this software without specific prior written +permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/.devenv/state/go/pkg/mod/github.com/pmezard/go-difflib@v1.0.0/README.md b/.devenv/state/go/pkg/mod/github.com/pmezard/go-difflib@v1.0.0/README.md new file mode 100644 index 0000000..e87f307 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/pmezard/go-difflib@v1.0.0/README.md @@ -0,0 +1,50 @@ +go-difflib +========== + +[](https://travis-ci.org/pmezard/go-difflib) +[](https://godoc.org/github.com/pmezard/go-difflib/difflib) + +Go-difflib is a partial port of python 3 difflib package. Its main goal +was to make unified and context diff available in pure Go, mostly for +testing purposes. + +The following class and functions (and related tests) have be ported: + +* `SequenceMatcher` +* `unified_diff()` +* `context_diff()` + +## Installation + +```bash +$ go get github.com/pmezard/go-difflib/difflib +``` + +### Quick Start + +Diffs are configured with Unified (or ContextDiff) structures, and can +be output to an io.Writer or returned as a string. + +```Go +diff := UnifiedDiff{ + A: difflib.SplitLines("foo\nbar\n"), + B: difflib.SplitLines("foo\nbaz\n"), + FromFile: "Original", + ToFile: "Current", + Context: 3, +} +text, _ := GetUnifiedDiffString(diff) +fmt.Printf(text) +``` + +would output: + +``` +--- Original ++++ Current +@@ -1,3 +1,3 @@ + foo +-bar ++baz +``` + diff --git a/.devenv/state/go/pkg/mod/github.com/pmezard/go-difflib@v1.0.0/difflib/difflib.go b/.devenv/state/go/pkg/mod/github.com/pmezard/go-difflib@v1.0.0/difflib/difflib.go new file mode 100644 index 0000000..003e99f --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/pmezard/go-difflib@v1.0.0/difflib/difflib.go @@ -0,0 +1,772 @@ +// Package difflib is a partial port of Python difflib module. +// +// It provides tools to compare sequences of strings and generate textual diffs. +// +// The following class and functions have been ported: +// +// - SequenceMatcher +// +// - unified_diff +// +// - context_diff +// +// Getting unified diffs was the main goal of the port. Keep in mind this code +// is mostly suitable to output text differences in a human friendly way, there +// are no guarantees generated diffs are consumable by patch(1). +package difflib + +import ( + "bufio" + "bytes" + "fmt" + "io" + "strings" +) + +func min(a, b int) int { + if a < b { + return a + } + return b +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func calculateRatio(matches, length int) float64 { + if length > 0 { + return 2.0 * float64(matches) / float64(length) + } + return 1.0 +} + +type Match struct { + A int + B int + Size int +} + +type OpCode struct { + Tag byte + I1 int + I2 int + J1 int + J2 int +} + +// SequenceMatcher compares sequence of strings. The basic +// algorithm predates, and is a little fancier than, an algorithm +// published in the late 1980's by Ratcliff and Obershelp under the +// hyperbolic name "gestalt pattern matching". The basic idea is to find +// the longest contiguous matching subsequence that contains no "junk" +// elements (R-O doesn't address junk). The same idea is then applied +// recursively to the pieces of the sequences to the left and to the right +// of the matching subsequence. This does not yield minimal edit +// sequences, but does tend to yield matches that "look right" to people. +// +// SequenceMatcher tries to compute a "human-friendly diff" between two +// sequences. Unlike e.g. UNIX(tm) diff, the fundamental notion is the +// longest *contiguous* & junk-free matching subsequence. That's what +// catches peoples' eyes. The Windows(tm) windiff has another interesting +// notion, pairing up elements that appear uniquely in each sequence. +// That, and the method here, appear to yield more intuitive difference +// reports than does diff. This method appears to be the least vulnerable +// to synching up on blocks of "junk lines", though (like blank lines in +// ordinary text files, or maybe "<P>" lines in HTML files). That may be +// because this is the only method of the 3 that has a *concept* of +// "junk" <wink>. +// +// Timing: Basic R-O is cubic time worst case and quadratic time expected +// case. SequenceMatcher is quadratic time for the worst case and has +// expected-case behavior dependent in a complicated way on how many +// elements the sequences have in common; best case time is linear. +type SequenceMatcher struct { + a []string + b []string + b2j map[string][]int + IsJunk func(string) bool + autoJunk bool + bJunk map[string]struct{} + matchingBlocks []Match + fullBCount map[string]int + bPopular map[string]struct{} + opCodes []OpCode +} + +func NewMatcher(a, b []string) *SequenceMatcher { + m := SequenceMatcher{autoJunk: true} + m.SetSeqs(a, b) + return &m +} + +func NewMatcherWithJunk(a, b []string, autoJunk bool, + isJunk func(string) bool) *SequenceMatcher { + + m := SequenceMatcher{IsJunk: isJunk, autoJunk: autoJunk} + m.SetSeqs(a, b) + return &m +} + +// Set two sequences to be compared. +func (m *SequenceMatcher) SetSeqs(a, b []string) { + m.SetSeq1(a) + m.SetSeq2(b) +} + +// Set the first sequence to be compared. The second sequence to be compared is +// not changed. +// +// SequenceMatcher computes and caches detailed information about the second +// sequence, so if you want to compare one sequence S against many sequences, +// use .SetSeq2(s) once and call .SetSeq1(x) repeatedly for each of the other +// sequences. +// +// See also SetSeqs() and SetSeq2(). +func (m *SequenceMatcher) SetSeq1(a []string) { + if &a == &m.a { + return + } + m.a = a + m.matchingBlocks = nil + m.opCodes = nil +} + +// Set the second sequence to be compared. The first sequence to be compared is +// not changed. +func (m *SequenceMatcher) SetSeq2(b []string) { + if &b == &m.b { + return + } + m.b = b + m.matchingBlocks = nil + m.opCodes = nil + m.fullBCount = nil + m.chainB() +} + +func (m *SequenceMatcher) chainB() { + // Populate line -> index mapping + b2j := map[string][]int{} + for i, s := range m.b { + indices := b2j[s] + indices = append(indices, i) + b2j[s] = indices + } + + // Purge junk elements + m.bJunk = map[string]struct{}{} + if m.IsJunk != nil { + junk := m.bJunk + for s, _ := range b2j { + if m.IsJunk(s) { + junk[s] = struct{}{} + } + } + for s, _ := range junk { + delete(b2j, s) + } + } + + // Purge remaining popular elements + popular := map[string]struct{}{} + n := len(m.b) + if m.autoJunk && n >= 200 { + ntest := n/100 + 1 + for s, indices := range b2j { + if len(indices) > ntest { + popular[s] = struct{}{} + } + } + for s, _ := range popular { + delete(b2j, s) + } + } + m.bPopular = popular + m.b2j = b2j +} + +func (m *SequenceMatcher) isBJunk(s string) bool { + _, ok := m.bJunk[s] + return ok +} + +// Find longest matching block in a[alo:ahi] and b[blo:bhi]. +// +// If IsJunk is not defined: +// +// Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where +// alo <= i <= i+k <= ahi +// blo <= j <= j+k <= bhi +// and for all (i',j',k') meeting those conditions, +// k >= k' +// i <= i' +// and if i == i', j <= j' +// +// In other words, of all maximal matching blocks, return one that +// starts earliest in a, and of all those maximal matching blocks that +// start earliest in a, return the one that starts earliest in b. +// +// If IsJunk is defined, first the longest matching block is +// determined as above, but with the additional restriction that no +// junk element appears in the block. Then that block is extended as +// far as possible by matching (only) junk elements on both sides. So +// the resulting block never matches on junk except as identical junk +// happens to be adjacent to an "interesting" match. +// +// If no blocks match, return (alo, blo, 0). +func (m *SequenceMatcher) findLongestMatch(alo, ahi, blo, bhi int) Match { + // CAUTION: stripping common prefix or suffix would be incorrect. + // E.g., + // ab + // acab + // Longest matching block is "ab", but if common prefix is + // stripped, it's "a" (tied with "b"). UNIX(tm) diff does so + // strip, so ends up claiming that ab is changed to acab by + // inserting "ca" in the middle. That's minimal but unintuitive: + // "it's obvious" that someone inserted "ac" at the front. + // Windiff ends up at the same place as diff, but by pairing up + // the unique 'b's and then matching the first two 'a's. + besti, bestj, bestsize := alo, blo, 0 + + // find longest junk-free match + // during an iteration of the loop, j2len[j] = length of longest + // junk-free match ending with a[i-1] and b[j] + j2len := map[int]int{} + for i := alo; i != ahi; i++ { + // look at all instances of a[i] in b; note that because + // b2j has no junk keys, the loop is skipped if a[i] is junk + newj2len := map[int]int{} + for _, j := range m.b2j[m.a[i]] { + // a[i] matches b[j] + if j < blo { + continue + } + if j >= bhi { + break + } + k := j2len[j-1] + 1 + newj2len[j] = k + if k > bestsize { + besti, bestj, bestsize = i-k+1, j-k+1, k + } + } + j2len = newj2len + } + + // Extend the best by non-junk elements on each end. In particular, + // "popular" non-junk elements aren't in b2j, which greatly speeds + // the inner loop above, but also means "the best" match so far + // doesn't contain any junk *or* popular non-junk elements. + for besti > alo && bestj > blo && !m.isBJunk(m.b[bestj-1]) && + m.a[besti-1] == m.b[bestj-1] { + besti, bestj, bestsize = besti-1, bestj-1, bestsize+1 + } + for besti+bestsize < ahi && bestj+bestsize < bhi && + !m.isBJunk(m.b[bestj+bestsize]) && + m.a[besti+bestsize] == m.b[bestj+bestsize] { + bestsize += 1 + } + + // Now that we have a wholly interesting match (albeit possibly + // empty!), we may as well suck up the matching junk on each + // side of it too. Can't think of a good reason not to, and it + // saves post-processing the (possibly considerable) expense of + // figuring out what to do with it. In the case of an empty + // interesting match, this is clearly the right thing to do, + // because no other kind of match is possible in the regions. + for besti > alo && bestj > blo && m.isBJunk(m.b[bestj-1]) && + m.a[besti-1] == m.b[bestj-1] { + besti, bestj, bestsize = besti-1, bestj-1, bestsize+1 + } + for besti+bestsize < ahi && bestj+bestsize < bhi && + m.isBJunk(m.b[bestj+bestsize]) && + m.a[besti+bestsize] == m.b[bestj+bestsize] { + bestsize += 1 + } + + return Match{A: besti, B: bestj, Size: bestsize} +} + +// Return list of triples describing matching subsequences. +// +// Each triple is of the form (i, j, n), and means that +// a[i:i+n] == b[j:j+n]. The triples are monotonically increasing in +// i and in j. It's also guaranteed that if (i, j, n) and (i', j', n') are +// adjacent triples in the list, and the second is not the last triple in the +// list, then i+n != i' or j+n != j'. IOW, adjacent triples never describe +// adjacent equal blocks. +// +// The last triple is a dummy, (len(a), len(b), 0), and is the only +// triple with n==0. +func (m *SequenceMatcher) GetMatchingBlocks() []Match { + if m.matchingBlocks != nil { + return m.matchingBlocks + } + + var matchBlocks func(alo, ahi, blo, bhi int, matched []Match) []Match + matchBlocks = func(alo, ahi, blo, bhi int, matched []Match) []Match { + match := m.findLongestMatch(alo, ahi, blo, bhi) + i, j, k := match.A, match.B, match.Size + if match.Size > 0 { + if alo < i && blo < j { + matched = matchBlocks(alo, i, blo, j, matched) + } + matched = append(matched, match) + if i+k < ahi && j+k < bhi { + matched = matchBlocks(i+k, ahi, j+k, bhi, matched) + } + } + return matched + } + matched := matchBlocks(0, len(m.a), 0, len(m.b), nil) + + // It's possible that we have adjacent equal blocks in the + // matching_blocks list now. + nonAdjacent := []Match{} + i1, j1, k1 := 0, 0, 0 + for _, b := range matched { + // Is this block adjacent to i1, j1, k1? + i2, j2, k2 := b.A, b.B, b.Size + if i1+k1 == i2 && j1+k1 == j2 { + // Yes, so collapse them -- this just increases the length of + // the first block by the length of the second, and the first + // block so lengthened remains the block to compare against. + k1 += k2 + } else { + // Not adjacent. Remember the first block (k1==0 means it's + // the dummy we started with), and make the second block the + // new block to compare against. + if k1 > 0 { + nonAdjacent = append(nonAdjacent, Match{i1, j1, k1}) + } + i1, j1, k1 = i2, j2, k2 + } + } + if k1 > 0 { + nonAdjacent = append(nonAdjacent, Match{i1, j1, k1}) + } + + nonAdjacent = append(nonAdjacent, Match{len(m.a), len(m.b), 0}) + m.matchingBlocks = nonAdjacent + return m.matchingBlocks +} + +// Return list of 5-tuples describing how to turn a into b. +// +// Each tuple is of the form (tag, i1, i2, j1, j2). The first tuple +// has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the +// tuple preceding it, and likewise for j1 == the previous j2. +// +// The tags are characters, with these meanings: +// +// 'r' (replace): a[i1:i2] should be replaced by b[j1:j2] +// +// 'd' (delete): a[i1:i2] should be deleted, j1==j2 in this case. +// +// 'i' (insert): b[j1:j2] should be inserted at a[i1:i1], i1==i2 in this case. +// +// 'e' (equal): a[i1:i2] == b[j1:j2] +func (m *SequenceMatcher) GetOpCodes() []OpCode { + if m.opCodes != nil { + return m.opCodes + } + i, j := 0, 0 + matching := m.GetMatchingBlocks() + opCodes := make([]OpCode, 0, len(matching)) + for _, m := range matching { + // invariant: we've pumped out correct diffs to change + // a[:i] into b[:j], and the next matching block is + // a[ai:ai+size] == b[bj:bj+size]. So we need to pump + // out a diff to change a[i:ai] into b[j:bj], pump out + // the matching block, and move (i,j) beyond the match + ai, bj, size := m.A, m.B, m.Size + tag := byte(0) + if i < ai && j < bj { + tag = 'r' + } else if i < ai { + tag = 'd' + } else if j < bj { + tag = 'i' + } + if tag > 0 { + opCodes = append(opCodes, OpCode{tag, i, ai, j, bj}) + } + i, j = ai+size, bj+size + // the list of matching blocks is terminated by a + // sentinel with size 0 + if size > 0 { + opCodes = append(opCodes, OpCode{'e', ai, i, bj, j}) + } + } + m.opCodes = opCodes + return m.opCodes +} + +// Isolate change clusters by eliminating ranges with no changes. +// +// Return a generator of groups with up to n lines of context. +// Each group is in the same format as returned by GetOpCodes(). +func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode { + if n < 0 { + n = 3 + } + codes := m.GetOpCodes() + if len(codes) == 0 { + codes = []OpCode{OpCode{'e', 0, 1, 0, 1}} + } + // Fixup leading and trailing groups if they show no changes. + if codes[0].Tag == 'e' { + c := codes[0] + i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 + codes[0] = OpCode{c.Tag, max(i1, i2-n), i2, max(j1, j2-n), j2} + } + if codes[len(codes)-1].Tag == 'e' { + c := codes[len(codes)-1] + i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 + codes[len(codes)-1] = OpCode{c.Tag, i1, min(i2, i1+n), j1, min(j2, j1+n)} + } + nn := n + n + groups := [][]OpCode{} + group := []OpCode{} + for _, c := range codes { + i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 + // End the current group and start a new one whenever + // there is a large range with no changes. + if c.Tag == 'e' && i2-i1 > nn { + group = append(group, OpCode{c.Tag, i1, min(i2, i1+n), + j1, min(j2, j1+n)}) + groups = append(groups, group) + group = []OpCode{} + i1, j1 = max(i1, i2-n), max(j1, j2-n) + } + group = append(group, OpCode{c.Tag, i1, i2, j1, j2}) + } + if len(group) > 0 && !(len(group) == 1 && group[0].Tag == 'e') { + groups = append(groups, group) + } + return groups +} + +// Return a measure of the sequences' similarity (float in [0,1]). +// +// Where T is the total number of elements in both sequences, and +// M is the number of matches, this is 2.0*M / T. +// Note that this is 1 if the sequences are identical, and 0 if +// they have nothing in common. +// +// .Ratio() is expensive to compute if you haven't already computed +// .GetMatchingBlocks() or .GetOpCodes(), in which case you may +// want to try .QuickRatio() or .RealQuickRation() first to get an +// upper bound. +func (m *SequenceMatcher) Ratio() float64 { + matches := 0 + for _, m := range m.GetMatchingBlocks() { + matches += m.Size + } + return calculateRatio(matches, len(m.a)+len(m.b)) +} + +// Return an upper bound on ratio() relatively quickly. +// +// This isn't defined beyond that it is an upper bound on .Ratio(), and +// is faster to compute. +func (m *SequenceMatcher) QuickRatio() float64 { + // viewing a and b as multisets, set matches to the cardinality + // of their intersection; this counts the number of matches + // without regard to order, so is clearly an upper bound + if m.fullBCount == nil { + m.fullBCount = map[string]int{} + for _, s := range m.b { + m.fullBCount[s] = m.fullBCount[s] + 1 + } + } + + // avail[x] is the number of times x appears in 'b' less the + // number of times we've seen it in 'a' so far ... kinda + avail := map[string]int{} + matches := 0 + for _, s := range m.a { + n, ok := avail[s] + if !ok { + n = m.fullBCount[s] + } + avail[s] = n - 1 + if n > 0 { + matches += 1 + } + } + return calculateRatio(matches, len(m.a)+len(m.b)) +} + +// Return an upper bound on ratio() very quickly. +// +// This isn't defined beyond that it is an upper bound on .Ratio(), and +// is faster to compute than either .Ratio() or .QuickRatio(). +func (m *SequenceMatcher) RealQuickRatio() float64 { + la, lb := len(m.a), len(m.b) + return calculateRatio(min(la, lb), la+lb) +} + +// Convert range to the "ed" format +func formatRangeUnified(start, stop int) string { + // Per the diff spec at http://www.unix.org/single_unix_specification/ + beginning := start + 1 // lines start numbering with one + length := stop - start + if length == 1 { + return fmt.Sprintf("%d", beginning) + } + if length == 0 { + beginning -= 1 // empty ranges begin at line just before the range + } + return fmt.Sprintf("%d,%d", beginning, length) +} + +// Unified diff parameters +type UnifiedDiff struct { + A []string // First sequence lines + FromFile string // First file name + FromDate string // First file time + B []string // Second sequence lines + ToFile string // Second file name + ToDate string // Second file time + Eol string // Headers end of line, defaults to LF + Context int // Number of context lines +} + +// Compare two sequences of lines; generate the delta as a unified diff. +// +// Unified diffs are a compact way of showing line changes and a few +// lines of context. The number of context lines is set by 'n' which +// defaults to three. +// +// By default, the diff control lines (those with ---, +++, or @@) are +// created with a trailing newline. This is helpful so that inputs +// created from file.readlines() result in diffs that are suitable for +// file.writelines() since both the inputs and outputs have trailing +// newlines. +// +// For inputs that do not have trailing newlines, set the lineterm +// argument to "" so that the output will be uniformly newline free. +// +// The unidiff format normally has a header for filenames and modification +// times. Any or all of these may be specified using strings for +// 'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'. +// The modification times are normally expressed in the ISO 8601 format. +func WriteUnifiedDiff(writer io.Writer, diff UnifiedDiff) error { + buf := bufio.NewWriter(writer) + defer buf.Flush() + wf := func(format string, args ...interface{}) error { + _, err := buf.WriteString(fmt.Sprintf(format, args...)) + return err + } + ws := func(s string) error { + _, err := buf.WriteString(s) + return err + } + + if len(diff.Eol) == 0 { + diff.Eol = "\n" + } + + started := false + m := NewMatcher(diff.A, diff.B) + for _, g := range m.GetGroupedOpCodes(diff.Context) { + if !started { + started = true + fromDate := "" + if len(diff.FromDate) > 0 { + fromDate = "\t" + diff.FromDate + } + toDate := "" + if len(diff.ToDate) > 0 { + toDate = "\t" + diff.ToDate + } + if diff.FromFile != "" || diff.ToFile != "" { + err := wf("--- %s%s%s", diff.FromFile, fromDate, diff.Eol) + if err != nil { + return err + } + err = wf("+++ %s%s%s", diff.ToFile, toDate, diff.Eol) + if err != nil { + return err + } + } + } + first, last := g[0], g[len(g)-1] + range1 := formatRangeUnified(first.I1, last.I2) + range2 := formatRangeUnified(first.J1, last.J2) + if err := wf("@@ -%s +%s @@%s", range1, range2, diff.Eol); err != nil { + return err + } + for _, c := range g { + i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 + if c.Tag == 'e' { + for _, line := range diff.A[i1:i2] { + if err := ws(" " + line); err != nil { + return err + } + } + continue + } + if c.Tag == 'r' || c.Tag == 'd' { + for _, line := range diff.A[i1:i2] { + if err := ws("-" + line); err != nil { + return err + } + } + } + if c.Tag == 'r' || c.Tag == 'i' { + for _, line := range diff.B[j1:j2] { + if err := ws("+" + line); err != nil { + return err + } + } + } + } + } + return nil +} + +// Like WriteUnifiedDiff but returns the diff a string. +func GetUnifiedDiffString(diff UnifiedDiff) (string, error) { + w := &bytes.Buffer{} + err := WriteUnifiedDiff(w, diff) + return string(w.Bytes()), err +} + +// Convert range to the "ed" format. +func formatRangeContext(start, stop int) string { + // Per the diff spec at http://www.unix.org/single_unix_specification/ + beginning := start + 1 // lines start numbering with one + length := stop - start + if length == 0 { + beginning -= 1 // empty ranges begin at line just before the range + } + if length <= 1 { + return fmt.Sprintf("%d", beginning) + } + return fmt.Sprintf("%d,%d", beginning, beginning+length-1) +} + +type ContextDiff UnifiedDiff + +// Compare two sequences of lines; generate the delta as a context diff. +// +// Context diffs are a compact way of showing line changes and a few +// lines of context. The number of context lines is set by diff.Context +// which defaults to three. +// +// By default, the diff control lines (those with *** or ---) are +// created with a trailing newline. +// +// For inputs that do not have trailing newlines, set the diff.Eol +// argument to "" so that the output will be uniformly newline free. +// +// The context diff format normally has a header for filenames and +// modification times. Any or all of these may be specified using +// strings for diff.FromFile, diff.ToFile, diff.FromDate, diff.ToDate. +// The modification times are normally expressed in the ISO 8601 format. +// If not specified, the strings default to blanks. +func WriteContextDiff(writer io.Writer, diff ContextDiff) error { + buf := bufio.NewWriter(writer) + defer buf.Flush() + var diffErr error + wf := func(format string, args ...interface{}) { + _, err := buf.WriteString(fmt.Sprintf(format, args...)) + if diffErr == nil && err != nil { + diffErr = err + } + } + ws := func(s string) { + _, err := buf.WriteString(s) + if diffErr == nil && err != nil { + diffErr = err + } + } + + if len(diff.Eol) == 0 { + diff.Eol = "\n" + } + + prefix := map[byte]string{ + 'i': "+ ", + 'd': "- ", + 'r': "! ", + 'e': " ", + } + + started := false + m := NewMatcher(diff.A, diff.B) + for _, g := range m.GetGroupedOpCodes(diff.Context) { + if !started { + started = true + fromDate := "" + if len(diff.FromDate) > 0 { + fromDate = "\t" + diff.FromDate + } + toDate := "" + if len(diff.ToDate) > 0 { + toDate = "\t" + diff.ToDate + } + if diff.FromFile != "" || diff.ToFile != "" { + wf("*** %s%s%s", diff.FromFile, fromDate, diff.Eol) + wf("--- %s%s%s", diff.ToFile, toDate, diff.Eol) + } + } + + first, last := g[0], g[len(g)-1] + ws("***************" + diff.Eol) + + range1 := formatRangeContext(first.I1, last.I2) + wf("*** %s ****%s", range1, diff.Eol) + for _, c := range g { + if c.Tag == 'r' || c.Tag == 'd' { + for _, cc := range g { + if cc.Tag == 'i' { + continue + } + for _, line := range diff.A[cc.I1:cc.I2] { + ws(prefix[cc.Tag] + line) + } + } + break + } + } + + range2 := formatRangeContext(first.J1, last.J2) + wf("--- %s ----%s", range2, diff.Eol) + for _, c := range g { + if c.Tag == 'r' || c.Tag == 'i' { + for _, cc := range g { + if cc.Tag == 'd' { + continue + } + for _, line := range diff.B[cc.J1:cc.J2] { + ws(prefix[cc.Tag] + line) + } + } + break + } + } + } + return diffErr +} + +// Like WriteContextDiff but returns the diff a string. +func GetContextDiffString(diff ContextDiff) (string, error) { + w := &bytes.Buffer{} + err := WriteContextDiff(w, diff) + return string(w.Bytes()), err +} + +// Split a string on "\n" while preserving them. The output can be used +// as input for UnifiedDiff and ContextDiff structures. +func SplitLines(s string) []string { + lines := strings.SplitAfter(s, "\n") + lines[len(lines)-1] += "\n" + return lines +} diff --git a/.devenv/state/go/pkg/mod/github.com/pmezard/go-difflib@v1.0.0/difflib/difflib_test.go b/.devenv/state/go/pkg/mod/github.com/pmezard/go-difflib@v1.0.0/difflib/difflib_test.go new file mode 100644 index 0000000..d725119 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/pmezard/go-difflib@v1.0.0/difflib/difflib_test.go @@ -0,0 +1,426 @@ +package difflib + +import ( + "bytes" + "fmt" + "math" + "reflect" + "strings" + "testing" +) + +func assertAlmostEqual(t *testing.T, a, b float64, places int) { + if math.Abs(a-b) > math.Pow10(-places) { + t.Errorf("%.7f != %.7f", a, b) + } +} + +func assertEqual(t *testing.T, a, b interface{}) { + if !reflect.DeepEqual(a, b) { + t.Errorf("%v != %v", a, b) + } +} + +func splitChars(s string) []string { + chars := make([]string, 0, len(s)) + // Assume ASCII inputs + for i := 0; i != len(s); i++ { + chars = append(chars, string(s[i])) + } + return chars +} + +func TestSequenceMatcherRatio(t *testing.T) { + s := NewMatcher(splitChars("abcd"), splitChars("bcde")) + assertEqual(t, s.Ratio(), 0.75) + assertEqual(t, s.QuickRatio(), 0.75) + assertEqual(t, s.RealQuickRatio(), 1.0) +} + +func TestGetOptCodes(t *testing.T) { + a := "qabxcd" + b := "abycdf" + s := NewMatcher(splitChars(a), splitChars(b)) + w := &bytes.Buffer{} + for _, op := range s.GetOpCodes() { + fmt.Fprintf(w, "%s a[%d:%d], (%s) b[%d:%d] (%s)\n", string(op.Tag), + op.I1, op.I2, a[op.I1:op.I2], op.J1, op.J2, b[op.J1:op.J2]) + } + result := string(w.Bytes()) + expected := `d a[0:1], (q) b[0:0] () +e a[1:3], (ab) b[0:2] (ab) +r a[3:4], (x) b[2:3] (y) +e a[4:6], (cd) b[3:5] (cd) +i a[6:6], () b[5:6] (f) +` + if expected != result { + t.Errorf("unexpected op codes: \n%s", result) + } +} + +func TestGroupedOpCodes(t *testing.T) { + a := []string{} + for i := 0; i != 39; i++ { + a = append(a, fmt.Sprintf("%02d", i)) + } + b := []string{} + b = append(b, a[:8]...) + b = append(b, " i") + b = append(b, a[8:19]...) + b = append(b, " x") + b = append(b, a[20:22]...) + b = append(b, a[27:34]...) + b = append(b, " y") + b = append(b, a[35:]...) + s := NewMatcher(a, b) + w := &bytes.Buffer{} + for _, g := range s.GetGroupedOpCodes(-1) { + fmt.Fprintf(w, "group\n") + for _, op := range g { + fmt.Fprintf(w, " %s, %d, %d, %d, %d\n", string(op.Tag), + op.I1, op.I2, op.J1, op.J2) + } + } + result := string(w.Bytes()) + expected := `group + e, 5, 8, 5, 8 + i, 8, 8, 8, 9 + e, 8, 11, 9, 12 +group + e, 16, 19, 17, 20 + r, 19, 20, 20, 21 + e, 20, 22, 21, 23 + d, 22, 27, 23, 23 + e, 27, 30, 23, 26 +group + e, 31, 34, 27, 30 + r, 34, 35, 30, 31 + e, 35, 38, 31, 34 +` + if expected != result { + t.Errorf("unexpected op codes: \n%s", result) + } +} + +func ExampleGetUnifiedDiffCode() { + a := `one +two +three +four +fmt.Printf("%s,%T",a,b)` + b := `zero +one +three +four` + diff := UnifiedDiff{ + A: SplitLines(a), + B: SplitLines(b), + FromFile: "Original", + FromDate: "2005-01-26 23:30:50", + ToFile: "Current", + ToDate: "2010-04-02 10:20:52", + Context: 3, + } + result, _ := GetUnifiedDiffString(diff) + fmt.Println(strings.Replace(result, "\t", " ", -1)) + // Output: + // --- Original 2005-01-26 23:30:50 + // +++ Current 2010-04-02 10:20:52 + // @@ -1,5 +1,4 @@ + // +zero + // one + // -two + // three + // four + // -fmt.Printf("%s,%T",a,b) +} + +func ExampleGetContextDiffCode() { + a := `one +two +three +four +fmt.Printf("%s,%T",a,b)` + b := `zero +one +tree +four` + diff := ContextDiff{ + A: SplitLines(a), + B: SplitLines(b), + FromFile: "Original", + ToFile: "Current", + Context: 3, + Eol: "\n", + } + result, _ := GetContextDiffString(diff) + fmt.Print(strings.Replace(result, "\t", " ", -1)) + // Output: + // *** Original + // --- Current + // *************** + // *** 1,5 **** + // one + // ! two + // ! three + // four + // - fmt.Printf("%s,%T",a,b) + // --- 1,4 ---- + // + zero + // one + // ! tree + // four +} + +func ExampleGetContextDiffString() { + a := `one +two +three +four` + b := `zero +one +tree +four` + diff := ContextDiff{ + A: SplitLines(a), + B: SplitLines(b), + FromFile: "Original", + ToFile: "Current", + Context: 3, + Eol: "\n", + } + result, _ := GetContextDiffString(diff) + fmt.Printf(strings.Replace(result, "\t", " ", -1)) + // Output: + // *** Original + // --- Current + // *************** + // *** 1,4 **** + // one + // ! two + // ! three + // four + // --- 1,4 ---- + // + zero + // one + // ! tree + // four +} + +func rep(s string, count int) string { + return strings.Repeat(s, count) +} + +func TestWithAsciiOneInsert(t *testing.T) { + sm := NewMatcher(splitChars(rep("b", 100)), + splitChars("a"+rep("b", 100))) + assertAlmostEqual(t, sm.Ratio(), 0.995, 3) + assertEqual(t, sm.GetOpCodes(), + []OpCode{{'i', 0, 0, 0, 1}, {'e', 0, 100, 1, 101}}) + assertEqual(t, len(sm.bPopular), 0) + + sm = NewMatcher(splitChars(rep("b", 100)), + splitChars(rep("b", 50)+"a"+rep("b", 50))) + assertAlmostEqual(t, sm.Ratio(), 0.995, 3) + assertEqual(t, sm.GetOpCodes(), + []OpCode{{'e', 0, 50, 0, 50}, {'i', 50, 50, 50, 51}, {'e', 50, 100, 51, 101}}) + assertEqual(t, len(sm.bPopular), 0) +} + +func TestWithAsciiOnDelete(t *testing.T) { + sm := NewMatcher(splitChars(rep("a", 40)+"c"+rep("b", 40)), + splitChars(rep("a", 40)+rep("b", 40))) + assertAlmostEqual(t, sm.Ratio(), 0.994, 3) + assertEqual(t, sm.GetOpCodes(), + []OpCode{{'e', 0, 40, 0, 40}, {'d', 40, 41, 40, 40}, {'e', 41, 81, 40, 80}}) +} + +func TestWithAsciiBJunk(t *testing.T) { + isJunk := func(s string) bool { + return s == " " + } + sm := NewMatcherWithJunk(splitChars(rep("a", 40)+rep("b", 40)), + splitChars(rep("a", 44)+rep("b", 40)), true, isJunk) + assertEqual(t, sm.bJunk, map[string]struct{}{}) + + sm = NewMatcherWithJunk(splitChars(rep("a", 40)+rep("b", 40)), + splitChars(rep("a", 44)+rep("b", 40)+rep(" ", 20)), false, isJunk) + assertEqual(t, sm.bJunk, map[string]struct{}{" ": struct{}{}}) + + isJunk = func(s string) bool { + return s == " " || s == "b" + } + sm = NewMatcherWithJunk(splitChars(rep("a", 40)+rep("b", 40)), + splitChars(rep("a", 44)+rep("b", 40)+rep(" ", 20)), false, isJunk) + assertEqual(t, sm.bJunk, map[string]struct{}{" ": struct{}{}, "b": struct{}{}}) +} + +func TestSFBugsRatioForNullSeqn(t *testing.T) { + sm := NewMatcher(nil, nil) + assertEqual(t, sm.Ratio(), 1.0) + assertEqual(t, sm.QuickRatio(), 1.0) + assertEqual(t, sm.RealQuickRatio(), 1.0) +} + +func TestSFBugsComparingEmptyLists(t *testing.T) { + groups := NewMatcher(nil, nil).GetGroupedOpCodes(-1) + assertEqual(t, len(groups), 0) + diff := UnifiedDiff{ + FromFile: "Original", + ToFile: "Current", + Context: 3, + } + result, err := GetUnifiedDiffString(diff) + assertEqual(t, err, nil) + assertEqual(t, result, "") +} + +func TestOutputFormatRangeFormatUnified(t *testing.T) { + // Per the diff spec at http://www.unix.org/single_unix_specification/ + // + // Each <range> field shall be of the form: + // %1d", <beginning line number> if the range contains exactly one line, + // and: + // "%1d,%1d", <beginning line number>, <number of lines> otherwise. + // If a range is empty, its beginning line number shall be the number of + // the line just before the range, or 0 if the empty range starts the file. + fm := formatRangeUnified + assertEqual(t, fm(3, 3), "3,0") + assertEqual(t, fm(3, 4), "4") + assertEqual(t, fm(3, 5), "4,2") + assertEqual(t, fm(3, 6), "4,3") + assertEqual(t, fm(0, 0), "0,0") +} + +func TestOutputFormatRangeFormatContext(t *testing.T) { + // Per the diff spec at http://www.unix.org/single_unix_specification/ + // + // The range of lines in file1 shall be written in the following format + // if the range contains two or more lines: + // "*** %d,%d ****\n", <beginning line number>, <ending line number> + // and the following format otherwise: + // "*** %d ****\n", <ending line number> + // The ending line number of an empty range shall be the number of the preceding line, + // or 0 if the range is at the start of the file. + // + // Next, the range of lines in file2 shall be written in the following format + // if the range contains two or more lines: + // "--- %d,%d ----\n", <beginning line number>, <ending line number> + // and the following format otherwise: + // "--- %d ----\n", <ending line number> + fm := formatRangeContext + assertEqual(t, fm(3, 3), "3") + assertEqual(t, fm(3, 4), "4") + assertEqual(t, fm(3, 5), "4,5") + assertEqual(t, fm(3, 6), "4,6") + assertEqual(t, fm(0, 0), "0") +} + +func TestOutputFormatTabDelimiter(t *testing.T) { + diff := UnifiedDiff{ + A: splitChars("one"), + B: splitChars("two"), + FromFile: "Original", + FromDate: "2005-01-26 23:30:50", + ToFile: "Current", + ToDate: "2010-04-12 10:20:52", + Eol: "\n", + } + ud, err := GetUnifiedDiffString(diff) + assertEqual(t, err, nil) + assertEqual(t, SplitLines(ud)[:2], []string{ + "--- Original\t2005-01-26 23:30:50\n", + "+++ Current\t2010-04-12 10:20:52\n", + }) + cd, err := GetContextDiffString(ContextDiff(diff)) + assertEqual(t, err, nil) + assertEqual(t, SplitLines(cd)[:2], []string{ + "*** Original\t2005-01-26 23:30:50\n", + "--- Current\t2010-04-12 10:20:52\n", + }) +} + +func TestOutputFormatNoTrailingTabOnEmptyFiledate(t *testing.T) { + diff := UnifiedDiff{ + A: splitChars("one"), + B: splitChars("two"), + FromFile: "Original", + ToFile: "Current", + Eol: "\n", + } + ud, err := GetUnifiedDiffString(diff) + assertEqual(t, err, nil) + assertEqual(t, SplitLines(ud)[:2], []string{"--- Original\n", "+++ Current\n"}) + + cd, err := GetContextDiffString(ContextDiff(diff)) + assertEqual(t, err, nil) + assertEqual(t, SplitLines(cd)[:2], []string{"*** Original\n", "--- Current\n"}) +} + +func TestOmitFilenames(t *testing.T) { + diff := UnifiedDiff{ + A: SplitLines("o\nn\ne\n"), + B: SplitLines("t\nw\no\n"), + Eol: "\n", + } + ud, err := GetUnifiedDiffString(diff) + assertEqual(t, err, nil) + assertEqual(t, SplitLines(ud), []string{ + "@@ -0,0 +1,2 @@\n", + "+t\n", + "+w\n", + "@@ -2,2 +3,0 @@\n", + "-n\n", + "-e\n", + "\n", + }) + + cd, err := GetContextDiffString(ContextDiff(diff)) + assertEqual(t, err, nil) + assertEqual(t, SplitLines(cd), []string{ + "***************\n", + "*** 0 ****\n", + "--- 1,2 ----\n", + "+ t\n", + "+ w\n", + "***************\n", + "*** 2,3 ****\n", + "- n\n", + "- e\n", + "--- 3 ----\n", + "\n", + }) +} + +func TestSplitLines(t *testing.T) { + allTests := []struct { + input string + want []string + }{ + {"foo", []string{"foo\n"}}, + {"foo\nbar", []string{"foo\n", "bar\n"}}, + {"foo\nbar\n", []string{"foo\n", "bar\n", "\n"}}, + } + for _, test := range allTests { + assertEqual(t, SplitLines(test.input), test.want) + } +} + +func benchmarkSplitLines(b *testing.B, count int) { + str := strings.Repeat("foo\n", count) + + b.ResetTimer() + + n := 0 + for i := 0; i < b.N; i++ { + n += len(SplitLines(str)) + } +} + +func BenchmarkSplitLines100(b *testing.B) { + benchmarkSplitLines(b, 100) +} + +func BenchmarkSplitLines10000(b *testing.B) { + benchmarkSplitLines(b, 10000) +} diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/.ci.gofmt.sh b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/.ci.gofmt.sh new file mode 100644 index 0000000..3f2e5d3 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/.ci.gofmt.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +if [ -n "$(gofmt -l .)" ]; then + echo "Go code is not formatted:" + gofmt -d . + exit 1 +fi + +go generate ./... +if [ -n "$(git status -s -uno)" ]; then + echo "Go generate output does not match commit." + echo "Did you forget to run go generate ./... ?" + exit 1 +fi diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/.ci.gogenerate.sh b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/.ci.gogenerate.sh new file mode 100644 index 0000000..3fc73fe --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/.ci.gogenerate.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# If GOMOD is defined we are running with Go Modules enabled, either +# automatically or via the GO111MODULE=on environment variable. Codegen only +# works with modules, so skip generation if modules is not in use. +if [[ -z "$(go env GOMOD)" ]]; then + echo "Skipping go generate because modules not enabled and required" + exit 0 +fi + +go generate ./... +if [ -n "$(git diff)" ]; then + echo "Go generate had not been run" + git diff + exit 1 +fi diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/.ci.govet.sh b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/.ci.govet.sh new file mode 100644 index 0000000..77aeb5c --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/.ci.govet.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +set -e + +go vet ./... diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/.github/dependabot.yml b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/.github/dependabot.yml new file mode 100644 index 0000000..bf6944e --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: +- package-ecosystem: gomod + directory: / + schedule: + interval: daily +- package-ecosystem: github-actions + directory: / + schedule: + interval: daily diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/.github/pull_request_template.md b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/.github/pull_request_template.md new file mode 100644 index 0000000..ba09b9b --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/.github/pull_request_template.md @@ -0,0 +1,15 @@ +## Summary +<!-- High-level, one sentence summary of what this PR accomplishes --> + +## Changes +<!-- * Description of change 1 --> +<!-- * Description of change 2 --> +<!-- ... --> + +## Motivation +<!-- Why were the changes necessary. --> + +<!-- ## Example usage (if applicable) --> + +## Related issues +<!-- Put `Closes #XXXX` for each issue number this PR fixes/closes --> diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/.github/workflows/main.yml b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/.github/workflows/main.yml new file mode 100644 index 0000000..12a23ea --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/.github/workflows/main.yml @@ -0,0 +1,19 @@ +name: All builds +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + go_version: ["1.18.1", "1.17.6", "1.16.5"] + steps: + - uses: actions/checkout@v3 + - name: Setup Go + uses: actions/setup-go@v3.2.0 + with: + go-version: ${{ matrix.go_version }} + - run: ./.ci.gogenerate.sh + - run: ./.ci.gofmt.sh + - run: ./.ci.govet.sh + - run: go test -v -race ./... diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/.gitignore b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/.gitignore new file mode 100644 index 0000000..5aacdb7 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe + +.DS_Store diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/CONTRIBUTING.md b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/CONTRIBUTING.md new file mode 100644 index 0000000..a3a062c --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/CONTRIBUTING.md @@ -0,0 +1,50 @@ +# Contributing to Testify + +So you'd like to contribute to Testify? First of all, thank you! Testify is widely used, so each +contribution has a significant impact within the Golang community! Below you'll find everything you +need to know to get up to speed on the project. + +## Philosophy + +The Testify maintainers generally attempt to follow widely accepted practices within the Golang +community. That being said, the first priority is always to make sure that the package is useful to +the community. A few general guidelines are listed here: + +*Keep it simple (whenever practical)* - Try not to expand the API unless the new surface area +provides meaningful benefits. For example, don't add functions because they might be useful to +someone, someday. Add what is useful to specific users, today. + +*Ease of use is paramount* - This means good documentation and package organization. It also means +that we should try hard to use meaningful, descriptive function names, avoid breaking the API +unnecessarily, and try not to surprise the user. + +*Quality isn't an afterthought* - Testify is a testing library, so it seems reasonable that we +should have a decent test suite. This is doubly important because a bug in Testify doesn't just mean +a bug in our users' code, it means a bug in our users' tests, which means a potentially unnoticed +and hard-to-find bug in our users' code. + +## Pull Requests + +We welcome pull requests! Please include the following in the description: + + * Motivation, why your change is important or helpful + * Example usage (if applicable) + * Whether you intend to add / change behavior or fix a bug + +Please be aware that the maintainers may ask for changes. This isn't a commentary on the quality of +your idea or your code. Testify is the result of many contributions from many individuals, so we +need to enforce certain practices and patterns to keep the package easy for others to understand. +Essentially, we recognize that there are often many good ways to do a given thing, but we have to +pick one and stick with it. + +See `MAINTAINERS.md` for a list of users who can approve / merge your changes. + +## Issues + +If you find a bug or think of a useful feature you'd like to see added to Testify, the best thing +you can do is make the necessary changes and open a pull request (see above). If that isn't an +option, or if you'd like to discuss your change before you write the code, open an issue! + +Please provide enough context in the issue description that other members of the community can +easily understand what it is that you'd like to see. + diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/LICENSE b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/LICENSE new file mode 100644 index 0000000..4b0421c --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/MAINTAINERS.md b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/MAINTAINERS.md new file mode 100644 index 0000000..b6c1dde --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/MAINTAINERS.md @@ -0,0 +1,9 @@ +# Testify Maintainers + +The individuals listed below are active in the project and have the ability to approve and merge +pull requests. + + * @glesica + * @boyan-soubachov + * @mvdkleijn + diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/README.md b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/README.md new file mode 100644 index 0000000..ce6d3de --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/README.md @@ -0,0 +1,371 @@ +Testify - Thou Shalt Write Tests +================================ + +â„¹ï¸ We are working on testify v2 and would love to hear what you'd like to see in it, have your say here: https://cutt.ly/testify + +[](https://travis-ci.org/stretchr/testify) [](https://goreportcard.com/report/github.com/stretchr/testify) [](https://pkg.go.dev/github.com/stretchr/testify) + +Go code (golang) set of packages that provide many tools for testifying that your code will behave as you intend. + +Features include: + + * [Easy assertions](#assert-package) + * [Mocking](#mock-package) + * [Testing suite interfaces and functions](#suite-package) + +Get started: + + * Install testify with [one line of code](#installation), or [update it with another](#staying-up-to-date) + * For an introduction to writing test code in Go, see http://golang.org/doc/code.html#Testing + * Check out the API Documentation http://godoc.org/github.com/stretchr/testify + * To make your testing life easier, check out our other project, [gorc](http://github.com/stretchr/gorc) + * A little about [Test-Driven Development (TDD)](http://en.wikipedia.org/wiki/Test-driven_development) + + + +[`assert`](http://godoc.org/github.com/stretchr/testify/assert "API documentation") package +------------------------------------------------------------------------------------------- + +The `assert` package provides some helpful methods that allow you to write better test code in Go. + + * Prints friendly, easy to read failure descriptions + * Allows for very readable code + * Optionally annotate each assertion with a message + +See it in action: + +```go +package yours + +import ( + "testing" + "github.com/stretchr/testify/assert" +) + +func TestSomething(t *testing.T) { + + // assert equality + assert.Equal(t, 123, 123, "they should be equal") + + // assert inequality + assert.NotEqual(t, 123, 456, "they should not be equal") + + // assert for nil (good for errors) + assert.Nil(t, object) + + // assert for not nil (good when you expect something) + if assert.NotNil(t, object) { + + // now we know that object isn't nil, we are safe to make + // further assertions without causing any errors + assert.Equal(t, "Something", object.Value) + + } + +} +``` + + * Every assert func takes the `testing.T` object as the first argument. This is how it writes the errors out through the normal `go test` capabilities. + * Every assert func returns a bool indicating whether the assertion was successful or not, this is useful for if you want to go on making further assertions under certain conditions. + +if you assert many times, use the below: + +```go +package yours + +import ( + "testing" + "github.com/stretchr/testify/assert" +) + +func TestSomething(t *testing.T) { + assert := assert.New(t) + + // assert equality + assert.Equal(123, 123, "they should be equal") + + // assert inequality + assert.NotEqual(123, 456, "they should not be equal") + + // assert for nil (good for errors) + assert.Nil(object) + + // assert for not nil (good when you expect something) + if assert.NotNil(object) { + + // now we know that object isn't nil, we are safe to make + // further assertions without causing any errors + assert.Equal("Something", object.Value) + } +} +``` + +[`require`](http://godoc.org/github.com/stretchr/testify/require "API documentation") package +--------------------------------------------------------------------------------------------- + +The `require` package provides same global functions as the `assert` package, but instead of returning a boolean result they terminate current test. + +See [t.FailNow](http://golang.org/pkg/testing/#T.FailNow) for details. + +[`mock`](http://godoc.org/github.com/stretchr/testify/mock "API documentation") package +---------------------------------------------------------------------------------------- + +The `mock` package provides a mechanism for easily writing mock objects that can be used in place of real objects when writing test code. + +An example test function that tests a piece of code that relies on an external object `testObj`, can setup expectations (testify) and assert that they indeed happened: + +```go +package yours + +import ( + "testing" + "github.com/stretchr/testify/mock" +) + +/* + Test objects +*/ + +// MyMockedObject is a mocked object that implements an interface +// that describes an object that the code I am testing relies on. +type MyMockedObject struct{ + mock.Mock +} + +// DoSomething is a method on MyMockedObject that implements some interface +// and just records the activity, and returns what the Mock object tells it to. +// +// In the real object, this method would do something useful, but since this +// is a mocked object - we're just going to stub it out. +// +// NOTE: This method is not being tested here, code that uses this object is. +func (m *MyMockedObject) DoSomething(number int) (bool, error) { + + args := m.Called(number) + return args.Bool(0), args.Error(1) + +} + +/* + Actual test functions +*/ + +// TestSomething is an example of how to use our test object to +// make assertions about some target code we are testing. +func TestSomething(t *testing.T) { + + // create an instance of our test object + testObj := new(MyMockedObject) + + // setup expectations + testObj.On("DoSomething", 123).Return(true, nil) + + // call the code we are testing + targetFuncThatDoesSomethingWithObj(testObj) + + // assert that the expectations were met + testObj.AssertExpectations(t) + + +} + +// TestSomethingWithPlaceholder is a second example of how to use our test object to +// make assertions about some target code we are testing. +// This time using a placeholder. Placeholders might be used when the +// data being passed in is normally dynamically generated and cannot be +// predicted beforehand (eg. containing hashes that are time sensitive) +func TestSomethingWithPlaceholder(t *testing.T) { + + // create an instance of our test object + testObj := new(MyMockedObject) + + // setup expectations with a placeholder in the argument list + testObj.On("DoSomething", mock.Anything).Return(true, nil) + + // call the code we are testing + targetFuncThatDoesSomethingWithObj(testObj) + + // assert that the expectations were met + testObj.AssertExpectations(t) + + +} + +// TestSomethingElse2 is a third example that shows how you can use +// the Unset method to cleanup handlers and then add new ones. +func TestSomethingElse2(t *testing.T) { + + // create an instance of our test object + testObj := new(MyMockedObject) + + // setup expectations with a placeholder in the argument list + mockCall := testObj.On("DoSomething", mock.Anything).Return(true, nil) + + // call the code we are testing + targetFuncThatDoesSomethingWithObj(testObj) + + // assert that the expectations were met + testObj.AssertExpectations(t) + + // remove the handler now so we can add another one that takes precedence + mockCall.Unset() + + // return false now instead of true + testObj.On("DoSomething", mock.Anything).Return(false, nil) + + testObj.AssertExpectations(t) +} +``` + +For more information on how to write mock code, check out the [API documentation for the `mock` package](http://godoc.org/github.com/stretchr/testify/mock). + +You can use the [mockery tool](http://github.com/vektra/mockery) to autogenerate the mock code against an interface as well, making using mocks much quicker. + +[`suite`](http://godoc.org/github.com/stretchr/testify/suite "API documentation") package +----------------------------------------------------------------------------------------- + +The `suite` package provides functionality that you might be used to from more common object oriented languages. With it, you can build a testing suite as a struct, build setup/teardown methods and testing methods on your struct, and run them with 'go test' as per normal. + +An example suite is shown below: + +```go +// Basic imports +import ( + "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +// Define the suite, and absorb the built-in basic suite +// functionality from testify - including a T() method which +// returns the current testing context +type ExampleTestSuite struct { + suite.Suite + VariableThatShouldStartAtFive int +} + +// Make sure that VariableThatShouldStartAtFive is set to five +// before each test +func (suite *ExampleTestSuite) SetupTest() { + suite.VariableThatShouldStartAtFive = 5 +} + +// All methods that begin with "Test" are run as tests within a +// suite. +func (suite *ExampleTestSuite) TestExample() { + assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive) +} + +// In order for 'go test' to run this suite, we need to create +// a normal test function and pass our suite to suite.Run +func TestExampleTestSuite(t *testing.T) { + suite.Run(t, new(ExampleTestSuite)) +} +``` + +For a more complete example, using all of the functionality provided by the suite package, look at our [example testing suite](https://github.com/stretchr/testify/blob/master/suite/suite_test.go) + +For more information on writing suites, check out the [API documentation for the `suite` package](http://godoc.org/github.com/stretchr/testify/suite). + +`Suite` object has assertion methods: + +```go +// Basic imports +import ( + "testing" + "github.com/stretchr/testify/suite" +) + +// Define the suite, and absorb the built-in basic suite +// functionality from testify - including assertion methods. +type ExampleTestSuite struct { + suite.Suite + VariableThatShouldStartAtFive int +} + +// Make sure that VariableThatShouldStartAtFive is set to five +// before each test +func (suite *ExampleTestSuite) SetupTest() { + suite.VariableThatShouldStartAtFive = 5 +} + +// All methods that begin with "Test" are run as tests within a +// suite. +func (suite *ExampleTestSuite) TestExample() { + suite.Equal(suite.VariableThatShouldStartAtFive, 5) +} + +// In order for 'go test' to run this suite, we need to create +// a normal test function and pass our suite to suite.Run +func TestExampleTestSuite(t *testing.T) { + suite.Run(t, new(ExampleTestSuite)) +} +``` + +------ + +Installation +============ + +To install Testify, use `go get`: + + go get github.com/stretchr/testify + +This will then make the following packages available to you: + + github.com/stretchr/testify/assert + github.com/stretchr/testify/require + github.com/stretchr/testify/mock + github.com/stretchr/testify/suite + github.com/stretchr/testify/http (deprecated) + +Import the `testify/assert` package into your code using this template: + +```go +package yours + +import ( + "testing" + "github.com/stretchr/testify/assert" +) + +func TestSomething(t *testing.T) { + + assert.True(t, true, "True is true!") + +} +``` + +------ + +Staying up to date +================== + +To update Testify to the latest version, use `go get -u github.com/stretchr/testify`. + +------ + +Supported go versions +================== + +We currently support the most recent major Go versions from 1.13 onward. + +------ + +Contributing +============ + +Please feel free to submit issues, fork the repository and send pull requests! + +When submitting an issue, we ask that you please include a complete test function that demonstrates the issue. Extra credit for those using Testify to write the test code that demonstrates it. + +Code generation is used. Look for `CODE GENERATED AUTOMATICALLY` at the top of some files. Run `go generate ./...` to update generated files. + +We also chat on the [Gophers Slack](https://gophers.slack.com) group in the `#testify` and `#testify-dev` channels. + +------ + +License +======= + +This project is licensed under the terms of the MIT license. diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_compare.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_compare.go new file mode 100644 index 0000000..95d8e59 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_compare.go @@ -0,0 +1,458 @@ +package assert + +import ( + "bytes" + "fmt" + "reflect" + "time" +) + +type CompareType int + +const ( + compareLess CompareType = iota - 1 + compareEqual + compareGreater +) + +var ( + intType = reflect.TypeOf(int(1)) + int8Type = reflect.TypeOf(int8(1)) + int16Type = reflect.TypeOf(int16(1)) + int32Type = reflect.TypeOf(int32(1)) + int64Type = reflect.TypeOf(int64(1)) + + uintType = reflect.TypeOf(uint(1)) + uint8Type = reflect.TypeOf(uint8(1)) + uint16Type = reflect.TypeOf(uint16(1)) + uint32Type = reflect.TypeOf(uint32(1)) + uint64Type = reflect.TypeOf(uint64(1)) + + float32Type = reflect.TypeOf(float32(1)) + float64Type = reflect.TypeOf(float64(1)) + + stringType = reflect.TypeOf("") + + timeType = reflect.TypeOf(time.Time{}) + bytesType = reflect.TypeOf([]byte{}) +) + +func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { + obj1Value := reflect.ValueOf(obj1) + obj2Value := reflect.ValueOf(obj2) + + // throughout this switch we try and avoid calling .Convert() if possible, + // as this has a pretty big performance impact + switch kind { + case reflect.Int: + { + intobj1, ok := obj1.(int) + if !ok { + intobj1 = obj1Value.Convert(intType).Interface().(int) + } + intobj2, ok := obj2.(int) + if !ok { + intobj2 = obj2Value.Convert(intType).Interface().(int) + } + if intobj1 > intobj2 { + return compareGreater, true + } + if intobj1 == intobj2 { + return compareEqual, true + } + if intobj1 < intobj2 { + return compareLess, true + } + } + case reflect.Int8: + { + int8obj1, ok := obj1.(int8) + if !ok { + int8obj1 = obj1Value.Convert(int8Type).Interface().(int8) + } + int8obj2, ok := obj2.(int8) + if !ok { + int8obj2 = obj2Value.Convert(int8Type).Interface().(int8) + } + if int8obj1 > int8obj2 { + return compareGreater, true + } + if int8obj1 == int8obj2 { + return compareEqual, true + } + if int8obj1 < int8obj2 { + return compareLess, true + } + } + case reflect.Int16: + { + int16obj1, ok := obj1.(int16) + if !ok { + int16obj1 = obj1Value.Convert(int16Type).Interface().(int16) + } + int16obj2, ok := obj2.(int16) + if !ok { + int16obj2 = obj2Value.Convert(int16Type).Interface().(int16) + } + if int16obj1 > int16obj2 { + return compareGreater, true + } + if int16obj1 == int16obj2 { + return compareEqual, true + } + if int16obj1 < int16obj2 { + return compareLess, true + } + } + case reflect.Int32: + { + int32obj1, ok := obj1.(int32) + if !ok { + int32obj1 = obj1Value.Convert(int32Type).Interface().(int32) + } + int32obj2, ok := obj2.(int32) + if !ok { + int32obj2 = obj2Value.Convert(int32Type).Interface().(int32) + } + if int32obj1 > int32obj2 { + return compareGreater, true + } + if int32obj1 == int32obj2 { + return compareEqual, true + } + if int32obj1 < int32obj2 { + return compareLess, true + } + } + case reflect.Int64: + { + int64obj1, ok := obj1.(int64) + if !ok { + int64obj1 = obj1Value.Convert(int64Type).Interface().(int64) + } + int64obj2, ok := obj2.(int64) + if !ok { + int64obj2 = obj2Value.Convert(int64Type).Interface().(int64) + } + if int64obj1 > int64obj2 { + return compareGreater, true + } + if int64obj1 == int64obj2 { + return compareEqual, true + } + if int64obj1 < int64obj2 { + return compareLess, true + } + } + case reflect.Uint: + { + uintobj1, ok := obj1.(uint) + if !ok { + uintobj1 = obj1Value.Convert(uintType).Interface().(uint) + } + uintobj2, ok := obj2.(uint) + if !ok { + uintobj2 = obj2Value.Convert(uintType).Interface().(uint) + } + if uintobj1 > uintobj2 { + return compareGreater, true + } + if uintobj1 == uintobj2 { + return compareEqual, true + } + if uintobj1 < uintobj2 { + return compareLess, true + } + } + case reflect.Uint8: + { + uint8obj1, ok := obj1.(uint8) + if !ok { + uint8obj1 = obj1Value.Convert(uint8Type).Interface().(uint8) + } + uint8obj2, ok := obj2.(uint8) + if !ok { + uint8obj2 = obj2Value.Convert(uint8Type).Interface().(uint8) + } + if uint8obj1 > uint8obj2 { + return compareGreater, true + } + if uint8obj1 == uint8obj2 { + return compareEqual, true + } + if uint8obj1 < uint8obj2 { + return compareLess, true + } + } + case reflect.Uint16: + { + uint16obj1, ok := obj1.(uint16) + if !ok { + uint16obj1 = obj1Value.Convert(uint16Type).Interface().(uint16) + } + uint16obj2, ok := obj2.(uint16) + if !ok { + uint16obj2 = obj2Value.Convert(uint16Type).Interface().(uint16) + } + if uint16obj1 > uint16obj2 { + return compareGreater, true + } + if uint16obj1 == uint16obj2 { + return compareEqual, true + } + if uint16obj1 < uint16obj2 { + return compareLess, true + } + } + case reflect.Uint32: + { + uint32obj1, ok := obj1.(uint32) + if !ok { + uint32obj1 = obj1Value.Convert(uint32Type).Interface().(uint32) + } + uint32obj2, ok := obj2.(uint32) + if !ok { + uint32obj2 = obj2Value.Convert(uint32Type).Interface().(uint32) + } + if uint32obj1 > uint32obj2 { + return compareGreater, true + } + if uint32obj1 == uint32obj2 { + return compareEqual, true + } + if uint32obj1 < uint32obj2 { + return compareLess, true + } + } + case reflect.Uint64: + { + uint64obj1, ok := obj1.(uint64) + if !ok { + uint64obj1 = obj1Value.Convert(uint64Type).Interface().(uint64) + } + uint64obj2, ok := obj2.(uint64) + if !ok { + uint64obj2 = obj2Value.Convert(uint64Type).Interface().(uint64) + } + if uint64obj1 > uint64obj2 { + return compareGreater, true + } + if uint64obj1 == uint64obj2 { + return compareEqual, true + } + if uint64obj1 < uint64obj2 { + return compareLess, true + } + } + case reflect.Float32: + { + float32obj1, ok := obj1.(float32) + if !ok { + float32obj1 = obj1Value.Convert(float32Type).Interface().(float32) + } + float32obj2, ok := obj2.(float32) + if !ok { + float32obj2 = obj2Value.Convert(float32Type).Interface().(float32) + } + if float32obj1 > float32obj2 { + return compareGreater, true + } + if float32obj1 == float32obj2 { + return compareEqual, true + } + if float32obj1 < float32obj2 { + return compareLess, true + } + } + case reflect.Float64: + { + float64obj1, ok := obj1.(float64) + if !ok { + float64obj1 = obj1Value.Convert(float64Type).Interface().(float64) + } + float64obj2, ok := obj2.(float64) + if !ok { + float64obj2 = obj2Value.Convert(float64Type).Interface().(float64) + } + if float64obj1 > float64obj2 { + return compareGreater, true + } + if float64obj1 == float64obj2 { + return compareEqual, true + } + if float64obj1 < float64obj2 { + return compareLess, true + } + } + case reflect.String: + { + stringobj1, ok := obj1.(string) + if !ok { + stringobj1 = obj1Value.Convert(stringType).Interface().(string) + } + stringobj2, ok := obj2.(string) + if !ok { + stringobj2 = obj2Value.Convert(stringType).Interface().(string) + } + if stringobj1 > stringobj2 { + return compareGreater, true + } + if stringobj1 == stringobj2 { + return compareEqual, true + } + if stringobj1 < stringobj2 { + return compareLess, true + } + } + // Check for known struct types we can check for compare results. + case reflect.Struct: + { + // All structs enter here. We're not interested in most types. + if !canConvert(obj1Value, timeType) { + break + } + + // time.Time can compared! + timeObj1, ok := obj1.(time.Time) + if !ok { + timeObj1 = obj1Value.Convert(timeType).Interface().(time.Time) + } + + timeObj2, ok := obj2.(time.Time) + if !ok { + timeObj2 = obj2Value.Convert(timeType).Interface().(time.Time) + } + + return compare(timeObj1.UnixNano(), timeObj2.UnixNano(), reflect.Int64) + } + case reflect.Slice: + { + // We only care about the []byte type. + if !canConvert(obj1Value, bytesType) { + break + } + + // []byte can be compared! + bytesObj1, ok := obj1.([]byte) + if !ok { + bytesObj1 = obj1Value.Convert(bytesType).Interface().([]byte) + + } + bytesObj2, ok := obj2.([]byte) + if !ok { + bytesObj2 = obj2Value.Convert(bytesType).Interface().([]byte) + } + + return CompareType(bytes.Compare(bytesObj1, bytesObj2)), true + } + } + + return compareEqual, false +} + +// Greater asserts that the first element is greater than the second +// +// assert.Greater(t, 2, 1) +// assert.Greater(t, float64(2), float64(1)) +// assert.Greater(t, "b", "a") +func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return compareTwoValues(t, e1, e2, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) +} + +// GreaterOrEqual asserts that the first element is greater than or equal to the second +// +// assert.GreaterOrEqual(t, 2, 1) +// assert.GreaterOrEqual(t, 2, 2) +// assert.GreaterOrEqual(t, "b", "a") +// assert.GreaterOrEqual(t, "b", "b") +func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return compareTwoValues(t, e1, e2, []CompareType{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) +} + +// Less asserts that the first element is less than the second +// +// assert.Less(t, 1, 2) +// assert.Less(t, float64(1), float64(2)) +// assert.Less(t, "a", "b") +func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return compareTwoValues(t, e1, e2, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) +} + +// LessOrEqual asserts that the first element is less than or equal to the second +// +// assert.LessOrEqual(t, 1, 2) +// assert.LessOrEqual(t, 2, 2) +// assert.LessOrEqual(t, "a", "b") +// assert.LessOrEqual(t, "b", "b") +func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) +} + +// Positive asserts that the specified element is positive +// +// assert.Positive(t, 1) +// assert.Positive(t, 1.23) +func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + zero := reflect.Zero(reflect.TypeOf(e)) + return compareTwoValues(t, e, zero.Interface(), []CompareType{compareGreater}, "\"%v\" is not positive", msgAndArgs...) +} + +// Negative asserts that the specified element is negative +// +// assert.Negative(t, -1) +// assert.Negative(t, -1.23) +func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + zero := reflect.Zero(reflect.TypeOf(e)) + return compareTwoValues(t, e, zero.Interface(), []CompareType{compareLess}, "\"%v\" is not negative", msgAndArgs...) +} + +func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + e1Kind := reflect.ValueOf(e1).Kind() + e2Kind := reflect.ValueOf(e2).Kind() + if e1Kind != e2Kind { + return Fail(t, "Elements should be the same type", msgAndArgs...) + } + + compareResult, isComparable := compare(e1, e2, e1Kind) + if !isComparable { + return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) + } + + if !containsValue(allowedComparesResults, compareResult) { + return Fail(t, fmt.Sprintf(failMessage, e1, e2), msgAndArgs...) + } + + return true +} + +func containsValue(values []CompareType, value CompareType) bool { + for _, v := range values { + if v == value { + return true + } + } + + return false +} diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_compare_can_convert.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_compare_can_convert.go new file mode 100644 index 0000000..da86790 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_compare_can_convert.go @@ -0,0 +1,16 @@ +//go:build go1.17 +// +build go1.17 + +// TODO: once support for Go 1.16 is dropped, this file can be +// merged/removed with assertion_compare_go1.17_test.go and +// assertion_compare_legacy.go + +package assert + +import "reflect" + +// Wrapper around reflect.Value.CanConvert, for compatibility +// reasons. +func canConvert(value reflect.Value, to reflect.Type) bool { + return value.CanConvert(to) +} diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_compare_go1.17_test.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_compare_go1.17_test.go new file mode 100644 index 0000000..53e01ed --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_compare_go1.17_test.go @@ -0,0 +1,182 @@ +//go:build go1.17 +// +build go1.17 + +// TODO: once support for Go 1.16 is dropped, this file can be +// merged/removed with assertion_compare_can_convert.go and +// assertion_compare_legacy.go + +package assert + +import ( + "bytes" + "reflect" + "testing" + "time" +) + +func TestCompare17(t *testing.T) { + type customTime time.Time + type customBytes []byte + for _, currCase := range []struct { + less interface{} + greater interface{} + cType string + }{ + {less: time.Now(), greater: time.Now().Add(time.Hour), cType: "time.Time"}, + {less: customTime(time.Now()), greater: customTime(time.Now().Add(time.Hour)), cType: "time.Time"}, + {less: []byte{1, 1}, greater: []byte{1, 2}, cType: "[]byte"}, + {less: customBytes([]byte{1, 1}), greater: customBytes([]byte{1, 2}), cType: "[]byte"}, + } { + resLess, isComparable := compare(currCase.less, currCase.greater, reflect.ValueOf(currCase.less).Kind()) + if !isComparable { + t.Error("object should be comparable for type " + currCase.cType) + } + + if resLess != compareLess { + t.Errorf("object less (%v) should be less than greater (%v) for type "+currCase.cType, + currCase.less, currCase.greater) + } + + resGreater, isComparable := compare(currCase.greater, currCase.less, reflect.ValueOf(currCase.less).Kind()) + if !isComparable { + t.Error("object are comparable for type " + currCase.cType) + } + + if resGreater != compareGreater { + t.Errorf("object greater should be greater than less for type " + currCase.cType) + } + + resEqual, isComparable := compare(currCase.less, currCase.less, reflect.ValueOf(currCase.less).Kind()) + if !isComparable { + t.Error("object are comparable for type " + currCase.cType) + } + + if resEqual != 0 { + t.Errorf("objects should be equal for type " + currCase.cType) + } + } +} + +func TestGreater17(t *testing.T) { + mockT := new(testing.T) + + if !Greater(mockT, 2, 1) { + t.Error("Greater should return true") + } + + if Greater(mockT, 1, 1) { + t.Error("Greater should return false") + } + + if Greater(mockT, 1, 2) { + t.Error("Greater should return false") + } + + // Check error report + for _, currCase := range []struct { + less interface{} + greater interface{} + msg string + }{ + {less: []byte{1, 1}, greater: []byte{1, 2}, msg: `"[1 1]" is not greater than "[1 2]"`}, + {less: time.Time{}, greater: time.Time{}.Add(time.Hour), msg: `"0001-01-01 00:00:00 +0000 UTC" is not greater than "0001-01-01 01:00:00 +0000 UTC"`}, + } { + out := &outputT{buf: bytes.NewBuffer(nil)} + False(t, Greater(out, currCase.less, currCase.greater)) + Contains(t, out.buf.String(), currCase.msg) + Contains(t, out.helpers, "github.com/stretchr/testify/assert.Greater") + } +} + +func TestGreaterOrEqual17(t *testing.T) { + mockT := new(testing.T) + + if !GreaterOrEqual(mockT, 2, 1) { + t.Error("GreaterOrEqual should return true") + } + + if !GreaterOrEqual(mockT, 1, 1) { + t.Error("GreaterOrEqual should return true") + } + + if GreaterOrEqual(mockT, 1, 2) { + t.Error("GreaterOrEqual should return false") + } + + // Check error report + for _, currCase := range []struct { + less interface{} + greater interface{} + msg string + }{ + {less: []byte{1, 1}, greater: []byte{1, 2}, msg: `"[1 1]" is not greater than or equal to "[1 2]"`}, + {less: time.Time{}, greater: time.Time{}.Add(time.Hour), msg: `"0001-01-01 00:00:00 +0000 UTC" is not greater than or equal to "0001-01-01 01:00:00 +0000 UTC"`}, + } { + out := &outputT{buf: bytes.NewBuffer(nil)} + False(t, GreaterOrEqual(out, currCase.less, currCase.greater)) + Contains(t, out.buf.String(), currCase.msg) + Contains(t, out.helpers, "github.com/stretchr/testify/assert.GreaterOrEqual") + } +} + +func TestLess17(t *testing.T) { + mockT := new(testing.T) + + if !Less(mockT, 1, 2) { + t.Error("Less should return true") + } + + if Less(mockT, 1, 1) { + t.Error("Less should return false") + } + + if Less(mockT, 2, 1) { + t.Error("Less should return false") + } + + // Check error report + for _, currCase := range []struct { + less interface{} + greater interface{} + msg string + }{ + {less: []byte{1, 1}, greater: []byte{1, 2}, msg: `"[1 2]" is not less than "[1 1]"`}, + {less: time.Time{}, greater: time.Time{}.Add(time.Hour), msg: `"0001-01-01 01:00:00 +0000 UTC" is not less than "0001-01-01 00:00:00 +0000 UTC"`}, + } { + out := &outputT{buf: bytes.NewBuffer(nil)} + False(t, Less(out, currCase.greater, currCase.less)) + Contains(t, out.buf.String(), currCase.msg) + Contains(t, out.helpers, "github.com/stretchr/testify/assert.Less") + } +} + +func TestLessOrEqual17(t *testing.T) { + mockT := new(testing.T) + + if !LessOrEqual(mockT, 1, 2) { + t.Error("LessOrEqual should return true") + } + + if !LessOrEqual(mockT, 1, 1) { + t.Error("LessOrEqual should return true") + } + + if LessOrEqual(mockT, 2, 1) { + t.Error("LessOrEqual should return false") + } + + // Check error report + for _, currCase := range []struct { + less interface{} + greater interface{} + msg string + }{ + {less: []byte{1, 1}, greater: []byte{1, 2}, msg: `"[1 2]" is not less than or equal to "[1 1]"`}, + {less: time.Time{}, greater: time.Time{}.Add(time.Hour), msg: `"0001-01-01 01:00:00 +0000 UTC" is not less than or equal to "0001-01-01 00:00:00 +0000 UTC"`}, + } { + out := &outputT{buf: bytes.NewBuffer(nil)} + False(t, LessOrEqual(out, currCase.greater, currCase.less)) + Contains(t, out.buf.String(), currCase.msg) + Contains(t, out.helpers, "github.com/stretchr/testify/assert.LessOrEqual") + } +} diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_compare_legacy.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_compare_legacy.go new file mode 100644 index 0000000..1701af2 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_compare_legacy.go @@ -0,0 +1,16 @@ +//go:build !go1.17 +// +build !go1.17 + +// TODO: once support for Go 1.16 is dropped, this file can be +// merged/removed with assertion_compare_go1.17_test.go and +// assertion_compare_can_convert.go + +package assert + +import "reflect" + +// Older versions of Go does not have the reflect.Value.CanConvert +// method. +func canConvert(value reflect.Value, to reflect.Type) bool { + return false +} diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_compare_test.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_compare_test.go new file mode 100644 index 0000000..a38d880 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_compare_test.go @@ -0,0 +1,449 @@ +package assert + +import ( + "bytes" + "fmt" + "reflect" + "runtime" + "testing" +) + +func TestCompare(t *testing.T) { + type customInt int + type customInt8 int8 + type customInt16 int16 + type customInt32 int32 + type customInt64 int64 + type customUInt uint + type customUInt8 uint8 + type customUInt16 uint16 + type customUInt32 uint32 + type customUInt64 uint64 + type customFloat32 float32 + type customFloat64 float64 + type customString string + for _, currCase := range []struct { + less interface{} + greater interface{} + cType string + }{ + {less: customString("a"), greater: customString("b"), cType: "string"}, + {less: "a", greater: "b", cType: "string"}, + {less: customInt(1), greater: customInt(2), cType: "int"}, + {less: int(1), greater: int(2), cType: "int"}, + {less: customInt8(1), greater: customInt8(2), cType: "int8"}, + {less: int8(1), greater: int8(2), cType: "int8"}, + {less: customInt16(1), greater: customInt16(2), cType: "int16"}, + {less: int16(1), greater: int16(2), cType: "int16"}, + {less: customInt32(1), greater: customInt32(2), cType: "int32"}, + {less: int32(1), greater: int32(2), cType: "int32"}, + {less: customInt64(1), greater: customInt64(2), cType: "int64"}, + {less: int64(1), greater: int64(2), cType: "int64"}, + {less: customUInt(1), greater: customUInt(2), cType: "uint"}, + {less: uint8(1), greater: uint8(2), cType: "uint8"}, + {less: customUInt8(1), greater: customUInt8(2), cType: "uint8"}, + {less: uint16(1), greater: uint16(2), cType: "uint16"}, + {less: customUInt16(1), greater: customUInt16(2), cType: "uint16"}, + {less: uint32(1), greater: uint32(2), cType: "uint32"}, + {less: customUInt32(1), greater: customUInt32(2), cType: "uint32"}, + {less: uint64(1), greater: uint64(2), cType: "uint64"}, + {less: customUInt64(1), greater: customUInt64(2), cType: "uint64"}, + {less: float32(1.23), greater: float32(2.34), cType: "float32"}, + {less: customFloat32(1.23), greater: customFloat32(2.23), cType: "float32"}, + {less: float64(1.23), greater: float64(2.34), cType: "float64"}, + {less: customFloat64(1.23), greater: customFloat64(2.34), cType: "float64"}, + } { + resLess, isComparable := compare(currCase.less, currCase.greater, reflect.ValueOf(currCase.less).Kind()) + if !isComparable { + t.Error("object should be comparable for type " + currCase.cType) + } + + if resLess != compareLess { + t.Errorf("object less (%v) should be less than greater (%v) for type "+currCase.cType, + currCase.less, currCase.greater) + } + + resGreater, isComparable := compare(currCase.greater, currCase.less, reflect.ValueOf(currCase.less).Kind()) + if !isComparable { + t.Error("object are comparable for type " + currCase.cType) + } + + if resGreater != compareGreater { + t.Errorf("object greater should be greater than less for type " + currCase.cType) + } + + resEqual, isComparable := compare(currCase.less, currCase.less, reflect.ValueOf(currCase.less).Kind()) + if !isComparable { + t.Error("object are comparable for type " + currCase.cType) + } + + if resEqual != 0 { + t.Errorf("objects should be equal for type " + currCase.cType) + } + } +} + +type outputT struct { + buf *bytes.Buffer + helpers map[string]struct{} +} + +// Implements TestingT +func (t *outputT) Errorf(format string, args ...interface{}) { + s := fmt.Sprintf(format, args...) + t.buf.WriteString(s) +} + +func (t *outputT) Helper() { + if t.helpers == nil { + t.helpers = make(map[string]struct{}) + } + t.helpers[callerName(1)] = struct{}{} +} + +// callerName gives the function name (qualified with a package path) +// for the caller after skip frames (where 0 means the current function). +func callerName(skip int) string { + // Make room for the skip PC. + var pc [1]uintptr + n := runtime.Callers(skip+2, pc[:]) // skip + runtime.Callers + callerName + if n == 0 { + panic("testing: zero callers found") + } + frames := runtime.CallersFrames(pc[:n]) + frame, _ := frames.Next() + return frame.Function +} + +func TestGreater(t *testing.T) { + mockT := new(testing.T) + + if !Greater(mockT, 2, 1) { + t.Error("Greater should return true") + } + + if Greater(mockT, 1, 1) { + t.Error("Greater should return false") + } + + if Greater(mockT, 1, 2) { + t.Error("Greater should return false") + } + + // Check error report + for _, currCase := range []struct { + less interface{} + greater interface{} + msg string + }{ + {less: "a", greater: "b", msg: `"a" is not greater than "b"`}, + {less: int(1), greater: int(2), msg: `"1" is not greater than "2"`}, + {less: int8(1), greater: int8(2), msg: `"1" is not greater than "2"`}, + {less: int16(1), greater: int16(2), msg: `"1" is not greater than "2"`}, + {less: int32(1), greater: int32(2), msg: `"1" is not greater than "2"`}, + {less: int64(1), greater: int64(2), msg: `"1" is not greater than "2"`}, + {less: uint8(1), greater: uint8(2), msg: `"1" is not greater than "2"`}, + {less: uint16(1), greater: uint16(2), msg: `"1" is not greater than "2"`}, + {less: uint32(1), greater: uint32(2), msg: `"1" is not greater than "2"`}, + {less: uint64(1), greater: uint64(2), msg: `"1" is not greater than "2"`}, + {less: float32(1.23), greater: float32(2.34), msg: `"1.23" is not greater than "2.34"`}, + {less: float64(1.23), greater: float64(2.34), msg: `"1.23" is not greater than "2.34"`}, + } { + out := &outputT{buf: bytes.NewBuffer(nil)} + False(t, Greater(out, currCase.less, currCase.greater)) + Contains(t, out.buf.String(), currCase.msg) + Contains(t, out.helpers, "github.com/stretchr/testify/assert.Greater") + } +} + +func TestGreaterOrEqual(t *testing.T) { + mockT := new(testing.T) + + if !GreaterOrEqual(mockT, 2, 1) { + t.Error("GreaterOrEqual should return true") + } + + if !GreaterOrEqual(mockT, 1, 1) { + t.Error("GreaterOrEqual should return true") + } + + if GreaterOrEqual(mockT, 1, 2) { + t.Error("GreaterOrEqual should return false") + } + + // Check error report + for _, currCase := range []struct { + less interface{} + greater interface{} + msg string + }{ + {less: "a", greater: "b", msg: `"a" is not greater than or equal to "b"`}, + {less: int(1), greater: int(2), msg: `"1" is not greater than or equal to "2"`}, + {less: int8(1), greater: int8(2), msg: `"1" is not greater than or equal to "2"`}, + {less: int16(1), greater: int16(2), msg: `"1" is not greater than or equal to "2"`}, + {less: int32(1), greater: int32(2), msg: `"1" is not greater than or equal to "2"`}, + {less: int64(1), greater: int64(2), msg: `"1" is not greater than or equal to "2"`}, + {less: uint8(1), greater: uint8(2), msg: `"1" is not greater than or equal to "2"`}, + {less: uint16(1), greater: uint16(2), msg: `"1" is not greater than or equal to "2"`}, + {less: uint32(1), greater: uint32(2), msg: `"1" is not greater than or equal to "2"`}, + {less: uint64(1), greater: uint64(2), msg: `"1" is not greater than or equal to "2"`}, + {less: float32(1.23), greater: float32(2.34), msg: `"1.23" is not greater than or equal to "2.34"`}, + {less: float64(1.23), greater: float64(2.34), msg: `"1.23" is not greater than or equal to "2.34"`}, + } { + out := &outputT{buf: bytes.NewBuffer(nil)} + False(t, GreaterOrEqual(out, currCase.less, currCase.greater)) + Contains(t, out.buf.String(), currCase.msg) + Contains(t, out.helpers, "github.com/stretchr/testify/assert.GreaterOrEqual") + } +} + +func TestLess(t *testing.T) { + mockT := new(testing.T) + + if !Less(mockT, 1, 2) { + t.Error("Less should return true") + } + + if Less(mockT, 1, 1) { + t.Error("Less should return false") + } + + if Less(mockT, 2, 1) { + t.Error("Less should return false") + } + + // Check error report + for _, currCase := range []struct { + less interface{} + greater interface{} + msg string + }{ + {less: "a", greater: "b", msg: `"b" is not less than "a"`}, + {less: int(1), greater: int(2), msg: `"2" is not less than "1"`}, + {less: int8(1), greater: int8(2), msg: `"2" is not less than "1"`}, + {less: int16(1), greater: int16(2), msg: `"2" is not less than "1"`}, + {less: int32(1), greater: int32(2), msg: `"2" is not less than "1"`}, + {less: int64(1), greater: int64(2), msg: `"2" is not less than "1"`}, + {less: uint8(1), greater: uint8(2), msg: `"2" is not less than "1"`}, + {less: uint16(1), greater: uint16(2), msg: `"2" is not less than "1"`}, + {less: uint32(1), greater: uint32(2), msg: `"2" is not less than "1"`}, + {less: uint64(1), greater: uint64(2), msg: `"2" is not less than "1"`}, + {less: float32(1.23), greater: float32(2.34), msg: `"2.34" is not less than "1.23"`}, + {less: float64(1.23), greater: float64(2.34), msg: `"2.34" is not less than "1.23"`}, + } { + out := &outputT{buf: bytes.NewBuffer(nil)} + False(t, Less(out, currCase.greater, currCase.less)) + Contains(t, out.buf.String(), currCase.msg) + Contains(t, out.helpers, "github.com/stretchr/testify/assert.Less") + } +} + +func TestLessOrEqual(t *testing.T) { + mockT := new(testing.T) + + if !LessOrEqual(mockT, 1, 2) { + t.Error("LessOrEqual should return true") + } + + if !LessOrEqual(mockT, 1, 1) { + t.Error("LessOrEqual should return true") + } + + if LessOrEqual(mockT, 2, 1) { + t.Error("LessOrEqual should return false") + } + + // Check error report + for _, currCase := range []struct { + less interface{} + greater interface{} + msg string + }{ + {less: "a", greater: "b", msg: `"b" is not less than or equal to "a"`}, + {less: int(1), greater: int(2), msg: `"2" is not less than or equal to "1"`}, + {less: int8(1), greater: int8(2), msg: `"2" is not less than or equal to "1"`}, + {less: int16(1), greater: int16(2), msg: `"2" is not less than or equal to "1"`}, + {less: int32(1), greater: int32(2), msg: `"2" is not less than or equal to "1"`}, + {less: int64(1), greater: int64(2), msg: `"2" is not less than or equal to "1"`}, + {less: uint8(1), greater: uint8(2), msg: `"2" is not less than or equal to "1"`}, + {less: uint16(1), greater: uint16(2), msg: `"2" is not less than or equal to "1"`}, + {less: uint32(1), greater: uint32(2), msg: `"2" is not less than or equal to "1"`}, + {less: uint64(1), greater: uint64(2), msg: `"2" is not less than or equal to "1"`}, + {less: float32(1.23), greater: float32(2.34), msg: `"2.34" is not less than or equal to "1.23"`}, + {less: float64(1.23), greater: float64(2.34), msg: `"2.34" is not less than or equal to "1.23"`}, + } { + out := &outputT{buf: bytes.NewBuffer(nil)} + False(t, LessOrEqual(out, currCase.greater, currCase.less)) + Contains(t, out.buf.String(), currCase.msg) + Contains(t, out.helpers, "github.com/stretchr/testify/assert.LessOrEqual") + } +} + +func TestPositive(t *testing.T) { + mockT := new(testing.T) + + if !Positive(mockT, 1) { + t.Error("Positive should return true") + } + + if !Positive(mockT, 1.23) { + t.Error("Positive should return true") + } + + if Positive(mockT, -1) { + t.Error("Positive should return false") + } + + if Positive(mockT, -1.23) { + t.Error("Positive should return false") + } + + // Check error report + for _, currCase := range []struct { + e interface{} + msg string + }{ + {e: int(-1), msg: `"-1" is not positive`}, + {e: int8(-1), msg: `"-1" is not positive`}, + {e: int16(-1), msg: `"-1" is not positive`}, + {e: int32(-1), msg: `"-1" is not positive`}, + {e: int64(-1), msg: `"-1" is not positive`}, + {e: float32(-1.23), msg: `"-1.23" is not positive`}, + {e: float64(-1.23), msg: `"-1.23" is not positive`}, + } { + out := &outputT{buf: bytes.NewBuffer(nil)} + False(t, Positive(out, currCase.e)) + Contains(t, out.buf.String(), currCase.msg) + Contains(t, out.helpers, "github.com/stretchr/testify/assert.Positive") + } +} + +func TestNegative(t *testing.T) { + mockT := new(testing.T) + + if !Negative(mockT, -1) { + t.Error("Negative should return true") + } + + if !Negative(mockT, -1.23) { + t.Error("Negative should return true") + } + + if Negative(mockT, 1) { + t.Error("Negative should return false") + } + + if Negative(mockT, 1.23) { + t.Error("Negative should return false") + } + + // Check error report + for _, currCase := range []struct { + e interface{} + msg string + }{ + {e: int(1), msg: `"1" is not negative`}, + {e: int8(1), msg: `"1" is not negative`}, + {e: int16(1), msg: `"1" is not negative`}, + {e: int32(1), msg: `"1" is not negative`}, + {e: int64(1), msg: `"1" is not negative`}, + {e: float32(1.23), msg: `"1.23" is not negative`}, + {e: float64(1.23), msg: `"1.23" is not negative`}, + } { + out := &outputT{buf: bytes.NewBuffer(nil)} + False(t, Negative(out, currCase.e)) + Contains(t, out.buf.String(), currCase.msg) + Contains(t, out.helpers, "github.com/stretchr/testify/assert.Negative") + } +} + +func Test_compareTwoValuesDifferentValuesTypes(t *testing.T) { + mockT := new(testing.T) + + for _, currCase := range []struct { + v1 interface{} + v2 interface{} + compareResult bool + }{ + {v1: 123, v2: "abc"}, + {v1: "abc", v2: 123456}, + {v1: float64(12), v2: "123"}, + {v1: "float(12)", v2: float64(1)}, + } { + compareResult := compareTwoValues(mockT, currCase.v1, currCase.v2, []CompareType{compareLess, compareEqual, compareGreater}, "testFailMessage") + False(t, compareResult) + } +} + +func Test_compareTwoValuesNotComparableValues(t *testing.T) { + mockT := new(testing.T) + + type CompareStruct struct { + } + + for _, currCase := range []struct { + v1 interface{} + v2 interface{} + }{ + {v1: CompareStruct{}, v2: CompareStruct{}}, + {v1: map[string]int{}, v2: map[string]int{}}, + {v1: make([]int, 5), v2: make([]int, 5)}, + } { + compareResult := compareTwoValues(mockT, currCase.v1, currCase.v2, []CompareType{compareLess, compareEqual, compareGreater}, "testFailMessage") + False(t, compareResult) + } +} + +func Test_compareTwoValuesCorrectCompareResult(t *testing.T) { + mockT := new(testing.T) + + for _, currCase := range []struct { + v1 interface{} + v2 interface{} + compareTypes []CompareType + }{ + {v1: 1, v2: 2, compareTypes: []CompareType{compareLess}}, + {v1: 1, v2: 2, compareTypes: []CompareType{compareLess, compareEqual}}, + {v1: 2, v2: 2, compareTypes: []CompareType{compareGreater, compareEqual}}, + {v1: 2, v2: 2, compareTypes: []CompareType{compareEqual}}, + {v1: 2, v2: 1, compareTypes: []CompareType{compareEqual, compareGreater}}, + {v1: 2, v2: 1, compareTypes: []CompareType{compareGreater}}, + } { + compareResult := compareTwoValues(mockT, currCase.v1, currCase.v2, currCase.compareTypes, "testFailMessage") + True(t, compareResult) + } +} + +func Test_containsValue(t *testing.T) { + for _, currCase := range []struct { + values []CompareType + value CompareType + result bool + }{ + {values: []CompareType{compareGreater}, value: compareGreater, result: true}, + {values: []CompareType{compareGreater, compareLess}, value: compareGreater, result: true}, + {values: []CompareType{compareGreater, compareLess}, value: compareLess, result: true}, + {values: []CompareType{compareGreater, compareLess}, value: compareEqual, result: false}, + } { + compareResult := containsValue(currCase.values, currCase.value) + Equal(t, currCase.result, compareResult) + } +} + +func TestComparingMsgAndArgsForwarding(t *testing.T) { + msgAndArgs := []interface{}{"format %s %x", "this", 0xc001} + expectedOutput := "format this c001\n" + funcs := []func(t TestingT){ + func(t TestingT) { Greater(t, 1, 2, msgAndArgs...) }, + func(t TestingT) { GreaterOrEqual(t, 1, 2, msgAndArgs...) }, + func(t TestingT) { Less(t, 2, 1, msgAndArgs...) }, + func(t TestingT) { LessOrEqual(t, 2, 1, msgAndArgs...) }, + func(t TestingT) { Positive(t, 0, msgAndArgs...) }, + func(t TestingT) { Negative(t, 0, msgAndArgs...) }, + } + for _, f := range funcs { + out := &outputT{buf: bytes.NewBuffer(nil)} + f(out) + Contains(t, out.buf.String(), expectedOutput) + } +} diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_format.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_format.go new file mode 100644 index 0000000..7880b8f --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_format.go @@ -0,0 +1,763 @@ +/* +* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen +* THIS FILE MUST NOT BE EDITED BY HAND + */ + +package assert + +import ( + http "net/http" + url "net/url" + time "time" +) + +// Conditionf uses a Comparison to assert a complex condition. +func Conditionf(t TestingT, comp Comparison, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Condition(t, comp, append([]interface{}{msg}, args...)...) +} + +// Containsf asserts that the specified string, list(array, slice...) or map contains the +// specified substring or element. +// +// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted") +// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") +// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") +func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Contains(t, s, contains, append([]interface{}{msg}, args...)...) +} + +// DirExistsf checks whether a directory exists in the given path. It also fails +// if the path is a file rather a directory or there is an error checking whether it exists. +func DirExistsf(t TestingT, path string, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return DirExists(t, path, append([]interface{}{msg}, args...)...) +} + +// ElementsMatchf asserts that the specified listA(array, slice...) is equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should match. +// +// assert.ElementsMatchf(t, [1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted") +func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return ElementsMatch(t, listA, listB, append([]interface{}{msg}, args...)...) +} + +// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// assert.Emptyf(t, obj, "error message %s", "formatted") +func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Empty(t, object, append([]interface{}{msg}, args...)...) +} + +// Equalf asserts that two objects are equal. +// +// assert.Equalf(t, 123, 123, "error message %s", "formatted") +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). Function equality +// cannot be determined and will always fail. +func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Equal(t, expected, actual, append([]interface{}{msg}, args...)...) +} + +// EqualErrorf asserts that a function returned an error (i.e. not `nil`) +// and that it is equal to the provided error. +// +// actualObj, err := SomeFunction() +// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") +func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return EqualError(t, theError, errString, append([]interface{}{msg}, args...)...) +} + +// EqualValuesf asserts that two objects are equal or convertable to the same types +// and equal. +// +// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") +func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return EqualValues(t, expected, actual, append([]interface{}{msg}, args...)...) +} + +// Errorf asserts that a function returned an error (i.e. not `nil`). +// +// actualObj, err := SomeFunction() +// if assert.Errorf(t, err, "error message %s", "formatted") { +// assert.Equal(t, expectedErrorf, err) +// } +func Errorf(t TestingT, err error, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Error(t, err, append([]interface{}{msg}, args...)...) +} + +// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. +// This is a wrapper for errors.As. +func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return ErrorAs(t, err, target, append([]interface{}{msg}, args...)...) +} + +// ErrorContainsf asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") +func ErrorContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return ErrorContains(t, theError, contains, append([]interface{}{msg}, args...)...) +} + +// ErrorIsf asserts that at least one of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return ErrorIs(t, err, target, append([]interface{}{msg}, args...)...) +} + +// Eventuallyf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. +// +// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Eventually(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...) +} + +// Exactlyf asserts that two objects are equal in value and type. +// +// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") +func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Exactly(t, expected, actual, append([]interface{}{msg}, args...)...) +} + +// Failf reports a failure through +func Failf(t TestingT, failureMessage string, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Fail(t, failureMessage, append([]interface{}{msg}, args...)...) +} + +// FailNowf fails test +func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return FailNow(t, failureMessage, append([]interface{}{msg}, args...)...) +} + +// Falsef asserts that the specified value is false. +// +// assert.Falsef(t, myBool, "error message %s", "formatted") +func Falsef(t TestingT, value bool, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return False(t, value, append([]interface{}{msg}, args...)...) +} + +// FileExistsf checks whether a file exists in the given path. It also fails if +// the path points to a directory or there is an error when trying to check the file. +func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return FileExists(t, path, append([]interface{}{msg}, args...)...) +} + +// Greaterf asserts that the first element is greater than the second +// +// assert.Greaterf(t, 2, 1, "error message %s", "formatted") +// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") +// assert.Greaterf(t, "b", "a", "error message %s", "formatted") +func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Greater(t, e1, e2, append([]interface{}{msg}, args...)...) +} + +// GreaterOrEqualf asserts that the first element is greater than or equal to the second +// +// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") +// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") +// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") +// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") +func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return GreaterOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...) +} + +// HTTPBodyContainsf asserts that a specified handler returns a +// body that contains a string. +// +// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return HTTPBodyContains(t, handler, method, url, values, str, append([]interface{}{msg}, args...)...) +} + +// HTTPBodyNotContainsf asserts that a specified handler returns a +// body that does not contain a string. +// +// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return HTTPBodyNotContains(t, handler, method, url, values, str, append([]interface{}{msg}, args...)...) +} + +// HTTPErrorf asserts that a specified handler returns an error status code. +// +// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return HTTPError(t, handler, method, url, values, append([]interface{}{msg}, args...)...) +} + +// HTTPRedirectf asserts that a specified handler returns a redirect status code. +// +// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return HTTPRedirect(t, handler, method, url, values, append([]interface{}{msg}, args...)...) +} + +// HTTPStatusCodef asserts that a specified handler returns a specified status code. +// +// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return HTTPStatusCode(t, handler, method, url, values, statuscode, append([]interface{}{msg}, args...)...) +} + +// HTTPSuccessf asserts that a specified handler returns a success status code. +// +// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return HTTPSuccess(t, handler, method, url, values, append([]interface{}{msg}, args...)...) +} + +// Implementsf asserts that an object is implemented by the specified interface. +// +// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Implements(t, interfaceObject, object, append([]interface{}{msg}, args...)...) +} + +// InDeltaf asserts that the two numerals are within delta of each other. +// +// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") +func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return InDelta(t, expected, actual, delta, append([]interface{}{msg}, args...)...) +} + +// InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. +func InDeltaMapValuesf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return InDeltaMapValues(t, expected, actual, delta, append([]interface{}{msg}, args...)...) +} + +// InDeltaSlicef is the same as InDelta, except it compares two slices. +func InDeltaSlicef(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return InDeltaSlice(t, expected, actual, delta, append([]interface{}{msg}, args...)...) +} + +// InEpsilonf asserts that expected and actual have a relative error less than epsilon +func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return InEpsilon(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...) +} + +// InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices. +func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return InEpsilonSlice(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...) +} + +// IsDecreasingf asserts that the collection is decreasing +// +// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return IsDecreasing(t, object, append([]interface{}{msg}, args...)...) +} + +// IsIncreasingf asserts that the collection is increasing +// +// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return IsIncreasing(t, object, append([]interface{}{msg}, args...)...) +} + +// IsNonDecreasingf asserts that the collection is not decreasing +// +// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return IsNonDecreasing(t, object, append([]interface{}{msg}, args...)...) +} + +// IsNonIncreasingf asserts that the collection is not increasing +// +// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return IsNonIncreasing(t, object, append([]interface{}{msg}, args...)...) +} + +// IsTypef asserts that the specified objects are of the same type. +func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return IsType(t, expectedType, object, append([]interface{}{msg}, args...)...) +} + +// JSONEqf asserts that two JSON strings are equivalent. +// +// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return JSONEq(t, expected, actual, append([]interface{}{msg}, args...)...) +} + +// Lenf asserts that the specified object has specific length. +// Lenf also fails if the object has a type that len() not accept. +// +// assert.Lenf(t, mySlice, 3, "error message %s", "formatted") +func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Len(t, object, length, append([]interface{}{msg}, args...)...) +} + +// Lessf asserts that the first element is less than the second +// +// assert.Lessf(t, 1, 2, "error message %s", "formatted") +// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") +// assert.Lessf(t, "a", "b", "error message %s", "formatted") +func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Less(t, e1, e2, append([]interface{}{msg}, args...)...) +} + +// LessOrEqualf asserts that the first element is less than or equal to the second +// +// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted") +// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted") +// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted") +// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted") +func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return LessOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...) +} + +// Negativef asserts that the specified element is negative +// +// assert.Negativef(t, -1, "error message %s", "formatted") +// assert.Negativef(t, -1.23, "error message %s", "formatted") +func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Negative(t, e, append([]interface{}{msg}, args...)...) +} + +// Neverf asserts that the given condition doesn't satisfy in waitFor time, +// periodically checking the target function each tick. +// +// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Never(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...) +} + +// Nilf asserts that the specified object is nil. +// +// assert.Nilf(t, err, "error message %s", "formatted") +func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Nil(t, object, append([]interface{}{msg}, args...)...) +} + +// NoDirExistsf checks whether a directory does not exist in the given path. +// It fails if the path points to an existing _directory_ only. +func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NoDirExists(t, path, append([]interface{}{msg}, args...)...) +} + +// NoErrorf asserts that a function returned no error (i.e. `nil`). +// +// actualObj, err := SomeFunction() +// if assert.NoErrorf(t, err, "error message %s", "formatted") { +// assert.Equal(t, expectedObj, actualObj) +// } +func NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NoError(t, err, append([]interface{}{msg}, args...)...) +} + +// NoFileExistsf checks whether a file does not exist in a given path. It fails +// if the path points to an existing _file_ only. +func NoFileExistsf(t TestingT, path string, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NoFileExists(t, path, append([]interface{}{msg}, args...)...) +} + +// NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the +// specified substring or element. +// +// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") +func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NotContains(t, s, contains, append([]interface{}{msg}, args...)...) +} + +// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// if assert.NotEmptyf(t, obj, "error message %s", "formatted") { +// assert.Equal(t, "two", obj[1]) +// } +func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NotEmpty(t, object, append([]interface{}{msg}, args...)...) +} + +// NotEqualf asserts that the specified values are NOT equal. +// +// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). +func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NotEqual(t, expected, actual, append([]interface{}{msg}, args...)...) +} + +// NotEqualValuesf asserts that two objects are not equal even when converted to the same type +// +// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") +func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NotEqualValues(t, expected, actual, append([]interface{}{msg}, args...)...) +} + +// NotErrorIsf asserts that at none of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NotErrorIs(t, err, target, append([]interface{}{msg}, args...)...) +} + +// NotNilf asserts that the specified object is not nil. +// +// assert.NotNilf(t, err, "error message %s", "formatted") +func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NotNil(t, object, append([]interface{}{msg}, args...)...) +} + +// NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. +// +// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") +func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NotPanics(t, f, append([]interface{}{msg}, args...)...) +} + +// NotRegexpf asserts that a specified regexp does not match a string. +// +// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") +// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") +func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NotRegexp(t, rx, str, append([]interface{}{msg}, args...)...) +} + +// NotSamef asserts that two pointers do not reference the same object. +// +// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NotSame(t, expected, actual, append([]interface{}{msg}, args...)...) +} + +// NotSubsetf asserts that the specified list(array, slice...) contains not all +// elements given in the specified subset(array, slice...). +// +// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NotSubset(t, list, subset, append([]interface{}{msg}, args...)...) +} + +// NotZerof asserts that i is not the zero value for its type. +func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NotZero(t, i, append([]interface{}{msg}, args...)...) +} + +// Panicsf asserts that the code inside the specified PanicTestFunc panics. +// +// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") +func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Panics(t, f, append([]interface{}{msg}, args...)...) +} + +// PanicsWithErrorf asserts that the code inside the specified PanicTestFunc +// panics, and that the recovered panic value is an error that satisfies the +// EqualError comparison. +// +// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +func PanicsWithErrorf(t TestingT, errString string, f PanicTestFunc, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return PanicsWithError(t, errString, f, append([]interface{}{msg}, args...)...) +} + +// PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that +// the recovered panic value equals the expected panic value. +// +// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return PanicsWithValue(t, expected, f, append([]interface{}{msg}, args...)...) +} + +// Positivef asserts that the specified element is positive +// +// assert.Positivef(t, 1, "error message %s", "formatted") +// assert.Positivef(t, 1.23, "error message %s", "formatted") +func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Positive(t, e, append([]interface{}{msg}, args...)...) +} + +// Regexpf asserts that a specified regexp matches a string. +// +// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") +// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") +func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Regexp(t, rx, str, append([]interface{}{msg}, args...)...) +} + +// Samef asserts that two pointers reference the same object. +// +// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted") +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func Samef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Same(t, expected, actual, append([]interface{}{msg}, args...)...) +} + +// Subsetf asserts that the specified list(array, slice...) contains all +// elements given in the specified subset(array, slice...). +// +// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Subset(t, list, subset, append([]interface{}{msg}, args...)...) +} + +// Truef asserts that the specified value is true. +// +// assert.Truef(t, myBool, "error message %s", "formatted") +func Truef(t TestingT, value bool, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return True(t, value, append([]interface{}{msg}, args...)...) +} + +// WithinDurationf asserts that the two times are within duration delta of each other. +// +// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") +func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...) +} + +// WithinRangef asserts that a time is within a time range (inclusive). +// +// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return WithinRange(t, actual, start, end, append([]interface{}{msg}, args...)...) +} + +// YAMLEqf asserts that two YAML strings are equivalent. +func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return YAMLEq(t, expected, actual, append([]interface{}{msg}, args...)...) +} + +// Zerof asserts that i is the zero value for its type. +func Zerof(t TestingT, i interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Zero(t, i, append([]interface{}{msg}, args...)...) +} diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_format.go.tmpl b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_format.go.tmpl new file mode 100644 index 0000000..d2bb0b8 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_format.go.tmpl @@ -0,0 +1,5 @@ +{{.CommentFormat}} +func {{.DocInfo.Name}}f(t TestingT, {{.ParamsFormat}}) bool { + if h, ok := t.(tHelper); ok { h.Helper() } + return {{.DocInfo.Name}}(t, {{.ForwardedParamsFormat}}) +} diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_forward.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_forward.go new file mode 100644 index 0000000..339515b --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_forward.go @@ -0,0 +1,1514 @@ +/* +* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen +* THIS FILE MUST NOT BE EDITED BY HAND + */ + +package assert + +import ( + http "net/http" + url "net/url" + time "time" +) + +// Condition uses a Comparison to assert a complex condition. +func (a *Assertions) Condition(comp Comparison, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Condition(a.t, comp, msgAndArgs...) +} + +// Conditionf uses a Comparison to assert a complex condition. +func (a *Assertions) Conditionf(comp Comparison, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Conditionf(a.t, comp, msg, args...) +} + +// Contains asserts that the specified string, list(array, slice...) or map contains the +// specified substring or element. +// +// a.Contains("Hello World", "World") +// a.Contains(["Hello", "World"], "World") +// a.Contains({"Hello": "World"}, "Hello") +func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Contains(a.t, s, contains, msgAndArgs...) +} + +// Containsf asserts that the specified string, list(array, slice...) or map contains the +// specified substring or element. +// +// a.Containsf("Hello World", "World", "error message %s", "formatted") +// a.Containsf(["Hello", "World"], "World", "error message %s", "formatted") +// a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted") +func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Containsf(a.t, s, contains, msg, args...) +} + +// DirExists checks whether a directory exists in the given path. It also fails +// if the path is a file rather a directory or there is an error checking whether it exists. +func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return DirExists(a.t, path, msgAndArgs...) +} + +// DirExistsf checks whether a directory exists in the given path. It also fails +// if the path is a file rather a directory or there is an error checking whether it exists. +func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return DirExistsf(a.t, path, msg, args...) +} + +// ElementsMatch asserts that the specified listA(array, slice...) is equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should match. +// +// a.ElementsMatch([1, 3, 2, 3], [1, 3, 3, 2]) +func (a *Assertions) ElementsMatch(listA interface{}, listB interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return ElementsMatch(a.t, listA, listB, msgAndArgs...) +} + +// ElementsMatchf asserts that the specified listA(array, slice...) is equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should match. +// +// a.ElementsMatchf([1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted") +func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return ElementsMatchf(a.t, listA, listB, msg, args...) +} + +// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// a.Empty(obj) +func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Empty(a.t, object, msgAndArgs...) +} + +// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// a.Emptyf(obj, "error message %s", "formatted") +func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Emptyf(a.t, object, msg, args...) +} + +// Equal asserts that two objects are equal. +// +// a.Equal(123, 123) +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). Function equality +// cannot be determined and will always fail. +func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Equal(a.t, expected, actual, msgAndArgs...) +} + +// EqualError asserts that a function returned an error (i.e. not `nil`) +// and that it is equal to the provided error. +// +// actualObj, err := SomeFunction() +// a.EqualError(err, expectedErrorString) +func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return EqualError(a.t, theError, errString, msgAndArgs...) +} + +// EqualErrorf asserts that a function returned an error (i.e. not `nil`) +// and that it is equal to the provided error. +// +// actualObj, err := SomeFunction() +// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted") +func (a *Assertions) EqualErrorf(theError error, errString string, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return EqualErrorf(a.t, theError, errString, msg, args...) +} + +// EqualValues asserts that two objects are equal or convertable to the same types +// and equal. +// +// a.EqualValues(uint32(123), int32(123)) +func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return EqualValues(a.t, expected, actual, msgAndArgs...) +} + +// EqualValuesf asserts that two objects are equal or convertable to the same types +// and equal. +// +// a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") +func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return EqualValuesf(a.t, expected, actual, msg, args...) +} + +// Equalf asserts that two objects are equal. +// +// a.Equalf(123, 123, "error message %s", "formatted") +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). Function equality +// cannot be determined and will always fail. +func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Equalf(a.t, expected, actual, msg, args...) +} + +// Error asserts that a function returned an error (i.e. not `nil`). +// +// actualObj, err := SomeFunction() +// if a.Error(err) { +// assert.Equal(t, expectedError, err) +// } +func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Error(a.t, err, msgAndArgs...) +} + +// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. +// This is a wrapper for errors.As. +func (a *Assertions) ErrorAs(err error, target interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return ErrorAs(a.t, err, target, msgAndArgs...) +} + +// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. +// This is a wrapper for errors.As. +func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return ErrorAsf(a.t, err, target, msg, args...) +} + +// ErrorContains asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// a.ErrorContains(err, expectedErrorSubString) +func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return ErrorContains(a.t, theError, contains, msgAndArgs...) +} + +// ErrorContainsf asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") +func (a *Assertions) ErrorContainsf(theError error, contains string, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return ErrorContainsf(a.t, theError, contains, msg, args...) +} + +// ErrorIs asserts that at least one of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return ErrorIs(a.t, err, target, msgAndArgs...) +} + +// ErrorIsf asserts that at least one of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return ErrorIsf(a.t, err, target, msg, args...) +} + +// Errorf asserts that a function returned an error (i.e. not `nil`). +// +// actualObj, err := SomeFunction() +// if a.Errorf(err, "error message %s", "formatted") { +// assert.Equal(t, expectedErrorf, err) +// } +func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Errorf(a.t, err, msg, args...) +} + +// Eventually asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. +// +// a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond) +func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Eventually(a.t, condition, waitFor, tick, msgAndArgs...) +} + +// Eventuallyf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. +// +// a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Eventuallyf(a.t, condition, waitFor, tick, msg, args...) +} + +// Exactly asserts that two objects are equal in value and type. +// +// a.Exactly(int32(123), int64(123)) +func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Exactly(a.t, expected, actual, msgAndArgs...) +} + +// Exactlyf asserts that two objects are equal in value and type. +// +// a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") +func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Exactlyf(a.t, expected, actual, msg, args...) +} + +// Fail reports a failure through +func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Fail(a.t, failureMessage, msgAndArgs...) +} + +// FailNow fails test +func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return FailNow(a.t, failureMessage, msgAndArgs...) +} + +// FailNowf fails test +func (a *Assertions) FailNowf(failureMessage string, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return FailNowf(a.t, failureMessage, msg, args...) +} + +// Failf reports a failure through +func (a *Assertions) Failf(failureMessage string, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Failf(a.t, failureMessage, msg, args...) +} + +// False asserts that the specified value is false. +// +// a.False(myBool) +func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return False(a.t, value, msgAndArgs...) +} + +// Falsef asserts that the specified value is false. +// +// a.Falsef(myBool, "error message %s", "formatted") +func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Falsef(a.t, value, msg, args...) +} + +// FileExists checks whether a file exists in the given path. It also fails if +// the path points to a directory or there is an error when trying to check the file. +func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return FileExists(a.t, path, msgAndArgs...) +} + +// FileExistsf checks whether a file exists in the given path. It also fails if +// the path points to a directory or there is an error when trying to check the file. +func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return FileExistsf(a.t, path, msg, args...) +} + +// Greater asserts that the first element is greater than the second +// +// a.Greater(2, 1) +// a.Greater(float64(2), float64(1)) +// a.Greater("b", "a") +func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Greater(a.t, e1, e2, msgAndArgs...) +} + +// GreaterOrEqual asserts that the first element is greater than or equal to the second +// +// a.GreaterOrEqual(2, 1) +// a.GreaterOrEqual(2, 2) +// a.GreaterOrEqual("b", "a") +// a.GreaterOrEqual("b", "b") +func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return GreaterOrEqual(a.t, e1, e2, msgAndArgs...) +} + +// GreaterOrEqualf asserts that the first element is greater than or equal to the second +// +// a.GreaterOrEqualf(2, 1, "error message %s", "formatted") +// a.GreaterOrEqualf(2, 2, "error message %s", "formatted") +// a.GreaterOrEqualf("b", "a", "error message %s", "formatted") +// a.GreaterOrEqualf("b", "b", "error message %s", "formatted") +func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return GreaterOrEqualf(a.t, e1, e2, msg, args...) +} + +// Greaterf asserts that the first element is greater than the second +// +// a.Greaterf(2, 1, "error message %s", "formatted") +// a.Greaterf(float64(2), float64(1), "error message %s", "formatted") +// a.Greaterf("b", "a", "error message %s", "formatted") +func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Greaterf(a.t, e1, e2, msg, args...) +} + +// HTTPBodyContains asserts that a specified handler returns a +// body that contains a string. +// +// a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return HTTPBodyContains(a.t, handler, method, url, values, str, msgAndArgs...) +} + +// HTTPBodyContainsf asserts that a specified handler returns a +// body that contains a string. +// +// a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return HTTPBodyContainsf(a.t, handler, method, url, values, str, msg, args...) +} + +// HTTPBodyNotContains asserts that a specified handler returns a +// body that does not contain a string. +// +// a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return HTTPBodyNotContains(a.t, handler, method, url, values, str, msgAndArgs...) +} + +// HTTPBodyNotContainsf asserts that a specified handler returns a +// body that does not contain a string. +// +// a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return HTTPBodyNotContainsf(a.t, handler, method, url, values, str, msg, args...) +} + +// HTTPError asserts that a specified handler returns an error status code. +// +// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return HTTPError(a.t, handler, method, url, values, msgAndArgs...) +} + +// HTTPErrorf asserts that a specified handler returns an error status code. +// +// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return HTTPErrorf(a.t, handler, method, url, values, msg, args...) +} + +// HTTPRedirect asserts that a specified handler returns a redirect status code. +// +// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return HTTPRedirect(a.t, handler, method, url, values, msgAndArgs...) +} + +// HTTPRedirectf asserts that a specified handler returns a redirect status code. +// +// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return HTTPRedirectf(a.t, handler, method, url, values, msg, args...) +} + +// HTTPStatusCode asserts that a specified handler returns a specified status code. +// +// a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return HTTPStatusCode(a.t, handler, method, url, values, statuscode, msgAndArgs...) +} + +// HTTPStatusCodef asserts that a specified handler returns a specified status code. +// +// a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return HTTPStatusCodef(a.t, handler, method, url, values, statuscode, msg, args...) +} + +// HTTPSuccess asserts that a specified handler returns a success status code. +// +// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return HTTPSuccess(a.t, handler, method, url, values, msgAndArgs...) +} + +// HTTPSuccessf asserts that a specified handler returns a success status code. +// +// a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return HTTPSuccessf(a.t, handler, method, url, values, msg, args...) +} + +// Implements asserts that an object is implemented by the specified interface. +// +// a.Implements((*MyInterface)(nil), new(MyObject)) +func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Implements(a.t, interfaceObject, object, msgAndArgs...) +} + +// Implementsf asserts that an object is implemented by the specified interface. +// +// a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Implementsf(a.t, interfaceObject, object, msg, args...) +} + +// InDelta asserts that the two numerals are within delta of each other. +// +// a.InDelta(math.Pi, 22/7.0, 0.01) +func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return InDelta(a.t, expected, actual, delta, msgAndArgs...) +} + +// InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. +func (a *Assertions) InDeltaMapValues(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return InDeltaMapValues(a.t, expected, actual, delta, msgAndArgs...) +} + +// InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. +func (a *Assertions) InDeltaMapValuesf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return InDeltaMapValuesf(a.t, expected, actual, delta, msg, args...) +} + +// InDeltaSlice is the same as InDelta, except it compares two slices. +func (a *Assertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return InDeltaSlice(a.t, expected, actual, delta, msgAndArgs...) +} + +// InDeltaSlicef is the same as InDelta, except it compares two slices. +func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return InDeltaSlicef(a.t, expected, actual, delta, msg, args...) +} + +// InDeltaf asserts that the two numerals are within delta of each other. +// +// a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") +func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return InDeltaf(a.t, expected, actual, delta, msg, args...) +} + +// InEpsilon asserts that expected and actual have a relative error less than epsilon +func (a *Assertions) InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...) +} + +// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. +func (a *Assertions) InEpsilonSlice(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return InEpsilonSlice(a.t, expected, actual, epsilon, msgAndArgs...) +} + +// InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices. +func (a *Assertions) InEpsilonSlicef(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return InEpsilonSlicef(a.t, expected, actual, epsilon, msg, args...) +} + +// InEpsilonf asserts that expected and actual have a relative error less than epsilon +func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return InEpsilonf(a.t, expected, actual, epsilon, msg, args...) +} + +// IsDecreasing asserts that the collection is decreasing +// +// a.IsDecreasing([]int{2, 1, 0}) +// a.IsDecreasing([]float{2, 1}) +// a.IsDecreasing([]string{"b", "a"}) +func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsDecreasing(a.t, object, msgAndArgs...) +} + +// IsDecreasingf asserts that the collection is decreasing +// +// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") +// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") +// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") +func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsDecreasingf(a.t, object, msg, args...) +} + +// IsIncreasing asserts that the collection is increasing +// +// a.IsIncreasing([]int{1, 2, 3}) +// a.IsIncreasing([]float{1, 2}) +// a.IsIncreasing([]string{"a", "b"}) +func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsIncreasing(a.t, object, msgAndArgs...) +} + +// IsIncreasingf asserts that the collection is increasing +// +// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") +// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") +// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") +func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsIncreasingf(a.t, object, msg, args...) +} + +// IsNonDecreasing asserts that the collection is not decreasing +// +// a.IsNonDecreasing([]int{1, 1, 2}) +// a.IsNonDecreasing([]float{1, 2}) +// a.IsNonDecreasing([]string{"a", "b"}) +func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsNonDecreasing(a.t, object, msgAndArgs...) +} + +// IsNonDecreasingf asserts that the collection is not decreasing +// +// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") +// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") +// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") +func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsNonDecreasingf(a.t, object, msg, args...) +} + +// IsNonIncreasing asserts that the collection is not increasing +// +// a.IsNonIncreasing([]int{2, 1, 1}) +// a.IsNonIncreasing([]float{2, 1}) +// a.IsNonIncreasing([]string{"b", "a"}) +func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsNonIncreasing(a.t, object, msgAndArgs...) +} + +// IsNonIncreasingf asserts that the collection is not increasing +// +// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") +// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") +// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") +func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsNonIncreasingf(a.t, object, msg, args...) +} + +// IsType asserts that the specified objects are of the same type. +func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsType(a.t, expectedType, object, msgAndArgs...) +} + +// IsTypef asserts that the specified objects are of the same type. +func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsTypef(a.t, expectedType, object, msg, args...) +} + +// JSONEq asserts that two JSON strings are equivalent. +// +// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return JSONEq(a.t, expected, actual, msgAndArgs...) +} + +// JSONEqf asserts that two JSON strings are equivalent. +// +// a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return JSONEqf(a.t, expected, actual, msg, args...) +} + +// Len asserts that the specified object has specific length. +// Len also fails if the object has a type that len() not accept. +// +// a.Len(mySlice, 3) +func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Len(a.t, object, length, msgAndArgs...) +} + +// Lenf asserts that the specified object has specific length. +// Lenf also fails if the object has a type that len() not accept. +// +// a.Lenf(mySlice, 3, "error message %s", "formatted") +func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Lenf(a.t, object, length, msg, args...) +} + +// Less asserts that the first element is less than the second +// +// a.Less(1, 2) +// a.Less(float64(1), float64(2)) +// a.Less("a", "b") +func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Less(a.t, e1, e2, msgAndArgs...) +} + +// LessOrEqual asserts that the first element is less than or equal to the second +// +// a.LessOrEqual(1, 2) +// a.LessOrEqual(2, 2) +// a.LessOrEqual("a", "b") +// a.LessOrEqual("b", "b") +func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return LessOrEqual(a.t, e1, e2, msgAndArgs...) +} + +// LessOrEqualf asserts that the first element is less than or equal to the second +// +// a.LessOrEqualf(1, 2, "error message %s", "formatted") +// a.LessOrEqualf(2, 2, "error message %s", "formatted") +// a.LessOrEqualf("a", "b", "error message %s", "formatted") +// a.LessOrEqualf("b", "b", "error message %s", "formatted") +func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return LessOrEqualf(a.t, e1, e2, msg, args...) +} + +// Lessf asserts that the first element is less than the second +// +// a.Lessf(1, 2, "error message %s", "formatted") +// a.Lessf(float64(1), float64(2), "error message %s", "formatted") +// a.Lessf("a", "b", "error message %s", "formatted") +func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Lessf(a.t, e1, e2, msg, args...) +} + +// Negative asserts that the specified element is negative +// +// a.Negative(-1) +// a.Negative(-1.23) +func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Negative(a.t, e, msgAndArgs...) +} + +// Negativef asserts that the specified element is negative +// +// a.Negativef(-1, "error message %s", "formatted") +// a.Negativef(-1.23, "error message %s", "formatted") +func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Negativef(a.t, e, msg, args...) +} + +// Never asserts that the given condition doesn't satisfy in waitFor time, +// periodically checking the target function each tick. +// +// a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) +func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Never(a.t, condition, waitFor, tick, msgAndArgs...) +} + +// Neverf asserts that the given condition doesn't satisfy in waitFor time, +// periodically checking the target function each tick. +// +// a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Neverf(a.t, condition, waitFor, tick, msg, args...) +} + +// Nil asserts that the specified object is nil. +// +// a.Nil(err) +func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Nil(a.t, object, msgAndArgs...) +} + +// Nilf asserts that the specified object is nil. +// +// a.Nilf(err, "error message %s", "formatted") +func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Nilf(a.t, object, msg, args...) +} + +// NoDirExists checks whether a directory does not exist in the given path. +// It fails if the path points to an existing _directory_ only. +func (a *Assertions) NoDirExists(path string, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NoDirExists(a.t, path, msgAndArgs...) +} + +// NoDirExistsf checks whether a directory does not exist in the given path. +// It fails if the path points to an existing _directory_ only. +func (a *Assertions) NoDirExistsf(path string, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NoDirExistsf(a.t, path, msg, args...) +} + +// NoError asserts that a function returned no error (i.e. `nil`). +// +// actualObj, err := SomeFunction() +// if a.NoError(err) { +// assert.Equal(t, expectedObj, actualObj) +// } +func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NoError(a.t, err, msgAndArgs...) +} + +// NoErrorf asserts that a function returned no error (i.e. `nil`). +// +// actualObj, err := SomeFunction() +// if a.NoErrorf(err, "error message %s", "formatted") { +// assert.Equal(t, expectedObj, actualObj) +// } +func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NoErrorf(a.t, err, msg, args...) +} + +// NoFileExists checks whether a file does not exist in a given path. It fails +// if the path points to an existing _file_ only. +func (a *Assertions) NoFileExists(path string, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NoFileExists(a.t, path, msgAndArgs...) +} + +// NoFileExistsf checks whether a file does not exist in a given path. It fails +// if the path points to an existing _file_ only. +func (a *Assertions) NoFileExistsf(path string, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NoFileExistsf(a.t, path, msg, args...) +} + +// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the +// specified substring or element. +// +// a.NotContains("Hello World", "Earth") +// a.NotContains(["Hello", "World"], "Earth") +// a.NotContains({"Hello": "World"}, "Earth") +func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotContains(a.t, s, contains, msgAndArgs...) +} + +// NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the +// specified substring or element. +// +// a.NotContainsf("Hello World", "Earth", "error message %s", "formatted") +// a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted") +// a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted") +func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotContainsf(a.t, s, contains, msg, args...) +} + +// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// if a.NotEmpty(obj) { +// assert.Equal(t, "two", obj[1]) +// } +func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotEmpty(a.t, object, msgAndArgs...) +} + +// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// if a.NotEmptyf(obj, "error message %s", "formatted") { +// assert.Equal(t, "two", obj[1]) +// } +func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotEmptyf(a.t, object, msg, args...) +} + +// NotEqual asserts that the specified values are NOT equal. +// +// a.NotEqual(obj1, obj2) +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). +func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotEqual(a.t, expected, actual, msgAndArgs...) +} + +// NotEqualValues asserts that two objects are not equal even when converted to the same type +// +// a.NotEqualValues(obj1, obj2) +func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotEqualValues(a.t, expected, actual, msgAndArgs...) +} + +// NotEqualValuesf asserts that two objects are not equal even when converted to the same type +// +// a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") +func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotEqualValuesf(a.t, expected, actual, msg, args...) +} + +// NotEqualf asserts that the specified values are NOT equal. +// +// a.NotEqualf(obj1, obj2, "error message %s", "formatted") +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). +func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotEqualf(a.t, expected, actual, msg, args...) +} + +// NotErrorIs asserts that at none of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotErrorIs(a.t, err, target, msgAndArgs...) +} + +// NotErrorIsf asserts that at none of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotErrorIsf(a.t, err, target, msg, args...) +} + +// NotNil asserts that the specified object is not nil. +// +// a.NotNil(err) +func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotNil(a.t, object, msgAndArgs...) +} + +// NotNilf asserts that the specified object is not nil. +// +// a.NotNilf(err, "error message %s", "formatted") +func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotNilf(a.t, object, msg, args...) +} + +// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. +// +// a.NotPanics(func(){ RemainCalm() }) +func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotPanics(a.t, f, msgAndArgs...) +} + +// NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. +// +// a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted") +func (a *Assertions) NotPanicsf(f PanicTestFunc, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotPanicsf(a.t, f, msg, args...) +} + +// NotRegexp asserts that a specified regexp does not match a string. +// +// a.NotRegexp(regexp.MustCompile("starts"), "it's starting") +// a.NotRegexp("^start", "it's not starting") +func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotRegexp(a.t, rx, str, msgAndArgs...) +} + +// NotRegexpf asserts that a specified regexp does not match a string. +// +// a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") +// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") +func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotRegexpf(a.t, rx, str, msg, args...) +} + +// NotSame asserts that two pointers do not reference the same object. +// +// a.NotSame(ptr1, ptr2) +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func (a *Assertions) NotSame(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotSame(a.t, expected, actual, msgAndArgs...) +} + +// NotSamef asserts that two pointers do not reference the same object. +// +// a.NotSamef(ptr1, ptr2, "error message %s", "formatted") +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotSamef(a.t, expected, actual, msg, args...) +} + +// NotSubset asserts that the specified list(array, slice...) contains not all +// elements given in the specified subset(array, slice...). +// +// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotSubset(a.t, list, subset, msgAndArgs...) +} + +// NotSubsetf asserts that the specified list(array, slice...) contains not all +// elements given in the specified subset(array, slice...). +// +// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotSubsetf(a.t, list, subset, msg, args...) +} + +// NotZero asserts that i is not the zero value for its type. +func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotZero(a.t, i, msgAndArgs...) +} + +// NotZerof asserts that i is not the zero value for its type. +func (a *Assertions) NotZerof(i interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotZerof(a.t, i, msg, args...) +} + +// Panics asserts that the code inside the specified PanicTestFunc panics. +// +// a.Panics(func(){ GoCrazy() }) +func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Panics(a.t, f, msgAndArgs...) +} + +// PanicsWithError asserts that the code inside the specified PanicTestFunc +// panics, and that the recovered panic value is an error that satisfies the +// EqualError comparison. +// +// a.PanicsWithError("crazy error", func(){ GoCrazy() }) +func (a *Assertions) PanicsWithError(errString string, f PanicTestFunc, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return PanicsWithError(a.t, errString, f, msgAndArgs...) +} + +// PanicsWithErrorf asserts that the code inside the specified PanicTestFunc +// panics, and that the recovered panic value is an error that satisfies the +// EqualError comparison. +// +// a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +func (a *Assertions) PanicsWithErrorf(errString string, f PanicTestFunc, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return PanicsWithErrorf(a.t, errString, f, msg, args...) +} + +// PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that +// the recovered panic value equals the expected panic value. +// +// a.PanicsWithValue("crazy error", func(){ GoCrazy() }) +func (a *Assertions) PanicsWithValue(expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return PanicsWithValue(a.t, expected, f, msgAndArgs...) +} + +// PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that +// the recovered panic value equals the expected panic value. +// +// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +func (a *Assertions) PanicsWithValuef(expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return PanicsWithValuef(a.t, expected, f, msg, args...) +} + +// Panicsf asserts that the code inside the specified PanicTestFunc panics. +// +// a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted") +func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Panicsf(a.t, f, msg, args...) +} + +// Positive asserts that the specified element is positive +// +// a.Positive(1) +// a.Positive(1.23) +func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Positive(a.t, e, msgAndArgs...) +} + +// Positivef asserts that the specified element is positive +// +// a.Positivef(1, "error message %s", "formatted") +// a.Positivef(1.23, "error message %s", "formatted") +func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Positivef(a.t, e, msg, args...) +} + +// Regexp asserts that a specified regexp matches a string. +// +// a.Regexp(regexp.MustCompile("start"), "it's starting") +// a.Regexp("start...$", "it's not starting") +func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Regexp(a.t, rx, str, msgAndArgs...) +} + +// Regexpf asserts that a specified regexp matches a string. +// +// a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") +// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") +func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Regexpf(a.t, rx, str, msg, args...) +} + +// Same asserts that two pointers reference the same object. +// +// a.Same(ptr1, ptr2) +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func (a *Assertions) Same(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Same(a.t, expected, actual, msgAndArgs...) +} + +// Samef asserts that two pointers reference the same object. +// +// a.Samef(ptr1, ptr2, "error message %s", "formatted") +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Samef(a.t, expected, actual, msg, args...) +} + +// Subset asserts that the specified list(array, slice...) contains all +// elements given in the specified subset(array, slice...). +// +// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Subset(a.t, list, subset, msgAndArgs...) +} + +// Subsetf asserts that the specified list(array, slice...) contains all +// elements given in the specified subset(array, slice...). +// +// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Subsetf(a.t, list, subset, msg, args...) +} + +// True asserts that the specified value is true. +// +// a.True(myBool) +func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return True(a.t, value, msgAndArgs...) +} + +// Truef asserts that the specified value is true. +// +// a.Truef(myBool, "error message %s", "formatted") +func (a *Assertions) Truef(value bool, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Truef(a.t, value, msg, args...) +} + +// WithinDuration asserts that the two times are within duration delta of each other. +// +// a.WithinDuration(time.Now(), time.Now(), 10*time.Second) +func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return WithinDuration(a.t, expected, actual, delta, msgAndArgs...) +} + +// WithinDurationf asserts that the two times are within duration delta of each other. +// +// a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") +func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return WithinDurationf(a.t, expected, actual, delta, msg, args...) +} + +// WithinRange asserts that a time is within a time range (inclusive). +// +// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return WithinRange(a.t, actual, start, end, msgAndArgs...) +} + +// WithinRangef asserts that a time is within a time range (inclusive). +// +// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return WithinRangef(a.t, actual, start, end, msg, args...) +} + +// YAMLEq asserts that two YAML strings are equivalent. +func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return YAMLEq(a.t, expected, actual, msgAndArgs...) +} + +// YAMLEqf asserts that two YAML strings are equivalent. +func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return YAMLEqf(a.t, expected, actual, msg, args...) +} + +// Zero asserts that i is the zero value for its type. +func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Zero(a.t, i, msgAndArgs...) +} + +// Zerof asserts that i is the zero value for its type. +func (a *Assertions) Zerof(i interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Zerof(a.t, i, msg, args...) +} diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_forward.go.tmpl b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_forward.go.tmpl new file mode 100644 index 0000000..188bb9e --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_forward.go.tmpl @@ -0,0 +1,5 @@ +{{.CommentWithoutT "a"}} +func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) bool { + if h, ok := a.t.(tHelper); ok { h.Helper() } + return {{.DocInfo.Name}}(a.t, {{.ForwardedParams}}) +} diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_order.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_order.go new file mode 100644 index 0000000..7594487 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_order.go @@ -0,0 +1,81 @@ +package assert + +import ( + "fmt" + "reflect" +) + +// isOrdered checks that collection contains orderable elements. +func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool { + objKind := reflect.TypeOf(object).Kind() + if objKind != reflect.Slice && objKind != reflect.Array { + return false + } + + objValue := reflect.ValueOf(object) + objLen := objValue.Len() + + if objLen <= 1 { + return true + } + + value := objValue.Index(0) + valueInterface := value.Interface() + firstValueKind := value.Kind() + + for i := 1; i < objLen; i++ { + prevValue := value + prevValueInterface := valueInterface + + value = objValue.Index(i) + valueInterface = value.Interface() + + compareResult, isComparable := compare(prevValueInterface, valueInterface, firstValueKind) + + if !isComparable { + return Fail(t, fmt.Sprintf("Can not compare type \"%s\" and \"%s\"", reflect.TypeOf(value), reflect.TypeOf(prevValue)), msgAndArgs...) + } + + if !containsValue(allowedComparesResults, compareResult) { + return Fail(t, fmt.Sprintf(failMessage, prevValue, value), msgAndArgs...) + } + } + + return true +} + +// IsIncreasing asserts that the collection is increasing +// +// assert.IsIncreasing(t, []int{1, 2, 3}) +// assert.IsIncreasing(t, []float{1, 2}) +// assert.IsIncreasing(t, []string{"a", "b"}) +func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) +} + +// IsNonIncreasing asserts that the collection is not increasing +// +// assert.IsNonIncreasing(t, []int{2, 1, 1}) +// assert.IsNonIncreasing(t, []float{2, 1}) +// assert.IsNonIncreasing(t, []string{"b", "a"}) +func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) +} + +// IsDecreasing asserts that the collection is decreasing +// +// assert.IsDecreasing(t, []int{2, 1, 0}) +// assert.IsDecreasing(t, []float{2, 1}) +// assert.IsDecreasing(t, []string{"b", "a"}) +func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) +} + +// IsNonDecreasing asserts that the collection is not decreasing +// +// assert.IsNonDecreasing(t, []int{1, 1, 2}) +// assert.IsNonDecreasing(t, []float{1, 2}) +// assert.IsNonDecreasing(t, []string{"a", "b"}) +func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) +} diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_order_test.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_order_test.go new file mode 100644 index 0000000..eefe061 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertion_order_test.go @@ -0,0 +1,203 @@ +package assert + +import ( + "bytes" + "testing" +) + +func TestIsIncreasing(t *testing.T) { + mockT := new(testing.T) + + if !IsIncreasing(mockT, []int{1, 2}) { + t.Error("IsIncreasing should return true") + } + + if !IsIncreasing(mockT, []int{1, 2, 3, 4, 5}) { + t.Error("IsIncreasing should return true") + } + + if IsIncreasing(mockT, []int{1, 1}) { + t.Error("IsIncreasing should return false") + } + + if IsIncreasing(mockT, []int{2, 1}) { + t.Error("IsIncreasing should return false") + } + + // Check error report + for _, currCase := range []struct { + collection interface{} + msg string + }{ + {collection: []string{"b", "a"}, msg: `"b" is not less than "a"`}, + {collection: []int{2, 1}, msg: `"2" is not less than "1"`}, + {collection: []int{2, 1, 3, 4, 5, 6, 7}, msg: `"2" is not less than "1"`}, + {collection: []int{-1, 0, 2, 1}, msg: `"2" is not less than "1"`}, + {collection: []int8{2, 1}, msg: `"2" is not less than "1"`}, + {collection: []int16{2, 1}, msg: `"2" is not less than "1"`}, + {collection: []int32{2, 1}, msg: `"2" is not less than "1"`}, + {collection: []int64{2, 1}, msg: `"2" is not less than "1"`}, + {collection: []uint8{2, 1}, msg: `"2" is not less than "1"`}, + {collection: []uint16{2, 1}, msg: `"2" is not less than "1"`}, + {collection: []uint32{2, 1}, msg: `"2" is not less than "1"`}, + {collection: []uint64{2, 1}, msg: `"2" is not less than "1"`}, + {collection: []float32{2.34, 1.23}, msg: `"2.34" is not less than "1.23"`}, + {collection: []float64{2.34, 1.23}, msg: `"2.34" is not less than "1.23"`}, + } { + out := &outputT{buf: bytes.NewBuffer(nil)} + False(t, IsIncreasing(out, currCase.collection)) + Contains(t, out.buf.String(), currCase.msg) + } +} + +func TestIsNonIncreasing(t *testing.T) { + mockT := new(testing.T) + + if !IsNonIncreasing(mockT, []int{2, 1}) { + t.Error("IsNonIncreasing should return true") + } + + if !IsNonIncreasing(mockT, []int{5, 4, 4, 3, 2, 1}) { + t.Error("IsNonIncreasing should return true") + } + + if !IsNonIncreasing(mockT, []int{1, 1}) { + t.Error("IsNonIncreasing should return true") + } + + if IsNonIncreasing(mockT, []int{1, 2}) { + t.Error("IsNonIncreasing should return false") + } + + // Check error report + for _, currCase := range []struct { + collection interface{} + msg string + }{ + {collection: []string{"a", "b"}, msg: `"a" is not greater than or equal to "b"`}, + {collection: []int{1, 2}, msg: `"1" is not greater than or equal to "2"`}, + {collection: []int{1, 2, 7, 6, 5, 4, 3}, msg: `"1" is not greater than or equal to "2"`}, + {collection: []int{5, 4, 3, 1, 2}, msg: `"1" is not greater than or equal to "2"`}, + {collection: []int8{1, 2}, msg: `"1" is not greater than or equal to "2"`}, + {collection: []int16{1, 2}, msg: `"1" is not greater than or equal to "2"`}, + {collection: []int32{1, 2}, msg: `"1" is not greater than or equal to "2"`}, + {collection: []int64{1, 2}, msg: `"1" is not greater than or equal to "2"`}, + {collection: []uint8{1, 2}, msg: `"1" is not greater than or equal to "2"`}, + {collection: []uint16{1, 2}, msg: `"1" is not greater than or equal to "2"`}, + {collection: []uint32{1, 2}, msg: `"1" is not greater than or equal to "2"`}, + {collection: []uint64{1, 2}, msg: `"1" is not greater than or equal to "2"`}, + {collection: []float32{1.23, 2.34}, msg: `"1.23" is not greater than or equal to "2.34"`}, + {collection: []float64{1.23, 2.34}, msg: `"1.23" is not greater than or equal to "2.34"`}, + } { + out := &outputT{buf: bytes.NewBuffer(nil)} + False(t, IsNonIncreasing(out, currCase.collection)) + Contains(t, out.buf.String(), currCase.msg) + } +} + +func TestIsDecreasing(t *testing.T) { + mockT := new(testing.T) + + if !IsDecreasing(mockT, []int{2, 1}) { + t.Error("IsDecreasing should return true") + } + + if !IsDecreasing(mockT, []int{5, 4, 3, 2, 1}) { + t.Error("IsDecreasing should return true") + } + + if IsDecreasing(mockT, []int{1, 1}) { + t.Error("IsDecreasing should return false") + } + + if IsDecreasing(mockT, []int{1, 2}) { + t.Error("IsDecreasing should return false") + } + + // Check error report + for _, currCase := range []struct { + collection interface{} + msg string + }{ + {collection: []string{"a", "b"}, msg: `"a" is not greater than "b"`}, + {collection: []int{1, 2}, msg: `"1" is not greater than "2"`}, + {collection: []int{1, 2, 7, 6, 5, 4, 3}, msg: `"1" is not greater than "2"`}, + {collection: []int{5, 4, 3, 1, 2}, msg: `"1" is not greater than "2"`}, + {collection: []int8{1, 2}, msg: `"1" is not greater than "2"`}, + {collection: []int16{1, 2}, msg: `"1" is not greater than "2"`}, + {collection: []int32{1, 2}, msg: `"1" is not greater than "2"`}, + {collection: []int64{1, 2}, msg: `"1" is not greater than "2"`}, + {collection: []uint8{1, 2}, msg: `"1" is not greater than "2"`}, + {collection: []uint16{1, 2}, msg: `"1" is not greater than "2"`}, + {collection: []uint32{1, 2}, msg: `"1" is not greater than "2"`}, + {collection: []uint64{1, 2}, msg: `"1" is not greater than "2"`}, + {collection: []float32{1.23, 2.34}, msg: `"1.23" is not greater than "2.34"`}, + {collection: []float64{1.23, 2.34}, msg: `"1.23" is not greater than "2.34"`}, + } { + out := &outputT{buf: bytes.NewBuffer(nil)} + False(t, IsDecreasing(out, currCase.collection)) + Contains(t, out.buf.String(), currCase.msg) + } +} + +func TestIsNonDecreasing(t *testing.T) { + mockT := new(testing.T) + + if !IsNonDecreasing(mockT, []int{1, 2}) { + t.Error("IsNonDecreasing should return true") + } + + if !IsNonDecreasing(mockT, []int{1, 1, 2, 3, 4, 5}) { + t.Error("IsNonDecreasing should return true") + } + + if !IsNonDecreasing(mockT, []int{1, 1}) { + t.Error("IsNonDecreasing should return false") + } + + if IsNonDecreasing(mockT, []int{2, 1}) { + t.Error("IsNonDecreasing should return false") + } + + // Check error report + for _, currCase := range []struct { + collection interface{} + msg string + }{ + {collection: []string{"b", "a"}, msg: `"b" is not less than or equal to "a"`}, + {collection: []int{2, 1}, msg: `"2" is not less than or equal to "1"`}, + {collection: []int{2, 1, 3, 4, 5, 6, 7}, msg: `"2" is not less than or equal to "1"`}, + {collection: []int{-1, 0, 2, 1}, msg: `"2" is not less than or equal to "1"`}, + {collection: []int8{2, 1}, msg: `"2" is not less than or equal to "1"`}, + {collection: []int16{2, 1}, msg: `"2" is not less than or equal to "1"`}, + {collection: []int32{2, 1}, msg: `"2" is not less than or equal to "1"`}, + {collection: []int64{2, 1}, msg: `"2" is not less than or equal to "1"`}, + {collection: []uint8{2, 1}, msg: `"2" is not less than or equal to "1"`}, + {collection: []uint16{2, 1}, msg: `"2" is not less than or equal to "1"`}, + {collection: []uint32{2, 1}, msg: `"2" is not less than or equal to "1"`}, + {collection: []uint64{2, 1}, msg: `"2" is not less than or equal to "1"`}, + {collection: []float32{2.34, 1.23}, msg: `"2.34" is not less than or equal to "1.23"`}, + {collection: []float64{2.34, 1.23}, msg: `"2.34" is not less than or equal to "1.23"`}, + } { + out := &outputT{buf: bytes.NewBuffer(nil)} + False(t, IsNonDecreasing(out, currCase.collection)) + Contains(t, out.buf.String(), currCase.msg) + } +} + +func TestOrderingMsgAndArgsForwarding(t *testing.T) { + msgAndArgs := []interface{}{"format %s %x", "this", 0xc001} + expectedOutput := "format this c001\n" + collection := []int{1, 2, 1} + funcs := []func(t TestingT){ + func(t TestingT) { IsIncreasing(t, collection, msgAndArgs...) }, + func(t TestingT) { IsNonIncreasing(t, collection, msgAndArgs...) }, + func(t TestingT) { IsDecreasing(t, collection, msgAndArgs...) }, + func(t TestingT) { IsNonDecreasing(t, collection, msgAndArgs...) }, + } + for _, f := range funcs { + out := &outputT{buf: bytes.NewBuffer(nil)} + f(out) + Contains(t, out.buf.String(), expectedOutput) + } +} diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertions.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertions.go new file mode 100644 index 0000000..2924cf3 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertions.go @@ -0,0 +1,1856 @@ +package assert + +import ( + "bufio" + "bytes" + "encoding/json" + "errors" + "fmt" + "math" + "os" + "reflect" + "regexp" + "runtime" + "runtime/debug" + "strings" + "time" + "unicode" + "unicode/utf8" + + "github.com/davecgh/go-spew/spew" + "github.com/pmezard/go-difflib/difflib" + yaml "gopkg.in/yaml.v3" +) + +//go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_format.go.tmpl" + +// TestingT is an interface wrapper around *testing.T +type TestingT interface { + Errorf(format string, args ...interface{}) +} + +// ComparisonAssertionFunc is a common function prototype when comparing two values. Can be useful +// for table driven tests. +type ComparisonAssertionFunc func(TestingT, interface{}, interface{}, ...interface{}) bool + +// ValueAssertionFunc is a common function prototype when validating a single value. Can be useful +// for table driven tests. +type ValueAssertionFunc func(TestingT, interface{}, ...interface{}) bool + +// BoolAssertionFunc is a common function prototype when validating a bool value. Can be useful +// for table driven tests. +type BoolAssertionFunc func(TestingT, bool, ...interface{}) bool + +// ErrorAssertionFunc is a common function prototype when validating an error value. Can be useful +// for table driven tests. +type ErrorAssertionFunc func(TestingT, error, ...interface{}) bool + +// Comparison is a custom function that returns true on success and false on failure +type Comparison func() (success bool) + +/* + Helper functions +*/ + +// ObjectsAreEqual determines if two objects are considered equal. +// +// This function does no assertion of any kind. +func ObjectsAreEqual(expected, actual interface{}) bool { + if expected == nil || actual == nil { + return expected == actual + } + + exp, ok := expected.([]byte) + if !ok { + return reflect.DeepEqual(expected, actual) + } + + act, ok := actual.([]byte) + if !ok { + return false + } + if exp == nil || act == nil { + return exp == nil && act == nil + } + return bytes.Equal(exp, act) +} + +// ObjectsAreEqualValues gets whether two objects are equal, or if their +// values are equal. +func ObjectsAreEqualValues(expected, actual interface{}) bool { + if ObjectsAreEqual(expected, actual) { + return true + } + + actualType := reflect.TypeOf(actual) + if actualType == nil { + return false + } + expectedValue := reflect.ValueOf(expected) + if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) { + // Attempt comparison after type conversion + return reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), actual) + } + + return false +} + +/* CallerInfo is necessary because the assert functions use the testing object +internally, causing it to print the file:line of the assert method, rather than where +the problem actually occurred in calling code.*/ + +// CallerInfo returns an array of strings containing the file and line number +// of each stack frame leading from the current test to the assert call that +// failed. +func CallerInfo() []string { + + var pc uintptr + var ok bool + var file string + var line int + var name string + + callers := []string{} + for i := 0; ; i++ { + pc, file, line, ok = runtime.Caller(i) + if !ok { + // The breaks below failed to terminate the loop, and we ran off the + // end of the call stack. + break + } + + // This is a huge edge case, but it will panic if this is the case, see #180 + if file == "<autogenerated>" { + break + } + + f := runtime.FuncForPC(pc) + if f == nil { + break + } + name = f.Name() + + // testing.tRunner is the standard library function that calls + // tests. Subtests are called directly by tRunner, without going through + // the Test/Benchmark/Example function that contains the t.Run calls, so + // with subtests we should break when we hit tRunner, without adding it + // to the list of callers. + if name == "testing.tRunner" { + break + } + + parts := strings.Split(file, "/") + if len(parts) > 1 { + filename := parts[len(parts)-1] + dir := parts[len(parts)-2] + if (dir != "assert" && dir != "mock" && dir != "require") || filename == "mock_test.go" { + callers = append(callers, fmt.Sprintf("%s:%d", file, line)) + } + } + + // Drop the package + segments := strings.Split(name, ".") + name = segments[len(segments)-1] + if isTest(name, "Test") || + isTest(name, "Benchmark") || + isTest(name, "Example") { + break + } + } + + return callers +} + +// Stolen from the `go test` tool. +// isTest tells whether name looks like a test (or benchmark, according to prefix). +// It is a Test (say) if there is a character after Test that is not a lower-case letter. +// We don't want TesticularCancer. +func isTest(name, prefix string) bool { + if !strings.HasPrefix(name, prefix) { + return false + } + if len(name) == len(prefix) { // "Test" is ok + return true + } + r, _ := utf8.DecodeRuneInString(name[len(prefix):]) + return !unicode.IsLower(r) +} + +func messageFromMsgAndArgs(msgAndArgs ...interface{}) string { + if len(msgAndArgs) == 0 || msgAndArgs == nil { + return "" + } + if len(msgAndArgs) == 1 { + msg := msgAndArgs[0] + if msgAsStr, ok := msg.(string); ok { + return msgAsStr + } + return fmt.Sprintf("%+v", msg) + } + if len(msgAndArgs) > 1 { + return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...) + } + return "" +} + +// Aligns the provided message so that all lines after the first line start at the same location as the first line. +// Assumes that the first line starts at the correct location (after carriage return, tab, label, spacer and tab). +// The longestLabelLen parameter specifies the length of the longest label in the output (required becaues this is the +// basis on which the alignment occurs). +func indentMessageLines(message string, longestLabelLen int) string { + outBuf := new(bytes.Buffer) + + for i, scanner := 0, bufio.NewScanner(strings.NewReader(message)); scanner.Scan(); i++ { + // no need to align first line because it starts at the correct location (after the label) + if i != 0 { + // append alignLen+1 spaces to align with "{{longestLabel}}:" before adding tab + outBuf.WriteString("\n\t" + strings.Repeat(" ", longestLabelLen+1) + "\t") + } + outBuf.WriteString(scanner.Text()) + } + + return outBuf.String() +} + +type failNower interface { + FailNow() +} + +// FailNow fails test +func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + Fail(t, failureMessage, msgAndArgs...) + + // We cannot extend TestingT with FailNow() and + // maintain backwards compatibility, so we fallback + // to panicking when FailNow is not available in + // TestingT. + // See issue #263 + + if t, ok := t.(failNower); ok { + t.FailNow() + } else { + panic("test failed and t is missing `FailNow()`") + } + return false +} + +// Fail reports a failure through +func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + content := []labeledContent{ + {"Error Trace", strings.Join(CallerInfo(), "\n\t\t\t")}, + {"Error", failureMessage}, + } + + // Add test name if the Go version supports it + if n, ok := t.(interface { + Name() string + }); ok { + content = append(content, labeledContent{"Test", n.Name()}) + } + + message := messageFromMsgAndArgs(msgAndArgs...) + if len(message) > 0 { + content = append(content, labeledContent{"Messages", message}) + } + + t.Errorf("\n%s", ""+labeledOutput(content...)) + + return false +} + +type labeledContent struct { + label string + content string +} + +// labeledOutput returns a string consisting of the provided labeledContent. Each labeled output is appended in the following manner: +// +// \t{{label}}:{{align_spaces}}\t{{content}}\n +// +// The initial carriage return is required to undo/erase any padding added by testing.T.Errorf. The "\t{{label}}:" is for the label. +// If a label is shorter than the longest label provided, padding spaces are added to make all the labels match in length. Once this +// alignment is achieved, "\t{{content}}\n" is added for the output. +// +// If the content of the labeledOutput contains line breaks, the subsequent lines are aligned so that they start at the same location as the first line. +func labeledOutput(content ...labeledContent) string { + longestLabel := 0 + for _, v := range content { + if len(v.label) > longestLabel { + longestLabel = len(v.label) + } + } + var output string + for _, v := range content { + output += "\t" + v.label + ":" + strings.Repeat(" ", longestLabel-len(v.label)) + "\t" + indentMessageLines(v.content, longestLabel) + "\n" + } + return output +} + +// Implements asserts that an object is implemented by the specified interface. +// +// assert.Implements(t, (*MyInterface)(nil), new(MyObject)) +func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + interfaceType := reflect.TypeOf(interfaceObject).Elem() + + if object == nil { + return Fail(t, fmt.Sprintf("Cannot check if nil implements %v", interfaceType), msgAndArgs...) + } + if !reflect.TypeOf(object).Implements(interfaceType) { + return Fail(t, fmt.Sprintf("%T must implement %v", object, interfaceType), msgAndArgs...) + } + + return true +} + +// IsType asserts that the specified objects are of the same type. +func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + if !ObjectsAreEqual(reflect.TypeOf(object), reflect.TypeOf(expectedType)) { + return Fail(t, fmt.Sprintf("Object expected to be of type %v, but was %v", reflect.TypeOf(expectedType), reflect.TypeOf(object)), msgAndArgs...) + } + + return true +} + +// Equal asserts that two objects are equal. +// +// assert.Equal(t, 123, 123) +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). Function equality +// cannot be determined and will always fail. +func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if err := validateEqualArgs(expected, actual); err != nil { + return Fail(t, fmt.Sprintf("Invalid operation: %#v == %#v (%s)", + expected, actual, err), msgAndArgs...) + } + + if !ObjectsAreEqual(expected, actual) { + diff := diff(expected, actual) + expected, actual = formatUnequalValues(expected, actual) + return Fail(t, fmt.Sprintf("Not equal: \n"+ + "expected: %s\n"+ + "actual : %s%s", expected, actual, diff), msgAndArgs...) + } + + return true + +} + +// validateEqualArgs checks whether provided arguments can be safely used in the +// Equal/NotEqual functions. +func validateEqualArgs(expected, actual interface{}) error { + if expected == nil && actual == nil { + return nil + } + + if isFunction(expected) || isFunction(actual) { + return errors.New("cannot take func type as argument") + } + return nil +} + +// Same asserts that two pointers reference the same object. +// +// assert.Same(t, ptr1, ptr2) +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + if !samePointers(expected, actual) { + return Fail(t, fmt.Sprintf("Not same: \n"+ + "expected: %p %#v\n"+ + "actual : %p %#v", expected, expected, actual, actual), msgAndArgs...) + } + + return true +} + +// NotSame asserts that two pointers do not reference the same object. +// +// assert.NotSame(t, ptr1, ptr2) +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func NotSame(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + if samePointers(expected, actual) { + return Fail(t, fmt.Sprintf( + "Expected and actual point to the same object: %p %#v", + expected, expected), msgAndArgs...) + } + return true +} + +// samePointers compares two generic interface objects and returns whether +// they point to the same object +func samePointers(first, second interface{}) bool { + firstPtr, secondPtr := reflect.ValueOf(first), reflect.ValueOf(second) + if firstPtr.Kind() != reflect.Ptr || secondPtr.Kind() != reflect.Ptr { + return false + } + + firstType, secondType := reflect.TypeOf(first), reflect.TypeOf(second) + if firstType != secondType { + return false + } + + // compare pointer addresses + return first == second +} + +// formatUnequalValues takes two values of arbitrary types and returns string +// representations appropriate to be presented to the user. +// +// If the values are not of like type, the returned strings will be prefixed +// with the type name, and the value will be enclosed in parenthesis similar +// to a type conversion in the Go grammar. +func formatUnequalValues(expected, actual interface{}) (e string, a string) { + if reflect.TypeOf(expected) != reflect.TypeOf(actual) { + return fmt.Sprintf("%T(%s)", expected, truncatingFormat(expected)), + fmt.Sprintf("%T(%s)", actual, truncatingFormat(actual)) + } + switch expected.(type) { + case time.Duration: + return fmt.Sprintf("%v", expected), fmt.Sprintf("%v", actual) + } + return truncatingFormat(expected), truncatingFormat(actual) +} + +// truncatingFormat formats the data and truncates it if it's too long. +// +// This helps keep formatted error messages lines from exceeding the +// bufio.MaxScanTokenSize max line length that the go testing framework imposes. +func truncatingFormat(data interface{}) string { + value := fmt.Sprintf("%#v", data) + max := bufio.MaxScanTokenSize - 100 // Give us some space the type info too if needed. + if len(value) > max { + value = value[0:max] + "<... truncated>" + } + return value +} + +// EqualValues asserts that two objects are equal or convertable to the same types +// and equal. +// +// assert.EqualValues(t, uint32(123), int32(123)) +func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + if !ObjectsAreEqualValues(expected, actual) { + diff := diff(expected, actual) + expected, actual = formatUnequalValues(expected, actual) + return Fail(t, fmt.Sprintf("Not equal: \n"+ + "expected: %s\n"+ + "actual : %s%s", expected, actual, diff), msgAndArgs...) + } + + return true + +} + +// Exactly asserts that two objects are equal in value and type. +// +// assert.Exactly(t, int32(123), int64(123)) +func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + aType := reflect.TypeOf(expected) + bType := reflect.TypeOf(actual) + + if aType != bType { + return Fail(t, fmt.Sprintf("Types expected to match exactly\n\t%v != %v", aType, bType), msgAndArgs...) + } + + return Equal(t, expected, actual, msgAndArgs...) + +} + +// NotNil asserts that the specified object is not nil. +// +// assert.NotNil(t, err) +func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + if !isNil(object) { + return true + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Fail(t, "Expected value not to be nil.", msgAndArgs...) +} + +// containsKind checks if a specified kind in the slice of kinds. +func containsKind(kinds []reflect.Kind, kind reflect.Kind) bool { + for i := 0; i < len(kinds); i++ { + if kind == kinds[i] { + return true + } + } + + return false +} + +// isNil checks if a specified object is nil or not, without Failing. +func isNil(object interface{}) bool { + if object == nil { + return true + } + + value := reflect.ValueOf(object) + kind := value.Kind() + isNilableKind := containsKind( + []reflect.Kind{ + reflect.Chan, reflect.Func, + reflect.Interface, reflect.Map, + reflect.Ptr, reflect.Slice, reflect.UnsafePointer}, + kind) + + if isNilableKind && value.IsNil() { + return true + } + + return false +} + +// Nil asserts that the specified object is nil. +// +// assert.Nil(t, err) +func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + if isNil(object) { + return true + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...) +} + +// isEmpty gets whether the specified object is considered empty or not. +func isEmpty(object interface{}) bool { + + // get nil case out of the way + if object == nil { + return true + } + + objValue := reflect.ValueOf(object) + + switch objValue.Kind() { + // collection types are empty when they have no element + case reflect.Chan, reflect.Map, reflect.Slice: + return objValue.Len() == 0 + // pointers are empty if nil or if the value they point to is empty + case reflect.Ptr: + if objValue.IsNil() { + return true + } + deref := objValue.Elem().Interface() + return isEmpty(deref) + // for all other types, compare against the zero value + // array types are empty when they match their zero-initialized state + default: + zero := reflect.Zero(objValue.Type()) + return reflect.DeepEqual(object, zero.Interface()) + } +} + +// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// assert.Empty(t, obj) +func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + pass := isEmpty(object) + if !pass { + if h, ok := t.(tHelper); ok { + h.Helper() + } + Fail(t, fmt.Sprintf("Should be empty, but was %v", object), msgAndArgs...) + } + + return pass + +} + +// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// if assert.NotEmpty(t, obj) { +// assert.Equal(t, "two", obj[1]) +// } +func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + pass := !isEmpty(object) + if !pass { + if h, ok := t.(tHelper); ok { + h.Helper() + } + Fail(t, fmt.Sprintf("Should NOT be empty, but was %v", object), msgAndArgs...) + } + + return pass + +} + +// getLen try to get length of object. +// return (false, 0) if impossible. +func getLen(x interface{}) (ok bool, length int) { + v := reflect.ValueOf(x) + defer func() { + if e := recover(); e != nil { + ok = false + } + }() + return true, v.Len() +} + +// Len asserts that the specified object has specific length. +// Len also fails if the object has a type that len() not accept. +// +// assert.Len(t, mySlice, 3) +func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + ok, l := getLen(object) + if !ok { + return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", object), msgAndArgs...) + } + + if l != length { + return Fail(t, fmt.Sprintf("\"%s\" should have %d item(s), but has %d", object, length, l), msgAndArgs...) + } + return true +} + +// True asserts that the specified value is true. +// +// assert.True(t, myBool) +func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { + if !value { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Fail(t, "Should be true", msgAndArgs...) + } + + return true + +} + +// False asserts that the specified value is false. +// +// assert.False(t, myBool) +func False(t TestingT, value bool, msgAndArgs ...interface{}) bool { + if value { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Fail(t, "Should be false", msgAndArgs...) + } + + return true + +} + +// NotEqual asserts that the specified values are NOT equal. +// +// assert.NotEqual(t, obj1, obj2) +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). +func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if err := validateEqualArgs(expected, actual); err != nil { + return Fail(t, fmt.Sprintf("Invalid operation: %#v != %#v (%s)", + expected, actual, err), msgAndArgs...) + } + + if ObjectsAreEqual(expected, actual) { + return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...) + } + + return true + +} + +// NotEqualValues asserts that two objects are not equal even when converted to the same type +// +// assert.NotEqualValues(t, obj1, obj2) +func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + if ObjectsAreEqualValues(expected, actual) { + return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...) + } + + return true +} + +// containsElement try loop over the list check if the list includes the element. +// return (false, false) if impossible. +// return (true, false) if element was not found. +// return (true, true) if element was found. +func containsElement(list interface{}, element interface{}) (ok, found bool) { + + listValue := reflect.ValueOf(list) + listType := reflect.TypeOf(list) + if listType == nil { + return false, false + } + listKind := listType.Kind() + defer func() { + if e := recover(); e != nil { + ok = false + found = false + } + }() + + if listKind == reflect.String { + elementValue := reflect.ValueOf(element) + return true, strings.Contains(listValue.String(), elementValue.String()) + } + + if listKind == reflect.Map { + mapKeys := listValue.MapKeys() + for i := 0; i < len(mapKeys); i++ { + if ObjectsAreEqual(mapKeys[i].Interface(), element) { + return true, true + } + } + return true, false + } + + for i := 0; i < listValue.Len(); i++ { + if ObjectsAreEqual(listValue.Index(i).Interface(), element) { + return true, true + } + } + return true, false + +} + +// Contains asserts that the specified string, list(array, slice...) or map contains the +// specified substring or element. +// +// assert.Contains(t, "Hello World", "World") +// assert.Contains(t, ["Hello", "World"], "World") +// assert.Contains(t, {"Hello": "World"}, "Hello") +func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + ok, found := containsElement(s, contains) + if !ok { + return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...) + } + if !found { + return Fail(t, fmt.Sprintf("%#v does not contain %#v", s, contains), msgAndArgs...) + } + + return true + +} + +// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the +// specified substring or element. +// +// assert.NotContains(t, "Hello World", "Earth") +// assert.NotContains(t, ["Hello", "World"], "Earth") +// assert.NotContains(t, {"Hello": "World"}, "Earth") +func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + ok, found := containsElement(s, contains) + if !ok { + return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...) + } + if found { + return Fail(t, fmt.Sprintf("\"%s\" should not contain \"%s\"", s, contains), msgAndArgs...) + } + + return true + +} + +// Subset asserts that the specified list(array, slice...) contains all +// elements given in the specified subset(array, slice...). +// +// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if subset == nil { + return true // we consider nil to be equal to the nil set + } + + listKind := reflect.TypeOf(list).Kind() + if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map { + return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...) + } + + subsetKind := reflect.TypeOf(subset).Kind() + if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map { + return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) + } + + if subsetKind == reflect.Map && listKind == reflect.Map { + subsetMap := reflect.ValueOf(subset) + actualMap := reflect.ValueOf(list) + + for _, k := range subsetMap.MapKeys() { + ev := subsetMap.MapIndex(k) + av := actualMap.MapIndex(k) + + if !av.IsValid() { + return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...) + } + if !ObjectsAreEqual(ev.Interface(), av.Interface()) { + return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...) + } + } + + return true + } + + subsetList := reflect.ValueOf(subset) + for i := 0; i < subsetList.Len(); i++ { + element := subsetList.Index(i).Interface() + ok, found := containsElement(list, element) + if !ok { + return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", list), msgAndArgs...) + } + if !found { + return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, element), msgAndArgs...) + } + } + + return true +} + +// NotSubset asserts that the specified list(array, slice...) contains not all +// elements given in the specified subset(array, slice...). +// +// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if subset == nil { + return Fail(t, "nil is the empty set which is a subset of every set", msgAndArgs...) + } + + listKind := reflect.TypeOf(list).Kind() + if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map { + return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...) + } + + subsetKind := reflect.TypeOf(subset).Kind() + if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map { + return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) + } + + if subsetKind == reflect.Map && listKind == reflect.Map { + subsetMap := reflect.ValueOf(subset) + actualMap := reflect.ValueOf(list) + + for _, k := range subsetMap.MapKeys() { + ev := subsetMap.MapIndex(k) + av := actualMap.MapIndex(k) + + if !av.IsValid() { + return true + } + if !ObjectsAreEqual(ev.Interface(), av.Interface()) { + return true + } + } + + return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...) + } + + subsetList := reflect.ValueOf(subset) + for i := 0; i < subsetList.Len(); i++ { + element := subsetList.Index(i).Interface() + ok, found := containsElement(list, element) + if !ok { + return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...) + } + if !found { + return true + } + } + + return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...) +} + +// ElementsMatch asserts that the specified listA(array, slice...) is equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should match. +// +// assert.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2]) +func ElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface{}) (ok bool) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if isEmpty(listA) && isEmpty(listB) { + return true + } + + if !isList(t, listA, msgAndArgs...) || !isList(t, listB, msgAndArgs...) { + return false + } + + extraA, extraB := diffLists(listA, listB) + + if len(extraA) == 0 && len(extraB) == 0 { + return true + } + + return Fail(t, formatListDiff(listA, listB, extraA, extraB), msgAndArgs...) +} + +// isList checks that the provided value is array or slice. +func isList(t TestingT, list interface{}, msgAndArgs ...interface{}) (ok bool) { + kind := reflect.TypeOf(list).Kind() + if kind != reflect.Array && kind != reflect.Slice { + return Fail(t, fmt.Sprintf("%q has an unsupported type %s, expecting array or slice", list, kind), + msgAndArgs...) + } + return true +} + +// diffLists diffs two arrays/slices and returns slices of elements that are only in A and only in B. +// If some element is present multiple times, each instance is counted separately (e.g. if something is 2x in A and +// 5x in B, it will be 0x in extraA and 3x in extraB). The order of items in both lists is ignored. +func diffLists(listA, listB interface{}) (extraA, extraB []interface{}) { + aValue := reflect.ValueOf(listA) + bValue := reflect.ValueOf(listB) + + aLen := aValue.Len() + bLen := bValue.Len() + + // Mark indexes in bValue that we already used + visited := make([]bool, bLen) + for i := 0; i < aLen; i++ { + element := aValue.Index(i).Interface() + found := false + for j := 0; j < bLen; j++ { + if visited[j] { + continue + } + if ObjectsAreEqual(bValue.Index(j).Interface(), element) { + visited[j] = true + found = true + break + } + } + if !found { + extraA = append(extraA, element) + } + } + + for j := 0; j < bLen; j++ { + if visited[j] { + continue + } + extraB = append(extraB, bValue.Index(j).Interface()) + } + + return +} + +func formatListDiff(listA, listB interface{}, extraA, extraB []interface{}) string { + var msg bytes.Buffer + + msg.WriteString("elements differ") + if len(extraA) > 0 { + msg.WriteString("\n\nextra elements in list A:\n") + msg.WriteString(spewConfig.Sdump(extraA)) + } + if len(extraB) > 0 { + msg.WriteString("\n\nextra elements in list B:\n") + msg.WriteString(spewConfig.Sdump(extraB)) + } + msg.WriteString("\n\nlistA:\n") + msg.WriteString(spewConfig.Sdump(listA)) + msg.WriteString("\n\nlistB:\n") + msg.WriteString(spewConfig.Sdump(listB)) + + return msg.String() +} + +// Condition uses a Comparison to assert a complex condition. +func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + result := comp() + if !result { + Fail(t, "Condition failed!", msgAndArgs...) + } + return result +} + +// PanicTestFunc defines a func that should be passed to the assert.Panics and assert.NotPanics +// methods, and represents a simple func that takes no arguments, and returns nothing. +type PanicTestFunc func() + +// didPanic returns true if the function passed to it panics. Otherwise, it returns false. +func didPanic(f PanicTestFunc) (didPanic bool, message interface{}, stack string) { + didPanic = true + + defer func() { + message = recover() + if didPanic { + stack = string(debug.Stack()) + } + }() + + // call the target function + f() + didPanic = false + + return +} + +// Panics asserts that the code inside the specified PanicTestFunc panics. +// +// assert.Panics(t, func(){ GoCrazy() }) +func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + if funcDidPanic, panicValue, _ := didPanic(f); !funcDidPanic { + return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) + } + + return true +} + +// PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that +// the recovered panic value equals the expected panic value. +// +// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) +func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + funcDidPanic, panicValue, panickedStack := didPanic(f) + if !funcDidPanic { + return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) + } + if panicValue != expected { + return Fail(t, fmt.Sprintf("func %#v should panic with value:\t%#v\n\tPanic value:\t%#v\n\tPanic stack:\t%s", f, expected, panicValue, panickedStack), msgAndArgs...) + } + + return true +} + +// PanicsWithError asserts that the code inside the specified PanicTestFunc +// panics, and that the recovered panic value is an error that satisfies the +// EqualError comparison. +// +// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) +func PanicsWithError(t TestingT, errString string, f PanicTestFunc, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + funcDidPanic, panicValue, panickedStack := didPanic(f) + if !funcDidPanic { + return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) + } + panicErr, ok := panicValue.(error) + if !ok || panicErr.Error() != errString { + return Fail(t, fmt.Sprintf("func %#v should panic with error message:\t%#v\n\tPanic value:\t%#v\n\tPanic stack:\t%s", f, errString, panicValue, panickedStack), msgAndArgs...) + } + + return true +} + +// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. +// +// assert.NotPanics(t, func(){ RemainCalm() }) +func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + if funcDidPanic, panicValue, panickedStack := didPanic(f); funcDidPanic { + return Fail(t, fmt.Sprintf("func %#v should not panic\n\tPanic value:\t%v\n\tPanic stack:\t%s", f, panicValue, panickedStack), msgAndArgs...) + } + + return true +} + +// WithinDuration asserts that the two times are within duration delta of each other. +// +// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) +func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + dt := expected.Sub(actual) + if dt < -delta || dt > delta { + return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...) + } + + return true +} + +// WithinRange asserts that a time is within a time range (inclusive). +// +// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +func WithinRange(t TestingT, actual, start, end time.Time, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + if end.Before(start) { + return Fail(t, "Start should be before end", msgAndArgs...) + } + + if actual.Before(start) { + return Fail(t, fmt.Sprintf("Time %v expected to be in time range %v to %v, but is before the range", actual, start, end), msgAndArgs...) + } else if actual.After(end) { + return Fail(t, fmt.Sprintf("Time %v expected to be in time range %v to %v, but is after the range", actual, start, end), msgAndArgs...) + } + + return true +} + +func toFloat(x interface{}) (float64, bool) { + var xf float64 + xok := true + + switch xn := x.(type) { + case uint: + xf = float64(xn) + case uint8: + xf = float64(xn) + case uint16: + xf = float64(xn) + case uint32: + xf = float64(xn) + case uint64: + xf = float64(xn) + case int: + xf = float64(xn) + case int8: + xf = float64(xn) + case int16: + xf = float64(xn) + case int32: + xf = float64(xn) + case int64: + xf = float64(xn) + case float32: + xf = float64(xn) + case float64: + xf = xn + case time.Duration: + xf = float64(xn) + default: + xok = false + } + + return xf, xok +} + +// InDelta asserts that the two numerals are within delta of each other. +// +// assert.InDelta(t, math.Pi, 22/7.0, 0.01) +func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + af, aok := toFloat(expected) + bf, bok := toFloat(actual) + + if !aok || !bok { + return Fail(t, "Parameters must be numerical", msgAndArgs...) + } + + if math.IsNaN(af) && math.IsNaN(bf) { + return true + } + + if math.IsNaN(af) { + return Fail(t, "Expected must not be NaN", msgAndArgs...) + } + + if math.IsNaN(bf) { + return Fail(t, fmt.Sprintf("Expected %v with delta %v, but was NaN", expected, delta), msgAndArgs...) + } + + dt := af - bf + if dt < -delta || dt > delta { + return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...) + } + + return true +} + +// InDeltaSlice is the same as InDelta, except it compares two slices. +func InDeltaSlice(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if expected == nil || actual == nil || + reflect.TypeOf(actual).Kind() != reflect.Slice || + reflect.TypeOf(expected).Kind() != reflect.Slice { + return Fail(t, "Parameters must be slice", msgAndArgs...) + } + + actualSlice := reflect.ValueOf(actual) + expectedSlice := reflect.ValueOf(expected) + + for i := 0; i < actualSlice.Len(); i++ { + result := InDelta(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), delta, msgAndArgs...) + if !result { + return result + } + } + + return true +} + +// InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. +func InDeltaMapValues(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if expected == nil || actual == nil || + reflect.TypeOf(actual).Kind() != reflect.Map || + reflect.TypeOf(expected).Kind() != reflect.Map { + return Fail(t, "Arguments must be maps", msgAndArgs...) + } + + expectedMap := reflect.ValueOf(expected) + actualMap := reflect.ValueOf(actual) + + if expectedMap.Len() != actualMap.Len() { + return Fail(t, "Arguments must have the same number of keys", msgAndArgs...) + } + + for _, k := range expectedMap.MapKeys() { + ev := expectedMap.MapIndex(k) + av := actualMap.MapIndex(k) + + if !ev.IsValid() { + return Fail(t, fmt.Sprintf("missing key %q in expected map", k), msgAndArgs...) + } + + if !av.IsValid() { + return Fail(t, fmt.Sprintf("missing key %q in actual map", k), msgAndArgs...) + } + + if !InDelta( + t, + ev.Interface(), + av.Interface(), + delta, + msgAndArgs..., + ) { + return false + } + } + + return true +} + +func calcRelativeError(expected, actual interface{}) (float64, error) { + af, aok := toFloat(expected) + bf, bok := toFloat(actual) + if !aok || !bok { + return 0, fmt.Errorf("Parameters must be numerical") + } + if math.IsNaN(af) && math.IsNaN(bf) { + return 0, nil + } + if math.IsNaN(af) { + return 0, errors.New("expected value must not be NaN") + } + if af == 0 { + return 0, fmt.Errorf("expected value must have a value other than zero to calculate the relative error") + } + if math.IsNaN(bf) { + return 0, errors.New("actual value must not be NaN") + } + + return math.Abs(af-bf) / math.Abs(af), nil +} + +// InEpsilon asserts that expected and actual have a relative error less than epsilon +func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if math.IsNaN(epsilon) { + return Fail(t, "epsilon must not be NaN") + } + actualEpsilon, err := calcRelativeError(expected, actual) + if err != nil { + return Fail(t, err.Error(), msgAndArgs...) + } + if actualEpsilon > epsilon { + return Fail(t, fmt.Sprintf("Relative error is too high: %#v (expected)\n"+ + " < %#v (actual)", epsilon, actualEpsilon), msgAndArgs...) + } + + return true +} + +// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. +func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if expected == nil || actual == nil || + reflect.TypeOf(actual).Kind() != reflect.Slice || + reflect.TypeOf(expected).Kind() != reflect.Slice { + return Fail(t, "Parameters must be slice", msgAndArgs...) + } + + actualSlice := reflect.ValueOf(actual) + expectedSlice := reflect.ValueOf(expected) + + for i := 0; i < actualSlice.Len(); i++ { + result := InEpsilon(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), epsilon) + if !result { + return result + } + } + + return true +} + +/* + Errors +*/ + +// NoError asserts that a function returned no error (i.e. `nil`). +// +// actualObj, err := SomeFunction() +// if assert.NoError(t, err) { +// assert.Equal(t, expectedObj, actualObj) +// } +func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { + if err != nil { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Fail(t, fmt.Sprintf("Received unexpected error:\n%+v", err), msgAndArgs...) + } + + return true +} + +// Error asserts that a function returned an error (i.e. not `nil`). +// +// actualObj, err := SomeFunction() +// if assert.Error(t, err) { +// assert.Equal(t, expectedError, err) +// } +func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { + if err == nil { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Fail(t, "An error is expected but got nil.", msgAndArgs...) + } + + return true +} + +// EqualError asserts that a function returned an error (i.e. not `nil`) +// and that it is equal to the provided error. +// +// actualObj, err := SomeFunction() +// assert.EqualError(t, err, expectedErrorString) +func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if !Error(t, theError, msgAndArgs...) { + return false + } + expected := errString + actual := theError.Error() + // don't need to use deep equals here, we know they are both strings + if expected != actual { + return Fail(t, fmt.Sprintf("Error message not equal:\n"+ + "expected: %q\n"+ + "actual : %q", expected, actual), msgAndArgs...) + } + return true +} + +// ErrorContains asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// assert.ErrorContains(t, err, expectedErrorSubString) +func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if !Error(t, theError, msgAndArgs...) { + return false + } + + actual := theError.Error() + if !strings.Contains(actual, contains) { + return Fail(t, fmt.Sprintf("Error %#v does not contain %#v", actual, contains), msgAndArgs...) + } + + return true +} + +// matchRegexp return true if a specified regexp matches a string. +func matchRegexp(rx interface{}, str interface{}) bool { + + var r *regexp.Regexp + if rr, ok := rx.(*regexp.Regexp); ok { + r = rr + } else { + r = regexp.MustCompile(fmt.Sprint(rx)) + } + + return (r.FindStringIndex(fmt.Sprint(str)) != nil) + +} + +// Regexp asserts that a specified regexp matches a string. +// +// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") +// assert.Regexp(t, "start...$", "it's not starting") +func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + match := matchRegexp(rx, str) + + if !match { + Fail(t, fmt.Sprintf("Expect \"%v\" to match \"%v\"", str, rx), msgAndArgs...) + } + + return match +} + +// NotRegexp asserts that a specified regexp does not match a string. +// +// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") +// assert.NotRegexp(t, "^start", "it's not starting") +func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + match := matchRegexp(rx, str) + + if match { + Fail(t, fmt.Sprintf("Expect \"%v\" to NOT match \"%v\"", str, rx), msgAndArgs...) + } + + return !match + +} + +// Zero asserts that i is the zero value for its type. +func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if i != nil && !reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) { + return Fail(t, fmt.Sprintf("Should be zero, but was %v", i), msgAndArgs...) + } + return true +} + +// NotZero asserts that i is not the zero value for its type. +func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if i == nil || reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) { + return Fail(t, fmt.Sprintf("Should not be zero, but was %v", i), msgAndArgs...) + } + return true +} + +// FileExists checks whether a file exists in the given path. It also fails if +// the path points to a directory or there is an error when trying to check the file. +func FileExists(t TestingT, path string, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + info, err := os.Lstat(path) + if err != nil { + if os.IsNotExist(err) { + return Fail(t, fmt.Sprintf("unable to find file %q", path), msgAndArgs...) + } + return Fail(t, fmt.Sprintf("error when running os.Lstat(%q): %s", path, err), msgAndArgs...) + } + if info.IsDir() { + return Fail(t, fmt.Sprintf("%q is a directory", path), msgAndArgs...) + } + return true +} + +// NoFileExists checks whether a file does not exist in a given path. It fails +// if the path points to an existing _file_ only. +func NoFileExists(t TestingT, path string, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + info, err := os.Lstat(path) + if err != nil { + return true + } + if info.IsDir() { + return true + } + return Fail(t, fmt.Sprintf("file %q exists", path), msgAndArgs...) +} + +// DirExists checks whether a directory exists in the given path. It also fails +// if the path is a file rather a directory or there is an error checking whether it exists. +func DirExists(t TestingT, path string, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + info, err := os.Lstat(path) + if err != nil { + if os.IsNotExist(err) { + return Fail(t, fmt.Sprintf("unable to find file %q", path), msgAndArgs...) + } + return Fail(t, fmt.Sprintf("error when running os.Lstat(%q): %s", path, err), msgAndArgs...) + } + if !info.IsDir() { + return Fail(t, fmt.Sprintf("%q is a file", path), msgAndArgs...) + } + return true +} + +// NoDirExists checks whether a directory does not exist in the given path. +// It fails if the path points to an existing _directory_ only. +func NoDirExists(t TestingT, path string, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + info, err := os.Lstat(path) + if err != nil { + if os.IsNotExist(err) { + return true + } + return true + } + if !info.IsDir() { + return true + } + return Fail(t, fmt.Sprintf("directory %q exists", path), msgAndArgs...) +} + +// JSONEq asserts that two JSON strings are equivalent. +// +// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + var expectedJSONAsInterface, actualJSONAsInterface interface{} + + if err := json.Unmarshal([]byte(expected), &expectedJSONAsInterface); err != nil { + return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid json.\nJSON parsing error: '%s'", expected, err.Error()), msgAndArgs...) + } + + if err := json.Unmarshal([]byte(actual), &actualJSONAsInterface); err != nil { + return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid json.\nJSON parsing error: '%s'", actual, err.Error()), msgAndArgs...) + } + + return Equal(t, expectedJSONAsInterface, actualJSONAsInterface, msgAndArgs...) +} + +// YAMLEq asserts that two YAML strings are equivalent. +func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + var expectedYAMLAsInterface, actualYAMLAsInterface interface{} + + if err := yaml.Unmarshal([]byte(expected), &expectedYAMLAsInterface); err != nil { + return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid yaml.\nYAML parsing error: '%s'", expected, err.Error()), msgAndArgs...) + } + + if err := yaml.Unmarshal([]byte(actual), &actualYAMLAsInterface); err != nil { + return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid yaml.\nYAML error: '%s'", actual, err.Error()), msgAndArgs...) + } + + return Equal(t, expectedYAMLAsInterface, actualYAMLAsInterface, msgAndArgs...) +} + +func typeAndKind(v interface{}) (reflect.Type, reflect.Kind) { + t := reflect.TypeOf(v) + k := t.Kind() + + if k == reflect.Ptr { + t = t.Elem() + k = t.Kind() + } + return t, k +} + +// diff returns a diff of both values as long as both are of the same type and +// are a struct, map, slice, array or string. Otherwise it returns an empty string. +func diff(expected interface{}, actual interface{}) string { + if expected == nil || actual == nil { + return "" + } + + et, ek := typeAndKind(expected) + at, _ := typeAndKind(actual) + + if et != at { + return "" + } + + if ek != reflect.Struct && ek != reflect.Map && ek != reflect.Slice && ek != reflect.Array && ek != reflect.String { + return "" + } + + var e, a string + + switch et { + case reflect.TypeOf(""): + e = reflect.ValueOf(expected).String() + a = reflect.ValueOf(actual).String() + case reflect.TypeOf(time.Time{}): + e = spewConfigStringerEnabled.Sdump(expected) + a = spewConfigStringerEnabled.Sdump(actual) + default: + e = spewConfig.Sdump(expected) + a = spewConfig.Sdump(actual) + } + + diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ + A: difflib.SplitLines(e), + B: difflib.SplitLines(a), + FromFile: "Expected", + FromDate: "", + ToFile: "Actual", + ToDate: "", + Context: 1, + }) + + return "\n\nDiff:\n" + diff +} + +func isFunction(arg interface{}) bool { + if arg == nil { + return false + } + return reflect.TypeOf(arg).Kind() == reflect.Func +} + +var spewConfig = spew.ConfigState{ + Indent: " ", + DisablePointerAddresses: true, + DisableCapacities: true, + SortKeys: true, + DisableMethods: true, + MaxDepth: 10, +} + +var spewConfigStringerEnabled = spew.ConfigState{ + Indent: " ", + DisablePointerAddresses: true, + DisableCapacities: true, + SortKeys: true, + MaxDepth: 10, +} + +type tHelper interface { + Helper() +} + +// Eventually asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. +// +// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) +func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + ch := make(chan bool, 1) + + timer := time.NewTimer(waitFor) + defer timer.Stop() + + ticker := time.NewTicker(tick) + defer ticker.Stop() + + for tick := ticker.C; ; { + select { + case <-timer.C: + return Fail(t, "Condition never satisfied", msgAndArgs...) + case <-tick: + tick = nil + go func() { ch <- condition() }() + case v := <-ch: + if v { + return true + } + tick = ticker.C + } + } +} + +// Never asserts that the given condition doesn't satisfy in waitFor time, +// periodically checking the target function each tick. +// +// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) +func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + ch := make(chan bool, 1) + + timer := time.NewTimer(waitFor) + defer timer.Stop() + + ticker := time.NewTicker(tick) + defer ticker.Stop() + + for tick := ticker.C; ; { + select { + case <-timer.C: + return true + case <-tick: + tick = nil + go func() { ch <- condition() }() + case v := <-ch: + if v { + return Fail(t, "Condition satisfied", msgAndArgs...) + } + tick = ticker.C + } + } +} + +// ErrorIs asserts that at least one of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func ErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if errors.Is(err, target) { + return true + } + + var expectedText string + if target != nil { + expectedText = target.Error() + } + + chain := buildErrorChainString(err) + + return Fail(t, fmt.Sprintf("Target error should be in err chain:\n"+ + "expected: %q\n"+ + "in chain: %s", expectedText, chain, + ), msgAndArgs...) +} + +// NotErrorIs asserts that at none of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func NotErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if !errors.Is(err, target) { + return true + } + + var expectedText string + if target != nil { + expectedText = target.Error() + } + + chain := buildErrorChainString(err) + + return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+ + "found: %q\n"+ + "in chain: %s", expectedText, chain, + ), msgAndArgs...) +} + +// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. +// This is a wrapper for errors.As. +func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if errors.As(err, target) { + return true + } + + chain := buildErrorChainString(err) + + return Fail(t, fmt.Sprintf("Should be in error chain:\n"+ + "expected: %q\n"+ + "in chain: %s", target, chain, + ), msgAndArgs...) +} + +func buildErrorChainString(err error) string { + if err == nil { + return "" + } + + e := errors.Unwrap(err) + chain := fmt.Sprintf("%q", err.Error()) + for e != nil { + chain += fmt.Sprintf("\n\t%q", e.Error()) + e = errors.Unwrap(e) + } + return chain +} diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertions_test.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertions_test.go new file mode 100644 index 0000000..cae11f8 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/assertions_test.go @@ -0,0 +1,2582 @@ +package assert + +import ( + "bufio" + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "math" + "os" + "reflect" + "regexp" + "runtime" + "strings" + "testing" + "time" + "unsafe" +) + +var ( + i interface{} + zeros = []interface{}{ + false, + byte(0), + complex64(0), + complex128(0), + float32(0), + float64(0), + int(0), + int8(0), + int16(0), + int32(0), + int64(0), + rune(0), + uint(0), + uint8(0), + uint16(0), + uint32(0), + uint64(0), + uintptr(0), + "", + [0]interface{}{}, + []interface{}(nil), + struct{ x int }{}, + (*interface{})(nil), + (func())(nil), + nil, + interface{}(nil), + map[interface{}]interface{}(nil), + (chan interface{})(nil), + (<-chan interface{})(nil), + (chan<- interface{})(nil), + } + nonZeros = []interface{}{ + true, + byte(1), + complex64(1), + complex128(1), + float32(1), + float64(1), + int(1), + int8(1), + int16(1), + int32(1), + int64(1), + rune(1), + uint(1), + uint8(1), + uint16(1), + uint32(1), + uint64(1), + uintptr(1), + "s", + [1]interface{}{1}, + []interface{}{}, + struct{ x int }{1}, + (&i), + (func() {}), + interface{}(1), + map[interface{}]interface{}{}, + (make(chan interface{})), + (<-chan interface{})(make(chan interface{})), + (chan<- interface{})(make(chan interface{})), + } +) + +// AssertionTesterInterface defines an interface to be used for testing assertion methods +type AssertionTesterInterface interface { + TestMethod() +} + +// AssertionTesterConformingObject is an object that conforms to the AssertionTesterInterface interface +type AssertionTesterConformingObject struct { +} + +func (a *AssertionTesterConformingObject) TestMethod() { +} + +// AssertionTesterNonConformingObject is an object that does not conform to the AssertionTesterInterface interface +type AssertionTesterNonConformingObject struct { +} + +func TestObjectsAreEqual(t *testing.T) { + cases := []struct { + expected interface{} + actual interface{} + result bool + }{ + // cases that are expected to be equal + {"Hello World", "Hello World", true}, + {123, 123, true}, + {123.5, 123.5, true}, + {[]byte("Hello World"), []byte("Hello World"), true}, + {nil, nil, true}, + + // cases that are expected not to be equal + {map[int]int{5: 10}, map[int]int{10: 20}, false}, + {'x', "x", false}, + {"x", 'x', false}, + {0, 0.1, false}, + {0.1, 0, false}, + {time.Now, time.Now, false}, + {func() {}, func() {}, false}, + {uint32(10), int32(10), false}, + } + + for _, c := range cases { + t.Run(fmt.Sprintf("ObjectsAreEqual(%#v, %#v)", c.expected, c.actual), func(t *testing.T) { + res := ObjectsAreEqual(c.expected, c.actual) + + if res != c.result { + t.Errorf("ObjectsAreEqual(%#v, %#v) should return %#v", c.expected, c.actual, c.result) + } + + }) + } + + // Cases where type differ but values are equal + if !ObjectsAreEqualValues(uint32(10), int32(10)) { + t.Error("ObjectsAreEqualValues should return true") + } + if ObjectsAreEqualValues(0, nil) { + t.Fail() + } + if ObjectsAreEqualValues(nil, 0) { + t.Fail() + } + +} + +func TestImplements(t *testing.T) { + + mockT := new(testing.T) + + if !Implements(mockT, (*AssertionTesterInterface)(nil), new(AssertionTesterConformingObject)) { + t.Error("Implements method should return true: AssertionTesterConformingObject implements AssertionTesterInterface") + } + if Implements(mockT, (*AssertionTesterInterface)(nil), new(AssertionTesterNonConformingObject)) { + t.Error("Implements method should return false: AssertionTesterNonConformingObject does not implements AssertionTesterInterface") + } + if Implements(mockT, (*AssertionTesterInterface)(nil), nil) { + t.Error("Implements method should return false: nil does not implement AssertionTesterInterface") + } + +} + +func TestIsType(t *testing.T) { + + mockT := new(testing.T) + + if !IsType(mockT, new(AssertionTesterConformingObject), new(AssertionTesterConformingObject)) { + t.Error("IsType should return true: AssertionTesterConformingObject is the same type as AssertionTesterConformingObject") + } + if IsType(mockT, new(AssertionTesterConformingObject), new(AssertionTesterNonConformingObject)) { + t.Error("IsType should return false: AssertionTesterConformingObject is not the same type as AssertionTesterNonConformingObject") + } + +} + +func TestEqual(t *testing.T) { + type myType string + + mockT := new(testing.T) + var m map[string]interface{} + + cases := []struct { + expected interface{} + actual interface{} + result bool + remark string + }{ + {"Hello World", "Hello World", true, ""}, + {123, 123, true, ""}, + {123.5, 123.5, true, ""}, + {[]byte("Hello World"), []byte("Hello World"), true, ""}, + {nil, nil, true, ""}, + {int32(123), int32(123), true, ""}, + {uint64(123), uint64(123), true, ""}, + {myType("1"), myType("1"), true, ""}, + {&struct{}{}, &struct{}{}, true, "pointer equality is based on equality of underlying value"}, + + // Not expected to be equal + {m["bar"], "something", false, ""}, + {myType("1"), myType("2"), false, ""}, + + // A case that might be confusing, especially with numeric literals + {10, uint(10), false, ""}, + } + + for _, c := range cases { + t.Run(fmt.Sprintf("Equal(%#v, %#v)", c.expected, c.actual), func(t *testing.T) { + res := Equal(mockT, c.expected, c.actual) + + if res != c.result { + t.Errorf("Equal(%#v, %#v) should return %#v: %s", c.expected, c.actual, c.result, c.remark) + } + }) + } +} + +func ptr(i int) *int { + return &i +} + +func TestSame(t *testing.T) { + + mockT := new(testing.T) + + if Same(mockT, ptr(1), ptr(1)) { + t.Error("Same should return false") + } + if Same(mockT, 1, 1) { + t.Error("Same should return false") + } + p := ptr(2) + if Same(mockT, p, *p) { + t.Error("Same should return false") + } + if !Same(mockT, p, p) { + t.Error("Same should return true") + } +} + +func TestNotSame(t *testing.T) { + + mockT := new(testing.T) + + if !NotSame(mockT, ptr(1), ptr(1)) { + t.Error("NotSame should return true; different pointers") + } + if !NotSame(mockT, 1, 1) { + t.Error("NotSame should return true; constant inputs") + } + p := ptr(2) + if !NotSame(mockT, p, *p) { + t.Error("NotSame should return true; mixed-type inputs") + } + if NotSame(mockT, p, p) { + t.Error("NotSame should return false") + } +} + +func Test_samePointers(t *testing.T) { + p := ptr(2) + + type args struct { + first interface{} + second interface{} + } + tests := []struct { + name string + args args + assertion BoolAssertionFunc + }{ + { + name: "1 != 2", + args: args{first: 1, second: 2}, + assertion: False, + }, + { + name: "1 != 1 (not same ptr)", + args: args{first: 1, second: 1}, + assertion: False, + }, + { + name: "ptr(1) == ptr(1)", + args: args{first: p, second: p}, + assertion: True, + }, + { + name: "int(1) != float32(1)", + args: args{first: int(1), second: float32(1)}, + assertion: False, + }, + { + name: "array != slice", + args: args{first: [2]int{1, 2}, second: []int{1, 2}}, + assertion: False, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.assertion(t, samePointers(tt.args.first, tt.args.second)) + }) + } +} + +// bufferT implements TestingT. Its implementation of Errorf writes the output that would be produced by +// testing.T.Errorf to an internal bytes.Buffer. +type bufferT struct { + buf bytes.Buffer +} + +func (t *bufferT) Errorf(format string, args ...interface{}) { + // implementation of decorate is copied from testing.T + decorate := func(s string) string { + _, file, line, ok := runtime.Caller(3) // decorate + log + public function. + if ok { + // Truncate file name at last file name separator. + if index := strings.LastIndex(file, "/"); index >= 0 { + file = file[index+1:] + } else if index = strings.LastIndex(file, "\\"); index >= 0 { + file = file[index+1:] + } + } else { + file = "???" + line = 1 + } + buf := new(bytes.Buffer) + // Every line is indented at least one tab. + buf.WriteByte('\t') + fmt.Fprintf(buf, "%s:%d: ", file, line) + lines := strings.Split(s, "\n") + if l := len(lines); l > 1 && lines[l-1] == "" { + lines = lines[:l-1] + } + for i, line := range lines { + if i > 0 { + // Second and subsequent lines are indented an extra tab. + buf.WriteString("\n\t\t") + } + buf.WriteString(line) + } + buf.WriteByte('\n') + return buf.String() + } + t.buf.WriteString(decorate(fmt.Sprintf(format, args...))) +} + +func TestStringEqual(t *testing.T) { + for i, currCase := range []struct { + equalWant string + equalGot string + msgAndArgs []interface{} + want string + }{ + {equalWant: "hi, \nmy name is", equalGot: "what,\nmy name is", want: "\tassertions.go:\\d+: \n\t+Error Trace:\t\n\t+Error:\\s+Not equal:\\s+\n\\s+expected: \"hi, \\\\nmy name is\"\n\\s+actual\\s+: \"what,\\\\nmy name is\"\n\\s+Diff:\n\\s+-+ Expected\n\\s+\\++ Actual\n\\s+@@ -1,2 \\+1,2 @@\n\\s+-hi, \n\\s+\\+what,\n\\s+my name is"}, + } { + mockT := &bufferT{} + Equal(mockT, currCase.equalWant, currCase.equalGot, currCase.msgAndArgs...) + Regexp(t, regexp.MustCompile(currCase.want), mockT.buf.String(), "Case %d", i) + } +} + +func TestEqualFormatting(t *testing.T) { + for i, currCase := range []struct { + equalWant string + equalGot string + msgAndArgs []interface{} + want string + }{ + {equalWant: "want", equalGot: "got", want: "\tassertions.go:\\d+: \n\t+Error Trace:\t\n\t+Error:\\s+Not equal:\\s+\n\\s+expected: \"want\"\n\\s+actual\\s+: \"got\"\n\\s+Diff:\n\\s+-+ Expected\n\\s+\\++ Actual\n\\s+@@ -1 \\+1 @@\n\\s+-want\n\\s+\\+got\n"}, + {equalWant: "want", equalGot: "got", msgAndArgs: []interface{}{"hello, %v!", "world"}, want: "\tassertions.go:[0-9]+: \n\t+Error Trace:\t\n\t+Error:\\s+Not equal:\\s+\n\\s+expected: \"want\"\n\\s+actual\\s+: \"got\"\n\\s+Diff:\n\\s+-+ Expected\n\\s+\\++ Actual\n\\s+@@ -1 \\+1 @@\n\\s+-want\n\\s+\\+got\n\\s+Messages:\\s+hello, world!\n"}, + {equalWant: "want", equalGot: "got", msgAndArgs: []interface{}{123}, want: "\tassertions.go:[0-9]+: \n\t+Error Trace:\t\n\t+Error:\\s+Not equal:\\s+\n\\s+expected: \"want\"\n\\s+actual\\s+: \"got\"\n\\s+Diff:\n\\s+-+ Expected\n\\s+\\++ Actual\n\\s+@@ -1 \\+1 @@\n\\s+-want\n\\s+\\+got\n\\s+Messages:\\s+123\n"}, + {equalWant: "want", equalGot: "got", msgAndArgs: []interface{}{struct{ a string }{"hello"}}, want: "\tassertions.go:[0-9]+: \n\t+Error Trace:\t\n\t+Error:\\s+Not equal:\\s+\n\\s+expected: \"want\"\n\\s+actual\\s+: \"got\"\n\\s+Diff:\n\\s+-+ Expected\n\\s+\\++ Actual\n\\s+@@ -1 \\+1 @@\n\\s+-want\n\\s+\\+got\n\\s+Messages:\\s+{a:hello}\n"}, + } { + mockT := &bufferT{} + Equal(mockT, currCase.equalWant, currCase.equalGot, currCase.msgAndArgs...) + Regexp(t, regexp.MustCompile(currCase.want), mockT.buf.String(), "Case %d", i) + } +} + +func TestFormatUnequalValues(t *testing.T) { + expected, actual := formatUnequalValues("foo", "bar") + Equal(t, `"foo"`, expected, "value should not include type") + Equal(t, `"bar"`, actual, "value should not include type") + + expected, actual = formatUnequalValues(123, 123) + Equal(t, `123`, expected, "value should not include type") + Equal(t, `123`, actual, "value should not include type") + + expected, actual = formatUnequalValues(int64(123), int32(123)) + Equal(t, `int64(123)`, expected, "value should include type") + Equal(t, `int32(123)`, actual, "value should include type") + + expected, actual = formatUnequalValues(int64(123), nil) + Equal(t, `int64(123)`, expected, "value should include type") + Equal(t, `<nil>(<nil>)`, actual, "value should include type") + + type testStructType struct { + Val string + } + + expected, actual = formatUnequalValues(&testStructType{Val: "test"}, &testStructType{Val: "test"}) + Equal(t, `&assert.testStructType{Val:"test"}`, expected, "value should not include type annotation") + Equal(t, `&assert.testStructType{Val:"test"}`, actual, "value should not include type annotation") +} + +func TestNotNil(t *testing.T) { + + mockT := new(testing.T) + + if !NotNil(mockT, new(AssertionTesterConformingObject)) { + t.Error("NotNil should return true: object is not nil") + } + if NotNil(mockT, nil) { + t.Error("NotNil should return false: object is nil") + } + if NotNil(mockT, (*struct{})(nil)) { + t.Error("NotNil should return false: object is (*struct{})(nil)") + } + +} + +func TestNil(t *testing.T) { + + mockT := new(testing.T) + + if !Nil(mockT, nil) { + t.Error("Nil should return true: object is nil") + } + if !Nil(mockT, (*struct{})(nil)) { + t.Error("Nil should return true: object is (*struct{})(nil)") + } + if Nil(mockT, new(AssertionTesterConformingObject)) { + t.Error("Nil should return false: object is not nil") + } + +} + +func TestTrue(t *testing.T) { + + mockT := new(testing.T) + + if !True(mockT, true) { + t.Error("True should return true") + } + if True(mockT, false) { + t.Error("True should return false") + } + +} + +func TestFalse(t *testing.T) { + + mockT := new(testing.T) + + if !False(mockT, false) { + t.Error("False should return true") + } + if False(mockT, true) { + t.Error("False should return false") + } + +} + +func TestExactly(t *testing.T) { + + mockT := new(testing.T) + + a := float32(1) + b := float64(1) + c := float32(1) + d := float32(2) + cases := []struct { + expected interface{} + actual interface{} + result bool + }{ + {a, b, false}, + {a, d, false}, + {a, c, true}, + {nil, a, false}, + {a, nil, false}, + } + + for _, c := range cases { + t.Run(fmt.Sprintf("Exactly(%#v, %#v)", c.expected, c.actual), func(t *testing.T) { + res := Exactly(mockT, c.expected, c.actual) + + if res != c.result { + t.Errorf("Exactly(%#v, %#v) should return %#v", c.expected, c.actual, c.result) + } + }) + } +} + +func TestNotEqual(t *testing.T) { + + mockT := new(testing.T) + + cases := []struct { + expected interface{} + actual interface{} + result bool + }{ + // cases that are expected not to match + {"Hello World", "Hello World!", true}, + {123, 1234, true}, + {123.5, 123.55, true}, + {[]byte("Hello World"), []byte("Hello World!"), true}, + {nil, new(AssertionTesterConformingObject), true}, + + // cases that are expected to match + {nil, nil, false}, + {"Hello World", "Hello World", false}, + {123, 123, false}, + {123.5, 123.5, false}, + {[]byte("Hello World"), []byte("Hello World"), false}, + {new(AssertionTesterConformingObject), new(AssertionTesterConformingObject), false}, + {&struct{}{}, &struct{}{}, false}, + {func() int { return 23 }, func() int { return 24 }, false}, + // A case that might be confusing, especially with numeric literals + {int(10), uint(10), true}, + } + + for _, c := range cases { + t.Run(fmt.Sprintf("NotEqual(%#v, %#v)", c.expected, c.actual), func(t *testing.T) { + res := NotEqual(mockT, c.expected, c.actual) + + if res != c.result { + t.Errorf("NotEqual(%#v, %#v) should return %#v", c.expected, c.actual, c.result) + } + }) + } +} + +func TestNotEqualValues(t *testing.T) { + mockT := new(testing.T) + + cases := []struct { + expected interface{} + actual interface{} + result bool + }{ + // cases that are expected not to match + {"Hello World", "Hello World!", true}, + {123, 1234, true}, + {123.5, 123.55, true}, + {[]byte("Hello World"), []byte("Hello World!"), true}, + {nil, new(AssertionTesterConformingObject), true}, + + // cases that are expected to match + {nil, nil, false}, + {"Hello World", "Hello World", false}, + {123, 123, false}, + {123.5, 123.5, false}, + {[]byte("Hello World"), []byte("Hello World"), false}, + {new(AssertionTesterConformingObject), new(AssertionTesterConformingObject), false}, + {&struct{}{}, &struct{}{}, false}, + + // Different behaviour from NotEqual() + {func() int { return 23 }, func() int { return 24 }, true}, + {int(10), int(11), true}, + {int(10), uint(10), false}, + + {struct{}{}, struct{}{}, false}, + } + + for _, c := range cases { + t.Run(fmt.Sprintf("NotEqualValues(%#v, %#v)", c.expected, c.actual), func(t *testing.T) { + res := NotEqualValues(mockT, c.expected, c.actual) + + if res != c.result { + t.Errorf("NotEqualValues(%#v, %#v) should return %#v", c.expected, c.actual, c.result) + } + }) + } +} + +func TestContainsNotContains(t *testing.T) { + + type A struct { + Name, Value string + } + list := []string{"Foo", "Bar"} + + complexList := []*A{ + {"b", "c"}, + {"d", "e"}, + {"g", "h"}, + {"j", "k"}, + } + simpleMap := map[interface{}]interface{}{"Foo": "Bar"} + var zeroMap map[interface{}]interface{} + + cases := []struct { + expected interface{} + actual interface{} + result bool + }{ + {"Hello World", "Hello", true}, + {"Hello World", "Salut", false}, + {list, "Bar", true}, + {list, "Salut", false}, + {complexList, &A{"g", "h"}, true}, + {complexList, &A{"g", "e"}, false}, + {simpleMap, "Foo", true}, + {simpleMap, "Bar", false}, + {zeroMap, "Bar", false}, + } + + for _, c := range cases { + t.Run(fmt.Sprintf("Contains(%#v, %#v)", c.expected, c.actual), func(t *testing.T) { + mockT := new(testing.T) + res := Contains(mockT, c.expected, c.actual) + + if res != c.result { + if res { + t.Errorf("Contains(%#v, %#v) should return true:\n\t%#v contains %#v", c.expected, c.actual, c.expected, c.actual) + } else { + t.Errorf("Contains(%#v, %#v) should return false:\n\t%#v does not contain %#v", c.expected, c.actual, c.expected, c.actual) + } + } + }) + } + + for _, c := range cases { + t.Run(fmt.Sprintf("NotContains(%#v, %#v)", c.expected, c.actual), func(t *testing.T) { + mockT := new(testing.T) + res := NotContains(mockT, c.expected, c.actual) + + // NotContains should be inverse of Contains. If it's not, something is wrong + if res == Contains(mockT, c.expected, c.actual) { + if res { + t.Errorf("NotContains(%#v, %#v) should return true:\n\t%#v does not contains %#v", c.expected, c.actual, c.expected, c.actual) + } else { + t.Errorf("NotContains(%#v, %#v) should return false:\n\t%#v contains %#v", c.expected, c.actual, c.expected, c.actual) + } + } + }) + } +} + +func TestContainsFailMessage(t *testing.T) { + + mockT := new(mockTestingT) + + Contains(mockT, "Hello World", errors.New("Hello")) + expectedFail := "\"Hello World\" does not contain &errors.errorString{s:\"Hello\"}" + actualFail := mockT.errorString() + if !strings.Contains(actualFail, expectedFail) { + t.Errorf("Contains failure should include %q but was %q", expectedFail, actualFail) + } +} + +func TestContainsNotContainsOnNilValue(t *testing.T) { + mockT := new(mockTestingT) + + Contains(mockT, nil, "key") + expectedFail := "<nil> could not be applied builtin len()" + actualFail := mockT.errorString() + if !strings.Contains(actualFail, expectedFail) { + t.Errorf("Contains failure should include %q but was %q", expectedFail, actualFail) + } + + NotContains(mockT, nil, "key") + if !strings.Contains(actualFail, expectedFail) { + t.Errorf("Contains failure should include %q but was %q", expectedFail, actualFail) + } +} + +func TestSubsetNotSubset(t *testing.T) { + cases := []struct { + list interface{} + subset interface{} + result bool + message string + }{ + // cases that are expected to contain + {[]int{1, 2, 3}, nil, true, `nil is the empty set which is a subset of every set`}, + {[]int{1, 2, 3}, []int{}, true, `[] is a subset of ['\x01' '\x02' '\x03']`}, + {[]int{1, 2, 3}, []int{1, 2}, true, `['\x01' '\x02'] is a subset of ['\x01' '\x02' '\x03']`}, + {[]int{1, 2, 3}, []int{1, 2, 3}, true, `['\x01' '\x02' '\x03'] is a subset of ['\x01' '\x02' '\x03']`}, + {[]string{"hello", "world"}, []string{"hello"}, true, `["hello"] is a subset of ["hello" "world"]`}, + {map[string]string{ + "a": "x", + "c": "z", + "b": "y", + }, map[string]string{ + "a": "x", + "b": "y", + }, true, `map["a":"x" "b":"y"] is a subset of map["a":"x" "b":"y" "c":"z"]`}, + + // cases that are expected not to contain + {[]string{"hello", "world"}, []string{"hello", "testify"}, false, `[]string{"hello", "world"} does not contain "testify"`}, + {[]int{1, 2, 3}, []int{4, 5}, false, `[]int{1, 2, 3} does not contain 4`}, + {[]int{1, 2, 3}, []int{1, 5}, false, `[]int{1, 2, 3} does not contain 5`}, + {map[string]string{ + "a": "x", + "c": "z", + "b": "y", + }, map[string]string{ + "a": "x", + "b": "z", + }, false, `map[string]string{"a":"x", "b":"y", "c":"z"} does not contain map[string]string{"a":"x", "b":"z"}`}, + {map[string]string{ + "a": "x", + "b": "y", + }, map[string]string{ + "a": "x", + "b": "y", + "c": "z", + }, false, `map[string]string{"a":"x", "b":"y"} does not contain map[string]string{"a":"x", "b":"y", "c":"z"}`}, + } + + for _, c := range cases { + t.Run("SubSet: "+c.message, func(t *testing.T) { + + mockT := new(mockTestingT) + res := Subset(mockT, c.list, c.subset) + + if res != c.result { + t.Errorf("Subset should return %t: %s", c.result, c.message) + } + if !c.result { + expectedFail := c.message + actualFail := mockT.errorString() + if !strings.Contains(actualFail, expectedFail) { + t.Log(actualFail) + t.Errorf("Subset failure should contain %q but was %q", expectedFail, actualFail) + } + } + }) + } + for _, c := range cases { + t.Run("NotSubSet: "+c.message, func(t *testing.T) { + mockT := new(mockTestingT) + res := NotSubset(mockT, c.list, c.subset) + + // NotSubset should match the inverse of Subset. If it doesn't, something is wrong + if res == Subset(mockT, c.list, c.subset) { + t.Errorf("NotSubset should return %t: %s", !c.result, c.message) + } + if c.result { + expectedFail := c.message + actualFail := mockT.errorString() + if !strings.Contains(actualFail, expectedFail) { + t.Log(actualFail) + t.Errorf("NotSubset failure should contain %q but was %q", expectedFail, actualFail) + } + } + }) + } +} + +func TestNotSubsetNil(t *testing.T) { + mockT := new(testing.T) + NotSubset(mockT, []string{"foo"}, nil) + if !mockT.Failed() { + t.Error("NotSubset on nil set should have failed the test") + } +} + +func Test_containsElement(t *testing.T) { + + list1 := []string{"Foo", "Bar"} + list2 := []int{1, 2} + simpleMap := map[interface{}]interface{}{"Foo": "Bar"} + + ok, found := containsElement("Hello World", "World") + True(t, ok) + True(t, found) + + ok, found = containsElement(list1, "Foo") + True(t, ok) + True(t, found) + + ok, found = containsElement(list1, "Bar") + True(t, ok) + True(t, found) + + ok, found = containsElement(list2, 1) + True(t, ok) + True(t, found) + + ok, found = containsElement(list2, 2) + True(t, ok) + True(t, found) + + ok, found = containsElement(list1, "Foo!") + True(t, ok) + False(t, found) + + ok, found = containsElement(list2, 3) + True(t, ok) + False(t, found) + + ok, found = containsElement(list2, "1") + True(t, ok) + False(t, found) + + ok, found = containsElement(simpleMap, "Foo") + True(t, ok) + True(t, found) + + ok, found = containsElement(simpleMap, "Bar") + True(t, ok) + False(t, found) + + ok, found = containsElement(1433, "1") + False(t, ok) + False(t, found) +} + +func TestElementsMatch(t *testing.T) { + mockT := new(testing.T) + + cases := []struct { + expected interface{} + actual interface{} + result bool + }{ + // matching + {nil, nil, true}, + + {nil, nil, true}, + {[]int{}, []int{}, true}, + {[]int{1}, []int{1}, true}, + {[]int{1, 1}, []int{1, 1}, true}, + {[]int{1, 2}, []int{1, 2}, true}, + {[]int{1, 2}, []int{2, 1}, true}, + {[2]int{1, 2}, [2]int{2, 1}, true}, + {[]string{"hello", "world"}, []string{"world", "hello"}, true}, + {[]string{"hello", "hello"}, []string{"hello", "hello"}, true}, + {[]string{"hello", "hello", "world"}, []string{"hello", "world", "hello"}, true}, + {[3]string{"hello", "hello", "world"}, [3]string{"hello", "world", "hello"}, true}, + {[]int{}, nil, true}, + + // not matching + {[]int{1}, []int{1, 1}, false}, + {[]int{1, 2}, []int{2, 2}, false}, + {[]string{"hello", "hello"}, []string{"hello"}, false}, + } + + for _, c := range cases { + t.Run(fmt.Sprintf("ElementsMatch(%#v, %#v)", c.expected, c.actual), func(t *testing.T) { + res := ElementsMatch(mockT, c.actual, c.expected) + + if res != c.result { + t.Errorf("ElementsMatch(%#v, %#v) should return %v", c.actual, c.expected, c.result) + } + }) + } +} + +func TestDiffLists(t *testing.T) { + tests := []struct { + name string + listA interface{} + listB interface{} + extraA []interface{} + extraB []interface{} + }{ + { + name: "equal empty", + listA: []string{}, + listB: []string{}, + extraA: nil, + extraB: nil, + }, + { + name: "equal same order", + listA: []string{"hello", "world"}, + listB: []string{"hello", "world"}, + extraA: nil, + extraB: nil, + }, + { + name: "equal different order", + listA: []string{"hello", "world"}, + listB: []string{"world", "hello"}, + extraA: nil, + extraB: nil, + }, + { + name: "extra A", + listA: []string{"hello", "hello", "world"}, + listB: []string{"hello", "world"}, + extraA: []interface{}{"hello"}, + extraB: nil, + }, + { + name: "extra A twice", + listA: []string{"hello", "hello", "hello", "world"}, + listB: []string{"hello", "world"}, + extraA: []interface{}{"hello", "hello"}, + extraB: nil, + }, + { + name: "extra B", + listA: []string{"hello", "world"}, + listB: []string{"hello", "hello", "world"}, + extraA: nil, + extraB: []interface{}{"hello"}, + }, + { + name: "extra B twice", + listA: []string{"hello", "world"}, + listB: []string{"hello", "hello", "world", "hello"}, + extraA: nil, + extraB: []interface{}{"hello", "hello"}, + }, + { + name: "integers 1", + listA: []int{1, 2, 3, 4, 5}, + listB: []int{5, 4, 3, 2, 1}, + extraA: nil, + extraB: nil, + }, + { + name: "integers 2", + listA: []int{1, 2, 1, 2, 1}, + listB: []int{2, 1, 2, 1, 2}, + extraA: []interface{}{1}, + extraB: []interface{}{2}, + }, + } + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + actualExtraA, actualExtraB := diffLists(test.listA, test.listB) + Equal(t, test.extraA, actualExtraA, "extra A does not match for listA=%v listB=%v", + test.listA, test.listB) + Equal(t, test.extraB, actualExtraB, "extra B does not match for listA=%v listB=%v", + test.listA, test.listB) + }) + } +} + +func TestCondition(t *testing.T) { + mockT := new(testing.T) + + if !Condition(mockT, func() bool { return true }, "Truth") { + t.Error("Condition should return true") + } + + if Condition(mockT, func() bool { return false }, "Lie") { + t.Error("Condition should return false") + } + +} + +func TestDidPanic(t *testing.T) { + + const panicMsg = "Panic!" + + if funcDidPanic, msg, _ := didPanic(func() { + panic(panicMsg) + }); !funcDidPanic || msg != panicMsg { + t.Error("didPanic should return true, panicMsg") + } + + if funcDidPanic, msg, _ := didPanic(func() { + panic(nil) + }); !funcDidPanic || msg != nil { + t.Error("didPanic should return true, nil") + } + + if funcDidPanic, _, _ := didPanic(func() { + }); funcDidPanic { + t.Error("didPanic should return false") + } + +} + +func TestPanics(t *testing.T) { + + mockT := new(testing.T) + + if !Panics(mockT, func() { + panic("Panic!") + }) { + t.Error("Panics should return true") + } + + if Panics(mockT, func() { + }) { + t.Error("Panics should return false") + } + +} + +func TestPanicsWithValue(t *testing.T) { + + mockT := new(testing.T) + + if !PanicsWithValue(mockT, "Panic!", func() { + panic("Panic!") + }) { + t.Error("PanicsWithValue should return true") + } + + if !PanicsWithValue(mockT, nil, func() { + panic(nil) + }) { + t.Error("PanicsWithValue should return true") + } + + if PanicsWithValue(mockT, "Panic!", func() { + }) { + t.Error("PanicsWithValue should return false") + } + + if PanicsWithValue(mockT, "at the disco", func() { + panic("Panic!") + }) { + t.Error("PanicsWithValue should return false") + } +} + +func TestPanicsWithError(t *testing.T) { + + mockT := new(testing.T) + + if !PanicsWithError(mockT, "panic", func() { + panic(errors.New("panic")) + }) { + t.Error("PanicsWithError should return true") + } + + if PanicsWithError(mockT, "Panic!", func() { + }) { + t.Error("PanicsWithError should return false") + } + + if PanicsWithError(mockT, "at the disco", func() { + panic(errors.New("panic")) + }) { + t.Error("PanicsWithError should return false") + } + + if PanicsWithError(mockT, "Panic!", func() { + panic("panic") + }) { + t.Error("PanicsWithError should return false") + } +} + +func TestNotPanics(t *testing.T) { + + mockT := new(testing.T) + + if !NotPanics(mockT, func() { + }) { + t.Error("NotPanics should return true") + } + + if NotPanics(mockT, func() { + panic("Panic!") + }) { + t.Error("NotPanics should return false") + } + +} + +func TestNoError(t *testing.T) { + + mockT := new(testing.T) + + // start with a nil error + var err error + + True(t, NoError(mockT, err), "NoError should return True for nil arg") + + // now set an error + err = errors.New("some error") + + False(t, NoError(mockT, err), "NoError with error should return False") + + // returning an empty error interface + err = func() error { + var err *customError + return err + }() + + if err == nil { // err is not nil here! + t.Errorf("Error should be nil due to empty interface: %s", err) + } + + False(t, NoError(mockT, err), "NoError should fail with empty error interface") +} + +type customError struct{} + +func (*customError) Error() string { return "fail" } + +func TestError(t *testing.T) { + + mockT := new(testing.T) + + // start with a nil error + var err error + + False(t, Error(mockT, err), "Error should return False for nil arg") + + // now set an error + err = errors.New("some error") + + True(t, Error(mockT, err), "Error with error should return True") + + // go vet check + True(t, Errorf(mockT, err, "example with %s", "formatted message"), "Errorf with error should rturn True") + + // returning an empty error interface + err = func() error { + var err *customError + return err + }() + + if err == nil { // err is not nil here! + t.Errorf("Error should be nil due to empty interface: %s", err) + } + + True(t, Error(mockT, err), "Error should pass with empty error interface") +} + +func TestEqualError(t *testing.T) { + mockT := new(testing.T) + + // start with a nil error + var err error + False(t, EqualError(mockT, err, ""), + "EqualError should return false for nil arg") + + // now set an error + err = errors.New("some error") + False(t, EqualError(mockT, err, "Not some error"), + "EqualError should return false for different error string") + True(t, EqualError(mockT, err, "some error"), + "EqualError should return true") +} + +func TestErrorContains(t *testing.T) { + mockT := new(testing.T) + + // start with a nil error + var err error + False(t, ErrorContains(mockT, err, ""), + "ErrorContains should return false for nil arg") + + // now set an error + err = errors.New("some error: another error") + False(t, ErrorContains(mockT, err, "bad error"), + "ErrorContains should return false for different error string") + True(t, ErrorContains(mockT, err, "some error"), + "ErrorContains should return true") + True(t, ErrorContains(mockT, err, "another error"), + "ErrorContains should return true") +} + +func Test_isEmpty(t *testing.T) { + + chWithValue := make(chan struct{}, 1) + chWithValue <- struct{}{} + + True(t, isEmpty("")) + True(t, isEmpty(nil)) + True(t, isEmpty([]string{})) + True(t, isEmpty(0)) + True(t, isEmpty(int32(0))) + True(t, isEmpty(int64(0))) + True(t, isEmpty(false)) + True(t, isEmpty(map[string]string{})) + True(t, isEmpty(new(time.Time))) + True(t, isEmpty(time.Time{})) + True(t, isEmpty(make(chan struct{}))) + True(t, isEmpty([1]int{})) + False(t, isEmpty("something")) + False(t, isEmpty(errors.New("something"))) + False(t, isEmpty([]string{"something"})) + False(t, isEmpty(1)) + False(t, isEmpty(true)) + False(t, isEmpty(map[string]string{"Hello": "World"})) + False(t, isEmpty(chWithValue)) + False(t, isEmpty([1]int{42})) +} + +func TestEmpty(t *testing.T) { + + mockT := new(testing.T) + chWithValue := make(chan struct{}, 1) + chWithValue <- struct{}{} + var tiP *time.Time + var tiNP time.Time + var s *string + var f *os.File + sP := &s + x := 1 + xP := &x + + type TString string + type TStruct struct { + x int + } + + True(t, Empty(mockT, ""), "Empty string is empty") + True(t, Empty(mockT, nil), "Nil is empty") + True(t, Empty(mockT, []string{}), "Empty string array is empty") + True(t, Empty(mockT, 0), "Zero int value is empty") + True(t, Empty(mockT, false), "False value is empty") + True(t, Empty(mockT, make(chan struct{})), "Channel without values is empty") + True(t, Empty(mockT, s), "Nil string pointer is empty") + True(t, Empty(mockT, f), "Nil os.File pointer is empty") + True(t, Empty(mockT, tiP), "Nil time.Time pointer is empty") + True(t, Empty(mockT, tiNP), "time.Time is empty") + True(t, Empty(mockT, TStruct{}), "struct with zero values is empty") + True(t, Empty(mockT, TString("")), "empty aliased string is empty") + True(t, Empty(mockT, sP), "ptr to nil value is empty") + True(t, Empty(mockT, [1]int{}), "array is state") + + False(t, Empty(mockT, "something"), "Non Empty string is not empty") + False(t, Empty(mockT, errors.New("something")), "Non nil object is not empty") + False(t, Empty(mockT, []string{"something"}), "Non empty string array is not empty") + False(t, Empty(mockT, 1), "Non-zero int value is not empty") + False(t, Empty(mockT, true), "True value is not empty") + False(t, Empty(mockT, chWithValue), "Channel with values is not empty") + False(t, Empty(mockT, TStruct{x: 1}), "struct with initialized values is empty") + False(t, Empty(mockT, TString("abc")), "non-empty aliased string is empty") + False(t, Empty(mockT, xP), "ptr to non-nil value is not empty") + False(t, Empty(mockT, [1]int{42}), "array is not state") +} + +func TestNotEmpty(t *testing.T) { + + mockT := new(testing.T) + chWithValue := make(chan struct{}, 1) + chWithValue <- struct{}{} + + False(t, NotEmpty(mockT, ""), "Empty string is empty") + False(t, NotEmpty(mockT, nil), "Nil is empty") + False(t, NotEmpty(mockT, []string{}), "Empty string array is empty") + False(t, NotEmpty(mockT, 0), "Zero int value is empty") + False(t, NotEmpty(mockT, false), "False value is empty") + False(t, NotEmpty(mockT, make(chan struct{})), "Channel without values is empty") + False(t, NotEmpty(mockT, [1]int{}), "array is state") + + True(t, NotEmpty(mockT, "something"), "Non Empty string is not empty") + True(t, NotEmpty(mockT, errors.New("something")), "Non nil object is not empty") + True(t, NotEmpty(mockT, []string{"something"}), "Non empty string array is not empty") + True(t, NotEmpty(mockT, 1), "Non-zero int value is not empty") + True(t, NotEmpty(mockT, true), "True value is not empty") + True(t, NotEmpty(mockT, chWithValue), "Channel with values is not empty") + True(t, NotEmpty(mockT, [1]int{42}), "array is not state") +} + +func Test_getLen(t *testing.T) { + falseCases := []interface{}{ + nil, + 0, + true, + false, + 'A', + struct{}{}, + } + for _, v := range falseCases { + ok, l := getLen(v) + False(t, ok, "Expected getLen fail to get length of %#v", v) + Equal(t, 0, l, "getLen should return 0 for %#v", v) + } + + ch := make(chan int, 5) + ch <- 1 + ch <- 2 + ch <- 3 + trueCases := []struct { + v interface{} + l int + }{ + {[]int{1, 2, 3}, 3}, + {[...]int{1, 2, 3}, 3}, + {"ABC", 3}, + {map[int]int{1: 2, 2: 4, 3: 6}, 3}, + {ch, 3}, + + {[]int{}, 0}, + {map[int]int{}, 0}, + {make(chan int), 0}, + + {[]int(nil), 0}, + {map[int]int(nil), 0}, + {(chan int)(nil), 0}, + } + + for _, c := range trueCases { + ok, l := getLen(c.v) + True(t, ok, "Expected getLen success to get length of %#v", c.v) + Equal(t, c.l, l) + } +} + +func TestLen(t *testing.T) { + mockT := new(testing.T) + + False(t, Len(mockT, nil, 0), "nil does not have length") + False(t, Len(mockT, 0, 0), "int does not have length") + False(t, Len(mockT, true, 0), "true does not have length") + False(t, Len(mockT, false, 0), "false does not have length") + False(t, Len(mockT, 'A', 0), "Rune does not have length") + False(t, Len(mockT, struct{}{}, 0), "Struct does not have length") + + ch := make(chan int, 5) + ch <- 1 + ch <- 2 + ch <- 3 + + cases := []struct { + v interface{} + l int + }{ + {[]int{1, 2, 3}, 3}, + {[...]int{1, 2, 3}, 3}, + {"ABC", 3}, + {map[int]int{1: 2, 2: 4, 3: 6}, 3}, + {ch, 3}, + + {[]int{}, 0}, + {map[int]int{}, 0}, + {make(chan int), 0}, + + {[]int(nil), 0}, + {map[int]int(nil), 0}, + {(chan int)(nil), 0}, + } + + for _, c := range cases { + True(t, Len(mockT, c.v, c.l), "%#v have %d items", c.v, c.l) + } + + cases = []struct { + v interface{} + l int + }{ + {[]int{1, 2, 3}, 4}, + {[...]int{1, 2, 3}, 2}, + {"ABC", 2}, + {map[int]int{1: 2, 2: 4, 3: 6}, 4}, + {ch, 2}, + + {[]int{}, 1}, + {map[int]int{}, 1}, + {make(chan int), 1}, + + {[]int(nil), 1}, + {map[int]int(nil), 1}, + {(chan int)(nil), 1}, + } + + for _, c := range cases { + False(t, Len(mockT, c.v, c.l), "%#v have %d items", c.v, c.l) + } +} + +func TestWithinDuration(t *testing.T) { + + mockT := new(testing.T) + a := time.Now() + b := a.Add(10 * time.Second) + + True(t, WithinDuration(mockT, a, b, 10*time.Second), "A 10s difference is within a 10s time difference") + True(t, WithinDuration(mockT, b, a, 10*time.Second), "A 10s difference is within a 10s time difference") + + False(t, WithinDuration(mockT, a, b, 9*time.Second), "A 10s difference is not within a 9s time difference") + False(t, WithinDuration(mockT, b, a, 9*time.Second), "A 10s difference is not within a 9s time difference") + + False(t, WithinDuration(mockT, a, b, -9*time.Second), "A 10s difference is not within a 9s time difference") + False(t, WithinDuration(mockT, b, a, -9*time.Second), "A 10s difference is not within a 9s time difference") + + False(t, WithinDuration(mockT, a, b, -11*time.Second), "A 10s difference is not within a 9s time difference") + False(t, WithinDuration(mockT, b, a, -11*time.Second), "A 10s difference is not within a 9s time difference") +} + +func TestWithinRange(t *testing.T) { + + mockT := new(testing.T) + n := time.Now() + s := n.Add(-time.Second) + e := n.Add(time.Second) + + True(t, WithinRange(mockT, n, n, n), "Exact same actual, start, and end values return true") + + True(t, WithinRange(mockT, n, s, e), "Time in range is within the time range") + True(t, WithinRange(mockT, s, s, e), "The start time is within the time range") + True(t, WithinRange(mockT, e, s, e), "The end time is within the time range") + + False(t, WithinRange(mockT, s.Add(-time.Nanosecond), s, e, "Just before the start time is not within the time range")) + False(t, WithinRange(mockT, e.Add(time.Nanosecond), s, e, "Just after the end time is not within the time range")) + + False(t, WithinRange(mockT, n, e, s, "Just after the end time is not within the time range")) +} + +func TestInDelta(t *testing.T) { + mockT := new(testing.T) + + True(t, InDelta(mockT, 1.001, 1, 0.01), "|1.001 - 1| <= 0.01") + True(t, InDelta(mockT, 1, 1.001, 0.01), "|1 - 1.001| <= 0.01") + True(t, InDelta(mockT, 1, 2, 1), "|1 - 2| <= 1") + False(t, InDelta(mockT, 1, 2, 0.5), "Expected |1 - 2| <= 0.5 to fail") + False(t, InDelta(mockT, 2, 1, 0.5), "Expected |2 - 1| <= 0.5 to fail") + False(t, InDelta(mockT, "", nil, 1), "Expected non numerals to fail") + False(t, InDelta(mockT, 42, math.NaN(), 0.01), "Expected NaN for actual to fail") + False(t, InDelta(mockT, math.NaN(), 42, 0.01), "Expected NaN for expected to fail") + True(t, InDelta(mockT, math.NaN(), math.NaN(), 0.01), "Expected NaN for both to pass") + + cases := []struct { + a, b interface{} + delta float64 + }{ + {uint(2), uint(1), 1}, + {uint8(2), uint8(1), 1}, + {uint16(2), uint16(1), 1}, + {uint32(2), uint32(1), 1}, + {uint64(2), uint64(1), 1}, + + {int(2), int(1), 1}, + {int8(2), int8(1), 1}, + {int16(2), int16(1), 1}, + {int32(2), int32(1), 1}, + {int64(2), int64(1), 1}, + + {float32(2), float32(1), 1}, + {float64(2), float64(1), 1}, + } + + for _, tc := range cases { + True(t, InDelta(mockT, tc.a, tc.b, tc.delta), "Expected |%V - %V| <= %v", tc.a, tc.b, tc.delta) + } +} + +func TestInDeltaSlice(t *testing.T) { + mockT := new(testing.T) + + True(t, InDeltaSlice(mockT, + []float64{1.001, math.NaN(), 0.999}, + []float64{1, math.NaN(), 1}, + 0.1), "{1.001, NaN, 0.009} is element-wise close to {1, NaN, 1} in delta=0.1") + + True(t, InDeltaSlice(mockT, + []float64{1, math.NaN(), 2}, + []float64{0, math.NaN(), 3}, + 1), "{1, NaN, 2} is element-wise close to {0, NaN, 3} in delta=1") + + False(t, InDeltaSlice(mockT, + []float64{1, math.NaN(), 2}, + []float64{0, math.NaN(), 3}, + 0.1), "{1, NaN, 2} is not element-wise close to {0, NaN, 3} in delta=0.1") + + False(t, InDeltaSlice(mockT, "", nil, 1), "Expected non numeral slices to fail") +} + +func TestInDeltaMapValues(t *testing.T) { + mockT := new(testing.T) + + for _, tc := range []struct { + title string + expect interface{} + actual interface{} + f func(TestingT, bool, ...interface{}) bool + delta float64 + }{ + { + title: "Within delta", + expect: map[string]float64{ + "foo": 1.0, + "bar": 2.0, + "baz": math.NaN(), + }, + actual: map[string]float64{ + "foo": 1.01, + "bar": 1.99, + "baz": math.NaN(), + }, + delta: 0.1, + f: True, + }, + { + title: "Within delta", + expect: map[int]float64{ + 1: 1.0, + 2: 2.0, + }, + actual: map[int]float64{ + 1: 1.0, + 2: 1.99, + }, + delta: 0.1, + f: True, + }, + { + title: "Different number of keys", + expect: map[int]float64{ + 1: 1.0, + 2: 2.0, + }, + actual: map[int]float64{ + 1: 1.0, + }, + delta: 0.1, + f: False, + }, + { + title: "Within delta with zero value", + expect: map[string]float64{ + "zero": 0, + }, + actual: map[string]float64{ + "zero": 0, + }, + delta: 0.1, + f: True, + }, + { + title: "With missing key with zero value", + expect: map[string]float64{ + "zero": 0, + "foo": 0, + }, + actual: map[string]float64{ + "zero": 0, + "bar": 0, + }, + f: False, + }, + } { + tc.f(t, InDeltaMapValues(mockT, tc.expect, tc.actual, tc.delta), tc.title+"\n"+diff(tc.expect, tc.actual)) + } +} + +func TestInEpsilon(t *testing.T) { + mockT := new(testing.T) + + cases := []struct { + a, b interface{} + epsilon float64 + }{ + {uint8(2), uint16(2), .001}, + {2.1, 2.2, 0.1}, + {2.2, 2.1, 0.1}, + {-2.1, -2.2, 0.1}, + {-2.2, -2.1, 0.1}, + {uint64(100), uint8(101), 0.01}, + {0.1, -0.1, 2}, + {0.1, 0, 2}, + {math.NaN(), math.NaN(), 1}, + {time.Second, time.Second + time.Millisecond, 0.002}, + } + + for _, tc := range cases { + True(t, InEpsilon(t, tc.a, tc.b, tc.epsilon, "Expected %V and %V to have a relative difference of %v", tc.a, tc.b, tc.epsilon), "test: %q", tc) + } + + cases = []struct { + a, b interface{} + epsilon float64 + }{ + {uint8(2), int16(-2), .001}, + {uint64(100), uint8(102), 0.01}, + {2.1, 2.2, 0.001}, + {2.2, 2.1, 0.001}, + {2.1, -2.2, 1}, + {2.1, "bla-bla", 0}, + {0.1, -0.1, 1.99}, + {0, 0.1, 2}, // expected must be different to zero + {time.Second, time.Second + 10*time.Millisecond, 0.002}, + {math.NaN(), 0, 1}, + {0, math.NaN(), 1}, + {0, 0, math.NaN()}, + } + + for _, tc := range cases { + False(t, InEpsilon(mockT, tc.a, tc.b, tc.epsilon, "Expected %V and %V to have a relative difference of %v", tc.a, tc.b, tc.epsilon)) + } + +} + +func TestInEpsilonSlice(t *testing.T) { + mockT := new(testing.T) + + True(t, InEpsilonSlice(mockT, + []float64{2.2, math.NaN(), 2.0}, + []float64{2.1, math.NaN(), 2.1}, + 0.06), "{2.2, NaN, 2.0} is element-wise close to {2.1, NaN, 2.1} in espilon=0.06") + + False(t, InEpsilonSlice(mockT, + []float64{2.2, 2.0}, + []float64{2.1, 2.1}, + 0.04), "{2.2, 2.0} is not element-wise close to {2.1, 2.1} in espilon=0.04") + + False(t, InEpsilonSlice(mockT, "", nil, 1), "Expected non numeral slices to fail") +} + +func TestRegexp(t *testing.T) { + mockT := new(testing.T) + + cases := []struct { + rx, str string + }{ + {"^start", "start of the line"}, + {"end$", "in the end"}, + {"[0-9]{3}[.-]?[0-9]{2}[.-]?[0-9]{2}", "My phone number is 650.12.34"}, + } + + for _, tc := range cases { + True(t, Regexp(mockT, tc.rx, tc.str)) + True(t, Regexp(mockT, regexp.MustCompile(tc.rx), tc.str)) + False(t, NotRegexp(mockT, tc.rx, tc.str)) + False(t, NotRegexp(mockT, regexp.MustCompile(tc.rx), tc.str)) + } + + cases = []struct { + rx, str string + }{ + {"^asdfastart", "Not the start of the line"}, + {"end$", "in the end."}, + {"[0-9]{3}[.-]?[0-9]{2}[.-]?[0-9]{2}", "My phone number is 650.12a.34"}, + } + + for _, tc := range cases { + False(t, Regexp(mockT, tc.rx, tc.str), "Expected \"%s\" to not match \"%s\"", tc.rx, tc.str) + False(t, Regexp(mockT, regexp.MustCompile(tc.rx), tc.str)) + True(t, NotRegexp(mockT, tc.rx, tc.str)) + True(t, NotRegexp(mockT, regexp.MustCompile(tc.rx), tc.str)) + } +} + +func testAutogeneratedFunction() { + defer func() { + if err := recover(); err == nil { + panic("did not panic") + } + CallerInfo() + }() + t := struct { + io.Closer + }{} + c := t + c.Close() +} + +func TestCallerInfoWithAutogeneratedFunctions(t *testing.T) { + NotPanics(t, func() { + testAutogeneratedFunction() + }) +} + +func TestZero(t *testing.T) { + mockT := new(testing.T) + + for _, test := range zeros { + True(t, Zero(mockT, test, "%#v is not the %v zero value", test, reflect.TypeOf(test))) + } + + for _, test := range nonZeros { + False(t, Zero(mockT, test, "%#v is not the %v zero value", test, reflect.TypeOf(test))) + } +} + +func TestNotZero(t *testing.T) { + mockT := new(testing.T) + + for _, test := range zeros { + False(t, NotZero(mockT, test, "%#v is not the %v zero value", test, reflect.TypeOf(test))) + } + + for _, test := range nonZeros { + True(t, NotZero(mockT, test, "%#v is not the %v zero value", test, reflect.TypeOf(test))) + } +} + +func TestFileExists(t *testing.T) { + mockT := new(testing.T) + True(t, FileExists(mockT, "assertions.go")) + + mockT = new(testing.T) + False(t, FileExists(mockT, "random_file")) + + mockT = new(testing.T) + False(t, FileExists(mockT, "../_codegen")) + + var tempFiles []string + + link, err := getTempSymlinkPath("assertions.go") + if err != nil { + t.Fatal("could not create temp symlink, err:", err) + } + tempFiles = append(tempFiles, link) + mockT = new(testing.T) + True(t, FileExists(mockT, link)) + + link, err = getTempSymlinkPath("non_existent_file") + if err != nil { + t.Fatal("could not create temp symlink, err:", err) + } + tempFiles = append(tempFiles, link) + mockT = new(testing.T) + True(t, FileExists(mockT, link)) + + errs := cleanUpTempFiles(tempFiles) + if len(errs) > 0 { + t.Fatal("could not clean up temporary files") + } +} + +func TestNoFileExists(t *testing.T) { + mockT := new(testing.T) + False(t, NoFileExists(mockT, "assertions.go")) + + mockT = new(testing.T) + True(t, NoFileExists(mockT, "non_existent_file")) + + mockT = new(testing.T) + True(t, NoFileExists(mockT, "../_codegen")) + + var tempFiles []string + + link, err := getTempSymlinkPath("assertions.go") + if err != nil { + t.Fatal("could not create temp symlink, err:", err) + } + tempFiles = append(tempFiles, link) + mockT = new(testing.T) + False(t, NoFileExists(mockT, link)) + + link, err = getTempSymlinkPath("non_existent_file") + if err != nil { + t.Fatal("could not create temp symlink, err:", err) + } + tempFiles = append(tempFiles, link) + mockT = new(testing.T) + False(t, NoFileExists(mockT, link)) + + errs := cleanUpTempFiles(tempFiles) + if len(errs) > 0 { + t.Fatal("could not clean up temporary files") + } +} + +func getTempSymlinkPath(file string) (string, error) { + link := file + "_symlink" + err := os.Symlink(file, link) + return link, err +} + +func cleanUpTempFiles(paths []string) []error { + var res []error + for _, path := range paths { + err := os.Remove(path) + if err != nil { + res = append(res, err) + } + } + return res +} + +func TestDirExists(t *testing.T) { + mockT := new(testing.T) + False(t, DirExists(mockT, "assertions.go")) + + mockT = new(testing.T) + False(t, DirExists(mockT, "non_existent_dir")) + + mockT = new(testing.T) + True(t, DirExists(mockT, "../_codegen")) + + var tempFiles []string + + link, err := getTempSymlinkPath("assertions.go") + if err != nil { + t.Fatal("could not create temp symlink, err:", err) + } + tempFiles = append(tempFiles, link) + mockT = new(testing.T) + False(t, DirExists(mockT, link)) + + link, err = getTempSymlinkPath("non_existent_dir") + if err != nil { + t.Fatal("could not create temp symlink, err:", err) + } + tempFiles = append(tempFiles, link) + mockT = new(testing.T) + False(t, DirExists(mockT, link)) + + errs := cleanUpTempFiles(tempFiles) + if len(errs) > 0 { + t.Fatal("could not clean up temporary files") + } +} + +func TestNoDirExists(t *testing.T) { + mockT := new(testing.T) + True(t, NoDirExists(mockT, "assertions.go")) + + mockT = new(testing.T) + True(t, NoDirExists(mockT, "non_existent_dir")) + + mockT = new(testing.T) + False(t, NoDirExists(mockT, "../_codegen")) + + var tempFiles []string + + link, err := getTempSymlinkPath("assertions.go") + if err != nil { + t.Fatal("could not create temp symlink, err:", err) + } + tempFiles = append(tempFiles, link) + mockT = new(testing.T) + True(t, NoDirExists(mockT, link)) + + link, err = getTempSymlinkPath("non_existent_dir") + if err != nil { + t.Fatal("could not create temp symlink, err:", err) + } + tempFiles = append(tempFiles, link) + mockT = new(testing.T) + True(t, NoDirExists(mockT, link)) + + errs := cleanUpTempFiles(tempFiles) + if len(errs) > 0 { + t.Fatal("could not clean up temporary files") + } +} + +func TestJSONEq_EqualSONString(t *testing.T) { + mockT := new(testing.T) + True(t, JSONEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`)) +} + +func TestJSONEq_EquivalentButNotEqual(t *testing.T) { + mockT := new(testing.T) + True(t, JSONEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)) +} + +func TestJSONEq_HashOfArraysAndHashes(t *testing.T) { + mockT := new(testing.T) + True(t, JSONEq(mockT, "{\r\n\t\"numeric\": 1.5,\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]],\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\"\r\n}", + "{\r\n\t\"numeric\": 1.5,\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\",\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]]\r\n}")) +} + +func TestJSONEq_Array(t *testing.T) { + mockT := new(testing.T) + True(t, JSONEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`)) +} + +func TestJSONEq_HashAndArrayNotEquivalent(t *testing.T) { + mockT := new(testing.T) + False(t, JSONEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`)) +} + +func TestJSONEq_HashesNotEquivalent(t *testing.T) { + mockT := new(testing.T) + False(t, JSONEq(mockT, `{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)) +} + +func TestJSONEq_ActualIsNotJSON(t *testing.T) { + mockT := new(testing.T) + False(t, JSONEq(mockT, `{"foo": "bar"}`, "Not JSON")) +} + +func TestJSONEq_ExpectedIsNotJSON(t *testing.T) { + mockT := new(testing.T) + False(t, JSONEq(mockT, "Not JSON", `{"foo": "bar", "hello": "world"}`)) +} + +func TestJSONEq_ExpectedAndActualNotJSON(t *testing.T) { + mockT := new(testing.T) + False(t, JSONEq(mockT, "Not JSON", "Not JSON")) +} + +func TestJSONEq_ArraysOfDifferentOrder(t *testing.T) { + mockT := new(testing.T) + False(t, JSONEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`)) +} + +func TestYAMLEq_EqualYAMLString(t *testing.T) { + mockT := new(testing.T) + True(t, YAMLEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`)) +} + +func TestYAMLEq_EquivalentButNotEqual(t *testing.T) { + mockT := new(testing.T) + True(t, YAMLEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)) +} + +func TestYAMLEq_HashOfArraysAndHashes(t *testing.T) { + mockT := new(testing.T) + expected := ` +numeric: 1.5 +array: + - foo: bar + - 1 + - "string" + - ["nested", "array", 5.5] +hash: + nested: hash + nested_slice: [this, is, nested] +string: "foo" +` + + actual := ` +numeric: 1.5 +hash: + nested: hash + nested_slice: [this, is, nested] +string: "foo" +array: + - foo: bar + - 1 + - "string" + - ["nested", "array", 5.5] +` + True(t, YAMLEq(mockT, expected, actual)) +} + +func TestYAMLEq_Array(t *testing.T) { + mockT := new(testing.T) + True(t, YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`)) +} + +func TestYAMLEq_HashAndArrayNotEquivalent(t *testing.T) { + mockT := new(testing.T) + False(t, YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`)) +} + +func TestYAMLEq_HashesNotEquivalent(t *testing.T) { + mockT := new(testing.T) + False(t, YAMLEq(mockT, `{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)) +} + +func TestYAMLEq_ActualIsSimpleString(t *testing.T) { + mockT := new(testing.T) + False(t, YAMLEq(mockT, `{"foo": "bar"}`, "Simple String")) +} + +func TestYAMLEq_ExpectedIsSimpleString(t *testing.T) { + mockT := new(testing.T) + False(t, YAMLEq(mockT, "Simple String", `{"foo": "bar", "hello": "world"}`)) +} + +func TestYAMLEq_ExpectedAndActualSimpleString(t *testing.T) { + mockT := new(testing.T) + True(t, YAMLEq(mockT, "Simple String", "Simple String")) +} + +func TestYAMLEq_ArraysOfDifferentOrder(t *testing.T) { + mockT := new(testing.T) + False(t, YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`)) +} + +type diffTestingStruct struct { + A string + B int +} + +func (d *diffTestingStruct) String() string { + return d.A +} + +func TestDiff(t *testing.T) { + expected := ` + +Diff: +--- Expected ++++ Actual +@@ -1,3 +1,3 @@ + (struct { foo string }) { +- foo: (string) (len=5) "hello" ++ foo: (string) (len=3) "bar" + } +` + actual := diff( + struct{ foo string }{"hello"}, + struct{ foo string }{"bar"}, + ) + Equal(t, expected, actual) + + expected = ` + +Diff: +--- Expected ++++ Actual +@@ -2,5 +2,5 @@ + (int) 1, +- (int) 2, + (int) 3, +- (int) 4 ++ (int) 5, ++ (int) 7 + } +` + actual = diff( + []int{1, 2, 3, 4}, + []int{1, 3, 5, 7}, + ) + Equal(t, expected, actual) + + expected = ` + +Diff: +--- Expected ++++ Actual +@@ -2,4 +2,4 @@ + (int) 1, +- (int) 2, +- (int) 3 ++ (int) 3, ++ (int) 5 + } +` + actual = diff( + []int{1, 2, 3, 4}[0:3], + []int{1, 3, 5, 7}[0:3], + ) + Equal(t, expected, actual) + + expected = ` + +Diff: +--- Expected ++++ Actual +@@ -1,6 +1,6 @@ + (map[string]int) (len=4) { +- (string) (len=4) "four": (int) 4, ++ (string) (len=4) "five": (int) 5, + (string) (len=3) "one": (int) 1, +- (string) (len=5) "three": (int) 3, +- (string) (len=3) "two": (int) 2 ++ (string) (len=5) "seven": (int) 7, ++ (string) (len=5) "three": (int) 3 + } +` + + actual = diff( + map[string]int{"one": 1, "two": 2, "three": 3, "four": 4}, + map[string]int{"one": 1, "three": 3, "five": 5, "seven": 7}, + ) + Equal(t, expected, actual) + + expected = ` + +Diff: +--- Expected ++++ Actual +@@ -1,3 +1,3 @@ + (*errors.errorString)({ +- s: (string) (len=19) "some expected error" ++ s: (string) (len=12) "actual error" + }) +` + + actual = diff( + errors.New("some expected error"), + errors.New("actual error"), + ) + Equal(t, expected, actual) + + expected = ` + +Diff: +--- Expected ++++ Actual +@@ -2,3 +2,3 @@ + A: (string) (len=11) "some string", +- B: (int) 10 ++ B: (int) 15 + } +` + + actual = diff( + diffTestingStruct{A: "some string", B: 10}, + diffTestingStruct{A: "some string", B: 15}, + ) + Equal(t, expected, actual) + + expected = ` + +Diff: +--- Expected ++++ Actual +@@ -1,2 +1,2 @@ +-(time.Time) 2020-09-24 00:00:00 +0000 UTC ++(time.Time) 2020-09-25 00:00:00 +0000 UTC + +` + + actual = diff( + time.Date(2020, 9, 24, 0, 0, 0, 0, time.UTC), + time.Date(2020, 9, 25, 0, 0, 0, 0, time.UTC), + ) + Equal(t, expected, actual) +} + +func TestTimeEqualityErrorFormatting(t *testing.T) { + mockT := new(mockTestingT) + + Equal(mockT, time.Second*2, time.Millisecond) + + expectedErr := "\\s+Error Trace:\\s+Error:\\s+Not equal:\\s+\n\\s+expected: 2s\n\\s+actual\\s+: 1ms\n" + Regexp(t, regexp.MustCompile(expectedErr), mockT.errorString()) +} + +func TestDiffEmptyCases(t *testing.T) { + Equal(t, "", diff(nil, nil)) + Equal(t, "", diff(struct{ foo string }{}, nil)) + Equal(t, "", diff(nil, struct{ foo string }{})) + Equal(t, "", diff(1, 2)) + Equal(t, "", diff(1, 2)) + Equal(t, "", diff([]int{1}, []bool{true})) +} + +// Ensure there are no data races +func TestDiffRace(t *testing.T) { + t.Parallel() + + expected := map[string]string{ + "a": "A", + "b": "B", + "c": "C", + } + + actual := map[string]string{ + "d": "D", + "e": "E", + "f": "F", + } + + // run diffs in parallel simulating tests with t.Parallel() + numRoutines := 10 + rChans := make([]chan string, numRoutines) + for idx := range rChans { + rChans[idx] = make(chan string) + go func(ch chan string) { + defer close(ch) + ch <- diff(expected, actual) + }(rChans[idx]) + } + + for _, ch := range rChans { + for msg := range ch { + NotZero(t, msg) // dummy assert + } + } +} + +type mockTestingT struct { + errorFmt string + args []interface{} +} + +func (m *mockTestingT) errorString() string { + return fmt.Sprintf(m.errorFmt, m.args...) +} + +func (m *mockTestingT) Errorf(format string, args ...interface{}) { + m.errorFmt = format + m.args = args +} + +func TestFailNowWithPlainTestingT(t *testing.T) { + mockT := &mockTestingT{} + + Panics(t, func() { + FailNow(mockT, "failed") + }, "should panic since mockT is missing FailNow()") +} + +type mockFailNowTestingT struct { +} + +func (m *mockFailNowTestingT) Errorf(format string, args ...interface{}) {} + +func (m *mockFailNowTestingT) FailNow() {} + +func TestFailNowWithFullTestingT(t *testing.T) { + mockT := &mockFailNowTestingT{} + + NotPanics(t, func() { + FailNow(mockT, "failed") + }, "should call mockT.FailNow() rather than panicking") +} + +func TestBytesEqual(t *testing.T) { + var cases = []struct { + a, b []byte + }{ + {make([]byte, 2), make([]byte, 2)}, + {make([]byte, 2), make([]byte, 2, 3)}, + {nil, make([]byte, 0)}, + } + for i, c := range cases { + Equal(t, reflect.DeepEqual(c.a, c.b), ObjectsAreEqual(c.a, c.b), "case %d failed", i+1) + } +} + +func BenchmarkBytesEqual(b *testing.B) { + const size = 1024 * 8 + s := make([]byte, size) + for i := range s { + s[i] = byte(i % 255) + } + s2 := make([]byte, size) + copy(s2, s) + + mockT := &mockFailNowTestingT{} + b.ResetTimer() + for i := 0; i < b.N; i++ { + Equal(mockT, s, s2) + } +} + +func BenchmarkNotNil(b *testing.B) { + for i := 0; i < b.N; i++ { + NotNil(b, b) + } +} + +func ExampleComparisonAssertionFunc() { + t := &testing.T{} // provided by test + + adder := func(x, y int) int { + return x + y + } + + type args struct { + x int + y int + } + + tests := []struct { + name string + args args + expect int + assertion ComparisonAssertionFunc + }{ + {"2+2=4", args{2, 2}, 4, Equal}, + {"2+2!=5", args{2, 2}, 5, NotEqual}, + {"2+3==5", args{2, 3}, 5, Exactly}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.assertion(t, tt.expect, adder(tt.args.x, tt.args.y)) + }) + } +} + +func TestComparisonAssertionFunc(t *testing.T) { + type iface interface { + Name() string + } + + tests := []struct { + name string + expect interface{} + got interface{} + assertion ComparisonAssertionFunc + }{ + {"implements", (*iface)(nil), t, Implements}, + {"isType", (*testing.T)(nil), t, IsType}, + {"equal", t, t, Equal}, + {"equalValues", t, t, EqualValues}, + {"notEqualValues", t, nil, NotEqualValues}, + {"exactly", t, t, Exactly}, + {"notEqual", t, nil, NotEqual}, + {"notContains", []int{1, 2, 3}, 4, NotContains}, + {"subset", []int{1, 2, 3, 4}, []int{2, 3}, Subset}, + {"notSubset", []int{1, 2, 3, 4}, []int{0, 3}, NotSubset}, + {"elementsMatch", []byte("abc"), []byte("bac"), ElementsMatch}, + {"regexp", "^t.*y$", "testify", Regexp}, + {"notRegexp", "^t.*y$", "Testify", NotRegexp}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.assertion(t, tt.expect, tt.got) + }) + } +} + +func ExampleValueAssertionFunc() { + t := &testing.T{} // provided by test + + dumbParse := func(input string) interface{} { + var x interface{} + _ = json.Unmarshal([]byte(input), &x) + return x + } + + tests := []struct { + name string + arg string + assertion ValueAssertionFunc + }{ + {"true is not nil", "true", NotNil}, + {"empty string is nil", "", Nil}, + {"zero is not nil", "0", NotNil}, + {"zero is zero", "0", Zero}, + {"false is zero", "false", Zero}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.assertion(t, dumbParse(tt.arg)) + }) + } +} + +func TestValueAssertionFunc(t *testing.T) { + tests := []struct { + name string + value interface{} + assertion ValueAssertionFunc + }{ + {"notNil", true, NotNil}, + {"nil", nil, Nil}, + {"empty", []int{}, Empty}, + {"notEmpty", []int{1}, NotEmpty}, + {"zero", false, Zero}, + {"notZero", 42, NotZero}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.assertion(t, tt.value) + }) + } +} + +func ExampleBoolAssertionFunc() { + t := &testing.T{} // provided by test + + isOkay := func(x int) bool { + return x >= 42 + } + + tests := []struct { + name string + arg int + assertion BoolAssertionFunc + }{ + {"-1 is bad", -1, False}, + {"42 is good", 42, True}, + {"41 is bad", 41, False}, + {"45 is cool", 45, True}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.assertion(t, isOkay(tt.arg)) + }) + } +} + +func TestBoolAssertionFunc(t *testing.T) { + tests := []struct { + name string + value bool + assertion BoolAssertionFunc + }{ + {"true", true, True}, + {"false", false, False}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.assertion(t, tt.value) + }) + } +} + +func ExampleErrorAssertionFunc() { + t := &testing.T{} // provided by test + + dumbParseNum := func(input string, v interface{}) error { + return json.Unmarshal([]byte(input), v) + } + + tests := []struct { + name string + arg string + assertion ErrorAssertionFunc + }{ + {"1.2 is number", "1.2", NoError}, + {"1.2.3 not number", "1.2.3", Error}, + {"true is not number", "true", Error}, + {"3 is number", "3", NoError}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var x float64 + tt.assertion(t, dumbParseNum(tt.arg, &x)) + }) + } +} + +func TestErrorAssertionFunc(t *testing.T) { + tests := []struct { + name string + err error + assertion ErrorAssertionFunc + }{ + {"noError", nil, NoError}, + {"error", errors.New("whoops"), Error}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.assertion(t, tt.err) + }) + } +} + +func TestEventuallyFalse(t *testing.T) { + mockT := new(testing.T) + + condition := func() bool { + return false + } + + False(t, Eventually(mockT, condition, 100*time.Millisecond, 20*time.Millisecond)) +} + +func TestEventuallyTrue(t *testing.T) { + state := 0 + condition := func() bool { + defer func() { + state += 1 + }() + return state == 2 + } + + True(t, Eventually(t, condition, 100*time.Millisecond, 20*time.Millisecond)) +} + +func TestNeverFalse(t *testing.T) { + condition := func() bool { + return false + } + + True(t, Never(t, condition, 100*time.Millisecond, 20*time.Millisecond)) +} + +func TestNeverTrue(t *testing.T) { + mockT := new(testing.T) + state := 0 + condition := func() bool { + defer func() { + state = state + 1 + }() + return state == 2 + } + + False(t, Never(mockT, condition, 100*time.Millisecond, 20*time.Millisecond)) +} + +func TestEventuallyIssue805(t *testing.T) { + mockT := new(testing.T) + + NotPanics(t, func() { + condition := func() bool { <-time.After(time.Millisecond); return true } + False(t, Eventually(mockT, condition, time.Millisecond, time.Microsecond)) + }) +} + +func Test_validateEqualArgs(t *testing.T) { + if validateEqualArgs(func() {}, func() {}) == nil { + t.Error("non-nil functions should error") + } + + if validateEqualArgs(func() {}, func() {}) == nil { + t.Error("non-nil functions should error") + } + + if validateEqualArgs(nil, nil) != nil { + t.Error("nil functions are equal") + } +} + +func Test_truncatingFormat(t *testing.T) { + + original := strings.Repeat("a", bufio.MaxScanTokenSize-102) + result := truncatingFormat(original) + Equal(t, fmt.Sprintf("%#v", original), result, "string should not be truncated") + + original = original + "x" + result = truncatingFormat(original) + NotEqual(t, fmt.Sprintf("%#v", original), result, "string should have been truncated.") + + if !strings.HasSuffix(result, "<... truncated>") { + t.Error("truncated string should have <... truncated> suffix") + } +} + +func TestErrorIs(t *testing.T) { + mockT := new(testing.T) + tests := []struct { + err error + target error + result bool + }{ + {io.EOF, io.EOF, true}, + {fmt.Errorf("wrap: %w", io.EOF), io.EOF, true}, + {io.EOF, io.ErrClosedPipe, false}, + {nil, io.EOF, false}, + {io.EOF, nil, false}, + {nil, nil, true}, + } + for _, tt := range tests { + tt := tt + t.Run(fmt.Sprintf("ErrorIs(%#v,%#v)", tt.err, tt.target), func(t *testing.T) { + res := ErrorIs(mockT, tt.err, tt.target) + if res != tt.result { + t.Errorf("ErrorIs(%#v,%#v) should return %t", tt.err, tt.target, tt.result) + } + }) + } +} + +func TestNotErrorIs(t *testing.T) { + mockT := new(testing.T) + tests := []struct { + err error + target error + result bool + }{ + {io.EOF, io.EOF, false}, + {fmt.Errorf("wrap: %w", io.EOF), io.EOF, false}, + {io.EOF, io.ErrClosedPipe, true}, + {nil, io.EOF, true}, + {io.EOF, nil, true}, + {nil, nil, false}, + } + for _, tt := range tests { + tt := tt + t.Run(fmt.Sprintf("NotErrorIs(%#v,%#v)", tt.err, tt.target), func(t *testing.T) { + res := NotErrorIs(mockT, tt.err, tt.target) + if res != tt.result { + t.Errorf("NotErrorIs(%#v,%#v) should return %t", tt.err, tt.target, tt.result) + } + }) + } +} + +func TestErrorAs(t *testing.T) { + mockT := new(testing.T) + tests := []struct { + err error + result bool + }{ + {fmt.Errorf("wrap: %w", &customError{}), true}, + {io.EOF, false}, + {nil, false}, + } + for _, tt := range tests { + tt := tt + var target *customError + t.Run(fmt.Sprintf("ErrorAs(%#v,%#v)", tt.err, target), func(t *testing.T) { + res := ErrorAs(mockT, tt.err, &target) + if res != tt.result { + t.Errorf("ErrorAs(%#v,%#v) should return %t)", tt.err, target, tt.result) + } + }) + } +} + +func TestIsNil(t *testing.T) { + var n unsafe.Pointer = nil + if !isNil(n) { + t.Fatal("fail") + } +} diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/doc.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/doc.go new file mode 100644 index 0000000..c9dccc4 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/doc.go @@ -0,0 +1,45 @@ +// Package assert provides a set of comprehensive testing tools for use with the normal Go testing system. +// +// Example Usage +// +// The following is a complete example using assert in a standard test function: +// import ( +// "testing" +// "github.com/stretchr/testify/assert" +// ) +// +// func TestSomething(t *testing.T) { +// +// var a string = "Hello" +// var b string = "Hello" +// +// assert.Equal(t, a, b, "The two words should be the same.") +// +// } +// +// if you assert many times, use the format below: +// +// import ( +// "testing" +// "github.com/stretchr/testify/assert" +// ) +// +// func TestSomething(t *testing.T) { +// assert := assert.New(t) +// +// var a string = "Hello" +// var b string = "Hello" +// +// assert.Equal(a, b, "The two words should be the same.") +// } +// +// Assertions +// +// Assertions allow you to easily write test code, and are global funcs in the `assert` package. +// All assertion functions take, as the first argument, the `*testing.T` object provided by the +// testing framework. This allows the assertion funcs to write the failings and other details to +// the correct place. +// +// Every assertion function also takes an optional string message as the final argument, +// allowing custom error messages to be appended to the message the assertion method outputs. +package assert diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/errors.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/errors.go new file mode 100644 index 0000000..ac9dc9d --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/errors.go @@ -0,0 +1,10 @@ +package assert + +import ( + "errors" +) + +// AnError is an error instance useful for testing. If the code does not care +// about error specifics, and only needs to return the error for example, this +// error should be used to make the test code more readable. +var AnError = errors.New("assert.AnError general error for testing") diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/forward_assertions.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/forward_assertions.go new file mode 100644 index 0000000..df189d2 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/forward_assertions.go @@ -0,0 +1,16 @@ +package assert + +// Assertions provides assertion methods around the +// TestingT interface. +type Assertions struct { + t TestingT +} + +// New makes a new Assertions object for the specified TestingT. +func New(t TestingT) *Assertions { + return &Assertions{ + t: t, + } +} + +//go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_forward.go.tmpl -include-format-funcs" diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/forward_assertions_test.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/forward_assertions_test.go new file mode 100644 index 0000000..496d3c0 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/forward_assertions_test.go @@ -0,0 +1,752 @@ +package assert + +import ( + "errors" + "regexp" + "testing" + "time" +) + +func TestImplementsWrapper(t *testing.T) { + assert := New(new(testing.T)) + + if !assert.Implements((*AssertionTesterInterface)(nil), new(AssertionTesterConformingObject)) { + t.Error("Implements method should return true: AssertionTesterConformingObject implements AssertionTesterInterface") + } + if assert.Implements((*AssertionTesterInterface)(nil), new(AssertionTesterNonConformingObject)) { + t.Error("Implements method should return false: AssertionTesterNonConformingObject does not implements AssertionTesterInterface") + } +} + +func TestIsTypeWrapper(t *testing.T) { + assert := New(new(testing.T)) + + if !assert.IsType(new(AssertionTesterConformingObject), new(AssertionTesterConformingObject)) { + t.Error("IsType should return true: AssertionTesterConformingObject is the same type as AssertionTesterConformingObject") + } + if assert.IsType(new(AssertionTesterConformingObject), new(AssertionTesterNonConformingObject)) { + t.Error("IsType should return false: AssertionTesterConformingObject is not the same type as AssertionTesterNonConformingObject") + } + +} + +func TestEqualWrapper(t *testing.T) { + assert := New(new(testing.T)) + + if !assert.Equal("Hello World", "Hello World") { + t.Error("Equal should return true") + } + if !assert.Equal(123, 123) { + t.Error("Equal should return true") + } + if !assert.Equal(123.5, 123.5) { + t.Error("Equal should return true") + } + if !assert.Equal([]byte("Hello World"), []byte("Hello World")) { + t.Error("Equal should return true") + } + if !assert.Equal(nil, nil) { + t.Error("Equal should return true") + } +} + +func TestEqualValuesWrapper(t *testing.T) { + assert := New(new(testing.T)) + + if !assert.EqualValues(uint32(10), int32(10)) { + t.Error("EqualValues should return true") + } +} + +func TestNotNilWrapper(t *testing.T) { + assert := New(new(testing.T)) + + if !assert.NotNil(new(AssertionTesterConformingObject)) { + t.Error("NotNil should return true: object is not nil") + } + if assert.NotNil(nil) { + t.Error("NotNil should return false: object is nil") + } + +} + +func TestNilWrapper(t *testing.T) { + assert := New(new(testing.T)) + + if !assert.Nil(nil) { + t.Error("Nil should return true: object is nil") + } + if assert.Nil(new(AssertionTesterConformingObject)) { + t.Error("Nil should return false: object is not nil") + } + +} + +func TestTrueWrapper(t *testing.T) { + assert := New(new(testing.T)) + + if !assert.True(true) { + t.Error("True should return true") + } + if assert.True(false) { + t.Error("True should return false") + } + +} + +func TestFalseWrapper(t *testing.T) { + assert := New(new(testing.T)) + + if !assert.False(false) { + t.Error("False should return true") + } + if assert.False(true) { + t.Error("False should return false") + } + +} + +func TestExactlyWrapper(t *testing.T) { + assert := New(new(testing.T)) + + a := float32(1) + b := float64(1) + c := float32(1) + d := float32(2) + + if assert.Exactly(a, b) { + t.Error("Exactly should return false") + } + if assert.Exactly(a, d) { + t.Error("Exactly should return false") + } + if !assert.Exactly(a, c) { + t.Error("Exactly should return true") + } + + if assert.Exactly(nil, a) { + t.Error("Exactly should return false") + } + if assert.Exactly(a, nil) { + t.Error("Exactly should return false") + } + +} + +func TestNotEqualWrapper(t *testing.T) { + + assert := New(new(testing.T)) + + if !assert.NotEqual("Hello World", "Hello World!") { + t.Error("NotEqual should return true") + } + if !assert.NotEqual(123, 1234) { + t.Error("NotEqual should return true") + } + if !assert.NotEqual(123.5, 123.55) { + t.Error("NotEqual should return true") + } + if !assert.NotEqual([]byte("Hello World"), []byte("Hello World!")) { + t.Error("NotEqual should return true") + } + if !assert.NotEqual(nil, new(AssertionTesterConformingObject)) { + t.Error("NotEqual should return true") + } +} + +func TestNotEqualValuesWrapper(t *testing.T) { + + assert := New(new(testing.T)) + + if !assert.NotEqualValues("Hello World", "Hello World!") { + t.Error("NotEqualValues should return true") + } + if !assert.NotEqualValues(123, 1234) { + t.Error("NotEqualValues should return true") + } + if !assert.NotEqualValues(123.5, 123.55) { + t.Error("NotEqualValues should return true") + } + if !assert.NotEqualValues([]byte("Hello World"), []byte("Hello World!")) { + t.Error("NotEqualValues should return true") + } + if !assert.NotEqualValues(nil, new(AssertionTesterConformingObject)) { + t.Error("NotEqualValues should return true") + } + if assert.NotEqualValues(10, uint(10)) { + t.Error("NotEqualValues should return false") + } +} + +func TestContainsWrapper(t *testing.T) { + + assert := New(new(testing.T)) + list := []string{"Foo", "Bar"} + + if !assert.Contains("Hello World", "Hello") { + t.Error("Contains should return true: \"Hello World\" contains \"Hello\"") + } + if assert.Contains("Hello World", "Salut") { + t.Error("Contains should return false: \"Hello World\" does not contain \"Salut\"") + } + + if !assert.Contains(list, "Foo") { + t.Error("Contains should return true: \"[\"Foo\", \"Bar\"]\" contains \"Foo\"") + } + if assert.Contains(list, "Salut") { + t.Error("Contains should return false: \"[\"Foo\", \"Bar\"]\" does not contain \"Salut\"") + } + +} + +func TestNotContainsWrapper(t *testing.T) { + + assert := New(new(testing.T)) + list := []string{"Foo", "Bar"} + + if !assert.NotContains("Hello World", "Hello!") { + t.Error("NotContains should return true: \"Hello World\" does not contain \"Hello!\"") + } + if assert.NotContains("Hello World", "Hello") { + t.Error("NotContains should return false: \"Hello World\" contains \"Hello\"") + } + + if !assert.NotContains(list, "Foo!") { + t.Error("NotContains should return true: \"[\"Foo\", \"Bar\"]\" does not contain \"Foo!\"") + } + if assert.NotContains(list, "Foo") { + t.Error("NotContains should return false: \"[\"Foo\", \"Bar\"]\" contains \"Foo\"") + } + +} + +func TestConditionWrapper(t *testing.T) { + + assert := New(new(testing.T)) + + if !assert.Condition(func() bool { return true }, "Truth") { + t.Error("Condition should return true") + } + + if assert.Condition(func() bool { return false }, "Lie") { + t.Error("Condition should return false") + } + +} + +func TestDidPanicWrapper(t *testing.T) { + + if funcDidPanic, _, _ := didPanic(func() { + panic("Panic!") + }); !funcDidPanic { + t.Error("didPanic should return true") + } + + if funcDidPanic, _, _ := didPanic(func() { + }); funcDidPanic { + t.Error("didPanic should return false") + } + +} + +func TestPanicsWrapper(t *testing.T) { + + assert := New(new(testing.T)) + + if !assert.Panics(func() { + panic("Panic!") + }) { + t.Error("Panics should return true") + } + + if assert.Panics(func() { + }) { + t.Error("Panics should return false") + } + +} + +func TestNotPanicsWrapper(t *testing.T) { + + assert := New(new(testing.T)) + + if !assert.NotPanics(func() { + }) { + t.Error("NotPanics should return true") + } + + if assert.NotPanics(func() { + panic("Panic!") + }) { + t.Error("NotPanics should return false") + } + +} + +func TestNoErrorWrapper(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + + // start with a nil error + var err error + + assert.True(mockAssert.NoError(err), "NoError should return True for nil arg") + + // now set an error + err = errors.New("Some error") + + assert.False(mockAssert.NoError(err), "NoError with error should return False") + +} + +func TestErrorWrapper(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + + // start with a nil error + var err error + + assert.False(mockAssert.Error(err), "Error should return False for nil arg") + + // now set an error + err = errors.New("Some error") + + assert.True(mockAssert.Error(err), "Error with error should return True") + +} + +func TestErrorContainsWrapper(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + + // start with a nil error + var err error + assert.False(mockAssert.ErrorContains(err, ""), + "ErrorContains should return false for nil arg") + + // now set an error + err = errors.New("some error: another error") + assert.False(mockAssert.ErrorContains(err, "different error"), + "ErrorContains should return false for different error string") + assert.True(mockAssert.ErrorContains(err, "some error"), + "ErrorContains should return true") + assert.True(mockAssert.ErrorContains(err, "another error"), + "ErrorContains should return true") +} + +func TestEqualErrorWrapper(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + + // start with a nil error + var err error + assert.False(mockAssert.EqualError(err, ""), + "EqualError should return false for nil arg") + + // now set an error + err = errors.New("some error") + assert.False(mockAssert.EqualError(err, "Not some error"), + "EqualError should return false for different error string") + assert.True(mockAssert.EqualError(err, "some error"), + "EqualError should return true") +} + +func TestEmptyWrapper(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + + assert.True(mockAssert.Empty(""), "Empty string is empty") + assert.True(mockAssert.Empty(nil), "Nil is empty") + assert.True(mockAssert.Empty([]string{}), "Empty string array is empty") + assert.True(mockAssert.Empty(0), "Zero int value is empty") + assert.True(mockAssert.Empty(false), "False value is empty") + + assert.False(mockAssert.Empty("something"), "Non Empty string is not empty") + assert.False(mockAssert.Empty(errors.New("something")), "Non nil object is not empty") + assert.False(mockAssert.Empty([]string{"something"}), "Non empty string array is not empty") + assert.False(mockAssert.Empty(1), "Non-zero int value is not empty") + assert.False(mockAssert.Empty(true), "True value is not empty") + +} + +func TestNotEmptyWrapper(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + + assert.False(mockAssert.NotEmpty(""), "Empty string is empty") + assert.False(mockAssert.NotEmpty(nil), "Nil is empty") + assert.False(mockAssert.NotEmpty([]string{}), "Empty string array is empty") + assert.False(mockAssert.NotEmpty(0), "Zero int value is empty") + assert.False(mockAssert.NotEmpty(false), "False value is empty") + + assert.True(mockAssert.NotEmpty("something"), "Non Empty string is not empty") + assert.True(mockAssert.NotEmpty(errors.New("something")), "Non nil object is not empty") + assert.True(mockAssert.NotEmpty([]string{"something"}), "Non empty string array is not empty") + assert.True(mockAssert.NotEmpty(1), "Non-zero int value is not empty") + assert.True(mockAssert.NotEmpty(true), "True value is not empty") + +} + +func TestLenWrapper(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + + assert.False(mockAssert.Len(nil, 0), "nil does not have length") + assert.False(mockAssert.Len(0, 0), "int does not have length") + assert.False(mockAssert.Len(true, 0), "true does not have length") + assert.False(mockAssert.Len(false, 0), "false does not have length") + assert.False(mockAssert.Len('A', 0), "Rune does not have length") + assert.False(mockAssert.Len(struct{}{}, 0), "Struct does not have length") + + ch := make(chan int, 5) + ch <- 1 + ch <- 2 + ch <- 3 + + cases := []struct { + v interface{} + l int + }{ + {[]int{1, 2, 3}, 3}, + {[...]int{1, 2, 3}, 3}, + {"ABC", 3}, + {map[int]int{1: 2, 2: 4, 3: 6}, 3}, + {ch, 3}, + + {[]int{}, 0}, + {map[int]int{}, 0}, + {make(chan int), 0}, + + {[]int(nil), 0}, + {map[int]int(nil), 0}, + {(chan int)(nil), 0}, + } + + for _, c := range cases { + assert.True(mockAssert.Len(c.v, c.l), "%#v have %d items", c.v, c.l) + } +} + +func TestWithinDurationWrapper(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + a := time.Now() + b := a.Add(10 * time.Second) + + assert.True(mockAssert.WithinDuration(a, b, 10*time.Second), "A 10s difference is within a 10s time difference") + assert.True(mockAssert.WithinDuration(b, a, 10*time.Second), "A 10s difference is within a 10s time difference") + + assert.False(mockAssert.WithinDuration(a, b, 9*time.Second), "A 10s difference is not within a 9s time difference") + assert.False(mockAssert.WithinDuration(b, a, 9*time.Second), "A 10s difference is not within a 9s time difference") + + assert.False(mockAssert.WithinDuration(a, b, -9*time.Second), "A 10s difference is not within a 9s time difference") + assert.False(mockAssert.WithinDuration(b, a, -9*time.Second), "A 10s difference is not within a 9s time difference") + + assert.False(mockAssert.WithinDuration(a, b, -11*time.Second), "A 10s difference is not within a 9s time difference") + assert.False(mockAssert.WithinDuration(b, a, -11*time.Second), "A 10s difference is not within a 9s time difference") +} + +func TestInDeltaWrapper(t *testing.T) { + assert := New(new(testing.T)) + + True(t, assert.InDelta(1.001, 1, 0.01), "|1.001 - 1| <= 0.01") + True(t, assert.InDelta(1, 1.001, 0.01), "|1 - 1.001| <= 0.01") + True(t, assert.InDelta(1, 2, 1), "|1 - 2| <= 1") + False(t, assert.InDelta(1, 2, 0.5), "Expected |1 - 2| <= 0.5 to fail") + False(t, assert.InDelta(2, 1, 0.5), "Expected |2 - 1| <= 0.5 to fail") + False(t, assert.InDelta("", nil, 1), "Expected non numerals to fail") + + cases := []struct { + a, b interface{} + delta float64 + }{ + {uint8(2), uint8(1), 1}, + {uint16(2), uint16(1), 1}, + {uint32(2), uint32(1), 1}, + {uint64(2), uint64(1), 1}, + + {int(2), int(1), 1}, + {int8(2), int8(1), 1}, + {int16(2), int16(1), 1}, + {int32(2), int32(1), 1}, + {int64(2), int64(1), 1}, + + {float32(2), float32(1), 1}, + {float64(2), float64(1), 1}, + } + + for _, tc := range cases { + True(t, assert.InDelta(tc.a, tc.b, tc.delta), "Expected |%V - %V| <= %v", tc.a, tc.b, tc.delta) + } +} + +func TestInEpsilonWrapper(t *testing.T) { + assert := New(new(testing.T)) + + cases := []struct { + a, b interface{} + epsilon float64 + }{ + {uint8(2), uint16(2), .001}, + {2.1, 2.2, 0.1}, + {2.2, 2.1, 0.1}, + {-2.1, -2.2, 0.1}, + {-2.2, -2.1, 0.1}, + {uint64(100), uint8(101), 0.01}, + {0.1, -0.1, 2}, + } + + for _, tc := range cases { + True(t, assert.InEpsilon(tc.a, tc.b, tc.epsilon, "Expected %V and %V to have a relative difference of %v", tc.a, tc.b, tc.epsilon)) + } + + cases = []struct { + a, b interface{} + epsilon float64 + }{ + {uint8(2), int16(-2), .001}, + {uint64(100), uint8(102), 0.01}, + {2.1, 2.2, 0.001}, + {2.2, 2.1, 0.001}, + {2.1, -2.2, 1}, + {2.1, "bla-bla", 0}, + {0.1, -0.1, 1.99}, + } + + for _, tc := range cases { + False(t, assert.InEpsilon(tc.a, tc.b, tc.epsilon, "Expected %V and %V to have a relative difference of %v", tc.a, tc.b, tc.epsilon)) + } +} + +func TestRegexpWrapper(t *testing.T) { + + assert := New(new(testing.T)) + + cases := []struct { + rx, str string + }{ + {"^start", "start of the line"}, + {"end$", "in the end"}, + {"[0-9]{3}[.-]?[0-9]{2}[.-]?[0-9]{2}", "My phone number is 650.12.34"}, + } + + for _, tc := range cases { + True(t, assert.Regexp(tc.rx, tc.str)) + True(t, assert.Regexp(regexp.MustCompile(tc.rx), tc.str)) + False(t, assert.NotRegexp(tc.rx, tc.str)) + False(t, assert.NotRegexp(regexp.MustCompile(tc.rx), tc.str)) + } + + cases = []struct { + rx, str string + }{ + {"^asdfastart", "Not the start of the line"}, + {"end$", "in the end."}, + {"[0-9]{3}[.-]?[0-9]{2}[.-]?[0-9]{2}", "My phone number is 650.12a.34"}, + } + + for _, tc := range cases { + False(t, assert.Regexp(tc.rx, tc.str), "Expected \"%s\" to not match \"%s\"", tc.rx, tc.str) + False(t, assert.Regexp(regexp.MustCompile(tc.rx), tc.str)) + True(t, assert.NotRegexp(tc.rx, tc.str)) + True(t, assert.NotRegexp(regexp.MustCompile(tc.rx), tc.str)) + } +} + +func TestZeroWrapper(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + + for _, test := range zeros { + assert.True(mockAssert.Zero(test), "Zero should return true for %v", test) + } + + for _, test := range nonZeros { + assert.False(mockAssert.Zero(test), "Zero should return false for %v", test) + } +} + +func TestNotZeroWrapper(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + + for _, test := range zeros { + assert.False(mockAssert.NotZero(test), "Zero should return true for %v", test) + } + + for _, test := range nonZeros { + assert.True(mockAssert.NotZero(test), "Zero should return false for %v", test) + } +} + +func TestJSONEqWrapper_EqualSONString(t *testing.T) { + assert := New(new(testing.T)) + if !assert.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`) { + t.Error("JSONEq should return true") + } + +} + +func TestJSONEqWrapper_EquivalentButNotEqual(t *testing.T) { + assert := New(new(testing.T)) + if !assert.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) { + t.Error("JSONEq should return true") + } + +} + +func TestJSONEqWrapper_HashOfArraysAndHashes(t *testing.T) { + assert := New(new(testing.T)) + if !assert.JSONEq("{\r\n\t\"numeric\": 1.5,\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]],\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\"\r\n}", + "{\r\n\t\"numeric\": 1.5,\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\",\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]]\r\n}") { + t.Error("JSONEq should return true") + } +} + +func TestJSONEqWrapper_Array(t *testing.T) { + assert := New(new(testing.T)) + if !assert.JSONEq(`["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`) { + t.Error("JSONEq should return true") + } + +} + +func TestJSONEqWrapper_HashAndArrayNotEquivalent(t *testing.T) { + assert := New(new(testing.T)) + if assert.JSONEq(`["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`) { + t.Error("JSONEq should return false") + } +} + +func TestJSONEqWrapper_HashesNotEquivalent(t *testing.T) { + assert := New(new(testing.T)) + if assert.JSONEq(`{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) { + t.Error("JSONEq should return false") + } +} + +func TestJSONEqWrapper_ActualIsNotJSON(t *testing.T) { + assert := New(new(testing.T)) + if assert.JSONEq(`{"foo": "bar"}`, "Not JSON") { + t.Error("JSONEq should return false") + } +} + +func TestJSONEqWrapper_ExpectedIsNotJSON(t *testing.T) { + assert := New(new(testing.T)) + if assert.JSONEq("Not JSON", `{"foo": "bar", "hello": "world"}`) { + t.Error("JSONEq should return false") + } +} + +func TestJSONEqWrapper_ExpectedAndActualNotJSON(t *testing.T) { + assert := New(new(testing.T)) + if assert.JSONEq("Not JSON", "Not JSON") { + t.Error("JSONEq should return false") + } +} + +func TestJSONEqWrapper_ArraysOfDifferentOrder(t *testing.T) { + assert := New(new(testing.T)) + if assert.JSONEq(`["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`) { + t.Error("JSONEq should return false") + } +} + +func TestYAMLEqWrapper_EqualYAMLString(t *testing.T) { + assert := New(new(testing.T)) + if !assert.YAMLEq(`{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`) { + t.Error("YAMLEq should return true") + } + +} + +func TestYAMLEqWrapper_EquivalentButNotEqual(t *testing.T) { + assert := New(new(testing.T)) + if !assert.YAMLEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) { + t.Error("YAMLEq should return true") + } + +} + +func TestYAMLEqWrapper_HashOfArraysAndHashes(t *testing.T) { + assert := New(new(testing.T)) + expected := ` +numeric: 1.5 +array: + - foo: bar + - 1 + - "string" + - ["nested", "array", 5.5] +hash: + nested: hash + nested_slice: [this, is, nested] +string: "foo" +` + + actual := ` +numeric: 1.5 +hash: + nested: hash + nested_slice: [this, is, nested] +string: "foo" +array: + - foo: bar + - 1 + - "string" + - ["nested", "array", 5.5] +` + if !assert.YAMLEq(expected, actual) { + t.Error("YAMLEq should return true") + } +} + +func TestYAMLEqWrapper_Array(t *testing.T) { + assert := New(new(testing.T)) + if !assert.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`) { + t.Error("YAMLEq should return true") + } + +} + +func TestYAMLEqWrapper_HashAndArrayNotEquivalent(t *testing.T) { + assert := New(new(testing.T)) + if assert.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`) { + t.Error("YAMLEq should return false") + } +} + +func TestYAMLEqWrapper_HashesNotEquivalent(t *testing.T) { + assert := New(new(testing.T)) + if assert.YAMLEq(`{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) { + t.Error("YAMLEq should return false") + } +} + +func TestYAMLEqWrapper_ActualIsSimpleString(t *testing.T) { + assert := New(new(testing.T)) + if assert.YAMLEq(`{"foo": "bar"}`, "Simple String") { + t.Error("YAMLEq should return false") + } +} + +func TestYAMLEqWrapper_ExpectedIsSimpleString(t *testing.T) { + assert := New(new(testing.T)) + if assert.YAMLEq("Simple String", `{"foo": "bar", "hello": "world"}`) { + t.Error("YAMLEq should return false") + } +} + +func TestYAMLEqWrapper_ExpectedAndActualSimpleString(t *testing.T) { + assert := New(new(testing.T)) + if !assert.YAMLEq("Simple String", "Simple String") { + t.Error("YAMLEq should return true") + } +} + +func TestYAMLEqWrapper_ArraysOfDifferentOrder(t *testing.T) { + assert := New(new(testing.T)) + if assert.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`) { + t.Error("YAMLEq should return false") + } +} diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/http_assertions.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/http_assertions.go new file mode 100644 index 0000000..4ed341d --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/http_assertions.go @@ -0,0 +1,162 @@ +package assert + +import ( + "fmt" + "net/http" + "net/http/httptest" + "net/url" + "strings" +) + +// httpCode is a helper that returns HTTP code of the response. It returns -1 and +// an error if building a new request fails. +func httpCode(handler http.HandlerFunc, method, url string, values url.Values) (int, error) { + w := httptest.NewRecorder() + req, err := http.NewRequest(method, url, nil) + if err != nil { + return -1, err + } + req.URL.RawQuery = values.Encode() + handler(w, req) + return w.Code, nil +} + +// HTTPSuccess asserts that a specified handler returns a success status code. +// +// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + code, err := httpCode(handler, method, url, values) + if err != nil { + Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) + } + + isSuccessCode := code >= http.StatusOK && code <= http.StatusPartialContent + if !isSuccessCode { + Fail(t, fmt.Sprintf("Expected HTTP success status code for %q but received %d", url+"?"+values.Encode(), code)) + } + + return isSuccessCode +} + +// HTTPRedirect asserts that a specified handler returns a redirect status code. +// +// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + code, err := httpCode(handler, method, url, values) + if err != nil { + Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) + } + + isRedirectCode := code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect + if !isRedirectCode { + Fail(t, fmt.Sprintf("Expected HTTP redirect status code for %q but received %d", url+"?"+values.Encode(), code)) + } + + return isRedirectCode +} + +// HTTPError asserts that a specified handler returns an error status code. +// +// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + code, err := httpCode(handler, method, url, values) + if err != nil { + Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) + } + + isErrorCode := code >= http.StatusBadRequest + if !isErrorCode { + Fail(t, fmt.Sprintf("Expected HTTP error status code for %q but received %d", url+"?"+values.Encode(), code)) + } + + return isErrorCode +} + +// HTTPStatusCode asserts that a specified handler returns a specified status code. +// +// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + code, err := httpCode(handler, method, url, values) + if err != nil { + Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) + } + + successful := code == statuscode + if !successful { + Fail(t, fmt.Sprintf("Expected HTTP status code %d for %q but received %d", statuscode, url+"?"+values.Encode(), code)) + } + + return successful +} + +// HTTPBody is a helper that returns HTTP body of the response. It returns +// empty string if building a new request fails. +func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string { + w := httptest.NewRecorder() + req, err := http.NewRequest(method, url+"?"+values.Encode(), nil) + if err != nil { + return "" + } + handler(w, req) + return w.Body.String() +} + +// HTTPBodyContains asserts that a specified handler returns a +// body that contains a string. +// +// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + body := HTTPBody(handler, method, url, values) + + contains := strings.Contains(body, fmt.Sprint(str)) + if !contains { + Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body)) + } + + return contains +} + +// HTTPBodyNotContains asserts that a specified handler returns a +// body that does not contain a string. +// +// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + body := HTTPBody(handler, method, url, values) + + contains := strings.Contains(body, fmt.Sprint(str)) + if contains { + Fail(t, fmt.Sprintf("Expected response body for \"%s\" to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body)) + } + + return !contains +} diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/http_assertions_test.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/http_assertions_test.go new file mode 100644 index 0000000..413c971 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/assert/http_assertions_test.go @@ -0,0 +1,182 @@ +package assert + +import ( + "fmt" + "net/http" + "net/url" + "testing" +) + +func httpOK(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) +} + +func httpRedirect(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusTemporaryRedirect) +} + +func httpError(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusInternalServerError) +} + +func httpStatusCode(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusSwitchingProtocols) +} + +func TestHTTPSuccess(t *testing.T) { + assert := New(t) + + mockT1 := new(testing.T) + assert.Equal(HTTPSuccess(mockT1, httpOK, "GET", "/", nil), true) + assert.False(mockT1.Failed()) + + mockT2 := new(testing.T) + assert.Equal(HTTPSuccess(mockT2, httpRedirect, "GET", "/", nil), false) + assert.True(mockT2.Failed()) + + mockT3 := new(testing.T) + assert.Equal(HTTPSuccess(mockT3, httpError, "GET", "/", nil), false) + assert.True(mockT3.Failed()) + + mockT4 := new(testing.T) + assert.Equal(HTTPSuccess(mockT4, httpStatusCode, "GET", "/", nil), false) + assert.True(mockT4.Failed()) +} + +func TestHTTPRedirect(t *testing.T) { + assert := New(t) + + mockT1 := new(testing.T) + assert.Equal(HTTPRedirect(mockT1, httpOK, "GET", "/", nil), false) + assert.True(mockT1.Failed()) + + mockT2 := new(testing.T) + assert.Equal(HTTPRedirect(mockT2, httpRedirect, "GET", "/", nil), true) + assert.False(mockT2.Failed()) + + mockT3 := new(testing.T) + assert.Equal(HTTPRedirect(mockT3, httpError, "GET", "/", nil), false) + assert.True(mockT3.Failed()) + + mockT4 := new(testing.T) + assert.Equal(HTTPRedirect(mockT4, httpStatusCode, "GET", "/", nil), false) + assert.True(mockT4.Failed()) +} + +func TestHTTPError(t *testing.T) { + assert := New(t) + + mockT1 := new(testing.T) + assert.Equal(HTTPError(mockT1, httpOK, "GET", "/", nil), false) + assert.True(mockT1.Failed()) + + mockT2 := new(testing.T) + assert.Equal(HTTPError(mockT2, httpRedirect, "GET", "/", nil), false) + assert.True(mockT2.Failed()) + + mockT3 := new(testing.T) + assert.Equal(HTTPError(mockT3, httpError, "GET", "/", nil), true) + assert.False(mockT3.Failed()) + + mockT4 := new(testing.T) + assert.Equal(HTTPError(mockT4, httpStatusCode, "GET", "/", nil), false) + assert.True(mockT4.Failed()) +} + +func TestHTTPStatusCode(t *testing.T) { + assert := New(t) + + mockT1 := new(testing.T) + assert.Equal(HTTPStatusCode(mockT1, httpOK, "GET", "/", nil, http.StatusSwitchingProtocols), false) + assert.True(mockT1.Failed()) + + mockT2 := new(testing.T) + assert.Equal(HTTPStatusCode(mockT2, httpRedirect, "GET", "/", nil, http.StatusSwitchingProtocols), false) + assert.True(mockT2.Failed()) + + mockT3 := new(testing.T) + assert.Equal(HTTPStatusCode(mockT3, httpError, "GET", "/", nil, http.StatusSwitchingProtocols), false) + assert.True(mockT3.Failed()) + + mockT4 := new(testing.T) + assert.Equal(HTTPStatusCode(mockT4, httpStatusCode, "GET", "/", nil, http.StatusSwitchingProtocols), true) + assert.False(mockT4.Failed()) +} + +func TestHTTPStatusesWrapper(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + + assert.Equal(mockAssert.HTTPSuccess(httpOK, "GET", "/", nil), true) + assert.Equal(mockAssert.HTTPSuccess(httpRedirect, "GET", "/", nil), false) + assert.Equal(mockAssert.HTTPSuccess(httpError, "GET", "/", nil), false) + + assert.Equal(mockAssert.HTTPRedirect(httpOK, "GET", "/", nil), false) + assert.Equal(mockAssert.HTTPRedirect(httpRedirect, "GET", "/", nil), true) + assert.Equal(mockAssert.HTTPRedirect(httpError, "GET", "/", nil), false) + + assert.Equal(mockAssert.HTTPError(httpOK, "GET", "/", nil), false) + assert.Equal(mockAssert.HTTPError(httpRedirect, "GET", "/", nil), false) + assert.Equal(mockAssert.HTTPError(httpError, "GET", "/", nil), true) +} + +func httpHelloName(w http.ResponseWriter, r *http.Request) { + name := r.FormValue("name") + _, _ = w.Write([]byte(fmt.Sprintf("Hello, %s!", name))) +} + +func TestHTTPRequestWithNoParams(t *testing.T) { + var got *http.Request + handler := func(w http.ResponseWriter, r *http.Request) { + got = r + w.WriteHeader(http.StatusOK) + } + + True(t, HTTPSuccess(t, handler, "GET", "/url", nil)) + + Empty(t, got.URL.Query()) + Equal(t, "/url", got.URL.RequestURI()) +} + +func TestHTTPRequestWithParams(t *testing.T) { + var got *http.Request + handler := func(w http.ResponseWriter, r *http.Request) { + got = r + w.WriteHeader(http.StatusOK) + } + params := url.Values{} + params.Add("id", "12345") + + True(t, HTTPSuccess(t, handler, "GET", "/url", params)) + + Equal(t, url.Values{"id": []string{"12345"}}, got.URL.Query()) + Equal(t, "/url?id=12345", got.URL.String()) + Equal(t, "/url?id=12345", got.URL.RequestURI()) +} + +func TestHttpBody(t *testing.T) { + assert := New(t) + mockT := new(testing.T) + + assert.True(HTTPBodyContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!")) + assert.True(HTTPBodyContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World")) + assert.False(HTTPBodyContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world")) + + assert.False(HTTPBodyNotContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!")) + assert.False(HTTPBodyNotContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World")) + assert.True(HTTPBodyNotContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world")) +} + +func TestHttpBodyWrappers(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + + assert.True(mockAssert.HTTPBodyContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!")) + assert.True(mockAssert.HTTPBodyContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World")) + assert.False(mockAssert.HTTPBodyContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world")) + + assert.False(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!")) + assert.False(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World")) + assert.True(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world")) + +} diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/doc.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/doc.go new file mode 100644 index 0000000..3460b46 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/doc.go @@ -0,0 +1,23 @@ +// ** We are working on testify v2 and would love to hear what you'd like to see in it, have your say here: https://cutt.ly/testify ** +// Package testify is a set of packages that provide many tools for testifying that your code will behave as you intend. +// +// testify contains the following packages: +// +// The assert package provides a comprehensive set of assertion functions that tie in to the Go testing system. +// +// The http package contains tools to make it easier to test http activity using the Go testing system. +// +// The mock package provides a system by which it is possible to mock your objects and verify calls are happening as expected. +// +// The suite package provides a basic structure for using structs as testing suites, and methods on those structs as tests. It includes setup/teardown functionality in the way of interfaces. +package testify + +// blank imports help docs. +import ( + // assert package + _ "github.com/stretchr/testify/assert" + // http package + _ "github.com/stretchr/testify/http" + // mock package + _ "github.com/stretchr/testify/mock" +) diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/go.mod b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/go.mod new file mode 100644 index 0000000..3fe9bab --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/go.mod @@ -0,0 +1,10 @@ +module github.com/stretchr/testify + +go 1.13 + +require ( + github.com/davecgh/go-spew v1.1.1 + github.com/pmezard/go-difflib v1.0.0 + github.com/stretchr/objx v0.5.0 + gopkg.in/yaml.v3 v3.0.1 +) diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/go.sum b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/go.sum new file mode 100644 index 0000000..4f3ced6 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/go.sum @@ -0,0 +1,16 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/http/doc.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/http/doc.go new file mode 100644 index 0000000..695167c --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/http/doc.go @@ -0,0 +1,2 @@ +// Package http DEPRECATED USE net/http/httptest +package http diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/http/test_response_writer.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/http/test_response_writer.go new file mode 100644 index 0000000..300a63b --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/http/test_response_writer.go @@ -0,0 +1,49 @@ +package http + +import ( + "net/http" +) + +// TestResponseWriter DEPRECATED: We recommend you use http://golang.org/pkg/net/http/httptest instead. +type TestResponseWriter struct { + + // StatusCode is the last int written by the call to WriteHeader(int) + StatusCode int + + // Output is a string containing the written bytes using the Write([]byte) func. + Output string + + // header is the internal storage of the http.Header object + header http.Header +} + +// Header DEPRECATED: We recommend you use http://golang.org/pkg/net/http/httptest instead. +func (rw *TestResponseWriter) Header() http.Header { + + if rw.header == nil { + rw.header = make(http.Header) + } + + return rw.header +} + +// Write DEPRECATED: We recommend you use http://golang.org/pkg/net/http/httptest instead. +func (rw *TestResponseWriter) Write(bytes []byte) (int, error) { + + // assume 200 success if no header has been set + if rw.StatusCode == 0 { + rw.WriteHeader(200) + } + + // add these bytes to the output string + rw.Output += string(bytes) + + // return normal values + return 0, nil + +} + +// WriteHeader DEPRECATED: We recommend you use http://golang.org/pkg/net/http/httptest instead. +func (rw *TestResponseWriter) WriteHeader(i int) { + rw.StatusCode = i +} diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/http/test_round_tripper.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/http/test_round_tripper.go new file mode 100644 index 0000000..11a024e --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/http/test_round_tripper.go @@ -0,0 +1,18 @@ +package http + +import ( + "net/http" + + "github.com/stretchr/testify/mock" +) + +// TestRoundTripper DEPRECATED USE net/http/httptest +type TestRoundTripper struct { + mock.Mock +} + +// RoundTrip DEPRECATED USE net/http/httptest +func (t *TestRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + args := t.Called(req) + return args.Get(0).(*http.Response), args.Error(1) +} diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/mock/doc.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/mock/doc.go new file mode 100644 index 0000000..7324128 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/mock/doc.go @@ -0,0 +1,44 @@ +// Package mock provides a system by which it is possible to mock your objects +// and verify calls are happening as expected. +// +// Example Usage +// +// The mock package provides an object, Mock, that tracks activity on another object. It is usually +// embedded into a test object as shown below: +// +// type MyTestObject struct { +// // add a Mock object instance +// mock.Mock +// +// // other fields go here as normal +// } +// +// When implementing the methods of an interface, you wire your functions up +// to call the Mock.Called(args...) method, and return the appropriate values. +// +// For example, to mock a method that saves the name and age of a person and returns +// the year of their birth or an error, you might write this: +// +// func (o *MyTestObject) SavePersonDetails(firstname, lastname string, age int) (int, error) { +// args := o.Called(firstname, lastname, age) +// return args.Int(0), args.Error(1) +// } +// +// The Int, Error and Bool methods are examples of strongly typed getters that take the argument +// index position. Given this argument list: +// +// (12, true, "Something") +// +// You could read them out strongly typed like this: +// +// args.Int(0) +// args.Bool(1) +// args.String(2) +// +// For objects of your own type, use the generic Arguments.Get(index) method and make a type assertion: +// +// return args.Get(0).(*MyObject), args.Get(1).(*AnotherObjectOfMine) +// +// This may cause a panic if the object you are getting is nil (the type assertion will fail), in those +// cases you should check for nil first. +package mock diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/mock/mock.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/mock/mock.go new file mode 100644 index 0000000..e6ff8df --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/mock/mock.go @@ -0,0 +1,1104 @@ +package mock + +import ( + "errors" + "fmt" + "reflect" + "regexp" + "runtime" + "strings" + "sync" + "time" + + "github.com/davecgh/go-spew/spew" + "github.com/pmezard/go-difflib/difflib" + "github.com/stretchr/objx" + "github.com/stretchr/testify/assert" +) + +// TestingT is an interface wrapper around *testing.T +type TestingT interface { + Logf(format string, args ...interface{}) + Errorf(format string, args ...interface{}) + FailNow() +} + +/* + Call +*/ + +// Call represents a method call and is used for setting expectations, +// as well as recording activity. +type Call struct { + Parent *Mock + + // The name of the method that was or will be called. + Method string + + // Holds the arguments of the method. + Arguments Arguments + + // Holds the arguments that should be returned when + // this method is called. + ReturnArguments Arguments + + // Holds the caller info for the On() call + callerInfo []string + + // The number of times to return the return arguments when setting + // expectations. 0 means to always return the value. + Repeatability int + + // Amount of times this call has been called + totalCalls int + + // Call to this method can be optional + optional bool + + // Holds a channel that will be used to block the Return until it either + // receives a message or is closed. nil means it returns immediately. + WaitFor <-chan time.Time + + waitTime time.Duration + + // Holds a handler used to manipulate arguments content that are passed by + // reference. It's useful when mocking methods such as unmarshalers or + // decoders. + RunFn func(Arguments) + + // PanicMsg holds msg to be used to mock panic on the function call + // if the PanicMsg is set to a non nil string the function call will panic + // irrespective of other settings + PanicMsg *string + + // Calls which must be satisfied before this call can be + requires []*Call +} + +func newCall(parent *Mock, methodName string, callerInfo []string, methodArguments ...interface{}) *Call { + return &Call{ + Parent: parent, + Method: methodName, + Arguments: methodArguments, + ReturnArguments: make([]interface{}, 0), + callerInfo: callerInfo, + Repeatability: 0, + WaitFor: nil, + RunFn: nil, + PanicMsg: nil, + } +} + +func (c *Call) lock() { + c.Parent.mutex.Lock() +} + +func (c *Call) unlock() { + c.Parent.mutex.Unlock() +} + +// Return specifies the return arguments for the expectation. +// +// Mock.On("DoSomething").Return(errors.New("failed")) +func (c *Call) Return(returnArguments ...interface{}) *Call { + c.lock() + defer c.unlock() + + c.ReturnArguments = returnArguments + + return c +} + +// Panic specifies if the functon call should fail and the panic message +// +// Mock.On("DoSomething").Panic("test panic") +func (c *Call) Panic(msg string) *Call { + c.lock() + defer c.unlock() + + c.PanicMsg = &msg + + return c +} + +// Once indicates that that the mock should only return the value once. +// +// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Once() +func (c *Call) Once() *Call { + return c.Times(1) +} + +// Twice indicates that that the mock should only return the value twice. +// +// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Twice() +func (c *Call) Twice() *Call { + return c.Times(2) +} + +// Times indicates that that the mock should only return the indicated number +// of times. +// +// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Times(5) +func (c *Call) Times(i int) *Call { + c.lock() + defer c.unlock() + c.Repeatability = i + return c +} + +// WaitUntil sets the channel that will block the mock's return until its closed +// or a message is received. +// +// Mock.On("MyMethod", arg1, arg2).WaitUntil(time.After(time.Second)) +func (c *Call) WaitUntil(w <-chan time.Time) *Call { + c.lock() + defer c.unlock() + c.WaitFor = w + return c +} + +// After sets how long to block until the call returns +// +// Mock.On("MyMethod", arg1, arg2).After(time.Second) +func (c *Call) After(d time.Duration) *Call { + c.lock() + defer c.unlock() + c.waitTime = d + return c +} + +// Run sets a handler to be called before returning. It can be used when +// mocking a method (such as an unmarshaler) that takes a pointer to a struct and +// sets properties in such struct +// +// Mock.On("Unmarshal", AnythingOfType("*map[string]interface{}")).Return().Run(func(args Arguments) { +// arg := args.Get(0).(*map[string]interface{}) +// arg["foo"] = "bar" +// }) +func (c *Call) Run(fn func(args Arguments)) *Call { + c.lock() + defer c.unlock() + c.RunFn = fn + return c +} + +// Maybe allows the method call to be optional. Not calling an optional method +// will not cause an error while asserting expectations +func (c *Call) Maybe() *Call { + c.lock() + defer c.unlock() + c.optional = true + return c +} + +// On chains a new expectation description onto the mocked interface. This +// allows syntax like. +// +// Mock. +// On("MyMethod", 1).Return(nil). +// On("MyOtherMethod", 'a', 'b', 'c').Return(errors.New("Some Error")) +//go:noinline +func (c *Call) On(methodName string, arguments ...interface{}) *Call { + return c.Parent.On(methodName, arguments...) +} + +// Unset removes a mock handler from being called. +// test.On("func", mock.Anything).Unset() +func (c *Call) Unset() *Call { + var unlockOnce sync.Once + + for _, arg := range c.Arguments { + if v := reflect.ValueOf(arg); v.Kind() == reflect.Func { + panic(fmt.Sprintf("cannot use Func in expectations. Use mock.AnythingOfType(\"%T\")", arg)) + } + } + + c.lock() + defer unlockOnce.Do(c.unlock) + + foundMatchingCall := false + + // in-place filter slice for calls to be removed - iterate from 0'th to last skipping unnecessary ones + var index int // write index + for _, call := range c.Parent.ExpectedCalls { + if call.Method == c.Method { + _, diffCount := call.Arguments.Diff(c.Arguments) + if diffCount == 0 { + foundMatchingCall = true + // Remove from ExpectedCalls - just skip it + continue + } + } + c.Parent.ExpectedCalls[index] = call + index++ + } + // trim slice up to last copied index + c.Parent.ExpectedCalls = c.Parent.ExpectedCalls[:index] + + if !foundMatchingCall { + unlockOnce.Do(c.unlock) + c.Parent.fail("\n\nmock: Could not find expected call\n-----------------------------\n\n%s\n\n", + callString(c.Method, c.Arguments, true), + ) + } + + return c +} + +// NotBefore indicates that the mock should only be called after the referenced +// calls have been called as expected. The referenced calls may be from the +// same mock instance and/or other mock instances. +// +// Mock.On("Do").Return(nil).Notbefore( +// Mock.On("Init").Return(nil) +// ) +func (c *Call) NotBefore(calls ...*Call) *Call { + c.lock() + defer c.unlock() + + for _, call := range calls { + if call.Parent == nil { + panic("not before calls must be created with Mock.On()") + } + } + + c.requires = append(c.requires, calls...) + return c +} + +// Mock is the workhorse used to track activity on another object. +// For an example of its usage, refer to the "Example Usage" section at the top +// of this document. +type Mock struct { + // Represents the calls that are expected of + // an object. + ExpectedCalls []*Call + + // Holds the calls that were made to this mocked object. + Calls []Call + + // test is An optional variable that holds the test struct, to be used when an + // invalid mock call was made. + test TestingT + + // TestData holds any data that might be useful for testing. Testify ignores + // this data completely allowing you to do whatever you like with it. + testData objx.Map + + mutex sync.Mutex +} + +// String provides a %v format string for Mock. +// Note: this is used implicitly by Arguments.Diff if a Mock is passed. +// It exists because go's default %v formatting traverses the struct +// without acquiring the mutex, which is detected by go test -race. +func (m *Mock) String() string { + return fmt.Sprintf("%[1]T<%[1]p>", m) +} + +// TestData holds any data that might be useful for testing. Testify ignores +// this data completely allowing you to do whatever you like with it. +func (m *Mock) TestData() objx.Map { + if m.testData == nil { + m.testData = make(objx.Map) + } + + return m.testData +} + +/* + Setting expectations +*/ + +// Test sets the test struct variable of the mock object +func (m *Mock) Test(t TestingT) { + m.mutex.Lock() + defer m.mutex.Unlock() + m.test = t +} + +// fail fails the current test with the given formatted format and args. +// In case that a test was defined, it uses the test APIs for failing a test, +// otherwise it uses panic. +func (m *Mock) fail(format string, args ...interface{}) { + m.mutex.Lock() + defer m.mutex.Unlock() + + if m.test == nil { + panic(fmt.Sprintf(format, args...)) + } + m.test.Errorf(format, args...) + m.test.FailNow() +} + +// On starts a description of an expectation of the specified method +// being called. +// +// Mock.On("MyMethod", arg1, arg2) +func (m *Mock) On(methodName string, arguments ...interface{}) *Call { + for _, arg := range arguments { + if v := reflect.ValueOf(arg); v.Kind() == reflect.Func { + panic(fmt.Sprintf("cannot use Func in expectations. Use mock.AnythingOfType(\"%T\")", arg)) + } + } + + m.mutex.Lock() + defer m.mutex.Unlock() + c := newCall(m, methodName, assert.CallerInfo(), arguments...) + m.ExpectedCalls = append(m.ExpectedCalls, c) + return c +} + +// /* +// Recording and responding to activity +// */ + +func (m *Mock) findExpectedCall(method string, arguments ...interface{}) (int, *Call) { + var expectedCall *Call + + for i, call := range m.ExpectedCalls { + if call.Method == method { + _, diffCount := call.Arguments.Diff(arguments) + if diffCount == 0 { + expectedCall = call + if call.Repeatability > -1 { + return i, call + } + } + } + } + + return -1, expectedCall +} + +type matchCandidate struct { + call *Call + mismatch string + diffCount int +} + +func (c matchCandidate) isBetterMatchThan(other matchCandidate) bool { + if c.call == nil { + return false + } + if other.call == nil { + return true + } + + if c.diffCount > other.diffCount { + return false + } + if c.diffCount < other.diffCount { + return true + } + + if c.call.Repeatability > 0 && other.call.Repeatability <= 0 { + return true + } + return false +} + +func (m *Mock) findClosestCall(method string, arguments ...interface{}) (*Call, string) { + var bestMatch matchCandidate + + for _, call := range m.expectedCalls() { + if call.Method == method { + + errInfo, tempDiffCount := call.Arguments.Diff(arguments) + tempCandidate := matchCandidate{ + call: call, + mismatch: errInfo, + diffCount: tempDiffCount, + } + if tempCandidate.isBetterMatchThan(bestMatch) { + bestMatch = tempCandidate + } + } + } + + return bestMatch.call, bestMatch.mismatch +} + +func callString(method string, arguments Arguments, includeArgumentValues bool) string { + var argValsString string + if includeArgumentValues { + var argVals []string + for argIndex, arg := range arguments { + argVals = append(argVals, fmt.Sprintf("%d: %#v", argIndex, arg)) + } + argValsString = fmt.Sprintf("\n\t\t%s", strings.Join(argVals, "\n\t\t")) + } + + return fmt.Sprintf("%s(%s)%s", method, arguments.String(), argValsString) +} + +// Called tells the mock object that a method has been called, and gets an array +// of arguments to return. Panics if the call is unexpected (i.e. not preceded by +// appropriate .On .Return() calls) +// If Call.WaitFor is set, blocks until the channel is closed or receives a message. +func (m *Mock) Called(arguments ...interface{}) Arguments { + // get the calling function's name + pc, _, _, ok := runtime.Caller(1) + if !ok { + panic("Couldn't get the caller information") + } + functionPath := runtime.FuncForPC(pc).Name() + // Next four lines are required to use GCCGO function naming conventions. + // For Ex: github_com_docker_libkv_store_mock.WatchTree.pN39_github_com_docker_libkv_store_mock.Mock + // uses interface information unlike golang github.com/docker/libkv/store/mock.(*Mock).WatchTree + // With GCCGO we need to remove interface information starting from pN<dd>. + re := regexp.MustCompile("\\.pN\\d+_") + if re.MatchString(functionPath) { + functionPath = re.Split(functionPath, -1)[0] + } + parts := strings.Split(functionPath, ".") + functionName := parts[len(parts)-1] + return m.MethodCalled(functionName, arguments...) +} + +// MethodCalled tells the mock object that the given method has been called, and gets +// an array of arguments to return. Panics if the call is unexpected (i.e. not preceded +// by appropriate .On .Return() calls) +// If Call.WaitFor is set, blocks until the channel is closed or receives a message. +func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Arguments { + m.mutex.Lock() + // TODO: could combine expected and closes in single loop + found, call := m.findExpectedCall(methodName, arguments...) + + if found < 0 { + // expected call found but it has already been called with repeatable times + if call != nil { + m.mutex.Unlock() + m.fail("\nassert: mock: The method has been called over %d times.\n\tEither do one more Mock.On(\"%s\").Return(...), or remove extra call.\n\tThis call was unexpected:\n\t\t%s\n\tat: %s", call.totalCalls, methodName, callString(methodName, arguments, true), assert.CallerInfo()) + } + // we have to fail here - because we don't know what to do + // as the return arguments. This is because: + // + // a) this is a totally unexpected call to this method, + // b) the arguments are not what was expected, or + // c) the developer has forgotten to add an accompanying On...Return pair. + closestCall, mismatch := m.findClosestCall(methodName, arguments...) + m.mutex.Unlock() + + if closestCall != nil { + m.fail("\n\nmock: Unexpected Method Call\n-----------------------------\n\n%s\n\nThe closest call I have is: \n\n%s\n\n%s\nDiff: %s", + callString(methodName, arguments, true), + callString(methodName, closestCall.Arguments, true), + diffArguments(closestCall.Arguments, arguments), + strings.TrimSpace(mismatch), + ) + } else { + m.fail("\nassert: mock: I don't know what to return because the method call was unexpected.\n\tEither do Mock.On(\"%s\").Return(...) first, or remove the %s() call.\n\tThis method was unexpected:\n\t\t%s\n\tat: %s", methodName, methodName, callString(methodName, arguments, true), assert.CallerInfo()) + } + } + + for _, requirement := range call.requires { + if satisfied, _ := requirement.Parent.checkExpectation(requirement); !satisfied { + m.mutex.Unlock() + m.fail("mock: Unexpected Method Call\n-----------------------------\n\n%s\n\nMust not be called before%s:\n\n%s", + callString(call.Method, call.Arguments, true), + func() (s string) { + if requirement.totalCalls > 0 { + s = " another call of" + } + if call.Parent != requirement.Parent { + s += " method from another mock instance" + } + return + }(), + callString(requirement.Method, requirement.Arguments, true), + ) + } + } + + if call.Repeatability == 1 { + call.Repeatability = -1 + } else if call.Repeatability > 1 { + call.Repeatability-- + } + call.totalCalls++ + + // add the call + m.Calls = append(m.Calls, *newCall(m, methodName, assert.CallerInfo(), arguments...)) + m.mutex.Unlock() + + // block if specified + if call.WaitFor != nil { + <-call.WaitFor + } else { + time.Sleep(call.waitTime) + } + + m.mutex.Lock() + panicMsg := call.PanicMsg + m.mutex.Unlock() + if panicMsg != nil { + panic(*panicMsg) + } + + m.mutex.Lock() + runFn := call.RunFn + m.mutex.Unlock() + + if runFn != nil { + runFn(arguments) + } + + m.mutex.Lock() + returnArgs := call.ReturnArguments + m.mutex.Unlock() + + return returnArgs +} + +/* + Assertions +*/ + +type assertExpectationser interface { + AssertExpectations(TestingT) bool +} + +// AssertExpectationsForObjects asserts that everything specified with On and Return +// of the specified objects was in fact called as expected. +// +// Calls may have occurred in any order. +func AssertExpectationsForObjects(t TestingT, testObjects ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + for _, obj := range testObjects { + if m, ok := obj.(*Mock); ok { + t.Logf("Deprecated mock.AssertExpectationsForObjects(myMock.Mock) use mock.AssertExpectationsForObjects(myMock)") + obj = m + } + m := obj.(assertExpectationser) + if !m.AssertExpectations(t) { + t.Logf("Expectations didn't match for Mock: %+v", reflect.TypeOf(m)) + return false + } + } + return true +} + +// AssertExpectations asserts that everything specified with On and Return was +// in fact called as expected. Calls may have occurred in any order. +func (m *Mock) AssertExpectations(t TestingT) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + m.mutex.Lock() + defer m.mutex.Unlock() + var failedExpectations int + + // iterate through each expectation + expectedCalls := m.expectedCalls() + for _, expectedCall := range expectedCalls { + satisfied, reason := m.checkExpectation(expectedCall) + if !satisfied { + failedExpectations++ + } + t.Logf(reason) + } + + if failedExpectations != 0 { + t.Errorf("FAIL: %d out of %d expectation(s) were met.\n\tThe code you are testing needs to make %d more call(s).\n\tat: %s", len(expectedCalls)-failedExpectations, len(expectedCalls), failedExpectations, assert.CallerInfo()) + } + + return failedExpectations == 0 +} + +func (m *Mock) checkExpectation(call *Call) (bool, string) { + if !call.optional && !m.methodWasCalled(call.Method, call.Arguments) && call.totalCalls == 0 { + return false, fmt.Sprintf("FAIL:\t%s(%s)\n\t\tat: %s", call.Method, call.Arguments.String(), call.callerInfo) + } + if call.Repeatability > 0 { + return false, fmt.Sprintf("FAIL:\t%s(%s)\n\t\tat: %s", call.Method, call.Arguments.String(), call.callerInfo) + } + return true, fmt.Sprintf("PASS:\t%s(%s)", call.Method, call.Arguments.String()) +} + +// AssertNumberOfCalls asserts that the method was called expectedCalls times. +func (m *Mock) AssertNumberOfCalls(t TestingT, methodName string, expectedCalls int) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + m.mutex.Lock() + defer m.mutex.Unlock() + var actualCalls int + for _, call := range m.calls() { + if call.Method == methodName { + actualCalls++ + } + } + return assert.Equal(t, expectedCalls, actualCalls, fmt.Sprintf("Expected number of calls (%d) does not match the actual number of calls (%d).", expectedCalls, actualCalls)) +} + +// AssertCalled asserts that the method was called. +// It can produce a false result when an argument is a pointer type and the underlying value changed after calling the mocked method. +func (m *Mock) AssertCalled(t TestingT, methodName string, arguments ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + m.mutex.Lock() + defer m.mutex.Unlock() + if !m.methodWasCalled(methodName, arguments) { + var calledWithArgs []string + for _, call := range m.calls() { + calledWithArgs = append(calledWithArgs, fmt.Sprintf("%v", call.Arguments)) + } + if len(calledWithArgs) == 0 { + return assert.Fail(t, "Should have called with given arguments", + fmt.Sprintf("Expected %q to have been called with:\n%v\nbut no actual calls happened", methodName, arguments)) + } + return assert.Fail(t, "Should have called with given arguments", + fmt.Sprintf("Expected %q to have been called with:\n%v\nbut actual calls were:\n %v", methodName, arguments, strings.Join(calledWithArgs, "\n"))) + } + return true +} + +// AssertNotCalled asserts that the method was not called. +// It can produce a false result when an argument is a pointer type and the underlying value changed after calling the mocked method. +func (m *Mock) AssertNotCalled(t TestingT, methodName string, arguments ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + m.mutex.Lock() + defer m.mutex.Unlock() + if m.methodWasCalled(methodName, arguments) { + return assert.Fail(t, "Should not have called with given arguments", + fmt.Sprintf("Expected %q to not have been called with:\n%v\nbut actually it was.", methodName, arguments)) + } + return true +} + +// IsMethodCallable checking that the method can be called +// If the method was called more than `Repeatability` return false +func (m *Mock) IsMethodCallable(t TestingT, methodName string, arguments ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + m.mutex.Lock() + defer m.mutex.Unlock() + + for _, v := range m.ExpectedCalls { + if v.Method != methodName { + continue + } + if len(arguments) != len(v.Arguments) { + continue + } + if v.Repeatability < v.totalCalls { + continue + } + if isArgsEqual(v.Arguments, arguments) { + return true + } + } + return false +} + +// isArgsEqual compares arguments +func isArgsEqual(expected Arguments, args []interface{}) bool { + if len(expected) != len(args) { + return false + } + for i, v := range args { + if !reflect.DeepEqual(expected[i], v) { + return false + } + } + return true +} + +func (m *Mock) methodWasCalled(methodName string, expected []interface{}) bool { + for _, call := range m.calls() { + if call.Method == methodName { + + _, differences := Arguments(expected).Diff(call.Arguments) + + if differences == 0 { + // found the expected call + return true + } + + } + } + // we didn't find the expected call + return false +} + +func (m *Mock) expectedCalls() []*Call { + return append([]*Call{}, m.ExpectedCalls...) +} + +func (m *Mock) calls() []Call { + return append([]Call{}, m.Calls...) +} + +/* + Arguments +*/ + +// Arguments holds an array of method arguments or return values. +type Arguments []interface{} + +const ( + // Anything is used in Diff and Assert when the argument being tested + // shouldn't be taken into consideration. + Anything = "mock.Anything" +) + +// AnythingOfTypeArgument is a string that contains the type of an argument +// for use when type checking. Used in Diff and Assert. +type AnythingOfTypeArgument string + +// AnythingOfType returns an AnythingOfTypeArgument object containing the +// name of the type to check for. Used in Diff and Assert. +// +// For example: +// Assert(t, AnythingOfType("string"), AnythingOfType("int")) +func AnythingOfType(t string) AnythingOfTypeArgument { + return AnythingOfTypeArgument(t) +} + +// IsTypeArgument is a struct that contains the type of an argument +// for use when type checking. This is an alternative to AnythingOfType. +// Used in Diff and Assert. +type IsTypeArgument struct { + t interface{} +} + +// IsType returns an IsTypeArgument object containing the type to check for. +// You can provide a zero-value of the type to check. This is an +// alternative to AnythingOfType. Used in Diff and Assert. +// +// For example: +// Assert(t, IsType(""), IsType(0)) +func IsType(t interface{}) *IsTypeArgument { + return &IsTypeArgument{t: t} +} + +// argumentMatcher performs custom argument matching, returning whether or +// not the argument is matched by the expectation fixture function. +type argumentMatcher struct { + // fn is a function which accepts one argument, and returns a bool. + fn reflect.Value +} + +func (f argumentMatcher) Matches(argument interface{}) bool { + expectType := f.fn.Type().In(0) + expectTypeNilSupported := false + switch expectType.Kind() { + case reflect.Interface, reflect.Chan, reflect.Func, reflect.Map, reflect.Slice, reflect.Ptr: + expectTypeNilSupported = true + } + + argType := reflect.TypeOf(argument) + var arg reflect.Value + if argType == nil { + arg = reflect.New(expectType).Elem() + } else { + arg = reflect.ValueOf(argument) + } + + if argType == nil && !expectTypeNilSupported { + panic(errors.New("attempting to call matcher with nil for non-nil expected type")) + } + if argType == nil || argType.AssignableTo(expectType) { + result := f.fn.Call([]reflect.Value{arg}) + return result[0].Bool() + } + return false +} + +func (f argumentMatcher) String() string { + return fmt.Sprintf("func(%s) bool", f.fn.Type().In(0).String()) +} + +// MatchedBy can be used to match a mock call based on only certain properties +// from a complex struct or some calculation. It takes a function that will be +// evaluated with the called argument and will return true when there's a match +// and false otherwise. +// +// Example: +// m.On("Do", MatchedBy(func(req *http.Request) bool { return req.Host == "example.com" })) +// +// |fn|, must be a function accepting a single argument (of the expected type) +// which returns a bool. If |fn| doesn't match the required signature, +// MatchedBy() panics. +func MatchedBy(fn interface{}) argumentMatcher { + fnType := reflect.TypeOf(fn) + + if fnType.Kind() != reflect.Func { + panic(fmt.Sprintf("assert: arguments: %s is not a func", fn)) + } + if fnType.NumIn() != 1 { + panic(fmt.Sprintf("assert: arguments: %s does not take exactly one argument", fn)) + } + if fnType.NumOut() != 1 || fnType.Out(0).Kind() != reflect.Bool { + panic(fmt.Sprintf("assert: arguments: %s does not return a bool", fn)) + } + + return argumentMatcher{fn: reflect.ValueOf(fn)} +} + +// Get Returns the argument at the specified index. +func (args Arguments) Get(index int) interface{} { + if index+1 > len(args) { + panic(fmt.Sprintf("assert: arguments: Cannot call Get(%d) because there are %d argument(s).", index, len(args))) + } + return args[index] +} + +// Is gets whether the objects match the arguments specified. +func (args Arguments) Is(objects ...interface{}) bool { + for i, obj := range args { + if obj != objects[i] { + return false + } + } + return true +} + +// Diff gets a string describing the differences between the arguments +// and the specified objects. +// +// Returns the diff string and number of differences found. +func (args Arguments) Diff(objects []interface{}) (string, int) { + // TODO: could return string as error and nil for No difference + + output := "\n" + var differences int + + maxArgCount := len(args) + if len(objects) > maxArgCount { + maxArgCount = len(objects) + } + + for i := 0; i < maxArgCount; i++ { + var actual, expected interface{} + var actualFmt, expectedFmt string + + if len(objects) <= i { + actual = "(Missing)" + actualFmt = "(Missing)" + } else { + actual = objects[i] + actualFmt = fmt.Sprintf("(%[1]T=%[1]v)", actual) + } + + if len(args) <= i { + expected = "(Missing)" + expectedFmt = "(Missing)" + } else { + expected = args[i] + expectedFmt = fmt.Sprintf("(%[1]T=%[1]v)", expected) + } + + if matcher, ok := expected.(argumentMatcher); ok { + var matches bool + func() { + defer func() { + if r := recover(); r != nil { + actualFmt = fmt.Sprintf("panic in argument matcher: %v", r) + } + }() + matches = matcher.Matches(actual) + }() + if matches { + output = fmt.Sprintf("%s\t%d: PASS: %s matched by %s\n", output, i, actualFmt, matcher) + } else { + differences++ + output = fmt.Sprintf("%s\t%d: FAIL: %s not matched by %s\n", output, i, actualFmt, matcher) + } + } else if reflect.TypeOf(expected) == reflect.TypeOf((*AnythingOfTypeArgument)(nil)).Elem() { + // type checking + if reflect.TypeOf(actual).Name() != string(expected.(AnythingOfTypeArgument)) && reflect.TypeOf(actual).String() != string(expected.(AnythingOfTypeArgument)) { + // not match + differences++ + output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected, reflect.TypeOf(actual).Name(), actualFmt) + } + } else if reflect.TypeOf(expected) == reflect.TypeOf((*IsTypeArgument)(nil)) { + t := expected.(*IsTypeArgument).t + if reflect.TypeOf(t) != reflect.TypeOf(actual) { + differences++ + output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, reflect.TypeOf(t).Name(), reflect.TypeOf(actual).Name(), actualFmt) + } + } else { + // normal checking + + if assert.ObjectsAreEqual(expected, Anything) || assert.ObjectsAreEqual(actual, Anything) || assert.ObjectsAreEqual(actual, expected) { + // match + output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, actualFmt, expectedFmt) + } else { + // not match + differences++ + output = fmt.Sprintf("%s\t%d: FAIL: %s != %s\n", output, i, actualFmt, expectedFmt) + } + } + + } + + if differences == 0 { + return "No differences.", differences + } + + return output, differences +} + +// Assert compares the arguments with the specified objects and fails if +// they do not exactly match. +func (args Arguments) Assert(t TestingT, objects ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + // get the differences + diff, diffCount := args.Diff(objects) + + if diffCount == 0 { + return true + } + + // there are differences... report them... + t.Logf(diff) + t.Errorf("%sArguments do not match.", assert.CallerInfo()) + + return false +} + +// String gets the argument at the specified index. Panics if there is no argument, or +// if the argument is of the wrong type. +// +// If no index is provided, String() returns a complete string representation +// of the arguments. +func (args Arguments) String(indexOrNil ...int) string { + if len(indexOrNil) == 0 { + // normal String() method - return a string representation of the args + var argsStr []string + for _, arg := range args { + argsStr = append(argsStr, fmt.Sprintf("%T", arg)) // handles nil nicely + } + return strings.Join(argsStr, ",") + } else if len(indexOrNil) == 1 { + // Index has been specified - get the argument at that index + index := indexOrNil[0] + var s string + var ok bool + if s, ok = args.Get(index).(string); !ok { + panic(fmt.Sprintf("assert: arguments: String(%d) failed because object wasn't correct type: %s", index, args.Get(index))) + } + return s + } + + panic(fmt.Sprintf("assert: arguments: Wrong number of arguments passed to String. Must be 0 or 1, not %d", len(indexOrNil))) +} + +// Int gets the argument at the specified index. Panics if there is no argument, or +// if the argument is of the wrong type. +func (args Arguments) Int(index int) int { + var s int + var ok bool + if s, ok = args.Get(index).(int); !ok { + panic(fmt.Sprintf("assert: arguments: Int(%d) failed because object wasn't correct type: %v", index, args.Get(index))) + } + return s +} + +// Error gets the argument at the specified index. Panics if there is no argument, or +// if the argument is of the wrong type. +func (args Arguments) Error(index int) error { + obj := args.Get(index) + var s error + var ok bool + if obj == nil { + return nil + } + if s, ok = obj.(error); !ok { + panic(fmt.Sprintf("assert: arguments: Error(%d) failed because object wasn't correct type: %v", index, args.Get(index))) + } + return s +} + +// Bool gets the argument at the specified index. Panics if there is no argument, or +// if the argument is of the wrong type. +func (args Arguments) Bool(index int) bool { + var s bool + var ok bool + if s, ok = args.Get(index).(bool); !ok { + panic(fmt.Sprintf("assert: arguments: Bool(%d) failed because object wasn't correct type: %v", index, args.Get(index))) + } + return s +} + +func typeAndKind(v interface{}) (reflect.Type, reflect.Kind) { + t := reflect.TypeOf(v) + k := t.Kind() + + if k == reflect.Ptr { + t = t.Elem() + k = t.Kind() + } + return t, k +} + +func diffArguments(expected Arguments, actual Arguments) string { + if len(expected) != len(actual) { + return fmt.Sprintf("Provided %v arguments, mocked for %v arguments", len(expected), len(actual)) + } + + for x := range expected { + if diffString := diff(expected[x], actual[x]); diffString != "" { + return fmt.Sprintf("Difference found in argument %v:\n\n%s", x, diffString) + } + } + + return "" +} + +// diff returns a diff of both values as long as both are of the same type and +// are a struct, map, slice or array. Otherwise it returns an empty string. +func diff(expected interface{}, actual interface{}) string { + if expected == nil || actual == nil { + return "" + } + + et, ek := typeAndKind(expected) + at, _ := typeAndKind(actual) + + if et != at { + return "" + } + + if ek != reflect.Struct && ek != reflect.Map && ek != reflect.Slice && ek != reflect.Array { + return "" + } + + e := spewConfig.Sdump(expected) + a := spewConfig.Sdump(actual) + + diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ + A: difflib.SplitLines(e), + B: difflib.SplitLines(a), + FromFile: "Expected", + FromDate: "", + ToFile: "Actual", + ToDate: "", + Context: 1, + }) + + return diff +} + +var spewConfig = spew.ConfigState{ + Indent: " ", + DisablePointerAddresses: true, + DisableCapacities: true, + SortKeys: true, +} + +type tHelper interface { + Helper() +} diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/mock/mock_test.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/mock/mock_test.go new file mode 100644 index 0000000..260bb9c --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/mock/mock_test.go @@ -0,0 +1,2084 @@ +package mock + +import ( + "errors" + "fmt" + "regexp" + "runtime" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +/* + Test objects +*/ + +// ExampleInterface represents an example interface. +type ExampleInterface interface { + TheExampleMethod(a, b, c int) (int, error) +} + +// TestExampleImplementation is a test implementation of ExampleInterface +type TestExampleImplementation struct { + Mock +} + +func (i *TestExampleImplementation) TheExampleMethod(a, b, c int) (int, error) { + args := i.Called(a, b, c) + return args.Int(0), errors.New("Whoops") +} + +//go:noinline +func (i *TestExampleImplementation) TheExampleMethod2(yesorno bool) { + i.Called(yesorno) +} + +type ExampleType struct { + ran bool +} + +func (i *TestExampleImplementation) TheExampleMethod3(et *ExampleType) error { + args := i.Called(et) + return args.Error(0) +} + +func (i *TestExampleImplementation) TheExampleMethod4(v ExampleInterface) error { + args := i.Called(v) + return args.Error(0) +} + +func (i *TestExampleImplementation) TheExampleMethod5(ch chan struct{}) error { + args := i.Called(ch) + return args.Error(0) +} + +func (i *TestExampleImplementation) TheExampleMethod6(m map[string]bool) error { + args := i.Called(m) + return args.Error(0) +} + +func (i *TestExampleImplementation) TheExampleMethod7(slice []bool) error { + args := i.Called(slice) + return args.Error(0) +} + +func (i *TestExampleImplementation) TheExampleMethodFunc(fn func(string) error) error { + args := i.Called(fn) + return args.Error(0) +} + +func (i *TestExampleImplementation) TheExampleMethodVariadic(a ...int) error { + args := i.Called(a) + return args.Error(0) +} + +func (i *TestExampleImplementation) TheExampleMethodVariadicInterface(a ...interface{}) error { + args := i.Called(a) + return args.Error(0) +} + +func (i *TestExampleImplementation) TheExampleMethodMixedVariadic(a int, b ...int) error { + args := i.Called(a, b) + return args.Error(0) +} + +type ExampleFuncType func(string) error + +func (i *TestExampleImplementation) TheExampleMethodFuncType(fn ExampleFuncType) error { + args := i.Called(fn) + return args.Error(0) +} + +// MockTestingT mocks a test struct +type MockTestingT struct { + logfCount, errorfCount, failNowCount int +} + +const mockTestingTFailNowCalled = "FailNow was called" + +func (m *MockTestingT) Logf(string, ...interface{}) { + m.logfCount++ +} + +func (m *MockTestingT) Errorf(string, ...interface{}) { + m.errorfCount++ +} + +// FailNow mocks the FailNow call. +// It panics in order to mimic the FailNow behavior in the sense that +// the execution stops. +// When expecting this method, the call that invokes it should use the following code: +// +// assert.PanicsWithValue(t, mockTestingTFailNowCalled, func() {...}) +func (m *MockTestingT) FailNow() { + m.failNowCount++ + + // this function should panic now to stop the execution as expected + panic(mockTestingTFailNowCalled) +} + +/* + Mock +*/ + +func Test_Mock_TestData(t *testing.T) { + + var mockedService = new(TestExampleImplementation) + + if assert.NotNil(t, mockedService.TestData()) { + + mockedService.TestData().Set("something", 123) + assert.Equal(t, 123, mockedService.TestData().Get("something").Data()) + } +} + +func Test_Mock_On(t *testing.T) { + + // make a test impl object + var mockedService = new(TestExampleImplementation) + + c := mockedService.On("TheExampleMethod") + assert.Equal(t, []*Call{c}, mockedService.ExpectedCalls) + assert.Equal(t, "TheExampleMethod", c.Method) +} + +func Test_Mock_Chained_On(t *testing.T) { + // make a test impl object + var mockedService = new(TestExampleImplementation) + + // determine our current line number so we can assert the expected calls callerInfo properly + _, filename, line, _ := runtime.Caller(0) + mockedService. + On("TheExampleMethod", 1, 2, 3). + Return(0). + On("TheExampleMethod3", AnythingOfType("*mock.ExampleType")). + Return(nil) + + expectedCalls := []*Call{ + { + Parent: &mockedService.Mock, + Method: "TheExampleMethod", + Arguments: []interface{}{1, 2, 3}, + ReturnArguments: []interface{}{0}, + callerInfo: []string{fmt.Sprintf("%s:%d", filename, line+2)}, + }, + { + Parent: &mockedService.Mock, + Method: "TheExampleMethod3", + Arguments: []interface{}{AnythingOfType("*mock.ExampleType")}, + ReturnArguments: []interface{}{nil}, + callerInfo: []string{fmt.Sprintf("%s:%d", filename, line+4)}, + }, + } + assert.Equal(t, expectedCalls, mockedService.ExpectedCalls) +} + +func Test_Mock_On_WithArgs(t *testing.T) { + + // make a test impl object + var mockedService = new(TestExampleImplementation) + + c := mockedService.On("TheExampleMethod", 1, 2, 3, 4) + + assert.Equal(t, []*Call{c}, mockedService.ExpectedCalls) + assert.Equal(t, "TheExampleMethod", c.Method) + assert.Equal(t, Arguments{1, 2, 3, 4}, c.Arguments) +} + +func Test_Mock_On_WithFuncArg(t *testing.T) { + + // make a test impl object + var mockedService = new(TestExampleImplementation) + + c := mockedService. + On("TheExampleMethodFunc", AnythingOfType("func(string) error")). + Return(nil) + + assert.Equal(t, []*Call{c}, mockedService.ExpectedCalls) + assert.Equal(t, "TheExampleMethodFunc", c.Method) + assert.Equal(t, 1, len(c.Arguments)) + assert.Equal(t, AnythingOfType("func(string) error"), c.Arguments[0]) + + fn := func(string) error { return nil } + + assert.NotPanics(t, func() { + mockedService.TheExampleMethodFunc(fn) + }) +} + +func Test_Mock_On_WithIntArgMatcher(t *testing.T) { + var mockedService TestExampleImplementation + + mockedService.On("TheExampleMethod", + MatchedBy(func(a int) bool { + return a == 1 + }), MatchedBy(func(b int) bool { + return b == 2 + }), MatchedBy(func(c int) bool { + return c == 3 + })).Return(0, nil) + + assert.Panics(t, func() { + mockedService.TheExampleMethod(1, 2, 4) + }) + assert.Panics(t, func() { + mockedService.TheExampleMethod(2, 2, 3) + }) + assert.NotPanics(t, func() { + mockedService.TheExampleMethod(1, 2, 3) + }) +} + +func Test_Mock_On_WithArgMatcherThatPanics(t *testing.T) { + var mockedService TestExampleImplementation + + mockedService.On("TheExampleMethod2", MatchedBy(func(_ interface{}) bool { + panic("try to lock mockedService") + })).Return() + + defer func() { + assertedExpectations := make(chan struct{}) + go func() { + tt := new(testing.T) + mockedService.AssertExpectations(tt) + close(assertedExpectations) + }() + select { + case <-assertedExpectations: + case <-time.After(time.Second): + t.Fatal("AssertExpectations() deadlocked, did the panic leave mockedService locked?") + } + }() + + assert.Panics(t, func() { + mockedService.TheExampleMethod2(false) + }) +} + +func TestMock_WithTest(t *testing.T) { + var ( + mockedService TestExampleImplementation + mockedTest MockTestingT + ) + + mockedService.Test(&mockedTest) + mockedService.On("TheExampleMethod", 1, 2, 3).Return(0, nil) + + // Test that on an expected call, the test was not failed + + mockedService.TheExampleMethod(1, 2, 3) + + // Assert that Errorf and FailNow were not called + assert.Equal(t, 0, mockedTest.errorfCount) + assert.Equal(t, 0, mockedTest.failNowCount) + + // Test that on unexpected call, the mocked test was called to fail the test + + assert.PanicsWithValue(t, mockTestingTFailNowCalled, func() { + mockedService.TheExampleMethod(1, 1, 1) + }) + + // Assert that Errorf and FailNow were called once + assert.Equal(t, 1, mockedTest.errorfCount) + assert.Equal(t, 1, mockedTest.failNowCount) +} + +func Test_Mock_On_WithPtrArgMatcher(t *testing.T) { + var mockedService TestExampleImplementation + + mockedService.On("TheExampleMethod3", + MatchedBy(func(a *ExampleType) bool { return a != nil && a.ran == true }), + ).Return(nil) + + mockedService.On("TheExampleMethod3", + MatchedBy(func(a *ExampleType) bool { return a != nil && a.ran == false }), + ).Return(errors.New("error")) + + mockedService.On("TheExampleMethod3", + MatchedBy(func(a *ExampleType) bool { return a == nil }), + ).Return(errors.New("error2")) + + assert.Equal(t, mockedService.TheExampleMethod3(&ExampleType{true}), nil) + assert.EqualError(t, mockedService.TheExampleMethod3(&ExampleType{false}), "error") + assert.EqualError(t, mockedService.TheExampleMethod3(nil), "error2") +} + +func Test_Mock_On_WithFuncArgMatcher(t *testing.T) { + var mockedService TestExampleImplementation + + fixture1, fixture2 := errors.New("fixture1"), errors.New("fixture2") + + mockedService.On("TheExampleMethodFunc", + MatchedBy(func(a func(string) error) bool { return a != nil && a("string") == fixture1 }), + ).Return(errors.New("fixture1")) + + mockedService.On("TheExampleMethodFunc", + MatchedBy(func(a func(string) error) bool { return a != nil && a("string") == fixture2 }), + ).Return(errors.New("fixture2")) + + mockedService.On("TheExampleMethodFunc", + MatchedBy(func(a func(string) error) bool { return a == nil }), + ).Return(errors.New("fixture3")) + + assert.EqualError(t, mockedService.TheExampleMethodFunc( + func(string) error { return fixture1 }), "fixture1") + assert.EqualError(t, mockedService.TheExampleMethodFunc( + func(string) error { return fixture2 }), "fixture2") + assert.EqualError(t, mockedService.TheExampleMethodFunc(nil), "fixture3") +} + +func Test_Mock_On_WithInterfaceArgMatcher(t *testing.T) { + var mockedService TestExampleImplementation + + mockedService.On("TheExampleMethod4", + MatchedBy(func(a ExampleInterface) bool { return a == nil }), + ).Return(errors.New("fixture1")) + + assert.EqualError(t, mockedService.TheExampleMethod4(nil), "fixture1") +} + +func Test_Mock_On_WithChannelArgMatcher(t *testing.T) { + var mockedService TestExampleImplementation + + mockedService.On("TheExampleMethod5", + MatchedBy(func(ch chan struct{}) bool { return ch == nil }), + ).Return(errors.New("fixture1")) + + assert.EqualError(t, mockedService.TheExampleMethod5(nil), "fixture1") +} + +func Test_Mock_On_WithMapArgMatcher(t *testing.T) { + var mockedService TestExampleImplementation + + mockedService.On("TheExampleMethod6", + MatchedBy(func(m map[string]bool) bool { return m == nil }), + ).Return(errors.New("fixture1")) + + assert.EqualError(t, mockedService.TheExampleMethod6(nil), "fixture1") +} + +func Test_Mock_On_WithSliceArgMatcher(t *testing.T) { + var mockedService TestExampleImplementation + + mockedService.On("TheExampleMethod7", + MatchedBy(func(slice []bool) bool { return slice == nil }), + ).Return(errors.New("fixture1")) + + assert.EqualError(t, mockedService.TheExampleMethod7(nil), "fixture1") +} + +func Test_Mock_On_WithVariadicFunc(t *testing.T) { + + // make a test impl object + var mockedService = new(TestExampleImplementation) + + c := mockedService. + On("TheExampleMethodVariadic", []int{1, 2, 3}). + Return(nil) + + assert.Equal(t, []*Call{c}, mockedService.ExpectedCalls) + assert.Equal(t, 1, len(c.Arguments)) + assert.Equal(t, []int{1, 2, 3}, c.Arguments[0]) + + assert.NotPanics(t, func() { + mockedService.TheExampleMethodVariadic(1, 2, 3) + }) + assert.Panics(t, func() { + mockedService.TheExampleMethodVariadic(1, 2) + }) + +} + +func Test_Mock_On_WithMixedVariadicFunc(t *testing.T) { + + // make a test impl object + var mockedService = new(TestExampleImplementation) + + c := mockedService. + On("TheExampleMethodMixedVariadic", 1, []int{2, 3, 4}). + Return(nil) + + assert.Equal(t, []*Call{c}, mockedService.ExpectedCalls) + assert.Equal(t, 2, len(c.Arguments)) + assert.Equal(t, 1, c.Arguments[0]) + assert.Equal(t, []int{2, 3, 4}, c.Arguments[1]) + + assert.NotPanics(t, func() { + mockedService.TheExampleMethodMixedVariadic(1, 2, 3, 4) + }) + assert.Panics(t, func() { + mockedService.TheExampleMethodMixedVariadic(1, 2, 3, 5) + }) + +} + +func Test_Mock_On_WithVariadicFuncWithInterface(t *testing.T) { + + // make a test impl object + var mockedService = new(TestExampleImplementation) + + c := mockedService.On("TheExampleMethodVariadicInterface", []interface{}{1, 2, 3}). + Return(nil) + + assert.Equal(t, []*Call{c}, mockedService.ExpectedCalls) + assert.Equal(t, 1, len(c.Arguments)) + assert.Equal(t, []interface{}{1, 2, 3}, c.Arguments[0]) + + assert.NotPanics(t, func() { + mockedService.TheExampleMethodVariadicInterface(1, 2, 3) + }) + assert.Panics(t, func() { + mockedService.TheExampleMethodVariadicInterface(1, 2) + }) + +} + +func Test_Mock_On_WithVariadicFuncWithEmptyInterfaceArray(t *testing.T) { + + // make a test impl object + var mockedService = new(TestExampleImplementation) + + var expected []interface{} + c := mockedService. + On("TheExampleMethodVariadicInterface", expected). + Return(nil) + + assert.Equal(t, []*Call{c}, mockedService.ExpectedCalls) + assert.Equal(t, 1, len(c.Arguments)) + assert.Equal(t, expected, c.Arguments[0]) + + assert.NotPanics(t, func() { + mockedService.TheExampleMethodVariadicInterface() + }) + assert.Panics(t, func() { + mockedService.TheExampleMethodVariadicInterface(1, 2) + }) + +} + +func Test_Mock_On_WithFuncPanics(t *testing.T) { + // make a test impl object + var mockedService = new(TestExampleImplementation) + + assert.Panics(t, func() { + mockedService.On("TheExampleMethodFunc", func(string) error { return nil }) + }) +} + +func Test_Mock_On_WithFuncTypeArg(t *testing.T) { + + // make a test impl object + var mockedService = new(TestExampleImplementation) + + c := mockedService. + On("TheExampleMethodFuncType", AnythingOfType("mock.ExampleFuncType")). + Return(nil) + + assert.Equal(t, []*Call{c}, mockedService.ExpectedCalls) + assert.Equal(t, 1, len(c.Arguments)) + assert.Equal(t, AnythingOfType("mock.ExampleFuncType"), c.Arguments[0]) + + fn := func(string) error { return nil } + assert.NotPanics(t, func() { + mockedService.TheExampleMethodFuncType(fn) + }) +} + +func Test_Mock_Unset(t *testing.T) { + // make a test impl object + var mockedService = new(TestExampleImplementation) + + call := mockedService. + On("TheExampleMethodFuncType", "argA"). + Return("blah") + + found, foundCall := mockedService.findExpectedCall("TheExampleMethodFuncType", "argA") + require.NotEqual(t, -1, found) + require.Equal(t, foundCall, call) + + call.Unset() + + found, foundCall = mockedService.findExpectedCall("TheExampleMethodFuncType", "argA") + require.Equal(t, -1, found) + + var expectedCall *Call + require.Equal(t, expectedCall, foundCall) + + fn := func(string) error { return nil } + assert.Panics(t, func() { + mockedService.TheExampleMethodFuncType(fn) + }) +} + +// Since every time you call On it creates a new object +// the last time you call Unset it will only unset the last call +func Test_Mock_Chained_UnsetOnlyUnsetsLastCall(t *testing.T) { + // make a test impl object + var mockedService = new(TestExampleImplementation) + + // determine our current line number so we can assert the expected calls callerInfo properly + _, filename, line, _ := runtime.Caller(0) + mockedService. + On("TheExampleMethod1", 1, 1). + Return(0). + On("TheExampleMethod2", 2, 2). + On("TheExampleMethod3", 3, 3, 3). + Return(nil). + Unset() + + expectedCalls := []*Call{ + { + Parent: &mockedService.Mock, + Method: "TheExampleMethod1", + Arguments: []interface{}{1, 1}, + ReturnArguments: []interface{}{0}, + callerInfo: []string{fmt.Sprintf("%s:%d", filename, line+2)}, + }, + { + Parent: &mockedService.Mock, + Method: "TheExampleMethod2", + Arguments: []interface{}{2, 2}, + ReturnArguments: []interface{}{}, + callerInfo: []string{fmt.Sprintf("%s:%d", filename, line+4)}, + }, + } + assert.Equal(t, 2, len(expectedCalls)) + assert.Equal(t, expectedCalls, mockedService.ExpectedCalls) +} + +func Test_Mock_UnsetIfAlreadyUnsetFails(t *testing.T) { + // make a test impl object + var mockedService = new(TestExampleImplementation) + + mock1 := mockedService. + On("TheExampleMethod1", 1, 1). + Return(1) + + assert.Equal(t, 1, len(mockedService.ExpectedCalls)) + mock1.Unset() + assert.Equal(t, 0, len(mockedService.ExpectedCalls)) + + assert.Panics(t, func() { + mock1.Unset() + }) + + assert.Equal(t, 0, len(mockedService.ExpectedCalls)) +} + +func Test_Mock_UnsetByOnMethodSpec(t *testing.T) { + // make a test impl object + var mockedService = new(TestExampleImplementation) + + mock1 := mockedService. + On("TheExampleMethod", 1, 2, 3). + Return(0, nil) + + assert.Equal(t, 1, len(mockedService.ExpectedCalls)) + mock1.On("TheExampleMethod", 1, 2, 3). + Return(0, nil).Unset() + + assert.Equal(t, 0, len(mockedService.ExpectedCalls)) + + assert.Panics(t, func() { + mock1.Unset() + }) + + assert.Equal(t, 0, len(mockedService.ExpectedCalls)) +} + +func Test_Mock_UnsetByOnMethodSpecAmongOthers(t *testing.T) { + // make a test impl object + var mockedService = new(TestExampleImplementation) + + _, filename, line, _ := runtime.Caller(0) + mock1 := mockedService. + On("TheExampleMethod", 1, 2, 3). + Return(0, nil). + On("TheExampleMethodVariadic", 1, 2, 3, 4, 5).Once(). + Return(nil) + mock1. + On("TheExampleMethodFuncType", Anything). + Return(nil) + + assert.Equal(t, 3, len(mockedService.ExpectedCalls)) + mock1.On("TheExampleMethod", 1, 2, 3). + Return(0, nil).Unset() + + assert.Equal(t, 2, len(mockedService.ExpectedCalls)) + + expectedCalls := []*Call{ + { + Parent: &mockedService.Mock, + Method: "TheExampleMethodVariadic", + Repeatability: 1, + Arguments: []interface{}{1, 2, 3, 4, 5}, + ReturnArguments: []interface{}{nil}, + callerInfo: []string{fmt.Sprintf("%s:%d", filename, line+4)}, + }, + { + Parent: &mockedService.Mock, + Method: "TheExampleMethodFuncType", + Arguments: []interface{}{Anything}, + ReturnArguments: []interface{}{nil}, + callerInfo: []string{fmt.Sprintf("%s:%d", filename, line+7)}, + }, + } + + assert.Equal(t, 2, len(mockedService.ExpectedCalls)) + assert.Equal(t, expectedCalls, mockedService.ExpectedCalls) +} + +func Test_Mock_Unset_WithFuncPanics(t *testing.T) { + // make a test impl object + var mockedService = new(TestExampleImplementation) + mock1 := mockedService.On("TheExampleMethod", 1) + mock1.Arguments = append(mock1.Arguments, func(string) error { return nil }) + + assert.Panics(t, func() { + mock1.Unset() + }) +} + +func Test_Mock_Return(t *testing.T) { + + // make a test impl object + var mockedService = new(TestExampleImplementation) + + c := mockedService. + On("TheExampleMethod", "A", "B", true). + Return(1, "two", true) + + require.Equal(t, []*Call{c}, mockedService.ExpectedCalls) + + call := mockedService.ExpectedCalls[0] + + assert.Equal(t, "TheExampleMethod", call.Method) + assert.Equal(t, "A", call.Arguments[0]) + assert.Equal(t, "B", call.Arguments[1]) + assert.Equal(t, true, call.Arguments[2]) + assert.Equal(t, 1, call.ReturnArguments[0]) + assert.Equal(t, "two", call.ReturnArguments[1]) + assert.Equal(t, true, call.ReturnArguments[2]) + assert.Equal(t, 0, call.Repeatability) + assert.Nil(t, call.WaitFor) +} + +func Test_Mock_Panic(t *testing.T) { + + // make a test impl object + var mockedService = new(TestExampleImplementation) + + c := mockedService. + On("TheExampleMethod", "A", "B", true). + Panic("panic message for example method") + + require.Equal(t, []*Call{c}, mockedService.ExpectedCalls) + + call := mockedService.ExpectedCalls[0] + + assert.Equal(t, "TheExampleMethod", call.Method) + assert.Equal(t, "A", call.Arguments[0]) + assert.Equal(t, "B", call.Arguments[1]) + assert.Equal(t, true, call.Arguments[2]) + assert.Equal(t, 0, call.Repeatability) + assert.Equal(t, 0, call.Repeatability) + assert.Equal(t, "panic message for example method", *call.PanicMsg) + assert.Nil(t, call.WaitFor) +} + +func Test_Mock_Return_WaitUntil(t *testing.T) { + + // make a test impl object + var mockedService = new(TestExampleImplementation) + ch := time.After(time.Second) + + c := mockedService.Mock. + On("TheExampleMethod", "A", "B", true). + WaitUntil(ch). + Return(1, "two", true) + + // assert that the call was created + require.Equal(t, []*Call{c}, mockedService.ExpectedCalls) + + call := mockedService.ExpectedCalls[0] + + assert.Equal(t, "TheExampleMethod", call.Method) + assert.Equal(t, "A", call.Arguments[0]) + assert.Equal(t, "B", call.Arguments[1]) + assert.Equal(t, true, call.Arguments[2]) + assert.Equal(t, 1, call.ReturnArguments[0]) + assert.Equal(t, "two", call.ReturnArguments[1]) + assert.Equal(t, true, call.ReturnArguments[2]) + assert.Equal(t, 0, call.Repeatability) + assert.Equal(t, ch, call.WaitFor) +} + +func Test_Mock_Return_After(t *testing.T) { + + // make a test impl object + var mockedService = new(TestExampleImplementation) + + c := mockedService.Mock. + On("TheExampleMethod", "A", "B", true). + Return(1, "two", true). + After(time.Second) + + require.Equal(t, []*Call{c}, mockedService.ExpectedCalls) + + call := mockedService.Mock.ExpectedCalls[0] + + assert.Equal(t, "TheExampleMethod", call.Method) + assert.Equal(t, "A", call.Arguments[0]) + assert.Equal(t, "B", call.Arguments[1]) + assert.Equal(t, true, call.Arguments[2]) + assert.Equal(t, 1, call.ReturnArguments[0]) + assert.Equal(t, "two", call.ReturnArguments[1]) + assert.Equal(t, true, call.ReturnArguments[2]) + assert.Equal(t, 0, call.Repeatability) + assert.NotEqual(t, nil, call.WaitFor) + +} + +func Test_Mock_Return_Run(t *testing.T) { + + // make a test impl object + var mockedService = new(TestExampleImplementation) + + fn := func(args Arguments) { + arg := args.Get(0).(*ExampleType) + arg.ran = true + } + + c := mockedService.Mock. + On("TheExampleMethod3", AnythingOfType("*mock.ExampleType")). + Return(nil). + Run(fn) + + require.Equal(t, []*Call{c}, mockedService.ExpectedCalls) + + call := mockedService.Mock.ExpectedCalls[0] + + assert.Equal(t, "TheExampleMethod3", call.Method) + assert.Equal(t, AnythingOfType("*mock.ExampleType"), call.Arguments[0]) + assert.Equal(t, nil, call.ReturnArguments[0]) + assert.Equal(t, 0, call.Repeatability) + assert.NotEqual(t, nil, call.WaitFor) + assert.NotNil(t, call.Run) + + et := ExampleType{} + assert.Equal(t, false, et.ran) + mockedService.TheExampleMethod3(&et) + assert.Equal(t, true, et.ran) +} + +func Test_Mock_Return_Run_Out_Of_Order(t *testing.T) { + // make a test impl object + var mockedService = new(TestExampleImplementation) + f := func(args Arguments) { + arg := args.Get(0).(*ExampleType) + arg.ran = true + } + + c := mockedService.Mock. + On("TheExampleMethod3", AnythingOfType("*mock.ExampleType")). + Run(f). + Return(nil) + + require.Equal(t, []*Call{c}, mockedService.ExpectedCalls) + + call := mockedService.Mock.ExpectedCalls[0] + + assert.Equal(t, "TheExampleMethod3", call.Method) + assert.Equal(t, AnythingOfType("*mock.ExampleType"), call.Arguments[0]) + assert.Equal(t, nil, call.ReturnArguments[0]) + assert.Equal(t, 0, call.Repeatability) + assert.NotEqual(t, nil, call.WaitFor) + assert.NotNil(t, call.Run) +} + +func Test_Mock_Return_Once(t *testing.T) { + + // make a test impl object + var mockedService = new(TestExampleImplementation) + + c := mockedService.On("TheExampleMethod", "A", "B", true). + Return(1, "two", true). + Once() + + require.Equal(t, []*Call{c}, mockedService.ExpectedCalls) + + call := mockedService.ExpectedCalls[0] + + assert.Equal(t, "TheExampleMethod", call.Method) + assert.Equal(t, "A", call.Arguments[0]) + assert.Equal(t, "B", call.Arguments[1]) + assert.Equal(t, true, call.Arguments[2]) + assert.Equal(t, 1, call.ReturnArguments[0]) + assert.Equal(t, "two", call.ReturnArguments[1]) + assert.Equal(t, true, call.ReturnArguments[2]) + assert.Equal(t, 1, call.Repeatability) + assert.Nil(t, call.WaitFor) +} + +func Test_Mock_Return_Twice(t *testing.T) { + + // make a test impl object + var mockedService = new(TestExampleImplementation) + + c := mockedService. + On("TheExampleMethod", "A", "B", true). + Return(1, "two", true). + Twice() + + require.Equal(t, []*Call{c}, mockedService.ExpectedCalls) + + call := mockedService.ExpectedCalls[0] + + assert.Equal(t, "TheExampleMethod", call.Method) + assert.Equal(t, "A", call.Arguments[0]) + assert.Equal(t, "B", call.Arguments[1]) + assert.Equal(t, true, call.Arguments[2]) + assert.Equal(t, 1, call.ReturnArguments[0]) + assert.Equal(t, "two", call.ReturnArguments[1]) + assert.Equal(t, true, call.ReturnArguments[2]) + assert.Equal(t, 2, call.Repeatability) + assert.Nil(t, call.WaitFor) +} + +func Test_Mock_Return_Times(t *testing.T) { + + // make a test impl object + var mockedService = new(TestExampleImplementation) + + c := mockedService. + On("TheExampleMethod", "A", "B", true). + Return(1, "two", true). + Times(5) + + require.Equal(t, []*Call{c}, mockedService.ExpectedCalls) + + call := mockedService.ExpectedCalls[0] + + assert.Equal(t, "TheExampleMethod", call.Method) + assert.Equal(t, "A", call.Arguments[0]) + assert.Equal(t, "B", call.Arguments[1]) + assert.Equal(t, true, call.Arguments[2]) + assert.Equal(t, 1, call.ReturnArguments[0]) + assert.Equal(t, "two", call.ReturnArguments[1]) + assert.Equal(t, true, call.ReturnArguments[2]) + assert.Equal(t, 5, call.Repeatability) + assert.Nil(t, call.WaitFor) +} + +func Test_Mock_Return_Nothing(t *testing.T) { + + // make a test impl object + var mockedService = new(TestExampleImplementation) + + c := mockedService. + On("TheExampleMethod", "A", "B", true). + Return() + + require.Equal(t, []*Call{c}, mockedService.ExpectedCalls) + + call := mockedService.ExpectedCalls[0] + + assert.Equal(t, "TheExampleMethod", call.Method) + assert.Equal(t, "A", call.Arguments[0]) + assert.Equal(t, "B", call.Arguments[1]) + assert.Equal(t, true, call.Arguments[2]) + assert.Equal(t, 0, len(call.ReturnArguments)) +} + +func Test_Mock_Return_NotBefore_In_Order(t *testing.T) { + var mockedService = new(TestExampleImplementation) + + b := mockedService. + On("TheExampleMethod", 1, 2, 3). + Return(4, nil) + c := mockedService. + On("TheExampleMethod2", true). + Return(). + NotBefore(b) + + require.Equal(t, []*Call{b, c}, mockedService.ExpectedCalls) + require.NotPanics(t, func() { + mockedService.TheExampleMethod(1, 2, 3) + }) + require.NotPanics(t, func() { + mockedService.TheExampleMethod2(true) + }) +} + +func Test_Mock_Return_NotBefore_Out_Of_Order(t *testing.T) { + var mockedService = new(TestExampleImplementation) + + b := mockedService. + On("TheExampleMethod", 1, 2, 3). + Return(4, nil).Twice() + c := mockedService. + On("TheExampleMethod2", true). + Return(). + NotBefore(b) + + require.Equal(t, []*Call{b, c}, mockedService.ExpectedCalls) + + expectedPanicString := `mock: Unexpected Method Call +----------------------------- + +TheExampleMethod2(bool) + 0: true + +Must not be called before: + +TheExampleMethod(int,int,int) + 0: 1 + 1: 2 + 2: 3` + require.PanicsWithValue(t, expectedPanicString, func() { + mockedService.TheExampleMethod2(true) + }) +} + +func Test_Mock_Return_NotBefore_Not_Enough_Times(t *testing.T) { + var mockedService = new(TestExampleImplementation) + + b := mockedService. + On("TheExampleMethod", 1, 2, 3). + Return(4, nil).Twice() + c := mockedService. + On("TheExampleMethod2", true). + Return(). + NotBefore(b) + + require.Equal(t, []*Call{b, c}, mockedService.ExpectedCalls) + + require.NotPanics(t, func() { + mockedService.TheExampleMethod(1, 2, 3) + }) + expectedPanicString := `mock: Unexpected Method Call +----------------------------- + +TheExampleMethod2(bool) + 0: true + +Must not be called before another call of: + +TheExampleMethod(int,int,int) + 0: 1 + 1: 2 + 2: 3` + require.PanicsWithValue(t, expectedPanicString, func() { + mockedService.TheExampleMethod2(true) + }) +} + +func Test_Mock_Return_NotBefore_Different_Mock_In_Order(t *testing.T) { + var ( + mockedService1 = new(TestExampleImplementation) + mockedService2 = new(TestExampleImplementation) + ) + + b := mockedService1. + On("TheExampleMethod", 1, 2, 3). + Return(4, nil) + c := mockedService2. + On("TheExampleMethod2", true). + Return(). + NotBefore(b) + + require.Equal(t, []*Call{c}, mockedService2.ExpectedCalls) + require.NotPanics(t, func() { + mockedService1.TheExampleMethod(1, 2, 3) + }) + require.NotPanics(t, func() { + mockedService2.TheExampleMethod2(true) + }) +} +func Test_Mock_Return_NotBefore_Different_Mock_Out_Of_Order(t *testing.T) { + var ( + mockedService1 = new(TestExampleImplementation) + mockedService2 = new(TestExampleImplementation) + ) + + b := mockedService1. + On("TheExampleMethod", 1, 2, 3). + Return(4, nil) + c := mockedService2. + On("TheExampleMethod2", true). + Return(). + NotBefore(b) + + require.Equal(t, []*Call{c}, mockedService2.ExpectedCalls) + + expectedPanicString := `mock: Unexpected Method Call +----------------------------- + +TheExampleMethod2(bool) + 0: true + +Must not be called before method from another mock instance: + +TheExampleMethod(int,int,int) + 0: 1 + 1: 2 + 2: 3` + require.PanicsWithValue(t, expectedPanicString, func() { + mockedService2.TheExampleMethod2(true) + }) +} + +func Test_Mock_Return_NotBefore_In_Order_With_Non_Dependant(t *testing.T) { + var mockedService = new(TestExampleImplementation) + + a := mockedService. + On("TheExampleMethod", 1, 2, 3). + Return(4, nil) + b := mockedService. + On("TheExampleMethod", 4, 5, 6). + Return(4, nil) + c := mockedService. + On("TheExampleMethod2", true). + Return(). + NotBefore(a, b) + d := mockedService. + On("TheExampleMethod7", []bool{}).Return(nil) + + require.Equal(t, []*Call{a, b, c, d}, mockedService.ExpectedCalls) + require.NotPanics(t, func() { + mockedService.TheExampleMethod7([]bool{}) + }) + require.NotPanics(t, func() { + mockedService.TheExampleMethod(1, 2, 3) + }) + require.NotPanics(t, func() { + mockedService.TheExampleMethod7([]bool{}) + }) + require.NotPanics(t, func() { + mockedService.TheExampleMethod(4, 5, 6) + }) + require.NotPanics(t, func() { + mockedService.TheExampleMethod7([]bool{}) + }) + require.NotPanics(t, func() { + mockedService.TheExampleMethod2(true) + }) + require.NotPanics(t, func() { + mockedService.TheExampleMethod7([]bool{}) + }) +} + +func Test_Mock_Return_NotBefore_Orphan_Call(t *testing.T) { + var mockedService = new(TestExampleImplementation) + + require.PanicsWithValue(t, "not before calls must be created with Mock.On()", func() { + mockedService. + On("TheExampleMethod2", true). + Return(). + NotBefore(&Call{Method: "Not", Arguments: Arguments{"how", "it's"}, ReturnArguments: Arguments{"done"}}) + }) +} + +func Test_Mock_findExpectedCall(t *testing.T) { + + m := new(Mock) + m.On("One", 1).Return("one") + m.On("Two", 2).Return("two") + m.On("Two", 3).Return("three") + + f, c := m.findExpectedCall("Two", 3) + + if assert.Equal(t, 2, f) { + if assert.NotNil(t, c) { + assert.Equal(t, "Two", c.Method) + assert.Equal(t, 3, c.Arguments[0]) + assert.Equal(t, "three", c.ReturnArguments[0]) + } + } + +} + +func Test_Mock_findExpectedCall_For_Unknown_Method(t *testing.T) { + + m := new(Mock) + m.On("One", 1).Return("one") + m.On("Two", 2).Return("two") + m.On("Two", 3).Return("three") + + f, _ := m.findExpectedCall("Two") + + assert.Equal(t, -1, f) + +} + +func Test_Mock_findExpectedCall_Respects_Repeatability(t *testing.T) { + + m := new(Mock) + m.On("One", 1).Return("one") + m.On("Two", 2).Return("two").Once() + m.On("Two", 3).Return("three").Twice() + m.On("Two", 3).Return("three").Times(8) + + f, c := m.findExpectedCall("Two", 3) + + if assert.Equal(t, 2, f) { + if assert.NotNil(t, c) { + assert.Equal(t, "Two", c.Method) + assert.Equal(t, 3, c.Arguments[0]) + assert.Equal(t, "three", c.ReturnArguments[0]) + } + } + + c = m.On("Once", 1).Return("one").Once() + c.Repeatability = -1 + f, c = m.findExpectedCall("Once", 1) + if assert.Equal(t, -1, f) { + if assert.NotNil(t, c) { + assert.Equal(t, "Once", c.Method) + assert.Equal(t, 1, c.Arguments[0]) + assert.Equal(t, "one", c.ReturnArguments[0]) + } + } +} + +func Test_callString(t *testing.T) { + + assert.Equal(t, `Method(int,bool,string)`, callString("Method", []interface{}{1, true, "something"}, false)) + assert.Equal(t, `Method(<nil>)`, callString("Method", []interface{}{nil}, false)) + +} + +func Test_Mock_Called(t *testing.T) { + + var mockedService = new(TestExampleImplementation) + + mockedService.On("Test_Mock_Called", 1, 2, 3).Return(5, "6", true) + + returnArguments := mockedService.Called(1, 2, 3) + + if assert.Equal(t, 1, len(mockedService.Calls)) { + assert.Equal(t, "Test_Mock_Called", mockedService.Calls[0].Method) + assert.Equal(t, 1, mockedService.Calls[0].Arguments[0]) + assert.Equal(t, 2, mockedService.Calls[0].Arguments[1]) + assert.Equal(t, 3, mockedService.Calls[0].Arguments[2]) + } + + if assert.Equal(t, 3, len(returnArguments)) { + assert.Equal(t, 5, returnArguments[0]) + assert.Equal(t, "6", returnArguments[1]) + assert.Equal(t, true, returnArguments[2]) + } + +} + +func asyncCall(m *Mock, ch chan Arguments) { + ch <- m.Called(1, 2, 3) +} + +func Test_Mock_Called_blocks(t *testing.T) { + + var mockedService = new(TestExampleImplementation) + + mockedService.Mock.On("asyncCall", 1, 2, 3).Return(5, "6", true).After(2 * time.Millisecond) + + ch := make(chan Arguments) + + go asyncCall(&mockedService.Mock, ch) + + select { + case <-ch: + t.Fatal("should have waited") + case <-time.After(1 * time.Millisecond): + } + + returnArguments := <-ch + + if assert.Equal(t, 1, len(mockedService.Mock.Calls)) { + assert.Equal(t, "asyncCall", mockedService.Mock.Calls[0].Method) + assert.Equal(t, 1, mockedService.Mock.Calls[0].Arguments[0]) + assert.Equal(t, 2, mockedService.Mock.Calls[0].Arguments[1]) + assert.Equal(t, 3, mockedService.Mock.Calls[0].Arguments[2]) + } + + if assert.Equal(t, 3, len(returnArguments)) { + assert.Equal(t, 5, returnArguments[0]) + assert.Equal(t, "6", returnArguments[1]) + assert.Equal(t, true, returnArguments[2]) + } + +} + +func Test_Mock_Called_For_Bounded_Repeatability(t *testing.T) { + + var mockedService = new(TestExampleImplementation) + + mockedService. + On("Test_Mock_Called_For_Bounded_Repeatability", 1, 2, 3). + Return(5, "6", true). + Once() + mockedService. + On("Test_Mock_Called_For_Bounded_Repeatability", 1, 2, 3). + Return(-1, "hi", false) + + returnArguments1 := mockedService.Called(1, 2, 3) + returnArguments2 := mockedService.Called(1, 2, 3) + + if assert.Equal(t, 2, len(mockedService.Calls)) { + assert.Equal(t, "Test_Mock_Called_For_Bounded_Repeatability", mockedService.Calls[0].Method) + assert.Equal(t, 1, mockedService.Calls[0].Arguments[0]) + assert.Equal(t, 2, mockedService.Calls[0].Arguments[1]) + assert.Equal(t, 3, mockedService.Calls[0].Arguments[2]) + + assert.Equal(t, "Test_Mock_Called_For_Bounded_Repeatability", mockedService.Calls[1].Method) + assert.Equal(t, 1, mockedService.Calls[1].Arguments[0]) + assert.Equal(t, 2, mockedService.Calls[1].Arguments[1]) + assert.Equal(t, 3, mockedService.Calls[1].Arguments[2]) + } + + if assert.Equal(t, 3, len(returnArguments1)) { + assert.Equal(t, 5, returnArguments1[0]) + assert.Equal(t, "6", returnArguments1[1]) + assert.Equal(t, true, returnArguments1[2]) + } + + if assert.Equal(t, 3, len(returnArguments2)) { + assert.Equal(t, -1, returnArguments2[0]) + assert.Equal(t, "hi", returnArguments2[1]) + assert.Equal(t, false, returnArguments2[2]) + } + +} + +func Test_Mock_Called_For_SetTime_Expectation(t *testing.T) { + + var mockedService = new(TestExampleImplementation) + + mockedService.On("TheExampleMethod", 1, 2, 3).Return(5, "6", true).Times(4) + + mockedService.TheExampleMethod(1, 2, 3) + mockedService.TheExampleMethod(1, 2, 3) + mockedService.TheExampleMethod(1, 2, 3) + mockedService.TheExampleMethod(1, 2, 3) + assert.Panics(t, func() { + mockedService.TheExampleMethod(1, 2, 3) + }) + +} + +func Test_Mock_Called_Unexpected(t *testing.T) { + + var mockedService = new(TestExampleImplementation) + + // make sure it panics if no expectation was made + assert.Panics(t, func() { + mockedService.Called(1, 2, 3) + }, "Calling unexpected method should panic") + +} + +func Test_AssertExpectationsForObjects_Helper(t *testing.T) { + + var mockedService1 = new(TestExampleImplementation) + var mockedService2 = new(TestExampleImplementation) + var mockedService3 = new(TestExampleImplementation) + var mockedService4 = new(TestExampleImplementation) // No expectations does not cause a panic + + mockedService1.On("Test_AssertExpectationsForObjects_Helper", 1).Return() + mockedService2.On("Test_AssertExpectationsForObjects_Helper", 2).Return() + mockedService3.On("Test_AssertExpectationsForObjects_Helper", 3).Return() + + mockedService1.Called(1) + mockedService2.Called(2) + mockedService3.Called(3) + + assert.True(t, AssertExpectationsForObjects(t, &mockedService1.Mock, &mockedService2.Mock, &mockedService3.Mock, &mockedService4.Mock)) + assert.True(t, AssertExpectationsForObjects(t, mockedService1, mockedService2, mockedService3, mockedService4)) + +} + +func Test_AssertExpectationsForObjects_Helper_Failed(t *testing.T) { + + var mockedService1 = new(TestExampleImplementation) + var mockedService2 = new(TestExampleImplementation) + var mockedService3 = new(TestExampleImplementation) + + mockedService1.On("Test_AssertExpectationsForObjects_Helper_Failed", 1).Return() + mockedService2.On("Test_AssertExpectationsForObjects_Helper_Failed", 2).Return() + mockedService3.On("Test_AssertExpectationsForObjects_Helper_Failed", 3).Return() + + mockedService1.Called(1) + mockedService3.Called(3) + + tt := new(testing.T) + assert.False(t, AssertExpectationsForObjects(tt, &mockedService1.Mock, &mockedService2.Mock, &mockedService3.Mock)) + assert.False(t, AssertExpectationsForObjects(tt, mockedService1, mockedService2, mockedService3)) + +} + +func Test_Mock_AssertExpectations(t *testing.T) { + + var mockedService = new(TestExampleImplementation) + + mockedService.On("Test_Mock_AssertExpectations", 1, 2, 3).Return(5, 6, 7) + + tt := new(testing.T) + assert.False(t, mockedService.AssertExpectations(tt)) + + // make the call now + mockedService.Called(1, 2, 3) + + // now assert expectations + assert.True(t, mockedService.AssertExpectations(tt)) + +} + +func Test_Mock_AssertExpectations_Placeholder_NoArgs(t *testing.T) { + + var mockedService = new(TestExampleImplementation) + + mockedService.On("Test_Mock_AssertExpectations_Placeholder_NoArgs").Return(5, 6, 7).Once() + mockedService.On("Test_Mock_AssertExpectations_Placeholder_NoArgs").Return(7, 6, 5) + + tt := new(testing.T) + assert.False(t, mockedService.AssertExpectations(tt)) + + // make the call now + mockedService.Called() + + // now assert expectations + assert.True(t, mockedService.AssertExpectations(tt)) + +} + +func Test_Mock_AssertExpectations_Placeholder(t *testing.T) { + + var mockedService = new(TestExampleImplementation) + + mockedService.On("Test_Mock_AssertExpectations_Placeholder", 1, 2, 3).Return(5, 6, 7).Once() + mockedService.On("Test_Mock_AssertExpectations_Placeholder", 3, 2, 1).Return(7, 6, 5) + + tt := new(testing.T) + assert.False(t, mockedService.AssertExpectations(tt)) + + // make the call now + mockedService.Called(1, 2, 3) + + // now assert expectations + assert.False(t, mockedService.AssertExpectations(tt)) + + // make call to the second expectation + mockedService.Called(3, 2, 1) + + // now assert expectations again + assert.True(t, mockedService.AssertExpectations(tt)) +} + +func Test_Mock_AssertExpectations_With_Pointers(t *testing.T) { + + var mockedService = new(TestExampleImplementation) + + mockedService.On("Test_Mock_AssertExpectations_With_Pointers", &struct{ Foo int }{1}).Return(1) + mockedService.On("Test_Mock_AssertExpectations_With_Pointers", &struct{ Foo int }{2}).Return(2) + + tt := new(testing.T) + assert.False(t, mockedService.AssertExpectations(tt)) + + s := struct{ Foo int }{1} + // make the calls now + mockedService.Called(&s) + s.Foo = 2 + mockedService.Called(&s) + + // now assert expectations + assert.True(t, mockedService.AssertExpectations(tt)) + +} + +func Test_Mock_AssertExpectationsCustomType(t *testing.T) { + + var mockedService = new(TestExampleImplementation) + + mockedService.On("TheExampleMethod3", AnythingOfType("*mock.ExampleType")).Return(nil).Once() + + tt := new(testing.T) + assert.False(t, mockedService.AssertExpectations(tt)) + + // make the call now + mockedService.TheExampleMethod3(&ExampleType{}) + + // now assert expectations + assert.True(t, mockedService.AssertExpectations(tt)) + +} + +func Test_Mock_AssertExpectations_With_Repeatability(t *testing.T) { + + var mockedService = new(TestExampleImplementation) + + mockedService.On("Test_Mock_AssertExpectations_With_Repeatability", 1, 2, 3).Return(5, 6, 7).Twice() + + tt := new(testing.T) + assert.False(t, mockedService.AssertExpectations(tt)) + + // make the call now + mockedService.Called(1, 2, 3) + + assert.False(t, mockedService.AssertExpectations(tt)) + + mockedService.Called(1, 2, 3) + + // now assert expectations + assert.True(t, mockedService.AssertExpectations(tt)) + +} + +func Test_Mock_TwoCallsWithDifferentArguments(t *testing.T) { + + var mockedService = new(TestExampleImplementation) + + mockedService.On("Test_Mock_TwoCallsWithDifferentArguments", 1, 2, 3).Return(5, 6, 7) + mockedService.On("Test_Mock_TwoCallsWithDifferentArguments", 4, 5, 6).Return(5, 6, 7) + + args1 := mockedService.Called(1, 2, 3) + assert.Equal(t, 5, args1.Int(0)) + assert.Equal(t, 6, args1.Int(1)) + assert.Equal(t, 7, args1.Int(2)) + + args2 := mockedService.Called(4, 5, 6) + assert.Equal(t, 5, args2.Int(0)) + assert.Equal(t, 6, args2.Int(1)) + assert.Equal(t, 7, args2.Int(2)) + +} + +func Test_Mock_AssertNumberOfCalls(t *testing.T) { + + var mockedService = new(TestExampleImplementation) + + mockedService.On("Test_Mock_AssertNumberOfCalls", 1, 2, 3).Return(5, 6, 7) + + mockedService.Called(1, 2, 3) + assert.True(t, mockedService.AssertNumberOfCalls(t, "Test_Mock_AssertNumberOfCalls", 1)) + + mockedService.Called(1, 2, 3) + assert.True(t, mockedService.AssertNumberOfCalls(t, "Test_Mock_AssertNumberOfCalls", 2)) + +} + +func Test_Mock_AssertCalled(t *testing.T) { + + var mockedService = new(TestExampleImplementation) + + mockedService.On("Test_Mock_AssertCalled", 1, 2, 3).Return(5, 6, 7) + + mockedService.Called(1, 2, 3) + + assert.True(t, mockedService.AssertCalled(t, "Test_Mock_AssertCalled", 1, 2, 3)) + +} + +func Test_Mock_AssertCalled_WithAnythingOfTypeArgument(t *testing.T) { + + var mockedService = new(TestExampleImplementation) + + mockedService. + On("Test_Mock_AssertCalled_WithAnythingOfTypeArgument", Anything, Anything, Anything). + Return() + + mockedService.Called(1, "two", []uint8("three")) + + assert.True(t, mockedService.AssertCalled(t, "Test_Mock_AssertCalled_WithAnythingOfTypeArgument", AnythingOfType("int"), AnythingOfType("string"), AnythingOfType("[]uint8"))) + +} + +func Test_Mock_AssertCalled_WithArguments(t *testing.T) { + + var mockedService = new(TestExampleImplementation) + + mockedService.On("Test_Mock_AssertCalled_WithArguments", 1, 2, 3).Return(5, 6, 7) + + mockedService.Called(1, 2, 3) + + tt := new(testing.T) + assert.True(t, mockedService.AssertCalled(tt, "Test_Mock_AssertCalled_WithArguments", 1, 2, 3)) + assert.False(t, mockedService.AssertCalled(tt, "Test_Mock_AssertCalled_WithArguments", 2, 3, 4)) + +} + +func Test_Mock_AssertCalled_WithArguments_With_Repeatability(t *testing.T) { + + var mockedService = new(TestExampleImplementation) + + mockedService.On("Test_Mock_AssertCalled_WithArguments_With_Repeatability", 1, 2, 3).Return(5, 6, 7).Once() + mockedService.On("Test_Mock_AssertCalled_WithArguments_With_Repeatability", 2, 3, 4).Return(5, 6, 7).Once() + + mockedService.Called(1, 2, 3) + mockedService.Called(2, 3, 4) + + tt := new(testing.T) + assert.True(t, mockedService.AssertCalled(tt, "Test_Mock_AssertCalled_WithArguments_With_Repeatability", 1, 2, 3)) + assert.True(t, mockedService.AssertCalled(tt, "Test_Mock_AssertCalled_WithArguments_With_Repeatability", 2, 3, 4)) + assert.False(t, mockedService.AssertCalled(tt, "Test_Mock_AssertCalled_WithArguments_With_Repeatability", 3, 4, 5)) + +} + +func Test_Mock_AssertNotCalled(t *testing.T) { + + var mockedService = new(TestExampleImplementation) + + mockedService.On("Test_Mock_AssertNotCalled", 1, 2, 3).Return(5, 6, 7) + + mockedService.Called(1, 2, 3) + + assert.True(t, mockedService.AssertNotCalled(t, "Test_Mock_NotCalled")) + +} + +func Test_Mock_IsMethodCallable(t *testing.T) { + var mockedService = new(TestExampleImplementation) + + arg := []Call{{Repeatability: 1}, {Repeatability: 2}} + arg2 := []Call{{Repeatability: 1}, {Repeatability: 1}} + arg3 := []Call{{Repeatability: 1}, {Repeatability: 1}} + + mockedService.On("Test_Mock_IsMethodCallable", arg2).Return(true).Twice() + + assert.False(t, mockedService.IsMethodCallable(t, "Test_Mock_IsMethodCallable", arg)) + assert.True(t, mockedService.IsMethodCallable(t, "Test_Mock_IsMethodCallable", arg2)) + assert.True(t, mockedService.IsMethodCallable(t, "Test_Mock_IsMethodCallable", arg3)) + + mockedService.MethodCalled("Test_Mock_IsMethodCallable", arg2) + mockedService.MethodCalled("Test_Mock_IsMethodCallable", arg2) + + assert.False(t, mockedService.IsMethodCallable(t, "Test_Mock_IsMethodCallable", arg2)) +} + +func TestIsArgsEqual(t *testing.T) { + var expected = Arguments{5, 3, 4, 6, 7, 2} + var args = make([]interface{}, 5) + for i := 1; i < len(expected); i++ { + args[i-1] = expected[i] + } + args[2] = expected[1] + assert.False(t, isArgsEqual(expected, args)) + + var arr = make([]interface{}, 6) + for i := 0; i < len(expected); i++ { + arr[i] = expected[i] + } + assert.True(t, isArgsEqual(expected, arr)) +} + +func Test_Mock_AssertOptional(t *testing.T) { + // Optional called + var ms1 = new(TestExampleImplementation) + ms1.On("TheExampleMethod", 1, 2, 3).Maybe().Return(4, nil) + ms1.TheExampleMethod(1, 2, 3) + + tt1 := new(testing.T) + assert.Equal(t, true, ms1.AssertExpectations(tt1)) + + // Optional not called + var ms2 = new(TestExampleImplementation) + ms2.On("TheExampleMethod", 1, 2, 3).Maybe().Return(4, nil) + + tt2 := new(testing.T) + assert.Equal(t, true, ms2.AssertExpectations(tt2)) + + // Non-optional called + var ms3 = new(TestExampleImplementation) + ms3.On("TheExampleMethod", 1, 2, 3).Return(4, nil) + ms3.TheExampleMethod(1, 2, 3) + + tt3 := new(testing.T) + assert.Equal(t, true, ms3.AssertExpectations(tt3)) +} + +/* + Arguments helper methods +*/ +func Test_Arguments_Get(t *testing.T) { + + var args = Arguments([]interface{}{"string", 123, true}) + + assert.Equal(t, "string", args.Get(0).(string)) + assert.Equal(t, 123, args.Get(1).(int)) + assert.Equal(t, true, args.Get(2).(bool)) + +} + +func Test_Arguments_Is(t *testing.T) { + + var args = Arguments([]interface{}{"string", 123, true}) + + assert.True(t, args.Is("string", 123, true)) + assert.False(t, args.Is("wrong", 456, false)) + +} + +func Test_Arguments_Diff(t *testing.T) { + + var args = Arguments([]interface{}{"Hello World", 123, true}) + var diff string + var count int + diff, count = args.Diff([]interface{}{"Hello World", 456, "false"}) + + assert.Equal(t, 2, count) + assert.Contains(t, diff, `(int=456) != (int=123)`) + assert.Contains(t, diff, `(string=false) != (bool=true)`) + +} + +func Test_Arguments_Diff_DifferentNumberOfArgs(t *testing.T) { + + var args = Arguments([]interface{}{"string", 123, true}) + var diff string + var count int + diff, count = args.Diff([]interface{}{"string", 456, "false", "extra"}) + + assert.Equal(t, 3, count) + assert.Contains(t, diff, `(string=extra) != (Missing)`) + +} + +func Test_Arguments_Diff_WithAnythingArgument(t *testing.T) { + + var args = Arguments([]interface{}{"string", 123, true}) + var count int + _, count = args.Diff([]interface{}{"string", Anything, true}) + + assert.Equal(t, 0, count) + +} + +func Test_Arguments_Diff_WithAnythingArgument_InActualToo(t *testing.T) { + + var args = Arguments([]interface{}{"string", Anything, true}) + var count int + _, count = args.Diff([]interface{}{"string", 123, true}) + + assert.Equal(t, 0, count) + +} + +func Test_Arguments_Diff_WithAnythingOfTypeArgument(t *testing.T) { + + var args = Arguments([]interface{}{"string", AnythingOfType("int"), true}) + var count int + _, count = args.Diff([]interface{}{"string", 123, true}) + + assert.Equal(t, 0, count) + +} + +func Test_Arguments_Diff_WithAnythingOfTypeArgument_Failing(t *testing.T) { + + var args = Arguments([]interface{}{"string", AnythingOfType("string"), true}) + var count int + var diff string + diff, count = args.Diff([]interface{}{"string", 123, true}) + + assert.Equal(t, 1, count) + assert.Contains(t, diff, `string != type int - (int=123)`) + +} + +func Test_Arguments_Diff_WithIsTypeArgument(t *testing.T) { + var args = Arguments([]interface{}{"string", IsType(0), true}) + var count int + _, count = args.Diff([]interface{}{"string", 123, true}) + + assert.Equal(t, 0, count) +} + +func Test_Arguments_Diff_WithIsTypeArgument_Failing(t *testing.T) { + var args = Arguments([]interface{}{"string", IsType(""), true}) + var count int + var diff string + diff, count = args.Diff([]interface{}{"string", 123, true}) + + assert.Equal(t, 1, count) + assert.Contains(t, diff, `string != type int - (int=123)`) +} + +func Test_Arguments_Diff_WithArgMatcher(t *testing.T) { + matchFn := func(a int) bool { + return a == 123 + } + var args = Arguments([]interface{}{"string", MatchedBy(matchFn), true}) + + diff, count := args.Diff([]interface{}{"string", 124, true}) + assert.Equal(t, 1, count) + assert.Contains(t, diff, `(int=124) not matched by func(int) bool`) + + diff, count = args.Diff([]interface{}{"string", false, true}) + assert.Equal(t, 1, count) + assert.Contains(t, diff, `(bool=false) not matched by func(int) bool`) + + diff, count = args.Diff([]interface{}{"string", 123, false}) + assert.Equal(t, 1, count) + assert.Contains(t, diff, `(int=123) matched by func(int) bool`) + + diff, count = args.Diff([]interface{}{"string", 123, true}) + assert.Equal(t, 0, count) + assert.Contains(t, diff, `No differences.`) +} + +func Test_Arguments_Assert(t *testing.T) { + + var args = Arguments([]interface{}{"string", 123, true}) + + assert.True(t, args.Assert(t, "string", 123, true)) + +} + +func Test_Arguments_String_Representation(t *testing.T) { + + var args = Arguments([]interface{}{"string", 123, true}) + assert.Equal(t, `string,int,bool`, args.String()) + +} + +func Test_Arguments_String(t *testing.T) { + + var args = Arguments([]interface{}{"string", 123, true}) + assert.Equal(t, "string", args.String(0)) + +} + +func Test_Arguments_Error(t *testing.T) { + + var err = errors.New("An Error") + var args = Arguments([]interface{}{"string", 123, true, err}) + assert.Equal(t, err, args.Error(3)) + +} + +func Test_Arguments_Error_Nil(t *testing.T) { + + var args = Arguments([]interface{}{"string", 123, true, nil}) + assert.Equal(t, nil, args.Error(3)) + +} + +func Test_Arguments_Int(t *testing.T) { + + var args = Arguments([]interface{}{"string", 123, true}) + assert.Equal(t, 123, args.Int(1)) + +} + +func Test_Arguments_Bool(t *testing.T) { + + var args = Arguments([]interface{}{"string", 123, true}) + assert.Equal(t, true, args.Bool(2)) + +} + +func Test_WaitUntil_Parallel(t *testing.T) { + + // make a test impl object + var mockedService = new(TestExampleImplementation) + + ch1 := make(chan time.Time) + ch2 := make(chan time.Time) + + mockedService.Mock.On("TheExampleMethod2", true).Return().WaitUntil(ch2).Run(func(args Arguments) { + ch1 <- time.Now() + }) + + mockedService.Mock.On("TheExampleMethod2", false).Return().WaitUntil(ch1) + + // Lock both goroutines on the .WaitUntil method + go func() { + mockedService.TheExampleMethod2(false) + }() + go func() { + mockedService.TheExampleMethod2(true) + }() + + // Allow the first call to execute, so the second one executes afterwards + ch2 <- time.Now() +} + +func Test_MockMethodCalled(t *testing.T) { + m := new(Mock) + m.On("foo", "hello").Return("world") + + retArgs := m.MethodCalled("foo", "hello") + require.True(t, len(retArgs) == 1) + require.Equal(t, "world", retArgs[0]) + m.AssertExpectations(t) +} + +func Test_MockMethodCalled_Panic(t *testing.T) { + m := new(Mock) + m.On("foo", "hello").Panic("world panics") + + require.PanicsWithValue(t, "world panics", func() { m.MethodCalled("foo", "hello") }) + m.AssertExpectations(t) +} + +// Test to validate fix for racy concurrent call access in MethodCalled() +func Test_MockReturnAndCalledConcurrent(t *testing.T) { + iterations := 1000 + m := &Mock{} + call := m.On("ConcurrencyTestMethod") + + wg := sync.WaitGroup{} + wg.Add(2) + + go func() { + for i := 0; i < iterations; i++ { + call.Return(10) + } + wg.Done() + }() + go func() { + for i := 0; i < iterations; i++ { + ConcurrencyTestMethod(m) + } + wg.Done() + }() + wg.Wait() +} + +type timer struct{ Mock } + +func (s *timer) GetTime(i int) string { + return s.Called(i).Get(0).(string) +} + +func (s *timer) GetTimes(times []int) string { + return s.Called(times).Get(0).(string) +} + +type tCustomLogger struct { + *testing.T + logs []string + errs []string +} + +func (tc *tCustomLogger) Logf(format string, args ...interface{}) { + tc.T.Logf(format, args...) + tc.logs = append(tc.logs, fmt.Sprintf(format, args...)) +} + +func (tc *tCustomLogger) Errorf(format string, args ...interface{}) { + tc.errs = append(tc.errs, fmt.Sprintf(format, args...)) +} + +func (tc *tCustomLogger) FailNow() {} + +func TestLoggingAssertExpectations(t *testing.T) { + m := new(timer) + m.On("GetTime", 0).Return("") + tcl := &tCustomLogger{t, []string{}, []string{}} + + AssertExpectationsForObjects(tcl, m, new(TestExampleImplementation)) + + require.Equal(t, 1, len(tcl.errs)) + assert.Regexp(t, regexp.MustCompile("(?s)FAIL: 0 out of 1 expectation\\(s\\) were met.*The code you are testing needs to make 1 more call\\(s\\).*"), tcl.errs[0]) + require.Equal(t, 2, len(tcl.logs)) + assert.Regexp(t, regexp.MustCompile("(?s)FAIL:\tGetTime\\(int\\).*"), tcl.logs[0]) + require.Equal(t, "Expectations didn't match for Mock: *mock.timer", tcl.logs[1]) +} + +func TestAfterTotalWaitTimeWhileExecution(t *testing.T) { + waitDuration := 1 + total, waitMs := 5, time.Millisecond*time.Duration(waitDuration) + aTimer := new(timer) + for i := 0; i < total; i++ { + aTimer.On("GetTime", i).After(waitMs).Return(fmt.Sprintf("Time%d", i)).Once() + } + time.Sleep(waitMs) + start := time.Now() + var results []string + + for i := 0; i < total; i++ { + results = append(results, aTimer.GetTime(i)) + } + + end := time.Now() + elapsedTime := end.Sub(start) + assert.True(t, elapsedTime > waitMs, fmt.Sprintf("Total elapsed time:%v should be atleast greater than %v", elapsedTime, waitMs)) + assert.Equal(t, total, len(results)) + for i := range results { + assert.Equal(t, fmt.Sprintf("Time%d", i), results[i], "Return value of method should be same") + } +} + +func TestArgumentMatcherToPrintMismatch(t *testing.T) { + defer func() { + if r := recover(); r != nil { + matchingExp := regexp.MustCompile( + `\s+mock: Unexpected Method Call\s+-*\s+GetTime\(int\)\s+0: 1\s+The closest call I have is:\s+GetTime\(mock.argumentMatcher\)\s+0: mock.argumentMatcher\{.*?\}\s+Diff:.*\(int=1\) not matched by func\(int\) bool`) + assert.Regexp(t, matchingExp, r) + } + }() + + m := new(timer) + m.On("GetTime", MatchedBy(func(i int) bool { return false })).Return("SomeTime").Once() + + res := m.GetTime(1) + require.Equal(t, "SomeTime", res) + m.AssertExpectations(t) +} + +func TestArgumentMatcherToPrintMismatchWithReferenceType(t *testing.T) { + defer func() { + if r := recover(); r != nil { + matchingExp := regexp.MustCompile( + `\s+mock: Unexpected Method Call\s+-*\s+GetTimes\(\[\]int\)\s+0: \[\]int\{1\}\s+The closest call I have is:\s+GetTimes\(mock.argumentMatcher\)\s+0: mock.argumentMatcher\{.*?\}\s+Diff:.*\(\[\]int=\[1\]\) not matched by func\(\[\]int\) bool`) + assert.Regexp(t, matchingExp, r) + } + }() + + m := new(timer) + m.On("GetTimes", MatchedBy(func(_ []int) bool { return false })).Return("SomeTime").Once() + + res := m.GetTimes([]int{1}) + require.Equal(t, "SomeTime", res) + m.AssertExpectations(t) +} + +func TestClosestCallMismatchedArgumentInformationShowsTheClosest(t *testing.T) { + defer func() { + if r := recover(); r != nil { + matchingExp := regexp.MustCompile(unexpectedCallRegex(`TheExampleMethod(int,int,int)`, `0: 1\s+1: 1\s+2: 2`, `0: 1\s+1: 1\s+2: 1`, `Diff: 0: PASS: \(int=1\) == \(int=1\)\s+1: PASS: \(int=1\) == \(int=1\)\s+2: FAIL: \(int=2\) != \(int=1\)`)) + assert.Regexp(t, matchingExp, r) + } + }() + + m := new(TestExampleImplementation) + m.On("TheExampleMethod", 1, 1, 1).Return(1, nil).Once() + m.On("TheExampleMethod", 2, 2, 2).Return(2, nil).Once() + + m.TheExampleMethod(1, 1, 2) +} + +func TestClosestCallFavorsFirstMock(t *testing.T) { + defer func() { + if r := recover(); r != nil { + diffRegExp := `Difference found in argument 0:\s+--- Expected\s+\+\+\+ Actual\s+@@ -2,4 \+2,4 @@\s+\(bool\) true,\s+- \(bool\) true,\s+- \(bool\) true\s+\+ \(bool\) false,\s+\+ \(bool\) false\s+}\s+` + matchingExp := regexp.MustCompile(unexpectedCallRegex(`TheExampleMethod7([]bool)`, `0: \[\]bool{true, false, false}`, `0: \[\]bool{true, true, true}`, diffRegExp)) + assert.Regexp(t, matchingExp, r) + } + }() + + m := new(TestExampleImplementation) + m.On("TheExampleMethod7", []bool{true, true, true}).Return(nil).Once() + m.On("TheExampleMethod7", []bool{false, false, false}).Return(nil).Once() + + m.TheExampleMethod7([]bool{true, false, false}) +} + +func TestClosestCallUsesRepeatabilityToFindClosest(t *testing.T) { + defer func() { + if r := recover(); r != nil { + diffRegExp := `Difference found in argument 0:\s+--- Expected\s+\+\+\+ Actual\s+@@ -1,4 \+1,4 @@\s+\(\[\]bool\) \(len=3\) {\s+- \(bool\) false,\s+- \(bool\) false,\s+\+ \(bool\) true,\s+\+ \(bool\) true,\s+\(bool\) false\s+` + matchingExp := regexp.MustCompile(unexpectedCallRegex(`TheExampleMethod7([]bool)`, `0: \[\]bool{true, true, false}`, `0: \[\]bool{false, false, false}`, diffRegExp)) + assert.Regexp(t, matchingExp, r) + } + }() + + m := new(TestExampleImplementation) + m.On("TheExampleMethod7", []bool{true, true, true}).Return(nil).Once() + m.On("TheExampleMethod7", []bool{false, false, false}).Return(nil).Once() + + m.TheExampleMethod7([]bool{true, true, true}) + + // Since the first mocked call has already been used, it now has no repeatability, + // thus the second mock should be shown as the closest match + m.TheExampleMethod7([]bool{true, true, false}) +} + +func TestClosestCallMismatchedArgumentValueInformation(t *testing.T) { + defer func() { + if r := recover(); r != nil { + matchingExp := regexp.MustCompile(unexpectedCallRegex(`GetTime(int)`, "0: 1", "0: 999", `Diff: 0: FAIL: \(int=1\) != \(int=999\)`)) + assert.Regexp(t, matchingExp, r) + } + }() + + m := new(timer) + m.On("GetTime", 999).Return("SomeTime").Once() + + _ = m.GetTime(1) +} + +func Test_isBetterMatchThanReturnsFalseIfCandidateCallIsNil(t *testing.T) { + assert.False(t, matchCandidate{}.isBetterMatchThan(matchCandidate{})) +} + +func Test_isBetterMatchThanReturnsTrueIfOtherCandidateCallIsNil(t *testing.T) { + assert.True(t, matchCandidate{call: &Call{}}.isBetterMatchThan(matchCandidate{})) +} + +func Test_isBetterMatchThanReturnsFalseIfDiffCountIsGreaterThanOther(t *testing.T) { + assert.False(t, matchCandidate{call: &Call{}, diffCount: 2}.isBetterMatchThan(matchCandidate{call: &Call{}, diffCount: 1})) +} + +func Test_isBetterMatchThanReturnsTrueIfDiffCountIsLessThanOther(t *testing.T) { + assert.True(t, matchCandidate{call: &Call{}, diffCount: 1}.isBetterMatchThan(matchCandidate{call: &Call{}, diffCount: 2})) +} + +func Test_isBetterMatchThanReturnsTrueIfRepeatabilityIsGreaterThanOther(t *testing.T) { + assert.True(t, matchCandidate{call: &Call{Repeatability: 1}, diffCount: 1}.isBetterMatchThan(matchCandidate{call: &Call{Repeatability: -1}, diffCount: 1})) +} + +func Test_isBetterMatchThanReturnsFalseIfRepeatabilityIsLessThanOrEqualToOther(t *testing.T) { + assert.False(t, matchCandidate{call: &Call{Repeatability: 1}, diffCount: 1}.isBetterMatchThan(matchCandidate{call: &Call{Repeatability: 1}, diffCount: 1})) +} + +func unexpectedCallRegex(method, calledArg, expectedArg, diff string) string { + rMethod := regexp.QuoteMeta(method) + return fmt.Sprintf(`\s+mock: Unexpected Method Call\s+-*\s+%s\s+%s\s+The closest call I have is:\s+%s\s+%s\s+%s`, + rMethod, calledArg, rMethod, expectedArg, diff) +} + +//go:noinline +func ConcurrencyTestMethod(m *Mock) { + m.Called() +} + +func TestConcurrentArgumentRead(t *testing.T) { + methodUnderTest := func(c caller, u user) { + go u.Use(c) + c.Call() + } + + c := &mockCaller{} + defer c.AssertExpectations(t) + + u := &mockUser{} + defer u.AssertExpectations(t) + + done := make(chan struct{}) + + c.On("Call").Return().Once() + u.On("Use", c).Return().Once().Run(func(args Arguments) { close(done) }) + + methodUnderTest(c, u) + <-done // wait until Use is called or assertions will fail +} + +type caller interface { + Call() +} + +type mockCaller struct{ Mock } + +func (m *mockCaller) Call() { m.Called() } + +type user interface { + Use(caller) +} + +type mockUser struct{ Mock } + +func (m *mockUser) Use(c caller) { m.Called(c) } diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/package_test.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/package_test.go new file mode 100644 index 0000000..f17ff15 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/package_test.go @@ -0,0 +1,13 @@ +package testify + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestImports(t *testing.T) { + if assert.Equal(t, 1, 1) != true { + t.Error("Something is wrong.") + } +} diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/doc.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/doc.go new file mode 100644 index 0000000..169de39 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/doc.go @@ -0,0 +1,28 @@ +// Package require implements the same assertions as the `assert` package but +// stops test execution when a test fails. +// +// Example Usage +// +// The following is a complete example using require in a standard test function: +// import ( +// "testing" +// "github.com/stretchr/testify/require" +// ) +// +// func TestSomething(t *testing.T) { +// +// var a string = "Hello" +// var b string = "Hello" +// +// require.Equal(t, a, b, "The two words should be the same.") +// +// } +// +// Assertions +// +// The `require` package have same global functions as in the `assert` package, +// but instead of returning a boolean result they call `t.FailNow()`. +// +// Every assertion function also takes an optional string message as the final argument, +// allowing custom error messages to be appended to the message the assertion method outputs. +package require diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/forward_requirements.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/forward_requirements.go new file mode 100644 index 0000000..1dcb233 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/forward_requirements.go @@ -0,0 +1,16 @@ +package require + +// Assertions provides assertion methods around the +// TestingT interface. +type Assertions struct { + t TestingT +} + +// New makes a new Assertions object for the specified TestingT. +func New(t TestingT) *Assertions { + return &Assertions{ + t: t, + } +} + +//go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=require -template=require_forward.go.tmpl -include-format-funcs" diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/forward_requirements_test.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/forward_requirements_test.go new file mode 100644 index 0000000..8fbcc15 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/forward_requirements_test.go @@ -0,0 +1,523 @@ +package require + +import ( + "errors" + "testing" + "time" +) + +func TestImplementsWrapper(t *testing.T) { + require := New(t) + + require.Implements((*AssertionTesterInterface)(nil), new(AssertionTesterConformingObject)) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.Implements((*AssertionTesterInterface)(nil), new(AssertionTesterNonConformingObject)) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestIsTypeWrapper(t *testing.T) { + require := New(t) + require.IsType(new(AssertionTesterConformingObject), new(AssertionTesterConformingObject)) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.IsType(new(AssertionTesterConformingObject), new(AssertionTesterNonConformingObject)) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestEqualWrapper(t *testing.T) { + require := New(t) + require.Equal(1, 1) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.Equal(1, 2) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestNotEqualWrapper(t *testing.T) { + require := New(t) + require.NotEqual(1, 2) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.NotEqual(2, 2) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestExactlyWrapper(t *testing.T) { + require := New(t) + + a := float32(1) + b := float32(1) + c := float64(1) + + require.Exactly(a, b) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.Exactly(a, c) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestNotNilWrapper(t *testing.T) { + require := New(t) + require.NotNil(t, new(AssertionTesterConformingObject)) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.NotNil(nil) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestNilWrapper(t *testing.T) { + require := New(t) + require.Nil(nil) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.Nil(new(AssertionTesterConformingObject)) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestTrueWrapper(t *testing.T) { + require := New(t) + require.True(true) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.True(false) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestFalseWrapper(t *testing.T) { + require := New(t) + require.False(false) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.False(true) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestContainsWrapper(t *testing.T) { + require := New(t) + require.Contains("Hello World", "Hello") + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.Contains("Hello World", "Salut") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestNotContainsWrapper(t *testing.T) { + require := New(t) + require.NotContains("Hello World", "Hello!") + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.NotContains("Hello World", "Hello") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestPanicsWrapper(t *testing.T) { + require := New(t) + require.Panics(func() { + panic("Panic!") + }) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.Panics(func() {}) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestNotPanicsWrapper(t *testing.T) { + require := New(t) + require.NotPanics(func() {}) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.NotPanics(func() { + panic("Panic!") + }) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestNoErrorWrapper(t *testing.T) { + require := New(t) + require.NoError(nil) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.NoError(errors.New("some error")) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestErrorWrapper(t *testing.T) { + require := New(t) + require.Error(errors.New("some error")) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.Error(nil) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestErrorContainsWrapper(t *testing.T) { + require := New(t) + require.ErrorContains(errors.New("some error: another error"), "some error") + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.ErrorContains(errors.New("some error: another error"), "different error") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestEqualErrorWrapper(t *testing.T) { + require := New(t) + require.EqualError(errors.New("some error"), "some error") + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.EqualError(errors.New("some error"), "Not some error") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestEmptyWrapper(t *testing.T) { + require := New(t) + require.Empty("") + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.Empty("x") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestNotEmptyWrapper(t *testing.T) { + require := New(t) + require.NotEmpty("x") + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.NotEmpty("") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestWithinDurationWrapper(t *testing.T) { + require := New(t) + a := time.Now() + b := a.Add(10 * time.Second) + + require.WithinDuration(a, b, 15*time.Second) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.WithinDuration(a, b, 5*time.Second) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestInDeltaWrapper(t *testing.T) { + require := New(t) + require.InDelta(1.001, 1, 0.01) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.InDelta(1, 2, 0.5) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestZeroWrapper(t *testing.T) { + require := New(t) + require.Zero(0) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.Zero(1) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestNotZeroWrapper(t *testing.T) { + require := New(t) + require.NotZero(1) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.NotZero(0) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestJSONEqWrapper_EqualSONString(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`) + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestJSONEqWrapper_EquivalentButNotEqual(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestJSONEqWrapper_HashOfArraysAndHashes(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.JSONEq("{\r\n\t\"numeric\": 1.5,\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]],\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\"\r\n}", + "{\r\n\t\"numeric\": 1.5,\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\",\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]]\r\n}") + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestJSONEqWrapper_Array(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.JSONEq(`["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`) + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestJSONEqWrapper_HashAndArrayNotEquivalent(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.JSONEq(`["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestJSONEqWrapper_HashesNotEquivalent(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.JSONEq(`{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestJSONEqWrapper_ActualIsNotJSON(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.JSONEq(`{"foo": "bar"}`, "Not JSON") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestJSONEqWrapper_ExpectedIsNotJSON(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.JSONEq("Not JSON", `{"foo": "bar", "hello": "world"}`) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestJSONEqWrapper_ExpectedAndActualNotJSON(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.JSONEq("Not JSON", "Not JSON") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestJSONEqWrapper_ArraysOfDifferentOrder(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.JSONEq(`["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestYAMLEqWrapper_EqualYAMLString(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.YAMLEq(`{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`) + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestYAMLEqWrapper_EquivalentButNotEqual(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.YAMLEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestYAMLEqWrapper_HashOfArraysAndHashes(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + expected := ` +numeric: 1.5 +array: + - foo: bar + - 1 + - "string" + - ["nested", "array", 5.5] +hash: + nested: hash + nested_slice: [this, is, nested] +string: "foo" +` + + actual := ` +numeric: 1.5 +hash: + nested: hash + nested_slice: [this, is, nested] +string: "foo" +array: + - foo: bar + - 1 + - "string" + - ["nested", "array", 5.5] +` + + mockRequire.YAMLEq(expected, actual) + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestYAMLEqWrapper_Array(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`) + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestYAMLEqWrapper_HashAndArrayNotEquivalent(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestYAMLEqWrapper_HashesNotEquivalent(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.YAMLEq(`{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestYAMLEqWrapper_ActualIsSimpleString(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.YAMLEq(`{"foo": "bar"}`, "Simple String") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestYAMLEqWrapper_ExpectedIsSimpleString(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.YAMLEq("Simple String", `{"foo": "bar", "hello": "world"}`) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestYAMLEqWrapper_ExpectedAndActualSimpleString(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.YAMLEq("Simple String", "Simple String") + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestYAMLEqWrapper_ArraysOfDifferentOrder(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`) + if !mockT.Failed { + t.Error("Check should fail") + } +} diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/require.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/require.go new file mode 100644 index 0000000..880853f --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/require.go @@ -0,0 +1,1935 @@ +/* +* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen +* THIS FILE MUST NOT BE EDITED BY HAND + */ + +package require + +import ( + assert "github.com/stretchr/testify/assert" + http "net/http" + url "net/url" + time "time" +) + +// Condition uses a Comparison to assert a complex condition. +func Condition(t TestingT, comp assert.Comparison, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Condition(t, comp, msgAndArgs...) { + return + } + t.FailNow() +} + +// Conditionf uses a Comparison to assert a complex condition. +func Conditionf(t TestingT, comp assert.Comparison, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Conditionf(t, comp, msg, args...) { + return + } + t.FailNow() +} + +// Contains asserts that the specified string, list(array, slice...) or map contains the +// specified substring or element. +// +// assert.Contains(t, "Hello World", "World") +// assert.Contains(t, ["Hello", "World"], "World") +// assert.Contains(t, {"Hello": "World"}, "Hello") +func Contains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Contains(t, s, contains, msgAndArgs...) { + return + } + t.FailNow() +} + +// Containsf asserts that the specified string, list(array, slice...) or map contains the +// specified substring or element. +// +// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted") +// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") +// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") +func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Containsf(t, s, contains, msg, args...) { + return + } + t.FailNow() +} + +// DirExists checks whether a directory exists in the given path. It also fails +// if the path is a file rather a directory or there is an error checking whether it exists. +func DirExists(t TestingT, path string, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.DirExists(t, path, msgAndArgs...) { + return + } + t.FailNow() +} + +// DirExistsf checks whether a directory exists in the given path. It also fails +// if the path is a file rather a directory or there is an error checking whether it exists. +func DirExistsf(t TestingT, path string, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.DirExistsf(t, path, msg, args...) { + return + } + t.FailNow() +} + +// ElementsMatch asserts that the specified listA(array, slice...) is equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should match. +// +// assert.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2]) +func ElementsMatch(t TestingT, listA interface{}, listB interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.ElementsMatch(t, listA, listB, msgAndArgs...) { + return + } + t.FailNow() +} + +// ElementsMatchf asserts that the specified listA(array, slice...) is equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should match. +// +// assert.ElementsMatchf(t, [1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted") +func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.ElementsMatchf(t, listA, listB, msg, args...) { + return + } + t.FailNow() +} + +// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// assert.Empty(t, obj) +func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Empty(t, object, msgAndArgs...) { + return + } + t.FailNow() +} + +// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// assert.Emptyf(t, obj, "error message %s", "formatted") +func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Emptyf(t, object, msg, args...) { + return + } + t.FailNow() +} + +// Equal asserts that two objects are equal. +// +// assert.Equal(t, 123, 123) +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). Function equality +// cannot be determined and will always fail. +func Equal(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Equal(t, expected, actual, msgAndArgs...) { + return + } + t.FailNow() +} + +// EqualError asserts that a function returned an error (i.e. not `nil`) +// and that it is equal to the provided error. +// +// actualObj, err := SomeFunction() +// assert.EqualError(t, err, expectedErrorString) +func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.EqualError(t, theError, errString, msgAndArgs...) { + return + } + t.FailNow() +} + +// EqualErrorf asserts that a function returned an error (i.e. not `nil`) +// and that it is equal to the provided error. +// +// actualObj, err := SomeFunction() +// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") +func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.EqualErrorf(t, theError, errString, msg, args...) { + return + } + t.FailNow() +} + +// EqualValues asserts that two objects are equal or convertable to the same types +// and equal. +// +// assert.EqualValues(t, uint32(123), int32(123)) +func EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.EqualValues(t, expected, actual, msgAndArgs...) { + return + } + t.FailNow() +} + +// EqualValuesf asserts that two objects are equal or convertable to the same types +// and equal. +// +// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") +func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.EqualValuesf(t, expected, actual, msg, args...) { + return + } + t.FailNow() +} + +// Equalf asserts that two objects are equal. +// +// assert.Equalf(t, 123, 123, "error message %s", "formatted") +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). Function equality +// cannot be determined and will always fail. +func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Equalf(t, expected, actual, msg, args...) { + return + } + t.FailNow() +} + +// Error asserts that a function returned an error (i.e. not `nil`). +// +// actualObj, err := SomeFunction() +// if assert.Error(t, err) { +// assert.Equal(t, expectedError, err) +// } +func Error(t TestingT, err error, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Error(t, err, msgAndArgs...) { + return + } + t.FailNow() +} + +// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. +// This is a wrapper for errors.As. +func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.ErrorAs(t, err, target, msgAndArgs...) { + return + } + t.FailNow() +} + +// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. +// This is a wrapper for errors.As. +func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.ErrorAsf(t, err, target, msg, args...) { + return + } + t.FailNow() +} + +// ErrorContains asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// assert.ErrorContains(t, err, expectedErrorSubString) +func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.ErrorContains(t, theError, contains, msgAndArgs...) { + return + } + t.FailNow() +} + +// ErrorContainsf asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") +func ErrorContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.ErrorContainsf(t, theError, contains, msg, args...) { + return + } + t.FailNow() +} + +// ErrorIs asserts that at least one of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func ErrorIs(t TestingT, err error, target error, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.ErrorIs(t, err, target, msgAndArgs...) { + return + } + t.FailNow() +} + +// ErrorIsf asserts that at least one of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.ErrorIsf(t, err, target, msg, args...) { + return + } + t.FailNow() +} + +// Errorf asserts that a function returned an error (i.e. not `nil`). +// +// actualObj, err := SomeFunction() +// if assert.Errorf(t, err, "error message %s", "formatted") { +// assert.Equal(t, expectedErrorf, err) +// } +func Errorf(t TestingT, err error, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Errorf(t, err, msg, args...) { + return + } + t.FailNow() +} + +// Eventually asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. +// +// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) +func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Eventually(t, condition, waitFor, tick, msgAndArgs...) { + return + } + t.FailNow() +} + +// Eventuallyf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. +// +// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Eventuallyf(t, condition, waitFor, tick, msg, args...) { + return + } + t.FailNow() +} + +// Exactly asserts that two objects are equal in value and type. +// +// assert.Exactly(t, int32(123), int64(123)) +func Exactly(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Exactly(t, expected, actual, msgAndArgs...) { + return + } + t.FailNow() +} + +// Exactlyf asserts that two objects are equal in value and type. +// +// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") +func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Exactlyf(t, expected, actual, msg, args...) { + return + } + t.FailNow() +} + +// Fail reports a failure through +func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Fail(t, failureMessage, msgAndArgs...) { + return + } + t.FailNow() +} + +// FailNow fails test +func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.FailNow(t, failureMessage, msgAndArgs...) { + return + } + t.FailNow() +} + +// FailNowf fails test +func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.FailNowf(t, failureMessage, msg, args...) { + return + } + t.FailNow() +} + +// Failf reports a failure through +func Failf(t TestingT, failureMessage string, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Failf(t, failureMessage, msg, args...) { + return + } + t.FailNow() +} + +// False asserts that the specified value is false. +// +// assert.False(t, myBool) +func False(t TestingT, value bool, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.False(t, value, msgAndArgs...) { + return + } + t.FailNow() +} + +// Falsef asserts that the specified value is false. +// +// assert.Falsef(t, myBool, "error message %s", "formatted") +func Falsef(t TestingT, value bool, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Falsef(t, value, msg, args...) { + return + } + t.FailNow() +} + +// FileExists checks whether a file exists in the given path. It also fails if +// the path points to a directory or there is an error when trying to check the file. +func FileExists(t TestingT, path string, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.FileExists(t, path, msgAndArgs...) { + return + } + t.FailNow() +} + +// FileExistsf checks whether a file exists in the given path. It also fails if +// the path points to a directory or there is an error when trying to check the file. +func FileExistsf(t TestingT, path string, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.FileExistsf(t, path, msg, args...) { + return + } + t.FailNow() +} + +// Greater asserts that the first element is greater than the second +// +// assert.Greater(t, 2, 1) +// assert.Greater(t, float64(2), float64(1)) +// assert.Greater(t, "b", "a") +func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Greater(t, e1, e2, msgAndArgs...) { + return + } + t.FailNow() +} + +// GreaterOrEqual asserts that the first element is greater than or equal to the second +// +// assert.GreaterOrEqual(t, 2, 1) +// assert.GreaterOrEqual(t, 2, 2) +// assert.GreaterOrEqual(t, "b", "a") +// assert.GreaterOrEqual(t, "b", "b") +func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.GreaterOrEqual(t, e1, e2, msgAndArgs...) { + return + } + t.FailNow() +} + +// GreaterOrEqualf asserts that the first element is greater than or equal to the second +// +// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") +// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") +// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") +// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") +func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.GreaterOrEqualf(t, e1, e2, msg, args...) { + return + } + t.FailNow() +} + +// Greaterf asserts that the first element is greater than the second +// +// assert.Greaterf(t, 2, 1, "error message %s", "formatted") +// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") +// assert.Greaterf(t, "b", "a", "error message %s", "formatted") +func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Greaterf(t, e1, e2, msg, args...) { + return + } + t.FailNow() +} + +// HTTPBodyContains asserts that a specified handler returns a +// body that contains a string. +// +// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.HTTPBodyContains(t, handler, method, url, values, str, msgAndArgs...) { + return + } + t.FailNow() +} + +// HTTPBodyContainsf asserts that a specified handler returns a +// body that contains a string. +// +// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.HTTPBodyContainsf(t, handler, method, url, values, str, msg, args...) { + return + } + t.FailNow() +} + +// HTTPBodyNotContains asserts that a specified handler returns a +// body that does not contain a string. +// +// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.HTTPBodyNotContains(t, handler, method, url, values, str, msgAndArgs...) { + return + } + t.FailNow() +} + +// HTTPBodyNotContainsf asserts that a specified handler returns a +// body that does not contain a string. +// +// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.HTTPBodyNotContainsf(t, handler, method, url, values, str, msg, args...) { + return + } + t.FailNow() +} + +// HTTPError asserts that a specified handler returns an error status code. +// +// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.HTTPError(t, handler, method, url, values, msgAndArgs...) { + return + } + t.FailNow() +} + +// HTTPErrorf asserts that a specified handler returns an error status code. +// +// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.HTTPErrorf(t, handler, method, url, values, msg, args...) { + return + } + t.FailNow() +} + +// HTTPRedirect asserts that a specified handler returns a redirect status code. +// +// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.HTTPRedirect(t, handler, method, url, values, msgAndArgs...) { + return + } + t.FailNow() +} + +// HTTPRedirectf asserts that a specified handler returns a redirect status code. +// +// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.HTTPRedirectf(t, handler, method, url, values, msg, args...) { + return + } + t.FailNow() +} + +// HTTPStatusCode asserts that a specified handler returns a specified status code. +// +// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.HTTPStatusCode(t, handler, method, url, values, statuscode, msgAndArgs...) { + return + } + t.FailNow() +} + +// HTTPStatusCodef asserts that a specified handler returns a specified status code. +// +// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.HTTPStatusCodef(t, handler, method, url, values, statuscode, msg, args...) { + return + } + t.FailNow() +} + +// HTTPSuccess asserts that a specified handler returns a success status code. +// +// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPSuccess(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.HTTPSuccess(t, handler, method, url, values, msgAndArgs...) { + return + } + t.FailNow() +} + +// HTTPSuccessf asserts that a specified handler returns a success status code. +// +// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.HTTPSuccessf(t, handler, method, url, values, msg, args...) { + return + } + t.FailNow() +} + +// Implements asserts that an object is implemented by the specified interface. +// +// assert.Implements(t, (*MyInterface)(nil), new(MyObject)) +func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Implements(t, interfaceObject, object, msgAndArgs...) { + return + } + t.FailNow() +} + +// Implementsf asserts that an object is implemented by the specified interface. +// +// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Implementsf(t, interfaceObject, object, msg, args...) { + return + } + t.FailNow() +} + +// InDelta asserts that the two numerals are within delta of each other. +// +// assert.InDelta(t, math.Pi, 22/7.0, 0.01) +func InDelta(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.InDelta(t, expected, actual, delta, msgAndArgs...) { + return + } + t.FailNow() +} + +// InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. +func InDeltaMapValues(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.InDeltaMapValues(t, expected, actual, delta, msgAndArgs...) { + return + } + t.FailNow() +} + +// InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. +func InDeltaMapValuesf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.InDeltaMapValuesf(t, expected, actual, delta, msg, args...) { + return + } + t.FailNow() +} + +// InDeltaSlice is the same as InDelta, except it compares two slices. +func InDeltaSlice(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.InDeltaSlice(t, expected, actual, delta, msgAndArgs...) { + return + } + t.FailNow() +} + +// InDeltaSlicef is the same as InDelta, except it compares two slices. +func InDeltaSlicef(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.InDeltaSlicef(t, expected, actual, delta, msg, args...) { + return + } + t.FailNow() +} + +// InDeltaf asserts that the two numerals are within delta of each other. +// +// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") +func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.InDeltaf(t, expected, actual, delta, msg, args...) { + return + } + t.FailNow() +} + +// InEpsilon asserts that expected and actual have a relative error less than epsilon +func InEpsilon(t TestingT, expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.InEpsilon(t, expected, actual, epsilon, msgAndArgs...) { + return + } + t.FailNow() +} + +// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. +func InEpsilonSlice(t TestingT, expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.InEpsilonSlice(t, expected, actual, epsilon, msgAndArgs...) { + return + } + t.FailNow() +} + +// InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices. +func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.InEpsilonSlicef(t, expected, actual, epsilon, msg, args...) { + return + } + t.FailNow() +} + +// InEpsilonf asserts that expected and actual have a relative error less than epsilon +func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.InEpsilonf(t, expected, actual, epsilon, msg, args...) { + return + } + t.FailNow() +} + +// IsDecreasing asserts that the collection is decreasing +// +// assert.IsDecreasing(t, []int{2, 1, 0}) +// assert.IsDecreasing(t, []float{2, 1}) +// assert.IsDecreasing(t, []string{"b", "a"}) +func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsDecreasing(t, object, msgAndArgs...) { + return + } + t.FailNow() +} + +// IsDecreasingf asserts that the collection is decreasing +// +// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsDecreasingf(t, object, msg, args...) { + return + } + t.FailNow() +} + +// IsIncreasing asserts that the collection is increasing +// +// assert.IsIncreasing(t, []int{1, 2, 3}) +// assert.IsIncreasing(t, []float{1, 2}) +// assert.IsIncreasing(t, []string{"a", "b"}) +func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsIncreasing(t, object, msgAndArgs...) { + return + } + t.FailNow() +} + +// IsIncreasingf asserts that the collection is increasing +// +// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsIncreasingf(t, object, msg, args...) { + return + } + t.FailNow() +} + +// IsNonDecreasing asserts that the collection is not decreasing +// +// assert.IsNonDecreasing(t, []int{1, 1, 2}) +// assert.IsNonDecreasing(t, []float{1, 2}) +// assert.IsNonDecreasing(t, []string{"a", "b"}) +func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsNonDecreasing(t, object, msgAndArgs...) { + return + } + t.FailNow() +} + +// IsNonDecreasingf asserts that the collection is not decreasing +// +// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsNonDecreasingf(t, object, msg, args...) { + return + } + t.FailNow() +} + +// IsNonIncreasing asserts that the collection is not increasing +// +// assert.IsNonIncreasing(t, []int{2, 1, 1}) +// assert.IsNonIncreasing(t, []float{2, 1}) +// assert.IsNonIncreasing(t, []string{"b", "a"}) +func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsNonIncreasing(t, object, msgAndArgs...) { + return + } + t.FailNow() +} + +// IsNonIncreasingf asserts that the collection is not increasing +// +// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsNonIncreasingf(t, object, msg, args...) { + return + } + t.FailNow() +} + +// IsType asserts that the specified objects are of the same type. +func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsType(t, expectedType, object, msgAndArgs...) { + return + } + t.FailNow() +} + +// IsTypef asserts that the specified objects are of the same type. +func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsTypef(t, expectedType, object, msg, args...) { + return + } + t.FailNow() +} + +// JSONEq asserts that two JSON strings are equivalent. +// +// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.JSONEq(t, expected, actual, msgAndArgs...) { + return + } + t.FailNow() +} + +// JSONEqf asserts that two JSON strings are equivalent. +// +// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.JSONEqf(t, expected, actual, msg, args...) { + return + } + t.FailNow() +} + +// Len asserts that the specified object has specific length. +// Len also fails if the object has a type that len() not accept. +// +// assert.Len(t, mySlice, 3) +func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Len(t, object, length, msgAndArgs...) { + return + } + t.FailNow() +} + +// Lenf asserts that the specified object has specific length. +// Lenf also fails if the object has a type that len() not accept. +// +// assert.Lenf(t, mySlice, 3, "error message %s", "formatted") +func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Lenf(t, object, length, msg, args...) { + return + } + t.FailNow() +} + +// Less asserts that the first element is less than the second +// +// assert.Less(t, 1, 2) +// assert.Less(t, float64(1), float64(2)) +// assert.Less(t, "a", "b") +func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Less(t, e1, e2, msgAndArgs...) { + return + } + t.FailNow() +} + +// LessOrEqual asserts that the first element is less than or equal to the second +// +// assert.LessOrEqual(t, 1, 2) +// assert.LessOrEqual(t, 2, 2) +// assert.LessOrEqual(t, "a", "b") +// assert.LessOrEqual(t, "b", "b") +func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.LessOrEqual(t, e1, e2, msgAndArgs...) { + return + } + t.FailNow() +} + +// LessOrEqualf asserts that the first element is less than or equal to the second +// +// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted") +// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted") +// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted") +// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted") +func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.LessOrEqualf(t, e1, e2, msg, args...) { + return + } + t.FailNow() +} + +// Lessf asserts that the first element is less than the second +// +// assert.Lessf(t, 1, 2, "error message %s", "formatted") +// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") +// assert.Lessf(t, "a", "b", "error message %s", "formatted") +func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Lessf(t, e1, e2, msg, args...) { + return + } + t.FailNow() +} + +// Negative asserts that the specified element is negative +// +// assert.Negative(t, -1) +// assert.Negative(t, -1.23) +func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Negative(t, e, msgAndArgs...) { + return + } + t.FailNow() +} + +// Negativef asserts that the specified element is negative +// +// assert.Negativef(t, -1, "error message %s", "formatted") +// assert.Negativef(t, -1.23, "error message %s", "formatted") +func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Negativef(t, e, msg, args...) { + return + } + t.FailNow() +} + +// Never asserts that the given condition doesn't satisfy in waitFor time, +// periodically checking the target function each tick. +// +// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) +func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Never(t, condition, waitFor, tick, msgAndArgs...) { + return + } + t.FailNow() +} + +// Neverf asserts that the given condition doesn't satisfy in waitFor time, +// periodically checking the target function each tick. +// +// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Neverf(t, condition, waitFor, tick, msg, args...) { + return + } + t.FailNow() +} + +// Nil asserts that the specified object is nil. +// +// assert.Nil(t, err) +func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Nil(t, object, msgAndArgs...) { + return + } + t.FailNow() +} + +// Nilf asserts that the specified object is nil. +// +// assert.Nilf(t, err, "error message %s", "formatted") +func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Nilf(t, object, msg, args...) { + return + } + t.FailNow() +} + +// NoDirExists checks whether a directory does not exist in the given path. +// It fails if the path points to an existing _directory_ only. +func NoDirExists(t TestingT, path string, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NoDirExists(t, path, msgAndArgs...) { + return + } + t.FailNow() +} + +// NoDirExistsf checks whether a directory does not exist in the given path. +// It fails if the path points to an existing _directory_ only. +func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NoDirExistsf(t, path, msg, args...) { + return + } + t.FailNow() +} + +// NoError asserts that a function returned no error (i.e. `nil`). +// +// actualObj, err := SomeFunction() +// if assert.NoError(t, err) { +// assert.Equal(t, expectedObj, actualObj) +// } +func NoError(t TestingT, err error, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NoError(t, err, msgAndArgs...) { + return + } + t.FailNow() +} + +// NoErrorf asserts that a function returned no error (i.e. `nil`). +// +// actualObj, err := SomeFunction() +// if assert.NoErrorf(t, err, "error message %s", "formatted") { +// assert.Equal(t, expectedObj, actualObj) +// } +func NoErrorf(t TestingT, err error, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NoErrorf(t, err, msg, args...) { + return + } + t.FailNow() +} + +// NoFileExists checks whether a file does not exist in a given path. It fails +// if the path points to an existing _file_ only. +func NoFileExists(t TestingT, path string, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NoFileExists(t, path, msgAndArgs...) { + return + } + t.FailNow() +} + +// NoFileExistsf checks whether a file does not exist in a given path. It fails +// if the path points to an existing _file_ only. +func NoFileExistsf(t TestingT, path string, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NoFileExistsf(t, path, msg, args...) { + return + } + t.FailNow() +} + +// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the +// specified substring or element. +// +// assert.NotContains(t, "Hello World", "Earth") +// assert.NotContains(t, ["Hello", "World"], "Earth") +// assert.NotContains(t, {"Hello": "World"}, "Earth") +func NotContains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotContains(t, s, contains, msgAndArgs...) { + return + } + t.FailNow() +} + +// NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the +// specified substring or element. +// +// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") +func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotContainsf(t, s, contains, msg, args...) { + return + } + t.FailNow() +} + +// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// if assert.NotEmpty(t, obj) { +// assert.Equal(t, "two", obj[1]) +// } +func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotEmpty(t, object, msgAndArgs...) { + return + } + t.FailNow() +} + +// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// if assert.NotEmptyf(t, obj, "error message %s", "formatted") { +// assert.Equal(t, "two", obj[1]) +// } +func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotEmptyf(t, object, msg, args...) { + return + } + t.FailNow() +} + +// NotEqual asserts that the specified values are NOT equal. +// +// assert.NotEqual(t, obj1, obj2) +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). +func NotEqual(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotEqual(t, expected, actual, msgAndArgs...) { + return + } + t.FailNow() +} + +// NotEqualValues asserts that two objects are not equal even when converted to the same type +// +// assert.NotEqualValues(t, obj1, obj2) +func NotEqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotEqualValues(t, expected, actual, msgAndArgs...) { + return + } + t.FailNow() +} + +// NotEqualValuesf asserts that two objects are not equal even when converted to the same type +// +// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") +func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotEqualValuesf(t, expected, actual, msg, args...) { + return + } + t.FailNow() +} + +// NotEqualf asserts that the specified values are NOT equal. +// +// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). +func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotEqualf(t, expected, actual, msg, args...) { + return + } + t.FailNow() +} + +// NotErrorIs asserts that at none of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func NotErrorIs(t TestingT, err error, target error, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotErrorIs(t, err, target, msgAndArgs...) { + return + } + t.FailNow() +} + +// NotErrorIsf asserts that at none of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotErrorIsf(t, err, target, msg, args...) { + return + } + t.FailNow() +} + +// NotNil asserts that the specified object is not nil. +// +// assert.NotNil(t, err) +func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotNil(t, object, msgAndArgs...) { + return + } + t.FailNow() +} + +// NotNilf asserts that the specified object is not nil. +// +// assert.NotNilf(t, err, "error message %s", "formatted") +func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotNilf(t, object, msg, args...) { + return + } + t.FailNow() +} + +// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. +// +// assert.NotPanics(t, func(){ RemainCalm() }) +func NotPanics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotPanics(t, f, msgAndArgs...) { + return + } + t.FailNow() +} + +// NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. +// +// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") +func NotPanicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotPanicsf(t, f, msg, args...) { + return + } + t.FailNow() +} + +// NotRegexp asserts that a specified regexp does not match a string. +// +// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") +// assert.NotRegexp(t, "^start", "it's not starting") +func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotRegexp(t, rx, str, msgAndArgs...) { + return + } + t.FailNow() +} + +// NotRegexpf asserts that a specified regexp does not match a string. +// +// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") +// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") +func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotRegexpf(t, rx, str, msg, args...) { + return + } + t.FailNow() +} + +// NotSame asserts that two pointers do not reference the same object. +// +// assert.NotSame(t, ptr1, ptr2) +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func NotSame(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotSame(t, expected, actual, msgAndArgs...) { + return + } + t.FailNow() +} + +// NotSamef asserts that two pointers do not reference the same object. +// +// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotSamef(t, expected, actual, msg, args...) { + return + } + t.FailNow() +} + +// NotSubset asserts that the specified list(array, slice...) contains not all +// elements given in the specified subset(array, slice...). +// +// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotSubset(t, list, subset, msgAndArgs...) { + return + } + t.FailNow() +} + +// NotSubsetf asserts that the specified list(array, slice...) contains not all +// elements given in the specified subset(array, slice...). +// +// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotSubsetf(t, list, subset, msg, args...) { + return + } + t.FailNow() +} + +// NotZero asserts that i is not the zero value for its type. +func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotZero(t, i, msgAndArgs...) { + return + } + t.FailNow() +} + +// NotZerof asserts that i is not the zero value for its type. +func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotZerof(t, i, msg, args...) { + return + } + t.FailNow() +} + +// Panics asserts that the code inside the specified PanicTestFunc panics. +// +// assert.Panics(t, func(){ GoCrazy() }) +func Panics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Panics(t, f, msgAndArgs...) { + return + } + t.FailNow() +} + +// PanicsWithError asserts that the code inside the specified PanicTestFunc +// panics, and that the recovered panic value is an error that satisfies the +// EqualError comparison. +// +// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) +func PanicsWithError(t TestingT, errString string, f assert.PanicTestFunc, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.PanicsWithError(t, errString, f, msgAndArgs...) { + return + } + t.FailNow() +} + +// PanicsWithErrorf asserts that the code inside the specified PanicTestFunc +// panics, and that the recovered panic value is an error that satisfies the +// EqualError comparison. +// +// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +func PanicsWithErrorf(t TestingT, errString string, f assert.PanicTestFunc, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.PanicsWithErrorf(t, errString, f, msg, args...) { + return + } + t.FailNow() +} + +// PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that +// the recovered panic value equals the expected panic value. +// +// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) +func PanicsWithValue(t TestingT, expected interface{}, f assert.PanicTestFunc, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.PanicsWithValue(t, expected, f, msgAndArgs...) { + return + } + t.FailNow() +} + +// PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that +// the recovered panic value equals the expected panic value. +// +// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +func PanicsWithValuef(t TestingT, expected interface{}, f assert.PanicTestFunc, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.PanicsWithValuef(t, expected, f, msg, args...) { + return + } + t.FailNow() +} + +// Panicsf asserts that the code inside the specified PanicTestFunc panics. +// +// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") +func Panicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Panicsf(t, f, msg, args...) { + return + } + t.FailNow() +} + +// Positive asserts that the specified element is positive +// +// assert.Positive(t, 1) +// assert.Positive(t, 1.23) +func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Positive(t, e, msgAndArgs...) { + return + } + t.FailNow() +} + +// Positivef asserts that the specified element is positive +// +// assert.Positivef(t, 1, "error message %s", "formatted") +// assert.Positivef(t, 1.23, "error message %s", "formatted") +func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Positivef(t, e, msg, args...) { + return + } + t.FailNow() +} + +// Regexp asserts that a specified regexp matches a string. +// +// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") +// assert.Regexp(t, "start...$", "it's not starting") +func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Regexp(t, rx, str, msgAndArgs...) { + return + } + t.FailNow() +} + +// Regexpf asserts that a specified regexp matches a string. +// +// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") +// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") +func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Regexpf(t, rx, str, msg, args...) { + return + } + t.FailNow() +} + +// Same asserts that two pointers reference the same object. +// +// assert.Same(t, ptr1, ptr2) +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func Same(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Same(t, expected, actual, msgAndArgs...) { + return + } + t.FailNow() +} + +// Samef asserts that two pointers reference the same object. +// +// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted") +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func Samef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Samef(t, expected, actual, msg, args...) { + return + } + t.FailNow() +} + +// Subset asserts that the specified list(array, slice...) contains all +// elements given in the specified subset(array, slice...). +// +// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Subset(t, list, subset, msgAndArgs...) { + return + } + t.FailNow() +} + +// Subsetf asserts that the specified list(array, slice...) contains all +// elements given in the specified subset(array, slice...). +// +// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Subsetf(t, list, subset, msg, args...) { + return + } + t.FailNow() +} + +// True asserts that the specified value is true. +// +// assert.True(t, myBool) +func True(t TestingT, value bool, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.True(t, value, msgAndArgs...) { + return + } + t.FailNow() +} + +// Truef asserts that the specified value is true. +// +// assert.Truef(t, myBool, "error message %s", "formatted") +func Truef(t TestingT, value bool, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Truef(t, value, msg, args...) { + return + } + t.FailNow() +} + +// WithinDuration asserts that the two times are within duration delta of each other. +// +// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) +func WithinDuration(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.WithinDuration(t, expected, actual, delta, msgAndArgs...) { + return + } + t.FailNow() +} + +// WithinDurationf asserts that the two times are within duration delta of each other. +// +// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") +func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.WithinDurationf(t, expected, actual, delta, msg, args...) { + return + } + t.FailNow() +} + +// WithinRange asserts that a time is within a time range (inclusive). +// +// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +func WithinRange(t TestingT, actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.WithinRange(t, actual, start, end, msgAndArgs...) { + return + } + t.FailNow() +} + +// WithinRangef asserts that a time is within a time range (inclusive). +// +// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.WithinRangef(t, actual, start, end, msg, args...) { + return + } + t.FailNow() +} + +// YAMLEq asserts that two YAML strings are equivalent. +func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.YAMLEq(t, expected, actual, msgAndArgs...) { + return + } + t.FailNow() +} + +// YAMLEqf asserts that two YAML strings are equivalent. +func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.YAMLEqf(t, expected, actual, msg, args...) { + return + } + t.FailNow() +} + +// Zero asserts that i is the zero value for its type. +func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Zero(t, i, msgAndArgs...) { + return + } + t.FailNow() +} + +// Zerof asserts that i is the zero value for its type. +func Zerof(t TestingT, i interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Zerof(t, i, msg, args...) { + return + } + t.FailNow() +} diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/require.go.tmpl b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/require.go.tmpl new file mode 100644 index 0000000..55e42dd --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/require.go.tmpl @@ -0,0 +1,6 @@ +{{.Comment}} +func {{.DocInfo.Name}}(t TestingT, {{.Params}}) { + if h, ok := t.(tHelper); ok { h.Helper() } + if assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) { return } + t.FailNow() +} diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/require_forward.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/require_forward.go new file mode 100644 index 0000000..960bf6f --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/require_forward.go @@ -0,0 +1,1515 @@ +/* +* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen +* THIS FILE MUST NOT BE EDITED BY HAND + */ + +package require + +import ( + assert "github.com/stretchr/testify/assert" + http "net/http" + url "net/url" + time "time" +) + +// Condition uses a Comparison to assert a complex condition. +func (a *Assertions) Condition(comp assert.Comparison, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Condition(a.t, comp, msgAndArgs...) +} + +// Conditionf uses a Comparison to assert a complex condition. +func (a *Assertions) Conditionf(comp assert.Comparison, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Conditionf(a.t, comp, msg, args...) +} + +// Contains asserts that the specified string, list(array, slice...) or map contains the +// specified substring or element. +// +// a.Contains("Hello World", "World") +// a.Contains(["Hello", "World"], "World") +// a.Contains({"Hello": "World"}, "Hello") +func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Contains(a.t, s, contains, msgAndArgs...) +} + +// Containsf asserts that the specified string, list(array, slice...) or map contains the +// specified substring or element. +// +// a.Containsf("Hello World", "World", "error message %s", "formatted") +// a.Containsf(["Hello", "World"], "World", "error message %s", "formatted") +// a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted") +func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Containsf(a.t, s, contains, msg, args...) +} + +// DirExists checks whether a directory exists in the given path. It also fails +// if the path is a file rather a directory or there is an error checking whether it exists. +func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + DirExists(a.t, path, msgAndArgs...) +} + +// DirExistsf checks whether a directory exists in the given path. It also fails +// if the path is a file rather a directory or there is an error checking whether it exists. +func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + DirExistsf(a.t, path, msg, args...) +} + +// ElementsMatch asserts that the specified listA(array, slice...) is equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should match. +// +// a.ElementsMatch([1, 3, 2, 3], [1, 3, 3, 2]) +func (a *Assertions) ElementsMatch(listA interface{}, listB interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + ElementsMatch(a.t, listA, listB, msgAndArgs...) +} + +// ElementsMatchf asserts that the specified listA(array, slice...) is equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should match. +// +// a.ElementsMatchf([1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted") +func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + ElementsMatchf(a.t, listA, listB, msg, args...) +} + +// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// a.Empty(obj) +func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Empty(a.t, object, msgAndArgs...) +} + +// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// a.Emptyf(obj, "error message %s", "formatted") +func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Emptyf(a.t, object, msg, args...) +} + +// Equal asserts that two objects are equal. +// +// a.Equal(123, 123) +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). Function equality +// cannot be determined and will always fail. +func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Equal(a.t, expected, actual, msgAndArgs...) +} + +// EqualError asserts that a function returned an error (i.e. not `nil`) +// and that it is equal to the provided error. +// +// actualObj, err := SomeFunction() +// a.EqualError(err, expectedErrorString) +func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EqualError(a.t, theError, errString, msgAndArgs...) +} + +// EqualErrorf asserts that a function returned an error (i.e. not `nil`) +// and that it is equal to the provided error. +// +// actualObj, err := SomeFunction() +// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted") +func (a *Assertions) EqualErrorf(theError error, errString string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EqualErrorf(a.t, theError, errString, msg, args...) +} + +// EqualValues asserts that two objects are equal or convertable to the same types +// and equal. +// +// a.EqualValues(uint32(123), int32(123)) +func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EqualValues(a.t, expected, actual, msgAndArgs...) +} + +// EqualValuesf asserts that two objects are equal or convertable to the same types +// and equal. +// +// a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") +func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EqualValuesf(a.t, expected, actual, msg, args...) +} + +// Equalf asserts that two objects are equal. +// +// a.Equalf(123, 123, "error message %s", "formatted") +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). Function equality +// cannot be determined and will always fail. +func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Equalf(a.t, expected, actual, msg, args...) +} + +// Error asserts that a function returned an error (i.e. not `nil`). +// +// actualObj, err := SomeFunction() +// if a.Error(err) { +// assert.Equal(t, expectedError, err) +// } +func (a *Assertions) Error(err error, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Error(a.t, err, msgAndArgs...) +} + +// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. +// This is a wrapper for errors.As. +func (a *Assertions) ErrorAs(err error, target interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + ErrorAs(a.t, err, target, msgAndArgs...) +} + +// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. +// This is a wrapper for errors.As. +func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + ErrorAsf(a.t, err, target, msg, args...) +} + +// ErrorContains asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// a.ErrorContains(err, expectedErrorSubString) +func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + ErrorContains(a.t, theError, contains, msgAndArgs...) +} + +// ErrorContainsf asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") +func (a *Assertions) ErrorContainsf(theError error, contains string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + ErrorContainsf(a.t, theError, contains, msg, args...) +} + +// ErrorIs asserts that at least one of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + ErrorIs(a.t, err, target, msgAndArgs...) +} + +// ErrorIsf asserts that at least one of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + ErrorIsf(a.t, err, target, msg, args...) +} + +// Errorf asserts that a function returned an error (i.e. not `nil`). +// +// actualObj, err := SomeFunction() +// if a.Errorf(err, "error message %s", "formatted") { +// assert.Equal(t, expectedErrorf, err) +// } +func (a *Assertions) Errorf(err error, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Errorf(a.t, err, msg, args...) +} + +// Eventually asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. +// +// a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond) +func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Eventually(a.t, condition, waitFor, tick, msgAndArgs...) +} + +// Eventuallyf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. +// +// a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Eventuallyf(a.t, condition, waitFor, tick, msg, args...) +} + +// Exactly asserts that two objects are equal in value and type. +// +// a.Exactly(int32(123), int64(123)) +func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Exactly(a.t, expected, actual, msgAndArgs...) +} + +// Exactlyf asserts that two objects are equal in value and type. +// +// a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") +func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Exactlyf(a.t, expected, actual, msg, args...) +} + +// Fail reports a failure through +func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Fail(a.t, failureMessage, msgAndArgs...) +} + +// FailNow fails test +func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + FailNow(a.t, failureMessage, msgAndArgs...) +} + +// FailNowf fails test +func (a *Assertions) FailNowf(failureMessage string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + FailNowf(a.t, failureMessage, msg, args...) +} + +// Failf reports a failure through +func (a *Assertions) Failf(failureMessage string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Failf(a.t, failureMessage, msg, args...) +} + +// False asserts that the specified value is false. +// +// a.False(myBool) +func (a *Assertions) False(value bool, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + False(a.t, value, msgAndArgs...) +} + +// Falsef asserts that the specified value is false. +// +// a.Falsef(myBool, "error message %s", "formatted") +func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Falsef(a.t, value, msg, args...) +} + +// FileExists checks whether a file exists in the given path. It also fails if +// the path points to a directory or there is an error when trying to check the file. +func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + FileExists(a.t, path, msgAndArgs...) +} + +// FileExistsf checks whether a file exists in the given path. It also fails if +// the path points to a directory or there is an error when trying to check the file. +func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + FileExistsf(a.t, path, msg, args...) +} + +// Greater asserts that the first element is greater than the second +// +// a.Greater(2, 1) +// a.Greater(float64(2), float64(1)) +// a.Greater("b", "a") +func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Greater(a.t, e1, e2, msgAndArgs...) +} + +// GreaterOrEqual asserts that the first element is greater than or equal to the second +// +// a.GreaterOrEqual(2, 1) +// a.GreaterOrEqual(2, 2) +// a.GreaterOrEqual("b", "a") +// a.GreaterOrEqual("b", "b") +func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + GreaterOrEqual(a.t, e1, e2, msgAndArgs...) +} + +// GreaterOrEqualf asserts that the first element is greater than or equal to the second +// +// a.GreaterOrEqualf(2, 1, "error message %s", "formatted") +// a.GreaterOrEqualf(2, 2, "error message %s", "formatted") +// a.GreaterOrEqualf("b", "a", "error message %s", "formatted") +// a.GreaterOrEqualf("b", "b", "error message %s", "formatted") +func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + GreaterOrEqualf(a.t, e1, e2, msg, args...) +} + +// Greaterf asserts that the first element is greater than the second +// +// a.Greaterf(2, 1, "error message %s", "formatted") +// a.Greaterf(float64(2), float64(1), "error message %s", "formatted") +// a.Greaterf("b", "a", "error message %s", "formatted") +func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Greaterf(a.t, e1, e2, msg, args...) +} + +// HTTPBodyContains asserts that a specified handler returns a +// body that contains a string. +// +// a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPBodyContains(a.t, handler, method, url, values, str, msgAndArgs...) +} + +// HTTPBodyContainsf asserts that a specified handler returns a +// body that contains a string. +// +// a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPBodyContainsf(a.t, handler, method, url, values, str, msg, args...) +} + +// HTTPBodyNotContains asserts that a specified handler returns a +// body that does not contain a string. +// +// a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPBodyNotContains(a.t, handler, method, url, values, str, msgAndArgs...) +} + +// HTTPBodyNotContainsf asserts that a specified handler returns a +// body that does not contain a string. +// +// a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPBodyNotContainsf(a.t, handler, method, url, values, str, msg, args...) +} + +// HTTPError asserts that a specified handler returns an error status code. +// +// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPError(a.t, handler, method, url, values, msgAndArgs...) +} + +// HTTPErrorf asserts that a specified handler returns an error status code. +// +// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPErrorf(a.t, handler, method, url, values, msg, args...) +} + +// HTTPRedirect asserts that a specified handler returns a redirect status code. +// +// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPRedirect(a.t, handler, method, url, values, msgAndArgs...) +} + +// HTTPRedirectf asserts that a specified handler returns a redirect status code. +// +// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPRedirectf(a.t, handler, method, url, values, msg, args...) +} + +// HTTPStatusCode asserts that a specified handler returns a specified status code. +// +// a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPStatusCode(a.t, handler, method, url, values, statuscode, msgAndArgs...) +} + +// HTTPStatusCodef asserts that a specified handler returns a specified status code. +// +// a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPStatusCodef(a.t, handler, method, url, values, statuscode, msg, args...) +} + +// HTTPSuccess asserts that a specified handler returns a success status code. +// +// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPSuccess(a.t, handler, method, url, values, msgAndArgs...) +} + +// HTTPSuccessf asserts that a specified handler returns a success status code. +// +// a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPSuccessf(a.t, handler, method, url, values, msg, args...) +} + +// Implements asserts that an object is implemented by the specified interface. +// +// a.Implements((*MyInterface)(nil), new(MyObject)) +func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Implements(a.t, interfaceObject, object, msgAndArgs...) +} + +// Implementsf asserts that an object is implemented by the specified interface. +// +// a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Implementsf(a.t, interfaceObject, object, msg, args...) +} + +// InDelta asserts that the two numerals are within delta of each other. +// +// a.InDelta(math.Pi, 22/7.0, 0.01) +func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + InDelta(a.t, expected, actual, delta, msgAndArgs...) +} + +// InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. +func (a *Assertions) InDeltaMapValues(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + InDeltaMapValues(a.t, expected, actual, delta, msgAndArgs...) +} + +// InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. +func (a *Assertions) InDeltaMapValuesf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + InDeltaMapValuesf(a.t, expected, actual, delta, msg, args...) +} + +// InDeltaSlice is the same as InDelta, except it compares two slices. +func (a *Assertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + InDeltaSlice(a.t, expected, actual, delta, msgAndArgs...) +} + +// InDeltaSlicef is the same as InDelta, except it compares two slices. +func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + InDeltaSlicef(a.t, expected, actual, delta, msg, args...) +} + +// InDeltaf asserts that the two numerals are within delta of each other. +// +// a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") +func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + InDeltaf(a.t, expected, actual, delta, msg, args...) +} + +// InEpsilon asserts that expected and actual have a relative error less than epsilon +func (a *Assertions) InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...) +} + +// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. +func (a *Assertions) InEpsilonSlice(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + InEpsilonSlice(a.t, expected, actual, epsilon, msgAndArgs...) +} + +// InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices. +func (a *Assertions) InEpsilonSlicef(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + InEpsilonSlicef(a.t, expected, actual, epsilon, msg, args...) +} + +// InEpsilonf asserts that expected and actual have a relative error less than epsilon +func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + InEpsilonf(a.t, expected, actual, epsilon, msg, args...) +} + +// IsDecreasing asserts that the collection is decreasing +// +// a.IsDecreasing([]int{2, 1, 0}) +// a.IsDecreasing([]float{2, 1}) +// a.IsDecreasing([]string{"b", "a"}) +func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsDecreasing(a.t, object, msgAndArgs...) +} + +// IsDecreasingf asserts that the collection is decreasing +// +// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") +// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") +// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") +func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsDecreasingf(a.t, object, msg, args...) +} + +// IsIncreasing asserts that the collection is increasing +// +// a.IsIncreasing([]int{1, 2, 3}) +// a.IsIncreasing([]float{1, 2}) +// a.IsIncreasing([]string{"a", "b"}) +func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsIncreasing(a.t, object, msgAndArgs...) +} + +// IsIncreasingf asserts that the collection is increasing +// +// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") +// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") +// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") +func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsIncreasingf(a.t, object, msg, args...) +} + +// IsNonDecreasing asserts that the collection is not decreasing +// +// a.IsNonDecreasing([]int{1, 1, 2}) +// a.IsNonDecreasing([]float{1, 2}) +// a.IsNonDecreasing([]string{"a", "b"}) +func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsNonDecreasing(a.t, object, msgAndArgs...) +} + +// IsNonDecreasingf asserts that the collection is not decreasing +// +// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") +// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") +// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") +func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsNonDecreasingf(a.t, object, msg, args...) +} + +// IsNonIncreasing asserts that the collection is not increasing +// +// a.IsNonIncreasing([]int{2, 1, 1}) +// a.IsNonIncreasing([]float{2, 1}) +// a.IsNonIncreasing([]string{"b", "a"}) +func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsNonIncreasing(a.t, object, msgAndArgs...) +} + +// IsNonIncreasingf asserts that the collection is not increasing +// +// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") +// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") +// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") +func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsNonIncreasingf(a.t, object, msg, args...) +} + +// IsType asserts that the specified objects are of the same type. +func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsType(a.t, expectedType, object, msgAndArgs...) +} + +// IsTypef asserts that the specified objects are of the same type. +func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsTypef(a.t, expectedType, object, msg, args...) +} + +// JSONEq asserts that two JSON strings are equivalent. +// +// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + JSONEq(a.t, expected, actual, msgAndArgs...) +} + +// JSONEqf asserts that two JSON strings are equivalent. +// +// a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + JSONEqf(a.t, expected, actual, msg, args...) +} + +// Len asserts that the specified object has specific length. +// Len also fails if the object has a type that len() not accept. +// +// a.Len(mySlice, 3) +func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Len(a.t, object, length, msgAndArgs...) +} + +// Lenf asserts that the specified object has specific length. +// Lenf also fails if the object has a type that len() not accept. +// +// a.Lenf(mySlice, 3, "error message %s", "formatted") +func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Lenf(a.t, object, length, msg, args...) +} + +// Less asserts that the first element is less than the second +// +// a.Less(1, 2) +// a.Less(float64(1), float64(2)) +// a.Less("a", "b") +func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Less(a.t, e1, e2, msgAndArgs...) +} + +// LessOrEqual asserts that the first element is less than or equal to the second +// +// a.LessOrEqual(1, 2) +// a.LessOrEqual(2, 2) +// a.LessOrEqual("a", "b") +// a.LessOrEqual("b", "b") +func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + LessOrEqual(a.t, e1, e2, msgAndArgs...) +} + +// LessOrEqualf asserts that the first element is less than or equal to the second +// +// a.LessOrEqualf(1, 2, "error message %s", "formatted") +// a.LessOrEqualf(2, 2, "error message %s", "formatted") +// a.LessOrEqualf("a", "b", "error message %s", "formatted") +// a.LessOrEqualf("b", "b", "error message %s", "formatted") +func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + LessOrEqualf(a.t, e1, e2, msg, args...) +} + +// Lessf asserts that the first element is less than the second +// +// a.Lessf(1, 2, "error message %s", "formatted") +// a.Lessf(float64(1), float64(2), "error message %s", "formatted") +// a.Lessf("a", "b", "error message %s", "formatted") +func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Lessf(a.t, e1, e2, msg, args...) +} + +// Negative asserts that the specified element is negative +// +// a.Negative(-1) +// a.Negative(-1.23) +func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Negative(a.t, e, msgAndArgs...) +} + +// Negativef asserts that the specified element is negative +// +// a.Negativef(-1, "error message %s", "formatted") +// a.Negativef(-1.23, "error message %s", "formatted") +func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Negativef(a.t, e, msg, args...) +} + +// Never asserts that the given condition doesn't satisfy in waitFor time, +// periodically checking the target function each tick. +// +// a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) +func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Never(a.t, condition, waitFor, tick, msgAndArgs...) +} + +// Neverf asserts that the given condition doesn't satisfy in waitFor time, +// periodically checking the target function each tick. +// +// a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Neverf(a.t, condition, waitFor, tick, msg, args...) +} + +// Nil asserts that the specified object is nil. +// +// a.Nil(err) +func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Nil(a.t, object, msgAndArgs...) +} + +// Nilf asserts that the specified object is nil. +// +// a.Nilf(err, "error message %s", "formatted") +func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Nilf(a.t, object, msg, args...) +} + +// NoDirExists checks whether a directory does not exist in the given path. +// It fails if the path points to an existing _directory_ only. +func (a *Assertions) NoDirExists(path string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NoDirExists(a.t, path, msgAndArgs...) +} + +// NoDirExistsf checks whether a directory does not exist in the given path. +// It fails if the path points to an existing _directory_ only. +func (a *Assertions) NoDirExistsf(path string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NoDirExistsf(a.t, path, msg, args...) +} + +// NoError asserts that a function returned no error (i.e. `nil`). +// +// actualObj, err := SomeFunction() +// if a.NoError(err) { +// assert.Equal(t, expectedObj, actualObj) +// } +func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NoError(a.t, err, msgAndArgs...) +} + +// NoErrorf asserts that a function returned no error (i.e. `nil`). +// +// actualObj, err := SomeFunction() +// if a.NoErrorf(err, "error message %s", "formatted") { +// assert.Equal(t, expectedObj, actualObj) +// } +func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NoErrorf(a.t, err, msg, args...) +} + +// NoFileExists checks whether a file does not exist in a given path. It fails +// if the path points to an existing _file_ only. +func (a *Assertions) NoFileExists(path string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NoFileExists(a.t, path, msgAndArgs...) +} + +// NoFileExistsf checks whether a file does not exist in a given path. It fails +// if the path points to an existing _file_ only. +func (a *Assertions) NoFileExistsf(path string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NoFileExistsf(a.t, path, msg, args...) +} + +// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the +// specified substring or element. +// +// a.NotContains("Hello World", "Earth") +// a.NotContains(["Hello", "World"], "Earth") +// a.NotContains({"Hello": "World"}, "Earth") +func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotContains(a.t, s, contains, msgAndArgs...) +} + +// NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the +// specified substring or element. +// +// a.NotContainsf("Hello World", "Earth", "error message %s", "formatted") +// a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted") +// a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted") +func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotContainsf(a.t, s, contains, msg, args...) +} + +// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// if a.NotEmpty(obj) { +// assert.Equal(t, "two", obj[1]) +// } +func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotEmpty(a.t, object, msgAndArgs...) +} + +// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// if a.NotEmptyf(obj, "error message %s", "formatted") { +// assert.Equal(t, "two", obj[1]) +// } +func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotEmptyf(a.t, object, msg, args...) +} + +// NotEqual asserts that the specified values are NOT equal. +// +// a.NotEqual(obj1, obj2) +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). +func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotEqual(a.t, expected, actual, msgAndArgs...) +} + +// NotEqualValues asserts that two objects are not equal even when converted to the same type +// +// a.NotEqualValues(obj1, obj2) +func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotEqualValues(a.t, expected, actual, msgAndArgs...) +} + +// NotEqualValuesf asserts that two objects are not equal even when converted to the same type +// +// a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") +func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotEqualValuesf(a.t, expected, actual, msg, args...) +} + +// NotEqualf asserts that the specified values are NOT equal. +// +// a.NotEqualf(obj1, obj2, "error message %s", "formatted") +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). +func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotEqualf(a.t, expected, actual, msg, args...) +} + +// NotErrorIs asserts that at none of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotErrorIs(a.t, err, target, msgAndArgs...) +} + +// NotErrorIsf asserts that at none of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotErrorIsf(a.t, err, target, msg, args...) +} + +// NotNil asserts that the specified object is not nil. +// +// a.NotNil(err) +func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotNil(a.t, object, msgAndArgs...) +} + +// NotNilf asserts that the specified object is not nil. +// +// a.NotNilf(err, "error message %s", "formatted") +func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotNilf(a.t, object, msg, args...) +} + +// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. +// +// a.NotPanics(func(){ RemainCalm() }) +func (a *Assertions) NotPanics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotPanics(a.t, f, msgAndArgs...) +} + +// NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. +// +// a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted") +func (a *Assertions) NotPanicsf(f assert.PanicTestFunc, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotPanicsf(a.t, f, msg, args...) +} + +// NotRegexp asserts that a specified regexp does not match a string. +// +// a.NotRegexp(regexp.MustCompile("starts"), "it's starting") +// a.NotRegexp("^start", "it's not starting") +func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotRegexp(a.t, rx, str, msgAndArgs...) +} + +// NotRegexpf asserts that a specified regexp does not match a string. +// +// a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") +// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") +func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotRegexpf(a.t, rx, str, msg, args...) +} + +// NotSame asserts that two pointers do not reference the same object. +// +// a.NotSame(ptr1, ptr2) +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func (a *Assertions) NotSame(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotSame(a.t, expected, actual, msgAndArgs...) +} + +// NotSamef asserts that two pointers do not reference the same object. +// +// a.NotSamef(ptr1, ptr2, "error message %s", "formatted") +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotSamef(a.t, expected, actual, msg, args...) +} + +// NotSubset asserts that the specified list(array, slice...) contains not all +// elements given in the specified subset(array, slice...). +// +// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotSubset(a.t, list, subset, msgAndArgs...) +} + +// NotSubsetf asserts that the specified list(array, slice...) contains not all +// elements given in the specified subset(array, slice...). +// +// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotSubsetf(a.t, list, subset, msg, args...) +} + +// NotZero asserts that i is not the zero value for its type. +func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotZero(a.t, i, msgAndArgs...) +} + +// NotZerof asserts that i is not the zero value for its type. +func (a *Assertions) NotZerof(i interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotZerof(a.t, i, msg, args...) +} + +// Panics asserts that the code inside the specified PanicTestFunc panics. +// +// a.Panics(func(){ GoCrazy() }) +func (a *Assertions) Panics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Panics(a.t, f, msgAndArgs...) +} + +// PanicsWithError asserts that the code inside the specified PanicTestFunc +// panics, and that the recovered panic value is an error that satisfies the +// EqualError comparison. +// +// a.PanicsWithError("crazy error", func(){ GoCrazy() }) +func (a *Assertions) PanicsWithError(errString string, f assert.PanicTestFunc, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + PanicsWithError(a.t, errString, f, msgAndArgs...) +} + +// PanicsWithErrorf asserts that the code inside the specified PanicTestFunc +// panics, and that the recovered panic value is an error that satisfies the +// EqualError comparison. +// +// a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +func (a *Assertions) PanicsWithErrorf(errString string, f assert.PanicTestFunc, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + PanicsWithErrorf(a.t, errString, f, msg, args...) +} + +// PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that +// the recovered panic value equals the expected panic value. +// +// a.PanicsWithValue("crazy error", func(){ GoCrazy() }) +func (a *Assertions) PanicsWithValue(expected interface{}, f assert.PanicTestFunc, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + PanicsWithValue(a.t, expected, f, msgAndArgs...) +} + +// PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that +// the recovered panic value equals the expected panic value. +// +// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +func (a *Assertions) PanicsWithValuef(expected interface{}, f assert.PanicTestFunc, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + PanicsWithValuef(a.t, expected, f, msg, args...) +} + +// Panicsf asserts that the code inside the specified PanicTestFunc panics. +// +// a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted") +func (a *Assertions) Panicsf(f assert.PanicTestFunc, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Panicsf(a.t, f, msg, args...) +} + +// Positive asserts that the specified element is positive +// +// a.Positive(1) +// a.Positive(1.23) +func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Positive(a.t, e, msgAndArgs...) +} + +// Positivef asserts that the specified element is positive +// +// a.Positivef(1, "error message %s", "formatted") +// a.Positivef(1.23, "error message %s", "formatted") +func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Positivef(a.t, e, msg, args...) +} + +// Regexp asserts that a specified regexp matches a string. +// +// a.Regexp(regexp.MustCompile("start"), "it's starting") +// a.Regexp("start...$", "it's not starting") +func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Regexp(a.t, rx, str, msgAndArgs...) +} + +// Regexpf asserts that a specified regexp matches a string. +// +// a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") +// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") +func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Regexpf(a.t, rx, str, msg, args...) +} + +// Same asserts that two pointers reference the same object. +// +// a.Same(ptr1, ptr2) +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func (a *Assertions) Same(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Same(a.t, expected, actual, msgAndArgs...) +} + +// Samef asserts that two pointers reference the same object. +// +// a.Samef(ptr1, ptr2, "error message %s", "formatted") +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Samef(a.t, expected, actual, msg, args...) +} + +// Subset asserts that the specified list(array, slice...) contains all +// elements given in the specified subset(array, slice...). +// +// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Subset(a.t, list, subset, msgAndArgs...) +} + +// Subsetf asserts that the specified list(array, slice...) contains all +// elements given in the specified subset(array, slice...). +// +// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Subsetf(a.t, list, subset, msg, args...) +} + +// True asserts that the specified value is true. +// +// a.True(myBool) +func (a *Assertions) True(value bool, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + True(a.t, value, msgAndArgs...) +} + +// Truef asserts that the specified value is true. +// +// a.Truef(myBool, "error message %s", "formatted") +func (a *Assertions) Truef(value bool, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Truef(a.t, value, msg, args...) +} + +// WithinDuration asserts that the two times are within duration delta of each other. +// +// a.WithinDuration(time.Now(), time.Now(), 10*time.Second) +func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + WithinDuration(a.t, expected, actual, delta, msgAndArgs...) +} + +// WithinDurationf asserts that the two times are within duration delta of each other. +// +// a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") +func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + WithinDurationf(a.t, expected, actual, delta, msg, args...) +} + +// WithinRange asserts that a time is within a time range (inclusive). +// +// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + WithinRange(a.t, actual, start, end, msgAndArgs...) +} + +// WithinRangef asserts that a time is within a time range (inclusive). +// +// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + WithinRangef(a.t, actual, start, end, msg, args...) +} + +// YAMLEq asserts that two YAML strings are equivalent. +func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + YAMLEq(a.t, expected, actual, msgAndArgs...) +} + +// YAMLEqf asserts that two YAML strings are equivalent. +func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + YAMLEqf(a.t, expected, actual, msg, args...) +} + +// Zero asserts that i is the zero value for its type. +func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Zero(a.t, i, msgAndArgs...) +} + +// Zerof asserts that i is the zero value for its type. +func (a *Assertions) Zerof(i interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Zerof(a.t, i, msg, args...) +} diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/require_forward.go.tmpl b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/require_forward.go.tmpl new file mode 100644 index 0000000..54124df --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/require_forward.go.tmpl @@ -0,0 +1,5 @@ +{{.CommentWithoutT "a"}} +func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) { + if h, ok := a.t.(tHelper); ok { h.Helper() } + {{.DocInfo.Name}}(a.t, {{.ForwardedParams}}) +} diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/requirements.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/requirements.go new file mode 100644 index 0000000..91772df --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/requirements.go @@ -0,0 +1,29 @@ +package require + +// TestingT is an interface wrapper around *testing.T +type TestingT interface { + Errorf(format string, args ...interface{}) + FailNow() +} + +type tHelper interface { + Helper() +} + +// ComparisonAssertionFunc is a common function prototype when comparing two values. Can be useful +// for table driven tests. +type ComparisonAssertionFunc func(TestingT, interface{}, interface{}, ...interface{}) + +// ValueAssertionFunc is a common function prototype when validating a single value. Can be useful +// for table driven tests. +type ValueAssertionFunc func(TestingT, interface{}, ...interface{}) + +// BoolAssertionFunc is a common function prototype when validating a bool value. Can be useful +// for table driven tests. +type BoolAssertionFunc func(TestingT, bool, ...interface{}) + +// ErrorAssertionFunc is a common function prototype when validating an error value. Can be useful +// for table driven tests. +type ErrorAssertionFunc func(TestingT, error, ...interface{}) + +//go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=require -template=require.go.tmpl -include-format-funcs" diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/requirements_test.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/requirements_test.go new file mode 100644 index 0000000..febf0c1 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/require/requirements_test.go @@ -0,0 +1,683 @@ +package require + +import ( + "encoding/json" + "errors" + "testing" + "time" +) + +// AssertionTesterInterface defines an interface to be used for testing assertion methods +type AssertionTesterInterface interface { + TestMethod() +} + +// AssertionTesterConformingObject is an object that conforms to the AssertionTesterInterface interface +type AssertionTesterConformingObject struct { +} + +func (a *AssertionTesterConformingObject) TestMethod() { +} + +// AssertionTesterNonConformingObject is an object that does not conform to the AssertionTesterInterface interface +type AssertionTesterNonConformingObject struct { +} + +type MockT struct { + Failed bool +} + +func (t *MockT) FailNow() { + t.Failed = true +} + +func (t *MockT) Errorf(format string, args ...interface{}) { + _, _ = format, args +} + +func TestImplements(t *testing.T) { + + Implements(t, (*AssertionTesterInterface)(nil), new(AssertionTesterConformingObject)) + + mockT := new(MockT) + Implements(mockT, (*AssertionTesterInterface)(nil), new(AssertionTesterNonConformingObject)) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestIsType(t *testing.T) { + + IsType(t, new(AssertionTesterConformingObject), new(AssertionTesterConformingObject)) + + mockT := new(MockT) + IsType(mockT, new(AssertionTesterConformingObject), new(AssertionTesterNonConformingObject)) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestEqual(t *testing.T) { + + Equal(t, 1, 1) + + mockT := new(MockT) + Equal(mockT, 1, 2) + if !mockT.Failed { + t.Error("Check should fail") + } + +} + +func TestNotEqual(t *testing.T) { + + NotEqual(t, 1, 2) + mockT := new(MockT) + NotEqual(mockT, 2, 2) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestExactly(t *testing.T) { + + a := float32(1) + b := float32(1) + c := float64(1) + + Exactly(t, a, b) + + mockT := new(MockT) + Exactly(mockT, a, c) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestNotNil(t *testing.T) { + + NotNil(t, new(AssertionTesterConformingObject)) + + mockT := new(MockT) + NotNil(mockT, nil) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestNil(t *testing.T) { + + Nil(t, nil) + + mockT := new(MockT) + Nil(mockT, new(AssertionTesterConformingObject)) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestTrue(t *testing.T) { + + True(t, true) + + mockT := new(MockT) + True(mockT, false) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestFalse(t *testing.T) { + + False(t, false) + + mockT := new(MockT) + False(mockT, true) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestContains(t *testing.T) { + + Contains(t, "Hello World", "Hello") + + mockT := new(MockT) + Contains(mockT, "Hello World", "Salut") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestNotContains(t *testing.T) { + + NotContains(t, "Hello World", "Hello!") + + mockT := new(MockT) + NotContains(mockT, "Hello World", "Hello") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestPanics(t *testing.T) { + + Panics(t, func() { + panic("Panic!") + }) + + mockT := new(MockT) + Panics(mockT, func() {}) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestNotPanics(t *testing.T) { + + NotPanics(t, func() {}) + + mockT := new(MockT) + NotPanics(mockT, func() { + panic("Panic!") + }) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestNoError(t *testing.T) { + + NoError(t, nil) + + mockT := new(MockT) + NoError(mockT, errors.New("some error")) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestError(t *testing.T) { + + Error(t, errors.New("some error")) + + mockT := new(MockT) + Error(mockT, nil) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestErrorContains(t *testing.T) { + + ErrorContains(t, errors.New("some error: another error"), "some error") + + mockT := new(MockT) + ErrorContains(mockT, errors.New("some error"), "different error") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestEqualError(t *testing.T) { + + EqualError(t, errors.New("some error"), "some error") + + mockT := new(MockT) + EqualError(mockT, errors.New("some error"), "Not some error") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestEmpty(t *testing.T) { + + Empty(t, "") + + mockT := new(MockT) + Empty(mockT, "x") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestNotEmpty(t *testing.T) { + + NotEmpty(t, "x") + + mockT := new(MockT) + NotEmpty(mockT, "") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestWithinDuration(t *testing.T) { + + a := time.Now() + b := a.Add(10 * time.Second) + + WithinDuration(t, a, b, 15*time.Second) + + mockT := new(MockT) + WithinDuration(mockT, a, b, 5*time.Second) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestInDelta(t *testing.T) { + + InDelta(t, 1.001, 1, 0.01) + + mockT := new(MockT) + InDelta(mockT, 1, 2, 0.5) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestZero(t *testing.T) { + + Zero(t, "") + + mockT := new(MockT) + Zero(mockT, "x") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestNotZero(t *testing.T) { + + NotZero(t, "x") + + mockT := new(MockT) + NotZero(mockT, "") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestJSONEq_EqualSONString(t *testing.T) { + mockT := new(MockT) + JSONEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`) + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestJSONEq_EquivalentButNotEqual(t *testing.T) { + mockT := new(MockT) + JSONEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestJSONEq_HashOfArraysAndHashes(t *testing.T) { + mockT := new(MockT) + JSONEq(mockT, "{\r\n\t\"numeric\": 1.5,\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]],\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\"\r\n}", + "{\r\n\t\"numeric\": 1.5,\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\",\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]]\r\n}") + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestJSONEq_Array(t *testing.T) { + mockT := new(MockT) + JSONEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`) + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestJSONEq_HashAndArrayNotEquivalent(t *testing.T) { + mockT := new(MockT) + JSONEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestJSONEq_HashesNotEquivalent(t *testing.T) { + mockT := new(MockT) + JSONEq(mockT, `{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestJSONEq_ActualIsNotJSON(t *testing.T) { + mockT := new(MockT) + JSONEq(mockT, `{"foo": "bar"}`, "Not JSON") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestJSONEq_ExpectedIsNotJSON(t *testing.T) { + mockT := new(MockT) + JSONEq(mockT, "Not JSON", `{"foo": "bar", "hello": "world"}`) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestJSONEq_ExpectedAndActualNotJSON(t *testing.T) { + mockT := new(MockT) + JSONEq(mockT, "Not JSON", "Not JSON") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestJSONEq_ArraysOfDifferentOrder(t *testing.T) { + mockT := new(MockT) + JSONEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestYAMLEq_EqualYAMLString(t *testing.T) { + mockT := new(MockT) + YAMLEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`) + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestYAMLEq_EquivalentButNotEqual(t *testing.T) { + mockT := new(MockT) + YAMLEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestYAMLEq_HashOfArraysAndHashes(t *testing.T) { + mockT := new(MockT) + expected := ` +numeric: 1.5 +array: + - foo: bar + - 1 + - "string" + - ["nested", "array", 5.5] +hash: + nested: hash + nested_slice: [this, is, nested] +string: "foo" +` + + actual := ` +numeric: 1.5 +hash: + nested: hash + nested_slice: [this, is, nested] +string: "foo" +array: + - foo: bar + - 1 + - "string" + - ["nested", "array", 5.5] +` + YAMLEq(mockT, expected, actual) + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestYAMLEq_Array(t *testing.T) { + mockT := new(MockT) + YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`) + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestYAMLEq_HashAndArrayNotEquivalent(t *testing.T) { + mockT := new(MockT) + YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestYAMLEq_HashesNotEquivalent(t *testing.T) { + mockT := new(MockT) + YAMLEq(mockT, `{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestYAMLEq_ActualIsSimpleString(t *testing.T) { + mockT := new(MockT) + YAMLEq(mockT, `{"foo": "bar"}`, "Simple String") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestYAMLEq_ExpectedIsSimpleString(t *testing.T) { + mockT := new(MockT) + YAMLEq(mockT, "Simple String", `{"foo": "bar", "hello": "world"}`) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestYAMLEq_ExpectedAndActualSimpleString(t *testing.T) { + mockT := new(MockT) + YAMLEq(mockT, "Simple String", "Simple String") + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestYAMLEq_ArraysOfDifferentOrder(t *testing.T) { + mockT := new(MockT) + YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func ExampleComparisonAssertionFunc() { + t := &testing.T{} // provided by test + + adder := func(x, y int) int { + return x + y + } + + type args struct { + x int + y int + } + + tests := []struct { + name string + args args + expect int + assertion ComparisonAssertionFunc + }{ + {"2+2=4", args{2, 2}, 4, Equal}, + {"2+2!=5", args{2, 2}, 5, NotEqual}, + {"2+3==5", args{2, 3}, 5, Exactly}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.assertion(t, tt.expect, adder(tt.args.x, tt.args.y)) + }) + } +} + +func TestComparisonAssertionFunc(t *testing.T) { + type iface interface { + Name() string + } + + tests := []struct { + name string + expect interface{} + got interface{} + assertion ComparisonAssertionFunc + }{ + {"implements", (*iface)(nil), t, Implements}, + {"isType", (*testing.T)(nil), t, IsType}, + {"equal", t, t, Equal}, + {"equalValues", t, t, EqualValues}, + {"exactly", t, t, Exactly}, + {"notEqual", t, nil, NotEqual}, + {"NotEqualValues", t, nil, NotEqualValues}, + {"notContains", []int{1, 2, 3}, 4, NotContains}, + {"subset", []int{1, 2, 3, 4}, []int{2, 3}, Subset}, + {"notSubset", []int{1, 2, 3, 4}, []int{0, 3}, NotSubset}, + {"elementsMatch", []byte("abc"), []byte("bac"), ElementsMatch}, + {"regexp", "^t.*y$", "testify", Regexp}, + {"notRegexp", "^t.*y$", "Testify", NotRegexp}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.assertion(t, tt.expect, tt.got) + }) + } +} + +func ExampleValueAssertionFunc() { + t := &testing.T{} // provided by test + + dumbParse := func(input string) interface{} { + var x interface{} + json.Unmarshal([]byte(input), &x) + return x + } + + tests := []struct { + name string + arg string + assertion ValueAssertionFunc + }{ + {"true is not nil", "true", NotNil}, + {"empty string is nil", "", Nil}, + {"zero is not nil", "0", NotNil}, + {"zero is zero", "0", Zero}, + {"false is zero", "false", Zero}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.assertion(t, dumbParse(tt.arg)) + }) + } +} + +func TestValueAssertionFunc(t *testing.T) { + tests := []struct { + name string + value interface{} + assertion ValueAssertionFunc + }{ + {"notNil", true, NotNil}, + {"nil", nil, Nil}, + {"empty", []int{}, Empty}, + {"notEmpty", []int{1}, NotEmpty}, + {"zero", false, Zero}, + {"notZero", 42, NotZero}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.assertion(t, tt.value) + }) + } +} + +func ExampleBoolAssertionFunc() { + t := &testing.T{} // provided by test + + isOkay := func(x int) bool { + return x >= 42 + } + + tests := []struct { + name string + arg int + assertion BoolAssertionFunc + }{ + {"-1 is bad", -1, False}, + {"42 is good", 42, True}, + {"41 is bad", 41, False}, + {"45 is cool", 45, True}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.assertion(t, isOkay(tt.arg)) + }) + } +} + +func TestBoolAssertionFunc(t *testing.T) { + tests := []struct { + name string + value bool + assertion BoolAssertionFunc + }{ + {"true", true, True}, + {"false", false, False}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.assertion(t, tt.value) + }) + } +} + +func ExampleErrorAssertionFunc() { + t := &testing.T{} // provided by test + + dumbParseNum := func(input string, v interface{}) error { + return json.Unmarshal([]byte(input), v) + } + + tests := []struct { + name string + arg string + assertion ErrorAssertionFunc + }{ + {"1.2 is number", "1.2", NoError}, + {"1.2.3 not number", "1.2.3", Error}, + {"true is not number", "true", Error}, + {"3 is number", "3", NoError}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var x float64 + tt.assertion(t, dumbParseNum(tt.arg, &x)) + }) + } +} + +func TestErrorAssertionFunc(t *testing.T) { + tests := []struct { + name string + err error + assertion ErrorAssertionFunc + }{ + {"noError", nil, NoError}, + {"error", errors.New("whoops"), Error}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.assertion(t, tt.err) + }) + } +} diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/suite/doc.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/suite/doc.go new file mode 100644 index 0000000..f91a245 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/suite/doc.go @@ -0,0 +1,65 @@ +// Package suite contains logic for creating testing suite structs +// and running the methods on those structs as tests. The most useful +// piece of this package is that you can create setup/teardown methods +// on your testing suites, which will run before/after the whole suite +// or individual tests (depending on which interface(s) you +// implement). +// +// A testing suite is usually built by first extending the built-in +// suite functionality from suite.Suite in testify. Alternatively, +// you could reproduce that logic on your own if you wanted (you +// just need to implement the TestingSuite interface from +// suite/interfaces.go). +// +// After that, you can implement any of the interfaces in +// suite/interfaces.go to add setup/teardown functionality to your +// suite, and add any methods that start with "Test" to add tests. +// Methods that do not match any suite interfaces and do not begin +// with "Test" will not be run by testify, and can safely be used as +// helper methods. +// +// Once you've built your testing suite, you need to run the suite +// (using suite.Run from testify) inside any function that matches the +// identity that "go test" is already looking for (i.e. +// func(*testing.T)). +// +// Regular expression to select test suites specified command-line +// argument "-run". Regular expression to select the methods +// of test suites specified command-line argument "-m". +// Suite object has assertion methods. +// +// A crude example: +// // Basic imports +// import ( +// "testing" +// "github.com/stretchr/testify/assert" +// "github.com/stretchr/testify/suite" +// ) +// +// // Define the suite, and absorb the built-in basic suite +// // functionality from testify - including a T() method which +// // returns the current testing context +// type ExampleTestSuite struct { +// suite.Suite +// VariableThatShouldStartAtFive int +// } +// +// // Make sure that VariableThatShouldStartAtFive is set to five +// // before each test +// func (suite *ExampleTestSuite) SetupTest() { +// suite.VariableThatShouldStartAtFive = 5 +// } +// +// // All methods that begin with "Test" are run as tests within a +// // suite. +// func (suite *ExampleTestSuite) TestExample() { +// assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive) +// suite.Equal(5, suite.VariableThatShouldStartAtFive) +// } +// +// // In order for 'go test' to run this suite, we need to create +// // a normal test function and pass our suite to suite.Run +// func TestExampleTestSuite(t *testing.T) { +// suite.Run(t, new(ExampleTestSuite)) +// } +package suite diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/suite/interfaces.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/suite/interfaces.go new file mode 100644 index 0000000..fed037d --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/suite/interfaces.go @@ -0,0 +1,66 @@ +package suite + +import "testing" + +// TestingSuite can store and return the current *testing.T context +// generated by 'go test'. +type TestingSuite interface { + T() *testing.T + SetT(*testing.T) + SetS(suite TestingSuite) +} + +// SetupAllSuite has a SetupSuite method, which will run before the +// tests in the suite are run. +type SetupAllSuite interface { + SetupSuite() +} + +// SetupTestSuite has a SetupTest method, which will run before each +// test in the suite. +type SetupTestSuite interface { + SetupTest() +} + +// TearDownAllSuite has a TearDownSuite method, which will run after +// all the tests in the suite have been run. +type TearDownAllSuite interface { + TearDownSuite() +} + +// TearDownTestSuite has a TearDownTest method, which will run after +// each test in the suite. +type TearDownTestSuite interface { + TearDownTest() +} + +// BeforeTest has a function to be executed right before the test +// starts and receives the suite and test names as input +type BeforeTest interface { + BeforeTest(suiteName, testName string) +} + +// AfterTest has a function to be executed right after the test +// finishes and receives the suite and test names as input +type AfterTest interface { + AfterTest(suiteName, testName string) +} + +// WithStats implements HandleStats, a function that will be executed +// when a test suite is finished. The stats contain information about +// the execution of that suite and its tests. +type WithStats interface { + HandleStats(suiteName string, stats *SuiteInformation) +} + +// SetupSubTest has a SetupSubTest method, which will run before each +// subtest in the suite. +type SetupSubTest interface { + SetupSubTest() +} + +// TearDownSubTest has a TearDownSubTest method, which will run after +// each subtest in the suite have been run. +type TearDownSubTest interface { + TearDownSubTest() +} diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/suite/stats.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/suite/stats.go new file mode 100644 index 0000000..261da37 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/suite/stats.go @@ -0,0 +1,46 @@ +package suite + +import "time" + +// SuiteInformation stats stores stats for the whole suite execution. +type SuiteInformation struct { + Start, End time.Time + TestStats map[string]*TestInformation +} + +// TestInformation stores information about the execution of each test. +type TestInformation struct { + TestName string + Start, End time.Time + Passed bool +} + +func newSuiteInformation() *SuiteInformation { + testStats := make(map[string]*TestInformation) + + return &SuiteInformation{ + TestStats: testStats, + } +} + +func (s SuiteInformation) start(testName string) { + s.TestStats[testName] = &TestInformation{ + TestName: testName, + Start: time.Now(), + } +} + +func (s SuiteInformation) end(testName string, passed bool) { + s.TestStats[testName].End = time.Now() + s.TestStats[testName].Passed = passed +} + +func (s SuiteInformation) Passed() bool { + for _, stats := range s.TestStats { + if !stats.Passed { + return false + } + } + + return true +} diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/suite/stats_test.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/suite/stats_test.go new file mode 100644 index 0000000..4446a6d --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/suite/stats_test.go @@ -0,0 +1,29 @@ +package suite + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestPassedReturnsTrueWhenAllTestsPass(t *testing.T) { + sinfo := newSuiteInformation() + sinfo.TestStats = map[string]*TestInformation{ + "Test1": {TestName: "Test1", Passed: true}, + "Test2": {TestName: "Test2", Passed: true}, + "Test3": {TestName: "Test3", Passed: true}, + } + + assert.True(t, sinfo.Passed()) +} + +func TestPassedReturnsFalseWhenSomeTestFails(t *testing.T) { + sinfo := newSuiteInformation() + sinfo.TestStats = map[string]*TestInformation{ + "Test1": {TestName: "Test1", Passed: true}, + "Test2": {TestName: "Test2", Passed: false}, + "Test3": {TestName: "Test3", Passed: true}, + } + + assert.False(t, sinfo.Passed()) +} diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/suite/suite.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/suite/suite.go new file mode 100644 index 0000000..8b4202d --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/suite/suite.go @@ -0,0 +1,248 @@ +package suite + +import ( + "flag" + "fmt" + "os" + "reflect" + "regexp" + "runtime/debug" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var allTestsFilter = func(_, _ string) (bool, error) { return true, nil } +var matchMethod = flag.String("testify.m", "", "regular expression to select tests of the testify suite to run") + +// Suite is a basic testing suite with methods for storing and +// retrieving the current *testing.T context. +type Suite struct { + *assert.Assertions + + mu sync.RWMutex + require *require.Assertions + t *testing.T + + // Parent suite to have access to the implemented methods of parent struct + s TestingSuite +} + +// T retrieves the current *testing.T context. +func (suite *Suite) T() *testing.T { + suite.mu.RLock() + defer suite.mu.RUnlock() + return suite.t +} + +// SetT sets the current *testing.T context. +func (suite *Suite) SetT(t *testing.T) { + suite.mu.Lock() + defer suite.mu.Unlock() + suite.t = t + suite.Assertions = assert.New(t) + suite.require = require.New(t) +} + +// SetS needs to set the current test suite as parent +// to get access to the parent methods +func (suite *Suite) SetS(s TestingSuite) { + suite.s = s +} + +// Require returns a require context for suite. +func (suite *Suite) Require() *require.Assertions { + suite.mu.Lock() + defer suite.mu.Unlock() + if suite.require == nil { + suite.require = require.New(suite.T()) + } + return suite.require +} + +// Assert returns an assert context for suite. Normally, you can call +// `suite.NoError(expected, actual)`, but for situations where the embedded +// methods are overridden (for example, you might want to override +// assert.Assertions with require.Assertions), this method is provided so you +// can call `suite.Assert().NoError()`. +func (suite *Suite) Assert() *assert.Assertions { + suite.mu.Lock() + defer suite.mu.Unlock() + if suite.Assertions == nil { + suite.Assertions = assert.New(suite.T()) + } + return suite.Assertions +} + +func recoverAndFailOnPanic(t *testing.T) { + r := recover() + failOnPanic(t, r) +} + +func failOnPanic(t *testing.T, r interface{}) { + if r != nil { + t.Errorf("test panicked: %v\n%s", r, debug.Stack()) + t.FailNow() + } +} + +// Run provides suite functionality around golang subtests. It should be +// called in place of t.Run(name, func(t *testing.T)) in test suite code. +// The passed-in func will be executed as a subtest with a fresh instance of t. +// Provides compatibility with go test pkg -run TestSuite/TestName/SubTestName. +func (suite *Suite) Run(name string, subtest func()) bool { + oldT := suite.T() + + if setupSubTest, ok := suite.s.(SetupSubTest); ok { + setupSubTest.SetupSubTest() + } + + defer func() { + suite.SetT(oldT) + if tearDownSubTest, ok := suite.s.(TearDownSubTest); ok { + tearDownSubTest.TearDownSubTest() + } + }() + + return oldT.Run(name, func(t *testing.T) { + suite.SetT(t) + subtest() + }) +} + +// Run takes a testing suite and runs all of the tests attached +// to it. +func Run(t *testing.T, suite TestingSuite) { + defer recoverAndFailOnPanic(t) + + suite.SetT(t) + suite.SetS(suite) + + var suiteSetupDone bool + + var stats *SuiteInformation + if _, ok := suite.(WithStats); ok { + stats = newSuiteInformation() + } + + tests := []testing.InternalTest{} + methodFinder := reflect.TypeOf(suite) + suiteName := methodFinder.Elem().Name() + + for i := 0; i < methodFinder.NumMethod(); i++ { + method := methodFinder.Method(i) + + ok, err := methodFilter(method.Name) + if err != nil { + fmt.Fprintf(os.Stderr, "testify: invalid regexp for -m: %s\n", err) + os.Exit(1) + } + + if !ok { + continue + } + + if !suiteSetupDone { + if stats != nil { + stats.Start = time.Now() + } + + if setupAllSuite, ok := suite.(SetupAllSuite); ok { + setupAllSuite.SetupSuite() + } + + suiteSetupDone = true + } + + test := testing.InternalTest{ + Name: method.Name, + F: func(t *testing.T) { + parentT := suite.T() + suite.SetT(t) + defer recoverAndFailOnPanic(t) + defer func() { + r := recover() + + if stats != nil { + passed := !t.Failed() && r == nil + stats.end(method.Name, passed) + } + + if afterTestSuite, ok := suite.(AfterTest); ok { + afterTestSuite.AfterTest(suiteName, method.Name) + } + + if tearDownTestSuite, ok := suite.(TearDownTestSuite); ok { + tearDownTestSuite.TearDownTest() + } + + suite.SetT(parentT) + failOnPanic(t, r) + }() + + if setupTestSuite, ok := suite.(SetupTestSuite); ok { + setupTestSuite.SetupTest() + } + if beforeTestSuite, ok := suite.(BeforeTest); ok { + beforeTestSuite.BeforeTest(methodFinder.Elem().Name(), method.Name) + } + + if stats != nil { + stats.start(method.Name) + } + + method.Func.Call([]reflect.Value{reflect.ValueOf(suite)}) + }, + } + tests = append(tests, test) + } + if suiteSetupDone { + defer func() { + if tearDownAllSuite, ok := suite.(TearDownAllSuite); ok { + tearDownAllSuite.TearDownSuite() + } + + if suiteWithStats, measureStats := suite.(WithStats); measureStats { + stats.End = time.Now() + suiteWithStats.HandleStats(suiteName, stats) + } + }() + } + + runTests(t, tests) +} + +// Filtering method according to set regular expression +// specified command-line argument -m +func methodFilter(name string) (bool, error) { + if ok, _ := regexp.MatchString("^Test", name); !ok { + return false, nil + } + return regexp.MatchString(*matchMethod, name) +} + +func runTests(t testing.TB, tests []testing.InternalTest) { + if len(tests) == 0 { + t.Log("warning: no tests to run") + return + } + + r, ok := t.(runner) + if !ok { // backwards compatibility with Go 1.6 and below + if !testing.RunTests(allTestsFilter, tests) { + t.Fail() + } + return + } + + for _, test := range tests { + r.Run(test.Name, test.F) + } +} + +type runner interface { + Run(name string, f func(t *testing.T)) bool +} diff --git a/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/suite/suite_test.go b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/suite/suite_test.go new file mode 100644 index 0000000..d684f52 --- /dev/null +++ b/.devenv/state/go/pkg/mod/github.com/stretchr/testify@v1.8.2/suite/suite_test.go @@ -0,0 +1,619 @@ +package suite + +import ( + "bytes" + "errors" + "flag" + "io/ioutil" + "math/rand" + "os" + "os/exec" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// SuiteRequireTwice is intended to test the usage of suite.Require in two +// different tests +type SuiteRequireTwice struct{ Suite } + +// TestSuiteRequireTwice checks for regressions of issue #149 where +// suite.requirements was not initialised in suite.SetT() +// A regression would result on these tests panicking rather than failing. +func TestSuiteRequireTwice(t *testing.T) { + ok := testing.RunTests( + allTestsFilter, + []testing.InternalTest{{ + Name: "TestSuiteRequireTwice", + F: func(t *testing.T) { + suite := new(SuiteRequireTwice) + Run(t, suite) + }, + }}, + ) + assert.Equal(t, false, ok) +} + +func (s *SuiteRequireTwice) TestRequireOne() { + r := s.Require() + r.Equal(1, 2) +} + +func (s *SuiteRequireTwice) TestRequireTwo() { + r := s.Require() + r.Equal(1, 2) +} + +type panickingSuite struct { + Suite + panicInSetupSuite bool + panicInSetupTest bool + panicInBeforeTest bool + panicInTest bool + panicInAfterTest bool + panicInTearDownTest bool + panicInTearDownSuite bool +} + +func (s *panickingSuite) SetupSuite() { + if s.panicInSetupSuite { + panic("oops in setup suite") + } +} + +func (s *panickingSuite) SetupTest() { + if s.panicInSetupTest { + panic("oops in setup test") + } +} + +func (s *panickingSuite) BeforeTest(_, _ string) { + if s.panicInBeforeTest { + panic("oops in before test") + } +} + +func (s *panickingSuite) Test() { + if s.panicInTest { + panic("oops in test") + } +} + +func (s *panickingSuite) AfterTest(_, _ string) { + if s.panicInAfterTest { + panic("oops in after test") + } +} + +func (s *panickingSuite) TearDownTest() { + if s.panicInTearDownTest { + panic("oops in tear down test") + } +} + +func (s *panickingSuite) TearDownSuite() { + if s.panicInTearDownSuite { + panic("oops in tear down suite") + } +} + +func TestSuiteRecoverPanic(t *testing.T) { + ok := true + panickingTests := []testing.InternalTest{ + { + Name: "TestPanicInSetupSuite", + F: func(t *testing.T) { Run(t, &panickingSuite{panicInSetupSuite: true}) }, + }, + { + Name: "TestPanicInSetupTest", + F: func(t *testing.T) { Run(t, &panickingSuite{panicInSetupTest: true}) }, + }, + { + Name: "TestPanicInBeforeTest", + F: func(t *testing.T) { Run(t, &panickingSuite{panicInBeforeTest: true}) }, + }, + { + Name: "TestPanicInTest", + F: func(t *testing.T) { Run(t, &panickingSuite{panicInTest: true}) }, + }, + { + Name: "TestPanicInAfterTest", + F: func(t *testing.T) { Run(t, &panickingSuite{panicInAfterTest: true}) }, + }, + { + Name: "TestPanicInTearDownTest", + F: func(t *testing.T) { Run(t, &panickingSuite{panicInTearDownTest: true}) }, + }, + { + Name: "TestPanicInTearDownSuite", + F: func(t *testing.T) { Run(t, &panickingSuite{panicInTearDownSuite: true}) }, + }, + } + + require.NotPanics(t, func() { + ok = testing.RunTests(allTestsFilter, panickingTests) + }) + + assert.False(t, ok) +} + +// This suite is intended to store values to make sure that only +// testing-suite-related methods are run. It's also a fully +// functional example of a testing suite, using setup/teardown methods +// and a helper method that is ignored by testify. To make this look +// more like a real world example, all tests in the suite perform some +// type of assertion. +type SuiteTester struct { + // Include our basic suite logic. + Suite + + // Keep counts of how many times each method is run. + SetupSuiteRunCount int + TearDownSuiteRunCount int + SetupTestRunCount int + TearDownTestRunCount int + TestOneRunCount int + TestTwoRunCount int + TestSubtestRunCount int + NonTestMethodRunCount int + SetupSubTestRunCount int + TearDownSubTestRunCount int + + SuiteNameBefore []string + TestNameBefore []string + + SuiteNameAfter []string + TestNameAfter []string + + TimeBefore []time.Time + TimeAfter []time.Time +} + +// The SetupSuite method will be run by testify once, at the very +// start of the testing suite, before any tests are run. +func (suite *SuiteTester) SetupSuite() { + suite.SetupSuiteRunCount++ +} + +func (suite *SuiteTester) BeforeTest(suiteName, testName string) { + suite.SuiteNameBefore = append(suite.SuiteNameBefore, suiteName) + suite.TestNameBefore = append(suite.TestNameBefore, testName) + suite.TimeBefore = append(suite.TimeBefore, time.Now()) +} + +func (suite *SuiteTester) AfterTest(suiteName, testName string) { + suite.SuiteNameAfter = append(suite.SuiteNameAfter, suiteName) + suite.TestNameAfter = append(suite.TestNameAfter, testName) + suite.TimeAfter = append(suite.TimeAfter, time.Now()) +} + +// The TearDownSuite method will be run by testify once, at the very +// end of the testing suite, after all tests have been run. +func (suite *SuiteTester) TearDownSuite() { + suite.TearDownSuiteRunCount++ +} + +// The SetupTest method will be run before every test in the suite. +func (suite *SuiteTester) SetupTest() { + suite.SetupTestRunCount++ +} + +// The TearDownTest method will be run after every test in the suite. +func (suite *SuiteTester) TearDownTest() { + suite.TearDownTestRunCount++ +} + +// Every method in a testing suite that begins with "Test" will be run +// as a test. TestOne is an example of a test. For the purposes of +// this example, we've included assertions in the tests, since most +// tests will issue assertions. +func (suite *SuiteTester) TestOne() { + beforeCount := suite.TestOneRunCount + suite.TestOneRunCount++ + assert.Equal(suite.T(), suite.TestOneRunCount, beforeCount+1) + suite.Equal(suite.TestOneRunCount, beforeCount+1) +} + +// TestTwo is another example of a test. +func (suite *SuiteTester) TestTwo() { + beforeCount := suite.TestTwoRunCount + suite.TestTwoRunCount++ + assert.NotEqual(suite.T(), suite.TestTwoRunCount, beforeCount) + suite.NotEqual(suite.TestTwoRunCount, beforeCount) +} + +func (suite *SuiteTester) TestSkip() { + suite.T().Skip() +} + +// NonTestMethod does not begin with "Test", so it will not be run by +// testify as a test in the suite. This is useful for creating helper +// methods for your tests. +func (suite *SuiteTester) NonTestMethod() { + suite.NonTestMethodRunCount++ +} + +func (suite *SuiteTester) TestSubtest() { + suite.TestSubtestRunCount++ + + for _, t := range []struct { + testName string + }{ + {"first"}, + {"second"}, + } { + suiteT := suite.T() + suite.Run(t.testName, func() { + // We should get a different *testing.T for subtests, so that + // go test recognizes them as proper subtests for output formatting + // and running individual subtests + subTestT := suite.T() + suite.NotEqual(subTestT, suiteT) + }) + suite.Equal(suiteT, suite.T()) + } +} + +func (suite *SuiteTester) TearDownSubTest() { + suite.TearDownSubTestRunCount++ +} + +func (suite *SuiteTester) SetupSubTest() { + suite.SetupSubTestRunCount++ +} + +type SuiteSkipTester struct { + // Include our basic suite logic. + Suite + + // Keep counts of how many times each method is run. + SetupSuiteRunCount int + TearDownSuiteRunCount int +} + +func (suite *SuiteSkipTester) SetupSuite() { + suite.SetupSuiteRunCount++ + suite.T().Skip() +} + +func (suite *SuiteSkipTester) TestNothing() { + // SetupSuite is only called when at least one test satisfies + // test filter. For this suite to be set up (and then tore down) + // it is necessary to add at least one test method. +} + +func (suite *SuiteSkipTester) TearDownSuite() { + suite.TearDownSuiteRunCount++ +} + +// TestRunSuite will be run by the 'go test' command, so within it, we +// can run our suite using the Run(*testing.T, TestingSuite) function. +func TestRunSuite(t *testing.T) { + suiteTester := new(SuiteTester) + Run(t, suiteTester) + + // Normally, the test would end here. The following are simply + // some assertions to ensure that the Run function is working as + // intended - they are not part of the example. + + // The suite was only run once, so the SetupSuite and TearDownSuite + // methods should have each been run only once. + assert.Equal(t, suiteTester.SetupSuiteRunCount, 1) + assert.Equal(t, suiteTester.TearDownSuiteRunCount, 1) + + assert.Equal(t, len(suiteTester.SuiteNameAfter), 4) + assert.Equal(t, len(suiteTester.SuiteNameBefore), 4) + assert.Equal(t, len(suiteTester.TestNameAfter), 4) + assert.Equal(t, len(suiteTester.TestNameBefore), 4) + + assert.Contains(t, suiteTester.TestNameAfter, "TestOne") + assert.Contains(t, suiteTester.TestNameAfter, "TestTwo") + assert.Contains(t, suiteTester.TestNameAfter, "TestSkip") + assert.Contains(t, suiteTester.TestNameAfter, "TestSubtest") + + assert.Contains(t, suiteTester.TestNameBefore, "TestOne") + assert.Contains(t, suiteTester.TestNameBefore, "TestTwo") + assert.Contains(t, suiteTester.TestNameBefore, "TestSkip") + assert.Contains(t, suiteTester.TestNameBefore, "TestSubtest") + + for _, suiteName := range suiteTester.SuiteNameAfter { + assert.Equal(t, "SuiteTester", suiteName) + } + + for _, suiteName := range suiteTester.SuiteNameBefore { + assert.Equal(t, "SuiteTester", suiteName) + } + + for _, when := range suiteTester.TimeAfter { + assert.False(t, when.IsZero()) + } + + for _, when := range suiteTester.TimeBefore { + assert.False(t, when.IsZero()) + } + + // There are four test methods (TestOne, TestTwo, TestSkip, and TestSubtest), so + // the SetupTest and TearDownTest methods (which should be run once for + // each test) should have been run four times. + assert.Equal(t, suiteTester.SetupTestRunCount, 4) + assert.Equal(t, suiteTester.TearDownTestRunCount, 4) + + // Each test should have been run once. + assert.Equal(t, suiteTester.TestOneRunCount, 1) + assert.Equal(t, suiteTester.TestTwoRunCount, 1) + assert.Equal(t, suiteTester.TestSubtestRunCount, 1) + + assert.Equal(t, suiteTester.TearDownSubTestRunCount, 2) + assert.Equal(t, suiteTester.SetupSubTestRunCount, 2) + + // Methods that don't match the test method identifier shouldn't + // have been run at all. + assert.Equal(t, suiteTester.NonTestMethodRunCount, 0) + + suiteSkipTester := new(SuiteSkipTester) + Run(t, suiteSkipTester) + + // The suite was only run once, so the SetupSuite and TearDownSuite + // methods should have each been run only once, even though SetupSuite + // called Skip() + assert.Equal(t, suiteSkipTester.SetupSuiteRunCount, 1) + assert.Equal(t, suiteSkipTester.TearDownSuiteRunCount, 1) + +} + +// This suite has no Test... methods. It's setup and teardown must be skipped. +type SuiteSetupSkipTester struct { + Suite + + setUp bool + toreDown bool +} + +func (s *SuiteSetupSkipTester) SetupSuite() { + s.setUp = true +} + +func (s *SuiteSetupSkipTester) NonTestMethod() { + +} + +func (s *SuiteSetupSkipTester) TearDownSuite() { + s.toreDown = true +} + +func TestSkippingSuiteSetup(t *testing.T) { + suiteTester := new(SuiteSetupSkipTester) + Run(t, suiteTester) + assert.False(t, suiteTester.setUp) + assert.False(t, suiteTester.toreDown) +} + +func TestSuiteGetters(t *testing.T) { + suite := new(SuiteTester) + suite.SetT(t) + assert.NotNil(t, suite.Assert()) + assert.Equal(t, suite.Assertions, suite.Assert()) + assert.NotNil(t, suite.Require()) + assert.Equal(t, suite.require, suite.Require()) +} + +type SuiteLoggingTester struct { + Suite +} + +func (s *SuiteLoggingTester) TestLoggingPass() { + s.T().Log("TESTLOGPASS") +} + +func (s *SuiteLoggingTester) TestLoggingFail() { + s.T().Log("TESTLOGFAIL") + assert.NotNil(s.T(), nil) // expected to fail +} + +type StdoutCapture struct { + oldStdout *os.File + readPipe *os.File +} + +func (sc *StdoutCapture) StartCapture() { + sc.oldStdout = os.Stdout + sc.readPipe, os.Stdout, _ = os.Pipe() +} + +func (sc *StdoutCapture) StopCapture() (string, error) { + if sc.oldStdout == nil || sc.readPipe == nil { + return "", errors.New("StartCapture not called before StopCapture") + } + os.Stdout.Close() + os.Stdout = sc.oldStdout + bytes, err := ioutil.ReadAll(sc.readPipe) + if err != nil { + return "", err + } + return string(bytes), nil +} + +func TestSuiteLogging(t *testing.T) { + suiteLoggingTester := new(SuiteLoggingTester) + capture := StdoutCapture{} + internalTest := testing.InternalTest{ + Name: "SomeTest", + F: func(subT *testing.T) { + Run(subT, suiteLoggingTester) + }, + } + capture.StartCapture() + testing.RunTests(allTestsFilter, []testing.InternalTest{internalTest}) + output, err := capture.StopCapture() + require.NoError(t, err, "Got an error trying to capture stdout and stderr!") + require.NotEmpty(t, output, "output content must not be empty") + + // Failed tests' output is always printed + assert.Contains(t, output, "TESTLOGFAIL") + + if testing.Verbose() { + // In verbose mode, output from successful tests is also printed + assert.Contains(t, output, "TESTLOGPASS") + } else { + assert.NotContains(t, output, "TESTLOGPASS") + } +} + +type CallOrderSuite struct { + Suite + callOrder []string +} + +func (s *CallOrderSuite) call(method string) { + time.Sleep(time.Duration(rand.Intn(300)) * time.Millisecond) + s.callOrder = append(s.callOrder, method) +} + +func TestSuiteCallOrder(t *testing.T) { + Run(t, new(CallOrderSuite)) +} +func (s *CallOrderSuite) SetupSuite() { + s.call("SetupSuite") +} + +func (s *CallOrderSuite) TearDownSuite() { + s.call("TearDownSuite") + assert.Equal(s.T(), "SetupSuite;SetupTest;Test A;TearDownTest;SetupTest;Test B;TearDownTest;TearDownSuite", strings.Join(s.callOrder, ";")) +} +func (s *CallOrderSuite) SetupTest() { + s.call("SetupTest") +} + +func (s *CallOrderSuite) TearDownTest() { + s.call("TearDownTest") +} + +func (s *CallOrderSuite) Test_A() { + s.call("Test A") +} + +func (s *CallOrderSuite) Test_B() { + s.call("Test B") +} + +type suiteWithStats struct { + Suite + wasCalled bool + stats *SuiteInformation +} + +func (s *suiteWithStats) HandleStats(suiteName string, stats *SuiteInformation) { + s.wasCalled = true + s.stats = stats +} + +func (s *suiteWithStats) TestSomething() { + s.Equal(1, 1) +} + +func (s *suiteWithStats) TestPanic() { + panic("oops") +} + +func TestSuiteWithStats(t *testing.T) { + suiteWithStats := new(suiteWithStats) + + testing.RunTests(allTestsFilter, []testing.InternalTest{ + { + Name: "WithStats", + F: func(t *testing.T) { + Run(t, suiteWithStats) + }, + }, + }) + + assert.True(t, suiteWithStats.wasCalled) + assert.NotZero(t, suiteWithStats.stats.Start) + assert.NotZero(t, suiteWithStats.stats.End) + assert.False(t, suiteWithStats.stats.Passed()) + + testStats := suiteWithStats.stats.TestStats + + assert.NotZero(t, testStats["TestSomething"].Start) + assert.NotZero(t, testStats["TestSomething"].End) + assert.True(t, testStats["TestSomething"].Passed) + + assert.NotZero(t, testStats["TestPanic"].Start) + assert.NotZero(t, testStats["TestPanic"].End) + assert.False(t, testStats["TestPanic"].Passed) +} + +// FailfastSuite will test the behavior when running with the failfast flag +// It logs calls in the callOrder slice which we then use to assert the correct calls were made +type FailfastSuite struct { + Suite + callOrder []string +} + +func (s *FailfastSuite) call(method string) { + s.callOrder = append(s.callOrder, method) +} + +func TestFailfastSuite(t *testing.T) { + // This test suite is run twice. Once normally and once with the -failfast flag by TestFailfastSuiteFailFastOn + // If you need to debug it run this test directly with the failfast flag set on/off as you need + failFast := flag.Lookup("test.failfast").Value.(flag.Getter).Get().(bool) + s := new(FailfastSuite) + ok := testing.RunTests( + allTestsFilter, + []testing.InternalTest{{ + Name: "TestFailfastSuite", + F: func(t *testing.T) { + Run(t, s) + }, + }}, + ) + assert.Equal(t, false, ok) + if failFast { + // Test A Fails and because we are running with failfast Test B never runs and we proceed straight to TearDownSuite + assert.Equal(t, "SetupSuite;SetupTest;Test A Fails;TearDownTest;TearDownSuite", strings.Join(s.callOrder, ";")) + } else { + // Test A Fails and because we are running without failfast we continue and run Test B and then proceed to TearDownSuite + assert.Equal(t, "SetupSuite;SetupTest;Test A Fails;TearDownTest;SetupTest;Test B Passes;TearDownTest;TearDownSuite", strings.Join(s.callOrder, ";")) + } +} +func TestFailfastSuiteFailFastOn(t *testing.T) { + // To test this with failfast on (and isolated from other intended test failures in our test suite) we launch it in its own process + cmd := exec.Command("go", "test", "-v", "-race", "-run", "TestFailfastSuite", "-failfast") + var out bytes.Buffer + cmd.Stdout = &out + t.Log("Running go test -v -race -run TestFailfastSuite -failfast") + err := cmd.Run() + t.Log(out.String()) + if err != nil { + t.Log(err) + t.Fail() + } +} +func (s *FailfastSuite) SetupSuite() { + s.call("SetupSuite") +} + +func (s *FailfastSuite) TearDownSuite() { + s.call("TearDownSuite") +} +func (s *FailfastSuite) SetupTest() { + s.call("SetupTest") +} + +func (s *FailfastSuite) TearDownTest() { + s.call("TearDownTest") +} + +func (s *FailfastSuite) Test_A_Fails() { + s.call("Test A Fails") + s.T().Error("Test A meant to fail") +} + +func (s *FailfastSuite) Test_B_Passes() { + s.call("Test B Passes") + s.Require().True(true) +} diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/.chglog/CHANGELOG.tpl.md b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/.chglog/CHANGELOG.tpl.md new file mode 100644 index 0000000..8e98c0b --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/.chglog/CHANGELOG.tpl.md @@ -0,0 +1,27 @@ +{{ range .Versions }} +<a name="{{ .Tag.Name }}"></a> +## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }} +{{ range .CommitGroups -}} +### {{ .Title }} +{{ range .Commits -}} +- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} +{{ end }} +{{ end -}} + +{{- if .NoteGroups -}} +{{ range .NoteGroups -}} +### {{ .Title }} +{{ range .Notes }} +{{ .Body }} +{{ end }} +{{ end -}} +{{ end -}} +{{ end -}} + +{{- if .Versions }} +{{ range .Versions -}} +{{ if .Tag.Previous -}} +[{{ .Tag.Name }}]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }} +{{ end -}} +{{ end -}} +{{ end -}} \ No newline at end of file diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/.chglog/config.yml b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/.chglog/config.yml new file mode 100644 index 0000000..237f6bc --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/.chglog/config.yml @@ -0,0 +1,57 @@ +style: gitlab +template: CHANGELOG.tpl.md +info: + title: CHANGELOG + repository_url: https://gitlab.schukai.com/oss/libraries/go/utilities/pathfinder +options: + commits: + filters: + Type: + - feat + - fix + - doc + - refactor + - perf + - test + - chore + ## deprecated types and typos + - docs + - documentation + - feat + - added + - add + - bugfix + - revert + - update + - updates + - change + - changed + commit_groups: + title_maps: + feat: Add Features + fix: Bug Fixes + doc: Documentation + refactor: Code Refactoring + perf: Performance Improvements + test: Tests + ## Chore is used for all other changes that don't fit in the other categories + chore: Changes + ## deprecated types and typos + docs: Documentation + documentation: Documentation + added: Add Features + add: Add Features + bugfix: Bug Fixes + revert: Reverts + update: Changes + updates: Changes + change: Changes + changed: Changes + header: + pattern: "^((\\w+)\\s.*)$" + pattern_maps: + - Subject + - Type + notes: + keywords: + - BREAKING CHANGE diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/.gitignore b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/.gitignore new file mode 100644 index 0000000..c55f7d0 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/.gitignore @@ -0,0 +1,149 @@ +# Created by https://www.toptal.com/developers/gitignore/api/intellij,go +# Edit at https://www.toptal.com/developers/gitignore?templates=intellij,go + +### Go ### +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +testdata/ + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work + +# Go Fuzz build +testdata/ + +### Go Patch ### +/vendor/ +/Godeps/ + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Intellij Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +# Azure Toolkit for IntelliJ plugin +# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij +.idea/**/azureSettings.xml + +# End of https://www.toptal.com/developers/gitignore/api/intellij,go \ No newline at end of file diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/CHANGELOG.md b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/CHANGELOG.md new file mode 100644 index 0000000..859750c --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/CHANGELOG.md @@ -0,0 +1,37 @@ + +<a name="v0.5.2"></a> +## [v0.5.2] - 2022-12-23 + +<a name="v0.5.1"></a> +## [v0.5.1] - 2022-12-23 + +<a name="v0.5.0"></a> +## [v0.5.0] - 2022-12-18 + +<a name="v0.4.0"></a> +## [v0.4.0] - 2022-12-17 + +<a name="v0.3.1"></a> +## [v0.3.1] - 2022-10-16 +### Bug Fixes +- fix secure access to structure with a constraint + + +<a name="v0.3.0"></a> +## [v0.3.0] - 2022-10-15 +### Code Refactoring +- refactor change function signatur + + +<a name="v0.2.0"></a> +## v0.2.0 - 2022-10-15 +### Add Features +- feat takeover from other project + + +[v0.5.2]: https://gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/compare/v0.5.1...v0.5.2 +[v0.5.1]: https://gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/compare/v0.5.0...v0.5.1 +[v0.5.0]: https://gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/compare/v0.4.0...v0.5.0 +[v0.4.0]: https://gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/compare/v0.3.1...v0.4.0 +[v0.3.1]: https://gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/compare/v0.3.0...v0.3.1 +[v0.3.0]: https://gitlab.schukai.com/oss/libraries/go/utilities/pathfinder/compare/v0.2.0...v0.3.0 diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/LICENSE b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/LICENSE new file mode 100644 index 0000000..22686f9 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/LICENSE @@ -0,0 +1,662 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + AGPL-3.0 + + Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +<https://www.gnu.org/licenses/>. \ No newline at end of file diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/Makefile b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/Makefile new file mode 100644 index 0000000..c149982 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/Makefile @@ -0,0 +1,157 @@ +## Copyright 2022 schukai GmbH. All rights reserved. +## Use of this source code is governed by a AGPL-3.0 +## license that can be found in the LICENSE file. + +PROJECT_ROOT:=$(dir $(realpath $(lastword $(MAKEFILE_LIST)))) +THIS_MAKEFILE:=$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)) +THIS_MAKEFILE_PATH:=$(PROJECT_ROOT)$(THIS_MAKEFILE) + +# @see .PHONY https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html#Phony-Targets +.DEFAULT_GOAL := help + +.PHONY: print +## Print Path +print: + @echo "THIS_MAKEFILE: $(THIS_MAKEFILE)" + @echo "THIS_MAKEFILE_PATH: $(THIS_MAKEFILE_PATH)" + @echo "PROJECT_ROOT: $(PROJECT_ROOT)" + +# Add a comment to the public targets so that it appears +# in this help Use two # characters for a help comment +.PHONY: help +help: + @printf "${COMMENT}Usage:${RESET}\n" + @printf " make [target] [arg=\"val\"...]\n\n" + @printf "${COMMENT}Available targets:${RESET}\n" + @awk '/^[a-zA-Z\-\\_0-9\.@]+:/ { \ + helpMessage = match(lastLine, /^## (.*)/); \ + if (helpMessage) { \ + helpCommand = substr($$1, 0, index($$1, ":")); \ + helpMessage = substr(lastLine, RSTART + 3, RLENGTH); \ + printf " ${INFO}%-22s${RESET} %s\n", helpCommand, helpMessage; \ + } \ + } \ + { lastLine = $$0 }' $(MAKEFILE_LIST) + @printf "\n${COMMENT}Available arguments:${RESET}\n\n" + @awk '/^(([a-zA-Z\-\\_0-9\.@]+)\s[?:]?=)/ { \ + helpMessage = match(lastLine, /^## (.*)/); \ + if (helpMessage) { \ + helpMessage = substr(lastLine, RSTART + 3, RLENGTH); \ + printf " ${INFO}%-22s${RESET} %s (Default: %s)\n", $$1, helpMessage, $$3; \ + } \ + } \ + { lastLine = $$0 }' $(MAKEFILE_LIST) + + +## run tests +test: + echo "Running tests" + go test -cover -v ./... + +## run tests with fuzzing +test-fuzz: + echo "Running fuzz tests" + go test -v -fuzztime=30s -fuzz=Fuzz ./... + +#### VERSION +BIN_DIR ?= $(shell echo $$HOME)/.local/bin/ +VERSION_NAME := version +EXECUTABLES = $(EXECUTABLES:-) $(VERSION_NAME) +VERSION_BIN_PATH := $(BIN_DIR)$(VERSION_NAME) + +VERSION_BIN := $(shell command -v $(VERSION_NAME) 2> /dev/null) + +ifndef VERSION_BIN + $(shell curl -o $(VERSION_BIN_PATH) http://download.schukai.com/tools/version/version-$(shell uname -s | tr [:upper:] [:lower:])-$(shell echo `uname -m | sed s/aarch64/arm64/ | sed s/x86_64/amd64/`)) + $(shell chmod +x $(VERSION_BIN_PATH)) +endif + +GIT_CHGLOG_BIN := $(shell command -v git-chglog 2> /dev/null) + +ifeq ($(GIT_CHGLOG_BIN),) + $(shell go install github.com/git-chglog/git-chglog/cmd/git-chglog@latest) +endif + +RELEASE_FILE ?= $(PROJECT_ROOT)release.json +CHANGELOG_FILE ?= $(PROJECT_ROOT)CHANGELOG.md + +ifeq ("$(wildcard $(RELEASE_FILE))","") + $(shell echo '{"version":"0.1.0"}' > $(RELEASE_FILE)) +endif + +PROJECT_VERSION ?= $(shell cat $(RELEASE_FILE) | jq -r .version) +PROJECT_BUILD_DATE ?= $(shell $(VERSION_BIN) date) + +.PHONY: next-patch-version +next-patch-version: check-clean-repo + echo "Creating next version" + $(VERSION_BIN) patch --path $(RELEASE_FILE) --selector "version" + git add $(RELEASE_FILE) && git commit -m "Bump version to $$(cat $(RELEASE_FILE) | jq -r .version)" + +.PHONY: next-minor-version +next-minor-version: check-clean-repo + echo "Creating next minor version" + $(VERSION_BIN) minor --path $(RELEASE_FILE) --selector "version" + git add $(RELEASE_FILE) && git commit -m "Bump version to $$( cat $(RELEASE_FILE) | jq -r .version)" + +.PHONY: next-major-version +next-major-version: check-clean-repo + echo "Creating next minor version" + $(VERSION_BIN) major --path $(RELEASE_FILE) --selector "version" + git add $(RELEASE_FILE) && git commit -m "Bump version to $$(cat $(RELEASE_FILE) | jq -r .version)" + +.PHONY: check-clean-repo +check-clean-repo: + git diff-index --quiet HEAD || (echo "There are uncommitted changes after running make. Please commit or stash them before running make."; exit 1) + +## tag repository with next patch version +tag-patch-version: next-patch-version + echo "Tagging patch version" + $(eval PROJECT_VERSION := $(shell cat $(RELEASE_FILE) | jq -r .version)) + git-chglog --next-tag v$(PROJECT_VERSION) -o $(CHANGELOG_FILE) + git add $(CHANGELOG_FILE) && git commit -m "Update changelog" + git tag -a v$(PROJECT_VERSION) -m "Version $(PROJECT_VERSION)" + +## tag repository with next minor version +tag-minor-version: next-minor-version + echo "Tagging minor version" + $(eval PROJECT_VERSION := $(shell cat $(RELEASE_FILE) | jq -r .version)) + git-chglog --next-tag v$(PROJECT_VERSION) -o $(CHANGELOG_FILE) + git add $(CHANGELOG_FILE) && git commit -m "Update changelog" + git tag -a v$(PROJECT_VERSION) -m "Version $(PROJECT_VERSION)" + +## tag repository with next major version +tag-major-version: next-major-version + echo "Tagging major version" + $(eval PROJECT_VERSION := $(shell cat $(RELEASE_FILE) | jq -r .version)) + git-chglog --next-tag v$(PROJECT_VERSION) -o $(CHANGELOG_FILE) + git add $(CHANGELOG_FILE) && git commit -m "Update changelog" + git tag -a v$(PROJECT_VERSION) -m "Version $(PROJECT_VERSION)" + +GO_MOD_FILE := $(SOURCE_PATH)go.mod + +ifeq ($(shell test -e $(GO_MOD_FILE) && echo -n yes),yes) + GO_CURRENT_MODULE := $(shell cat $(GO_MOD_FILE) | head -n1 | cut -d" " -f2) + # go install github.com/google/go-licenses@latest + EXECUTABLES = $(EXECUTABLES:-) go-licenses; +endif + +.PHONY: fetch-licenses +## Fetch licenses for all modules +fetch-licenses: + go-licenses save $(GO_CURRENT_MODULE) --ignore gitlab.schukai.com --force --save_path $(PROJECT_ROOT)licenses/ + +# https://spdx.github.io/spdx-spec/v2.3/SPDX-license-list/ +ADDLICENSE_BIN ?= addlicense +ifeq ($(shell command -v $(ADDLICENSE_BIN) 2> /dev/null),) + $(shell go install github.com/google/addlicense@latest) + EXECUTABLES = $(EXECUTABLES:-) $(ADDLICENSE_BIN); +endif + +.PHONY: add-licenses +## Add license headers to all go files +add-licenses: + addlicense -c "schukai GmbH" -s -l "AGPL-3.0" ./*.go + + + diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/README.md b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/README.md new file mode 100644 index 0000000..4d1522c --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/README.md @@ -0,0 +1,69 @@ +## Pathfinder + +## What does this library? + +This library provides a simple way to get and set values in a nested structure. + +It supports: + +* [X] Set values in a nested structure +* [X] Get values from a nested structure + +## Installation + +```shell +go get gitlab.schukai.com/oss/libraries/go/utilities/pathfinder +``` + +**Note:** This library uses [Go Modules](https://github.com/golang/go/wiki/Modules) to manage dependencies. + +## Usage + +### Set values + +```go +s := &StructA{} +err := GetValue[*StructA](s, "my.key") +``` + +### Get values + +```go +s := &StructA{} +err := SetValue[*StructA](s, "my.key", "value") +``` + +## Contributing + +Merge requests are welcome. For major changes, please open an issue first to discuss what +you would like to change. **Please make sure to update tests as appropriate.** + +Versioning is done with [SemVer](https://semver.org/). +Changelog is generated with [git-chglog](https://github.com/git-chglog/git-chglog#git-chglog) + +Commit messages should follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification. +Messages are started with a type, which is one of the following: + +- **feat**: A new feature +- **fix**: A bug fix +- **doc**: Documentation only changes +- **refactor**: A code change that neither fixes a bug nor adds a feature +- **perf**: A code change that improves performance +- **test**: Adding missing or correcting existing tests +- **chore**: Other changes that don't modify src or test files + +The footer would be used for a reference to an issue or a breaking change. + +A commit that has a footer `BREAKING CHANGE:`, or appends a ! after the type/scope, +introduces a breaking API change (correlating with MAJOR in semantic versioning). +A BREAKING CHANGE can be part of commits of any type. + +the following is an example of a commit message: + +```text +feat: add 'extras' field +``` + +## License + +[AGPL-3.0](https://choosealicense.com/licenses/agpl-3.0/) diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/error.go b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/error.go new file mode 100644 index 0000000..614b13e --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/error.go @@ -0,0 +1,39 @@ +// Copyright 2022 schukai GmbH +// SPDX-License-Identifier: AGPL-3.0 + +package pathfinder + +import ( + "errors" + "reflect" +) + +type InvalidPathError error + +func newInvalidPathError(path string) InvalidPathError { + return InvalidPathError(errors.New("invalid path " + path)) +} + +type UnsupportedTypeAtTopOfPathError error + +func newUnsupportedTypeAtTopOfPathError(path string, t reflect.Type) UnsupportedTypeAtTopOfPathError { + return UnsupportedTypeAtTopOfPathError(errors.New("unsupported type " + t.String() + " at top of path " + path)) +} + +type UnsupportedTypePathError error + +func newUnsupportedTypePathError(path string, t reflect.Type) UnsupportedTypePathError { + return UnsupportedTypePathError(errors.New("unsupported type " + t.String() + " at path " + path)) +} + +type CannotSetError error + +func newCannotSetError(name string) CannotSetError { + return CannotSetError(errors.New("cannot set " + name)) +} + +type InvalidTypeForPathError error + +func newInvalidTypeForPathError(path string, pt string, nt string) InvalidTypeForPathError { + return InvalidTypeForPathError(errors.New("invalid type for path " + path + ": expected " + pt + ", got " + nt)) +} diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/error_test.go b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/error_test.go new file mode 100644 index 0000000..e245946 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/error_test.go @@ -0,0 +1,40 @@ +// Copyright 2022 schukai GmbH +// SPDX-License-Identifier: AGPL-3.0 + +package pathfinder + +import ( + "github.com/stretchr/testify/assert" + "reflect" + "testing" +) + +func TestInvalidPathError(t *testing.T) { + err := newInvalidPathError("test") + _, ok := err.(InvalidPathError) + assert.True(t, ok) +} + +func TestUnsupportedTypeAtTopOfPathError(t *testing.T) { + err := newUnsupportedTypeAtTopOfPathError("test", reflect.TypeOf(1)) + _, ok := err.(UnsupportedTypeAtTopOfPathError) + assert.True(t, ok) +} + +func TestUnsupportedTypePathError(t *testing.T) { + err := newUnsupportedTypePathError("test", reflect.TypeOf(1)) + _, ok := err.(UnsupportedTypePathError) + assert.True(t, ok) +} + +func TestCannotSetError(t *testing.T) { + err := newCannotSetError("test") + _, ok := err.(CannotSetError) + assert.True(t, ok) +} + +func TestInvalidTypeForPathError(t *testing.T) { + err := newInvalidTypeForPathError("test", "test", "test") + _, ok := err.(InvalidTypeForPathError) + assert.True(t, ok) +} diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/get.go b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/get.go new file mode 100644 index 0000000..79789c4 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/get.go @@ -0,0 +1,58 @@ +// Copyright 2022 schukai GmbH +// SPDX-License-Identifier: AGPL-3.0 + +package pathfinder + +import ( + "reflect" + "strconv" + "strings" +) + +// This function returns the value of a field in a struct, given a path to the field. +func GetValue[D any](obj D, keyWithDots string) (any, error) { + keySlice := strings.Split(keyWithDots, ".") + v := reflect.ValueOf(obj) + + for _, key := range keySlice[0:len(keySlice)] { + + switch v.Kind() { + case reflect.Ptr, reflect.Slice, reflect.Array, reflect.Interface: + v = v.Elem() + } + + switch v.Kind() { + case reflect.Map: + v = v.MapIndex(reflect.ValueOf(key)) + if !v.IsValid() { + return nil, newInvalidPathError(keyWithDots) + } + + case reflect.Slice, reflect.Array: + index, err := strconv.Atoi(key) + if err != nil { + return nil, newInvalidPathError(keyWithDots) + } + v = v.Index(index) + case reflect.Struct: + v = v.FieldByName(key) + if !v.IsValid() { + return nil, newInvalidPathError(keyWithDots) + } + default: + return nil, newInvalidPathError(keyWithDots) + } + + } + + if v.Kind() == reflect.Invalid { + return nil, newInvalidPathError(keyWithDots) + } + + for v.Kind() == reflect.Ptr { + v = v.Elem() + } + + return v.Interface(), nil + +} diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/get_test.go b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/get_test.go new file mode 100644 index 0000000..2340260 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/get_test.go @@ -0,0 +1,100 @@ +// Copyright 2022 schukai GmbH +// SPDX-License-Identifier: AGPL-3.0 + +package pathfinder + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestGetIndexFromArray(t *testing.T) { + m := map[string]any{ + "A": "true", + "B": []string{ + "1", + "2", + "3", + }, + } + + v, err := GetValue[map[string]any](m, "B.1") + if err != nil { + t.Error(err) + } + + assert.Equal(t, "2", v) + +} + +func TestGetValueFrom(t *testing.T) { + + m := map[string]string{ + "KeyA": "true", + "KeyB": "2", + "KeyC": "3", + "KeyD": "4.0", + } + + v, err := GetValue[map[string]string](m, "KeyA") + if err != nil { + t.Error(err) + } + + assert.Equal(t, v, "true") + +} + +func TestPathGetValueFromX(t *testing.T) { + m := map[string]any{ + "A": "true", + "B": map[string]any{ + "C": 2, + "D": map[string]any{ + "E": "3", + }, + }, + "X": "3", + "Y": "4.0", + } + + v, err := GetValue[map[string]any](m, "B.D.E") + if err != nil { + t.Error(err) + } + + assert.Equal(t, "3", v) + + v, err = GetValue[map[string]any](m, "B.C") + if err != nil { + t.Error(err) + } + + assert.Equal(t, 2, v) + +} + +func TestPathGetValueFrom(t *testing.T) { + + type PathfindTestStruct1 struct { + A bool + Sub1 struct { + B int + Sub2 struct { + C bool + } + D int + } + } + + var testData PathfindTestStruct1 + testData.A = true + testData.Sub1.B = 2 + testData.Sub1.Sub2.C = true + testData.Sub1.D = 4 + + GetValue[PathfindTestStruct1](testData, "Sub1.B") + GetValue[PathfindTestStruct1](testData, "Sub1.Sub2.C") + GetValue[PathfindTestStruct1](testData, "Sub1.D") + +} diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/go.mod b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/go.mod new file mode 100644 index 0000000..2d2e207 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/go.mod @@ -0,0 +1,10 @@ +module gitlab.schukai.com/oss/libraries/go/utilities/pathfinder + +go 1.19 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/testify v1.8.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/go.sum b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/go.sum new file mode 100644 index 0000000..b410979 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/go.sum @@ -0,0 +1,14 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/pathfinder.iml b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/pathfinder.iml new file mode 100644 index 0000000..49df094 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/pathfinder.iml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="WEB_MODULE" version="4"> + <component name="Go" enabled="true" /> + <component name="NewModuleRootManager" inherit-compiler-output="true"> + <exclude-output /> + <content url="file://$MODULE_DIR$" /> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + </component> +</module> \ No newline at end of file diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/release.json b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/release.json new file mode 100644 index 0000000..ccd00c2 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/release.json @@ -0,0 +1 @@ +{"version":"0.5.2"} diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/set.go b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/set.go new file mode 100644 index 0000000..f5ca2f4 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/set.go @@ -0,0 +1,154 @@ +// Copyright 2022 schukai GmbH +// SPDX-License-Identifier: AGPL-3.0 + +package pathfinder + +import ( + "fmt" + "reflect" + "strconv" + "strings" +) + +// This function sets the value of a field in a struct, given a path to the field. +func SetValue[D any](obj D, keyWithDots string, newValue any) error { + + keySlice := strings.Split(keyWithDots, ".") + v := reflect.ValueOf(obj) + + for _, key := range keySlice[0 : len(keySlice)-1] { + for v.Kind() != reflect.Ptr { + if v.Kind() == reflect.Invalid { + return newInvalidPathError(keyWithDots) + } + v = v.Addr() + } + + if v.Kind() != reflect.Ptr { + return newUnsupportedTypePathError(keyWithDots, v.Type()) + } + + elem := v.Elem() + if elem.Kind() != reflect.Struct { + return newUnsupportedTypePathError(keyWithDots, v.Type()) + } + + v = elem.FieldByName(key) + + } + + if v.Kind() == reflect.Invalid { + return newInvalidPathError(keyWithDots) + } + + for v.Kind() == reflect.Ptr { + v = v.Elem() + } + + // non-supporter type at the top of the path + if v.Kind() != reflect.Struct { + return newUnsupportedTypeAtTopOfPathError(keyWithDots, v.Type()) + } + + v = v.FieldByName(keySlice[len(keySlice)-1]) + if !v.IsValid() { + return newInvalidPathError(keyWithDots) + } + + if !v.CanSet() { + return newCannotSetError(keyWithDots) + } + + switch v.Kind() { + case reflect.Ptr: + if newValue == nil { + v.Set(reflect.Zero(v.Type())) + } else { + v.Set(reflect.ValueOf(&newValue)) + } + return nil + } + + newValueType := reflect.TypeOf(newValue) + if newValueType == nil { + return newUnsupportedTypePathError(keyWithDots, v.Type()) + } + + newValueKind := reflect.TypeOf(newValue).Kind() + + switch v.Kind() { + case reflect.String: + if newValueKind == reflect.String { + v.SetString(newValue.(string)) + } else { + v.SetString(fmt.Sprintf("%v", newValue)) + } + + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + + if newValueKind == reflect.Int { + v.SetInt(int64(newValue.(int))) + } else { + s, err := strconv.ParseInt(fmt.Sprintf("%v", newValue), 10, 64) + if err != nil { + return err + } + v.SetInt(s) + } + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + + if newValueKind == reflect.Int { + v.SetUint(uint64(newValue.(int))) + } else { + s, err := strconv.ParseInt(fmt.Sprintf("%v", newValue), 10, 64) + if err != nil { + return err + } + v.SetUint(uint64(s)) + } + + case reflect.Bool: + + if newValueKind == reflect.Bool { + v.SetBool(newValue.(bool)) + } else { + b, err := strconv.ParseBool(fmt.Sprintf("%v", newValue)) + if err != nil { + return err + } + + v.SetBool(b) + } + + case reflect.Float64, reflect.Float32: + + if newValueKind == reflect.Float64 { + v.SetFloat(newValue.(float64)) + } else { + s, err := strconv.ParseFloat(fmt.Sprintf("%v", newValue), 64) + if err != nil { + return err + } + + v.SetFloat(s) + } + + case reflect.Slice, reflect.Array: + + if newValueKind == reflect.Ptr { + newValue = reflect.ValueOf(newValue).Elem().Interface() + v.Set(reflect.ValueOf(newValue)) + } else if newValueKind == reflect.Slice { + v.Set(reflect.ValueOf(newValue)) + } else { + return newUnsupportedTypePathError(keyWithDots, v.Type()) + } + + default: + return newInvalidTypeForPathError(keyWithDots, v.Type().String(), newValueKind.String()) + } + + return nil + +} diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/set_test.go b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/set_test.go new file mode 100644 index 0000000..37adcb7 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.5.2/set_test.go @@ -0,0 +1,278 @@ +// Copyright 2022 schukai GmbH +// SPDX-License-Identifier: AGPL-3.0 + +package pathfinder + +import "testing" + +type PathfindTestStruct1 struct { + A bool + Sub1 struct { + B bool + Bi int + Bs string + Bf float64 + Sub2 struct { + C bool + Ci int + Cs string + Cf float64 + + Sub3 struct { + D bool + Di int + Ds string + Df float64 + } + } + } +} + +func TestPathFindError(t *testing.T) { + + s := PathfindTestStruct1{} + + _, err := GetValue[PathfindTestStruct1](s, "Sub1.Sub2.Sub3.XX") + if err == nil { + t.Error("err == nil") + } + +} + +func TestPathFindSetValueString(t *testing.T) { + + testData := map[string]string{ + "Sub1.B": "true", + "Sub1.Bi": "2", + "Sub1.Bs": "3", + "Sub1.Bf": "4.0", + "Sub1.Sub2.C": "true", + "Sub1.Sub2.Ci": "2", + "Sub1.Sub2.Cs": "3", + "Sub1.Sub2.Cf": "4.0", + "Sub1.Sub2.Sub3.D": "true", + "Sub1.Sub2.Sub3.Di": "2", + "Sub1.Sub2.Sub3.Ds": "3", + "Sub1.Sub2.Sub3.Df": "4.0", + } + + for k, v := range testData { + s := &PathfindTestStruct1{} + err := SetValue[*PathfindTestStruct1](s, k, v) + if err != nil { + t.Error(err) + } + } +} + +func TestPathFindGetValueFrom(t *testing.T) { + + s := PathfindTestStruct1{} + + s.Sub1.B = true + s.Sub1.Bi = 2 + s.Sub1.Bs = "3" + s.Sub1.Bf = 4.0 + + v, err := GetValue[PathfindTestStruct1](s, "Sub1.B") + if err != nil { + t.Error(err) + } + + if v != true { + t.Error("v != true") + } + + v, err = GetValue[PathfindTestStruct1](s, "Sub1.Bi") + if err != nil { + t.Error(err) + } + + if v != 2 { + t.Error("v != 2") + } + + v, err = GetValue[PathfindTestStruct1](s, "Sub1.Bs") + if err != nil { + t.Error(err) + } + + if v != "3" { + t.Error("v != 3") + } + + v, err = GetValue[PathfindTestStruct1](s, "Sub1.Bf") + if err != nil { + t.Error(err) + } + + if v != 4.0 { + t.Error("v != 4.0") + } + + s.Sub1.Sub2.C = true + s.Sub1.Sub2.Ci = 2 + s.Sub1.Sub2.Cs = "3" + s.Sub1.Sub2.Cf = 4.0 + + v, err = GetValue[PathfindTestStruct1](s, "Sub1.Sub2.C") + if err != nil { + t.Error(err) + } + + if v != true { + t.Error("v != true") + } + + v, err = GetValue[PathfindTestStruct1](s, "Sub1.Sub2.Ci") + if err != nil { + t.Error(err) + } + + if v != 2 { + t.Error("v != 2") + } + + v, err = GetValue[PathfindTestStruct1](s, "Sub1.Sub2.Cs") + if err != nil { + t.Error(err) + } + + if v != "3" { + t.Error("v != 3") + } + + v, err = GetValue[PathfindTestStruct1](s, "Sub1.Sub2.Cf") + if err != nil { + t.Error(err) + } + + if v != 4.0 { + t.Error("v != 4.0") + } + + s.Sub1.Sub2.Sub3.D = true + s.Sub1.Sub2.Sub3.Di = 2 + s.Sub1.Sub2.Sub3.Ds = "3" + s.Sub1.Sub2.Sub3.Df = 4.0 + + v, err = GetValue[PathfindTestStruct1](s, "Sub1.Sub2.Sub3.D") + if err != nil { + t.Error(err) + + } + + if v != true { + t.Error("v != true") + } + + v, err = GetValue[PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Di") + if err != nil { + t.Error(err) + } + + if v != 2 { + t.Error("v != 2") + } + + v, err = GetValue[PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Ds") + if err != nil { + t.Error(err) + } + + if v != "3" { + t.Error("v != 3") + } + + v, err = GetValue[PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Df") + if err != nil { + t.Error(err) + } + + if v != 4.0 { + t.Error("v != 4.0") + } + +} + +func TestPathFindSetValueFrom(t *testing.T) { + s := &PathfindTestStruct1{} + + SetValue[*PathfindTestStruct1](s, "Sub1.B", "true") + SetValue[*PathfindTestStruct1](s, "Sub1.Bi", "2") + SetValue[*PathfindTestStruct1](s, "Sub1.Bs", "3") + SetValue[*PathfindTestStruct1](s, "Sub1.Bf", "4.0") + + if s.Sub1.B != true { + t.Error("s.Sub1.B != true") + } + + if s.Sub1.Bi != 2 { + t.Error("s.Sub1.Bi != 2") + + } + + if s.Sub1.Bs != "3" { + t.Error("s.Sub1.Bs != 3") + } + + if s.Sub1.Bf != 4.0 { + t.Error("s.Sub1.Bf != 4.0") + } + + SetValue[*PathfindTestStruct1](s, "Sub1.Sub2.C", "true") + SetValue[*PathfindTestStruct1](s, "Sub1.Sub2.Ci", "2") + SetValue[*PathfindTestStruct1](s, "Sub1.Sub2.Cs", "3") + SetValue[*PathfindTestStruct1](s, "Sub1.Sub2.Cf", "4.0") + + if s.Sub1.Sub2.C != true { + t.Error("s.Sub1.Sub2.C != true") + + } + + if s.Sub1.Sub2.Ci != 2 { + t.Error("s.Sub1.Sub2.Ci != 2") + + } + + if s.Sub1.Sub2.Cs != "3" { + t.Error("s.Sub1.Sub2.Cs != 3") + + } + + if s.Sub1.Sub2.Cf != 4.0 { + t.Error("s.Sub1.Sub2.Cf != 4.0") + + } + + if s.Sub1.Sub2.Sub3.D != false { + t.Error("s.Sub1.Sub2.Sub3.D != false") + + } + + SetValue[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.D", "true") + SetValue[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Di", "2") + SetValue[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Ds", "3") + SetValue[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Df", "4.0") + + if s.Sub1.Sub2.Sub3.D != true { + t.Error("s.Sub1.Sub2.Sub3.D != true") + + } + + if s.Sub1.Sub2.Sub3.Di != 2 { + t.Error("s.Sub1.Sub2.Sub3.Di != 2") + + } + + if s.Sub1.Sub2.Sub3.Ds != "3" { + t.Error("s.Sub1.Sub2.Sub3.Ds != 3") + + } + + if s.Sub1.Sub2.Sub3.Df != 4.0 { + t.Error("s.Sub1.Sub2.Sub3.Df != 4.0") + + } + +} diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.chglog/CHANGELOG.tpl.md b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.chglog/CHANGELOG.tpl.md new file mode 100644 index 0000000..8e98c0b --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.chglog/CHANGELOG.tpl.md @@ -0,0 +1,27 @@ +{{ range .Versions }} +<a name="{{ .Tag.Name }}"></a> +## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }} +{{ range .CommitGroups -}} +### {{ .Title }} +{{ range .Commits -}} +- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} +{{ end }} +{{ end -}} + +{{- if .NoteGroups -}} +{{ range .NoteGroups -}} +### {{ .Title }} +{{ range .Notes }} +{{ .Body }} +{{ end }} +{{ end -}} +{{ end -}} +{{ end -}} + +{{- if .Versions }} +{{ range .Versions -}} +{{ if .Tag.Previous -}} +[{{ .Tag.Name }}]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }} +{{ end -}} +{{ end -}} +{{ end -}} \ No newline at end of file diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.chglog/config.yml b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.chglog/config.yml new file mode 100644 index 0000000..237f6bc --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.chglog/config.yml @@ -0,0 +1,57 @@ +style: gitlab +template: CHANGELOG.tpl.md +info: + title: CHANGELOG + repository_url: https://gitlab.schukai.com/oss/libraries/go/utilities/pathfinder +options: + commits: + filters: + Type: + - feat + - fix + - doc + - refactor + - perf + - test + - chore + ## deprecated types and typos + - docs + - documentation + - feat + - added + - add + - bugfix + - revert + - update + - updates + - change + - changed + commit_groups: + title_maps: + feat: Add Features + fix: Bug Fixes + doc: Documentation + refactor: Code Refactoring + perf: Performance Improvements + test: Tests + ## Chore is used for all other changes that don't fit in the other categories + chore: Changes + ## deprecated types and typos + docs: Documentation + documentation: Documentation + added: Add Features + add: Add Features + bugfix: Bug Fixes + revert: Reverts + update: Changes + updates: Changes + change: Changes + changed: Changes + header: + pattern: "^((\\w+)\\s.*)$" + pattern_maps: + - Subject + - Type + notes: + keywords: + - BREAKING CHANGE diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.envrc b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.envrc new file mode 100644 index 0000000..6de8a8a --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.envrc @@ -0,0 +1,3 @@ +source_url "https://raw.githubusercontent.com/cachix/devenv/d1f7b48e35e6dee421cfd0f51481d17f77586997/direnvrc" "sha256-YBzqskFZxmNb3kYVoKD9ZixoPXJh1C9ZvTLGFRkauZ0=" + +use devenv \ No newline at end of file diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.gitignore b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.gitignore new file mode 100644 index 0000000..13bb7b5 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.gitignore @@ -0,0 +1,155 @@ +# Created by https://www.toptal.com/developers/gitignore/api/intellij,go +# Edit at https://www.toptal.com/developers/gitignore?templates=intellij,go + +### Go ### +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work + +### Go Patch ### +/vendor/ +/Godeps/ + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Intellij Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +# Azure Toolkit for IntelliJ plugin +# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij +.idea/**/azureSettings.xml + +# End of https://www.toptal.com/developers/gitignore/api/intellij,go +# Devenv +.devenv* +devenv.local.nix + +# direnv +.direnv + +# pre-commit +.pre-commit-config.yaml + +/Session.vim diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.gitlab-ci.yml b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.gitlab-ci.yml new file mode 100644 index 0000000..4339280 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.gitlab-ci.yml @@ -0,0 +1,69 @@ + +# THIS FILE IS AUTOGENERATED BY THE DEVENVSHELL +# DO NOT EDIT THIS FILE MANUALLY +# INSTEAD EDIT THE DEVENVSHELL CONFIGURATION FILE devenv.nix +# AND OPEN A SHELL WITH THE COMMAND devenv shell +# + +image: docker-registry.schukai.com:443/nixos-ci-devenv:latest + +services: + - docker:dind + +variables: + # The repo name as used in + # https://github.com/nix-community/NUR/blob/master/repos.json + NIXOS_VERSION: "23.05" + NIXPKGS_ALLOW_UNFREE: "1" + NIXPKGS_ALLOW_INSECURE: "1" + DOCKER_DRIVER: overlay2 + GIT_DEPTH: 10 + +stages: + - test + - deploy + +before_script: + - nix shell nixpkgs#coreutils-full -c mkdir -p /certs/client/ + - nix shell nixpkgs#coreutils-full -c ln -fs /etc/ssl/certs/ca-bundle.crt /certs/client/ca.pem + - echo > .env-gitlab-ci + - variables=("HOME=$HOME" "CI_COMMIT_REF_NAME=$CI_COMMIT_REF_NAME" "CI_REPOSITORY_URL=$CI_REPOSITORY_URL" "GITLAB_TOKEN=$GITLAB_TOKEN" "CI_JOB_TOKEN=$CI_JOB_TOKEN" "GITLAB_USER_EMAIL=$GITLAB_USER_EMAIL" "GITLAB_USER_NAME=\"$GITLAB_USER_NAME\"" "CI_REGISTRY_USER=$CI_REGISTRY_USER" "CI_PROJECT_ID=$CI_PROJECT_ID" "CI_PROJECT_DIR=$CI_PROJECT_DIR" "CI_API_V4_URL=$CI_API_V4_URL" "CI_PROJECT_NAME=$CI_PROJECT_NAME" "CI_COMMIT_SHORT_SHA=$CI_COMMIT_SHORT_SHA"); for var in "${variables[@]}"; do echo "$var" >> .env-gitlab-ci; done + - cat .env-gitlab-ci + +after_script: + - if [ -f .env-gitlab-ci ]; then rm .env-gitlab-ci; fi + +test: + stage: test + tags: + - nixos + script: + - devenv shell test-lib + + cache: + - key: nixos + paths: + - /nix/store + + artifacts: + paths: + - dist + +deploy: + stage: deploy + tags: + - nixos + script: + - devenv shell -c deploy-lib + + when: on_success + + cache: + - key: nixos + paths: + - /nix/store + + + artifacts: + paths: + - dist diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.idea/.gitignore b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.idea/markdown.xml b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.idea/markdown.xml new file mode 100644 index 0000000..ec0b30f --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.idea/markdown.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="MarkdownSettings"> + <enabledExtensions> + <entry key="MermaidLanguageExtension" value="false" /> + <entry key="PlantUMLLanguageExtension" value="true" /> + </enabledExtensions> + </component> +</project> \ No newline at end of file diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.idea/misc.xml b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.idea/misc.xml new file mode 100644 index 0000000..639900d --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.idea/misc.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ProjectRootManager"> + <output url="file://$PROJECT_DIR$/out" /> + </component> +</project> \ No newline at end of file diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.idea/modules.xml b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.idea/modules.xml new file mode 100644 index 0000000..921c18f --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.idea/modules.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ProjectModuleManager"> + <modules> + <module fileurl="file://$PROJECT_DIR$/.idea/pathfinder.iml" filepath="$PROJECT_DIR$/.idea/pathfinder.iml" /> + </modules> + </component> +</project> \ No newline at end of file diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.idea/pathfinder.iml b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.idea/pathfinder.iml new file mode 100644 index 0000000..25ed3f6 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.idea/pathfinder.iml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="Go" enabled="true" /> + <component name="NewModuleRootManager" inherit-compiler-output="true"> + <exclude-output /> + <content url="file://$MODULE_DIR$" /> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + </component> +</module> \ No newline at end of file diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.idea/vcs.xml b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/.idea/vcs.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="VcsDirectoryMappings"> + <mapping directory="" vcs="Git" /> + </component> +</project> \ No newline at end of file diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/LICENSE b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/LICENSE new file mode 100644 index 0000000..22686f9 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/LICENSE @@ -0,0 +1,662 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + AGPL-3.0 + + Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +<https://www.gnu.org/licenses/>. \ No newline at end of file diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/README.md b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/README.md new file mode 100644 index 0000000..4d1522c --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/README.md @@ -0,0 +1,69 @@ +## Pathfinder + +## What does this library? + +This library provides a simple way to get and set values in a nested structure. + +It supports: + +* [X] Set values in a nested structure +* [X] Get values from a nested structure + +## Installation + +```shell +go get gitlab.schukai.com/oss/libraries/go/utilities/pathfinder +``` + +**Note:** This library uses [Go Modules](https://github.com/golang/go/wiki/Modules) to manage dependencies. + +## Usage + +### Set values + +```go +s := &StructA{} +err := GetValue[*StructA](s, "my.key") +``` + +### Get values + +```go +s := &StructA{} +err := SetValue[*StructA](s, "my.key", "value") +``` + +## Contributing + +Merge requests are welcome. For major changes, please open an issue first to discuss what +you would like to change. **Please make sure to update tests as appropriate.** + +Versioning is done with [SemVer](https://semver.org/). +Changelog is generated with [git-chglog](https://github.com/git-chglog/git-chglog#git-chglog) + +Commit messages should follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification. +Messages are started with a type, which is one of the following: + +- **feat**: A new feature +- **fix**: A bug fix +- **doc**: Documentation only changes +- **refactor**: A code change that neither fixes a bug nor adds a feature +- **perf**: A code change that improves performance +- **test**: Adding missing or correcting existing tests +- **chore**: Other changes that don't modify src or test files + +The footer would be used for a reference to an issue or a breaking change. + +A commit that has a footer `BREAKING CHANGE:`, or appends a ! after the type/scope, +introduces a breaking API change (correlating with MAJOR in semantic versioning). +A BREAKING CHANGE can be part of commits of any type. + +the following is an example of a commit message: + +```text +feat: add 'extras' field +``` + +## License + +[AGPL-3.0](https://choosealicense.com/licenses/agpl-3.0/) diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/Taskfile.yml b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/Taskfile.yml new file mode 100644 index 0000000..7095ba5 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/Taskfile.yml @@ -0,0 +1,59 @@ + +# THIS FILE IS AUTOGENERATED BY THE DEVENVSHELL +# DO NOT EDIT THIS FILE MANUALLY +# INSTEAD EDIT THE DEVENVSHELL CONFIGURATION FILE devenv.nix +# AND OPEN A SHELL WITH THE COMMAND devenv shell +# +# Information about the task runner can be found here: +# https://taskfile.dev + +version: '3' + +tasks: + default: + cmds: + - task --list + silent: true + + test: + desc: Execute unit tests in Go. + cmds: + - echo "Execute unit tests in Go." + - go test -cover -v ./... + - go test -bench . + - go test -race . + + test-fuzz: + desc: Conduct fuzzing tests.# + cmds: + - echo "Conduct fuzzing tests." + - go test -v -fuzztime=30s -fuzz=Fuzz ./... + + add-licenses: + desc: Attach license headers to Go files. + cmds: + - echo "Attach license headers to Go files." + - go install github.com/google/addlicense@latest + - addlicense -c "schukai GmbH" -s -l "AGPL-3.0" ./*.go + silent: true + + check-licenses: + desc: Check license headers of Go files. + silent: true + cmds: + - go-licenses save "$(get-go-default-packages)" --ignore "gitlab.schukai.com" --force --save_path ${DEVENV_ROOT}/licenses/ + + check: + desc: Confirm repository status. + cmds: + - git diff-index --quiet HEAD || (echo "There are uncommitted changes after running make. Please commit or stash them before running make."; exit 1) + silent: true + + commit: + desc: Commit changes to the repository. + aliases: + - c + - ci + - git-commit + cmds: + - do-git-commit diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/devenv.lock b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/devenv.lock new file mode 100644 index 0000000..8993ca8 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/devenv.lock @@ -0,0 +1,190 @@ +{ + "nodes": { + "devenv": { + "locked": { + "dir": "src/modules", + "lastModified": 1692003204, + "narHash": "sha256-gO2DXwXuArjpywgtRTDb3aKscWMbnI7YwFaqvV46yv0=", + "owner": "cachix", + "repo": "devenv", + "rev": "ade3ae522baf366296598e232b7b063d81740bbb", + "type": "github" + }, + "original": { + "dir": "src/modules", + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1685518550, + "narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1660459072, + "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1691950488, + "narHash": "sha256-iUNEeudc4dGjx+HsHccnGiuZUVE/nhjXuQ1DVCsHIUY=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "720e61ed8de116eec48d6baea1d54469b536b985", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-23.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1685801374, + "narHash": "sha256-otaSUoFEMM+LjBI1XL/xGB5ao6IwnZOXc47qhIgJe8U=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "c37ca420157f4abc31e26f436c1145f8951ff373", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1691950488, + "narHash": "sha256-iUNEeudc4dGjx+HsHccnGiuZUVE/nhjXuQ1DVCsHIUY=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "720e61ed8de116eec48d6baea1d54469b536b985", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "ref": "nixos-23.05", + "type": "indirect" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "flake-utils": "flake-utils", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1691747570, + "narHash": "sha256-J3fnIwJtHVQ0tK2JMBv4oAmII+1mCdXdpeCxtIsrL2A=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "c5ac3aa3324bd8aebe8622a3fc92eeb3975d317a", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "nixpkgs": "nixpkgs", + "pre-commit-hooks": "pre-commit-hooks", + "version": "version" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "version": { + "inputs": { + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1690668568, + "narHash": "sha256-jzixQKFFW4oxO0S4GYqbkFCXzhBd6com6Z9+MtVKakU=", + "ref": "refs/heads/master", + "rev": "3838f03165b726e47d586c04a1821749375e1001", + "revCount": 37, + "type": "git", + "url": "https://gitlab.schukai.com/oss/utilities/version.git" + }, + "original": { + "type": "git", + "url": "https://gitlab.schukai.com/oss/utilities/version.git" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/devenv.nix b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/devenv.nix new file mode 100644 index 0000000..9a8bbe4 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/devenv.nix @@ -0,0 +1,638 @@ +{ pkgs, inputs, phps, lib, config, modulesPath,... }: + +{ + # https://devenv.sh/packages/ + packages = with pkgs; [ + inputs.version.defaultPackage."${builtins.currentSystem}" + appimage-run + blackbox + blackbox-terminal + coreutils-full + dbeaver + delve + dialog + drill + exa + fd + fd + gcc12 + gdlv + git + glab + gnugrep + gnumake + gnused + go-licenses + go-task + gum + httpie + hurl + jq + libffi + logrotate + meld + memcached + netcat + nixfmt + procps + ranger + unixtools.xxd + unzip + util-linux + wget + zlib + ]; + + + # https://devenv.sh/languages/ + # languages.nix.enable = true; + languages = { + go = { enable = true; }; + }; + + difftastic.enable = true; + + scripts.get-go-default-packages.exec = '' +#!${pkgs.bash}/bin/bash +echo $(awk -F ' ' '/^module / { print $2 }' go.mod) +''; + + # This script is executed when the app is built + # You can use it to build the app + scripts.test-lib.exec = '' +#!${pkgs.bash}/bin/bash +#set -euo pipefail +set -x + +PATH="''${PATH}":${pkgs.coreutils}/bin +PATH="''${PATH}":${pkgs.findutils}/bin +PATH="''${PATH}":${pkgs.jq}/bin/ +PATH="''${PATH}":${pkgs.rsync}/bin/ +PATH="''${PATH}":${pkgs.bash}/bin/ +PATH="''${PATH}":${pkgs.curl}/bin/ +PATH="''${PATH}":${pkgs.moreutils}/bin/ +PATH="''${PATH}":${pkgs.gnutar}/bin +PATH="''${PATH}":${pkgs.gzip}/bin/ +PATH="''${PATH}":${pkgs.procps}/bin/ +PATH="''${PATH}":${pkgs.exa}/bin/ +PATH="''${PATH}":${pkgs.git}/bin/ +PATH="''${PATH}":${pkgs.gnugrep}/bin/ +PATH="''${PATH}":${inputs.version.defaultPackage."${builtins.currentSystem}"}/bin/ + +export PATH + +task test + +''; + + # This scritp is used to deploy the app to the gitlab registry + # It is used by the gitlab-ci.yml file + # The environment variables are set in the gitlab project settings + scripts.deploy-lib.exec = '' +#!${pkgs.bash}/bin/bash + +PATH="''${PATH}":${pkgs.coreutils}/bin +PATH="''${PATH}":${pkgs.jq}/bin/ +PATH="''${PATH}":${pkgs.curl}/bin/ +PATH="''${PATH}":${pkgs.moreutils}/bin/ +PATH="''${PATH}":${pkgs.gnutar}/bin +PATH="''${PATH}":${pkgs.gzip}/bin/ +PATH="''${PATH}":${pkgs.exa}/bin/ +PATH="''${PATH}":${pkgs.git}/bin/ +PATH="''${PATH}":${inputs.version.defaultPackage."${builtins.currentSystem}"}/bin/ + +export PATH + +if [[ -f .env-gitlab-ci ]]; then + source .env-gitlab-ci + rm .env-gitlab-ci +fi + +set -x +## if $HOME not set, set it to current directory +if [[ -z "''${HOME}" ]]; then + HOME=$(pwd) +fi + +export HOME + +git config user.email "''${GITLAB_USER_EMAIL}" +git config user.name "''${GITLAB_USER_NAME:-"Gitlab CI"}" +git config pull.rebase true +git config http.sslVerify "false" +git remote set-url origin https://pad:''${GITLAB_TOKEN}@''${CI_REPOSITORY_URL#*@} + +git fetch --all --tags --unshallow +git reset --hard origin/master +git checkout $CI_COMMIT_REF_NAME +git pull origin $CI_COMMIT_REF_NAME + +if [ ! -z "''${CI_PROJECT_DIR}" ]; then + echo "CI_PROJECT_DIR is set, using it as project root." + project_root=$(realpath "''${CI_PROJECT_DIR}")/ +elif [ ! -z "''${DEVENV_ROOT}" ]; then + echo "DEVENV_ROOT is set, using it as project root." + project_root=$(realpath "''${DEVENV_ROOT}")/ +else + echo "Error: DEVENV_ROOT or CI_PROJECT_DIR environment variables are not set." + exit 1 +fi + +if [ ! -d "''${project_root}" ]; then + echo "Error: Project root directory does not seem to be valid." + echo "Check the DEVENV_ROOT or CI_PROJECT_DIR environment variables." + exit 1 +fi + +if [ -z "'CI_JOB_TOKEN" ]; then + echo "Error: CI_JOB_TOKEN variable is not set." + exit 1 +fi + +git --no-pager log --decorate=short --pretty=oneline +gitVersion=v$(version predict) +git tag -a $gitVersion -m"chore: bump version" +git --no-pager log --decorate=short --pretty=oneline +git push -o ci.skip origin ''${CI_COMMIT_REF_NAME} --tags + +echo "done" + +''; + + enterShell = '' + +cat <<'EOF' > Taskfile.yml + +# THIS FILE IS AUTOGENERATED BY THE DEVENVSHELL +# DO NOT EDIT THIS FILE MANUALLY +# INSTEAD EDIT THE DEVENVSHELL CONFIGURATION FILE devenv.nix +# AND OPEN A SHELL WITH THE COMMAND devenv shell +# +# Information about the task runner can be found here: +# https://taskfile.dev + +version: '3' + +tasks: + default: + cmds: + - task --list + silent: true + + test: + desc: Execute unit tests in Go. + cmds: + - echo "Execute unit tests in Go." + - go test -cover -v ./... + - go test -bench . + - go test -race . + + test-fuzz: + desc: Conduct fuzzing tests.# + cmds: + - echo "Conduct fuzzing tests." + - go test -v -fuzztime=30s -fuzz=Fuzz ./... + + add-licenses: + desc: Attach license headers to Go files. + cmds: + - echo "Attach license headers to Go files." + - go install github.com/google/addlicense@latest + - addlicense -c "schukai GmbH" -s -l "AGPL-3.0" ./*.go + silent: true + + check-licenses: + desc: Check license headers of Go files. + silent: true + cmds: + - go-licenses save "$(get-go-default-packages)" --ignore "gitlab.schukai.com" --force --save_path ''${DEVENV_ROOT}/licenses/ + + check: + desc: Confirm repository status. + cmds: + - git diff-index --quiet HEAD || (echo "There are uncommitted changes after running make. Please commit or stash them before running make."; exit 1) + silent: true + + commit: + desc: Commit changes to the repository. + aliases: + - c + - ci + - git-commit + cmds: + - do-git-commit +EOF + +cat <<'EOF' > .gitlab-ci.yml + +# THIS FILE IS AUTOGENERATED BY THE DEVENVSHELL +# DO NOT EDIT THIS FILE MANUALLY +# INSTEAD EDIT THE DEVENVSHELL CONFIGURATION FILE devenv.nix +# AND OPEN A SHELL WITH THE COMMAND devenv shell +# + +image: docker-registry.schukai.com:443/nixos-ci-devenv:latest + +services: + - docker:dind + +variables: + # The repo name as used in + # https://github.com/nix-community/NUR/blob/master/repos.json + NIXOS_VERSION: "23.05" + NIXPKGS_ALLOW_UNFREE: "1" + NIXPKGS_ALLOW_INSECURE: "1" + DOCKER_DRIVER: overlay2 + GIT_DEPTH: 10 + +stages: + - test + - deploy + +before_script: + - nix shell nixpkgs#coreutils-full -c mkdir -p /certs/client/ + - nix shell nixpkgs#coreutils-full -c ln -fs /etc/ssl/certs/ca-bundle.crt /certs/client/ca.pem + - echo > .env-gitlab-ci + - variables=("HOME=''$HOME" "CI_COMMIT_REF_NAME=''$CI_COMMIT_REF_NAME" "CI_REPOSITORY_URL=''$CI_REPOSITORY_URL" "GITLAB_TOKEN=''$GITLAB_TOKEN" "CI_JOB_TOKEN=''$CI_JOB_TOKEN" "GITLAB_USER_EMAIL=''$GITLAB_USER_EMAIL" "GITLAB_USER_NAME=\"''$GITLAB_USER_NAME\"" "CI_REGISTRY_USER=''$CI_REGISTRY_USER" "CI_PROJECT_ID=''$CI_PROJECT_ID" "CI_PROJECT_DIR=''$CI_PROJECT_DIR" "CI_API_V4_URL=''$CI_API_V4_URL" "CI_PROJECT_NAME=''$CI_PROJECT_NAME" "CI_COMMIT_SHORT_SHA=''$CI_COMMIT_SHORT_SHA"); for var in "''${variables[@]}"; do echo "''$var" >> .env-gitlab-ci; done + - cat .env-gitlab-ci + +after_script: + - if [ -f .env-gitlab-ci ]; then rm .env-gitlab-ci; fi + +test: + stage: test + tags: + - nixos + script: + - devenv shell test-lib + + cache: + - key: nixos + paths: + - /nix/store + + artifacts: + paths: + - dist + +deploy: + stage: deploy + tags: + - nixos + script: + - devenv shell -c deploy-lib + + when: on_success + + cache: + - key: nixos + paths: + - /nix/store + + + artifacts: + paths: + - dist +EOF + + + + ''; + + scripts.do-git-commit.exec = '' +#!/usr/bin/env bash + +# Define colors if the terminal supports it +if [ -t 1 ]; then + RED='\033[0;31m' + GREEN='\033[0;32m' + RESET='\033[0m' + BOLD='\033[1m' +else + RED="" + GREEN="" + RESET="" +fi + +step=1 + +reset +clear + +# create random log file +LOGFILE="''$(mktemp)" +if [ $? -ne 0 ]; then + echo -e "''${RED}✖ Could not create temporary log file. Exiting.''${RESET}" + exit 1 +fi + +log_and_display() { + echo -e "''${GREEN}==> $step. $1''${RESET}" | tee -a $LOGFILE + step=$((step + 1)) +} + +log_error_and_display() { + echo -e "''${RED}==> $step. $1''${RESET}" | tee -a $LOGFILE +} + +printLogfileAndExit() { + exit_code=$1 + echo -e "\n\n========================================\n\n\n" + + echo -e "\n\n''${BOLD}Git and GitLab Automation Script''${RESET}\n\nI have performed the following actions:\n\n" + cat "$LOGFILE" + + # Optional: Remove log file + rm -f "$LOGFILE" + + if [ $exit_code -eq 0 ]; then + echo -e "\n''${GREEN}✔''${RESET} All actions were successful" | tee -a $LOGFILE + elif [ $exit_code -eq -1 ]; then + echo -e "\n''${RED}✖''${RESET} The script was manually cancelled" | tee -a $LOGFILE + exit_code=0 + else + echo -e "\n''${RED}✖''${RESET} Some actions failed" | tee -a $LOGFILE + fi + + exit $exit_code +} + +print_headline() { + local title=$1 + local underline=$(printf '─%.0s' $(seq 1 ''${#title})) + echo -e "\n\n''${BOLD}''${title}\n''${underline}''${RESET}\n" +} + +do_cancel() { + echo -e "''${RED}==> ✖ Cancelled.''${RESET}" | tee -a $LOGFILE + printLogfileAndExit -1 +} + +# Function for unified logging and display +log_action() { + if [ $? -eq 0 ]; then + echo -e " ''${GREEN}✔''${RESET} $1: Successful" | tee -a $LOGFILE + else + echo -e " ''${RED}✖''${RESET} $1: Failed" | tee -a $LOGFILE + printLogfileAndExit 1 + fi +} + +print_what_to_do() { + echo -e "\n\nWhat do you want to do?\n" +} + +git_status=$(git status --porcelain) +if [[ -z "$git_status" ]]; then + log_error_and_display "No changes to commit. Exiting." + printLogfileAndExit 0 +fi + +print_headline "Choose commit type" +selection=$(gum choose "feat: (new feature for the user, not a new feature for build script)" "fix: (bug fix for the user, not a fix to a build script)" "chore: (updating grunt tasks etc.; no production code change)" "docs: (changes to the documentation)" "style: (formatting, missing semi colons, etc; no production code change)" "refactor: (refactoring production code, eg. renaming a variable)" "test: (adding missing tests, refactoring tests; no production code change)" "Cancel") + +commit_type=$(echo "$selection" | awk -F':' '{print $1}') + +if [[ "$commit_type" == "Cancel" ]]; then + do_cancel +fi + +log_and_display "You chose the commit type: $commit_type" + +# NEXT STEP ISSUE HANDLING ############################################################################################################ +#log_and_display "Issue handling" + +gitlabIssues=() +while IFS= read -r line; do + if [[ $line =~ ^# ]]; then + id=$(echo "$line" | awk '{print substr($1, 2)}') + title=$(echo "$line" | awk -F'about' '{print $1}' | awk '{$1=$2=""; print substr($0, 3)}') + gitlabIssues+=("$id > $title") + fi +done < <(gum spin --spinner dot --show-output --title "Ask gitlab ..." -- glab issue list --output-format=details) + +## if issues are available, ask if user wants to use an existing issue or create a new one +createOption="Create new issue" +existingOption="Use existing issue" +cancelOption="Cancel" + +print_headline "Choose issue handling" +if [ ''${#gitlabIssues[@]} -eq 0 ]; then + log_and_display "There are no issues available." + + print_what_to_do + choice=$(gum choose "$createOption" "$cancelOption") + +else + log_and_display "There are ''${#gitlabIssues[@]} issues available." + print_what_to_do + choice=$(gum choose "$createOption" "$existingOption" "$cancelOption") +fi + +if [[ "$choice" == "$cancelOption" ]]; then + do_cancel +fi + +## array of issue ids +work_on_issue_ids=() + +issue_text="" + +if [[ "$choice" == "$createOption" ]]; then + print_headline "Create new issue" + issue_text=$(gum input --placeholder "Enter issue title") + echo -e "Enter issue description. ''${RED}End with Ctrl+D''${RESET}\n" + issue_description=$(gum write --placeholder "Enter issue description. End with Ctrl+D") + + if [[ -z "$issue_text" ]]; then + log_error_and_display "Issue title is empty. Exiting." + printLogfileAndExit 1 + fi + + log_and_display "You entered the issue title: $issue_text" + log_and_display "You entered the issue description: $issue_description" + echo -e "\n" + + gum confirm "Do you want to create this issue?" + # gum confirm exits with status 0 if confirmed and status 1 if cancelled. + if [ $? -eq 1 ]; then + do_cancel + fi + + issue_output=$(glab issue create -t"$issue_text" --no-editor --description "$issue_description") + issue_id=$(echo "$issue_output" | grep -oP '(?<=/issues/)\d+') + + work_on_issue_ids+=("$issue_id") + + log_action "glab issue with id $issue_id created" + +else + + print_headline "Use existing issue" + echo -e "Select issue with arrow keys and press tab or space to select. Press enter to confirm.\n" + issue_ids=$(gum choose --no-limit "''${gitlabIssues[@]}") + + # assign issue_ids to work_on_issue_ids. iterate over lines and take integer from beginning of line + while IFS= read -r line; do + work_on_issue_ids+=($(echo "$line" | grep -oP '^\d+')) + done <<<"$issue_ids" + +fi + +if [ ''${#work_on_issue_ids[@]} -eq 0 ]; then + log_and_display "No issue selected. Exiting." + printLogfileAndExit 0 +fi + +# NEXT STEP COMMIT MESSAGE ############################################################################################################ +# print work_on_issue_ids +work_on_issue_ids_string="" +for i in "''${work_on_issue_ids[@]}"; do + work_on_issue_ids_string+="#$i " +done + +log_and_display "You chose to work on the following issues: ''${work_on_issue_ids_string}" + + +print_headline "Check for changes to commit" + +# ' ' = unmodified +# M = modified +# T = file type changed (regular file, symbolic link or submodule) +# A = added +# D = deleted +# R = renamed +# C = copied (if config option status.renames is set to "copies") +# U = updated but unmerged +# https://man.freebsd.org/cgi/man.cgi?query=git-status&sektion=1&manpath=freebsd-release-ports + +count_all_changes=$(echo "$git_status" | wc -l) +count_staged_changes=$(echo "$git_status" | grep -c '^M') +count_new_staged_files=$(echo "$git_status" | grep -c '^A') +count_staged_changes=$((count_staged_changes + count_new_staged_files)) + + + +git_options_all="All $count_all_changes changes" +git_options_staged="Only the $count_staged_changes staged changes" +git_options_select_files="Select files" +git_options_cancel="Cancel" + +git_options_array=() +if [[ $count_all_changes -gt 0 ]]; then + git_options_array+=("$git_options_all") +fi + +if [[ $count_staged_changes -gt 0 ]]; then + git_options_array+=("$git_options_staged") +fi + +git_options_array+=( "$git_options_select_files" ) +git_options_array+=( "$git_options_cancel" ) + + +selection=$(gum choose "''${git_options_array[@]}") +if [[ "$selection" == "$git_options_cancel" ]]; then + do_cancel +fi + +if [[ "$selection" == "$git_options_all" ]]; then + git add -A + echo "1" +elif [[ "$selection" == "$git_options_select_files" ]]; then + + files=() + while IFS= read -r line; do + files+=("$line") + done <<<"$git_status" + + selected_files=$(gum choose --no-limit "''${files[@]}") + + # no files selected + if [[ -z "$selected_files" ]]; then + log_and_display "No files selected. Exiting." + printLogfileAndExit 0 + fi + + # add selected files + while IFS= read -r line; do + ## git proclimne could have letter, ? or ! at the beginning of the line + file=$(echo "$line" | awk '{print $2}') + if [[ -z "$file" || ! -f "$file" ]]; then + log_and_display "No file found in line: $line" + continue + fi + + git add "$file" + done <<<"$selected_files" + +fi + +## count staged changes again and print +count_staged_changes=$(echo "$git_status" | grep -c '^M') +count_new_staged_files=$(echo "$git_status" | grep -c '^A') +count_staged_changes=$((count_staged_changes + count_new_staged_files)) + +log_and_display "You have $count_staged_changes staged changes to commit." + +# NEXT STEP COMMIT MESSAGE ############################################################################################################ + +print_headline "Enter commit message" +commit_message=$(gum input --placeholder "Enter commit message" --value "$commit_type: $issue_text $work_on_issue_ids_string") + +if [[ -z "$commit_message" ]]; then + log_error_and_display "Commit message is empty. Exiting." + printLogfileAndExit 1 +fi + +log_and_display "You entered the commit message: $commit_message" + +gum confirm "Do you want to commit with this message?" +if [ $? -eq 1 ]; then + do_cancel +fi + +# NEXT STEP COMMIT #################################################################################################################### +print_headline "Committing changes" + +if ! git commit -m "$commit_message" ; then + log_error_and_display "Commit failed. Exiting." + printLogfileAndExit 1 +fi + +log_and_display "Commit successful." + +# NEXT STEP PUSH ###################################################################################################################### + +print_headline "Pushing changes" + +if ! git push ; then + log_error_and_display "Push failed. Exiting." + printLogfileAndExit 1 +fi + +log_and_display "Push successful." + +# Close issue ###################################################################################################################### + +print_headline "Closing issues" + +for issue_id in "''${work_on_issue_ids[@]}"; do + + gum confirm "Do you want to close issue #$issue_id?" + if [ $? -eq 1 ]; then + continue + fi + + if ! glab issue close "$issue_id" ; then + log_error_and_display "Closing issue $issue_id failed. Exiting." + else + log_and_display "Closing issue $issue_id successful." + fi +done + +printLogfileAndExit 0 +''; + + +} diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/devenv.yaml b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/devenv.yaml new file mode 100644 index 0000000..525a6f0 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/devenv.yaml @@ -0,0 +1,7 @@ +inputs: + nixpkgs: + url: github:nixos/nixpkgs/nixos-23.05 + + version: + url: git+https://gitlab.schukai.com/oss/utilities/version.git + flake: true diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/error.go b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/error.go new file mode 100644 index 0000000..614b13e --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/error.go @@ -0,0 +1,39 @@ +// Copyright 2022 schukai GmbH +// SPDX-License-Identifier: AGPL-3.0 + +package pathfinder + +import ( + "errors" + "reflect" +) + +type InvalidPathError error + +func newInvalidPathError(path string) InvalidPathError { + return InvalidPathError(errors.New("invalid path " + path)) +} + +type UnsupportedTypeAtTopOfPathError error + +func newUnsupportedTypeAtTopOfPathError(path string, t reflect.Type) UnsupportedTypeAtTopOfPathError { + return UnsupportedTypeAtTopOfPathError(errors.New("unsupported type " + t.String() + " at top of path " + path)) +} + +type UnsupportedTypePathError error + +func newUnsupportedTypePathError(path string, t reflect.Type) UnsupportedTypePathError { + return UnsupportedTypePathError(errors.New("unsupported type " + t.String() + " at path " + path)) +} + +type CannotSetError error + +func newCannotSetError(name string) CannotSetError { + return CannotSetError(errors.New("cannot set " + name)) +} + +type InvalidTypeForPathError error + +func newInvalidTypeForPathError(path string, pt string, nt string) InvalidTypeForPathError { + return InvalidTypeForPathError(errors.New("invalid type for path " + path + ": expected " + pt + ", got " + nt)) +} diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/error_test.go b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/error_test.go new file mode 100644 index 0000000..e245946 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/error_test.go @@ -0,0 +1,40 @@ +// Copyright 2022 schukai GmbH +// SPDX-License-Identifier: AGPL-3.0 + +package pathfinder + +import ( + "github.com/stretchr/testify/assert" + "reflect" + "testing" +) + +func TestInvalidPathError(t *testing.T) { + err := newInvalidPathError("test") + _, ok := err.(InvalidPathError) + assert.True(t, ok) +} + +func TestUnsupportedTypeAtTopOfPathError(t *testing.T) { + err := newUnsupportedTypeAtTopOfPathError("test", reflect.TypeOf(1)) + _, ok := err.(UnsupportedTypeAtTopOfPathError) + assert.True(t, ok) +} + +func TestUnsupportedTypePathError(t *testing.T) { + err := newUnsupportedTypePathError("test", reflect.TypeOf(1)) + _, ok := err.(UnsupportedTypePathError) + assert.True(t, ok) +} + +func TestCannotSetError(t *testing.T) { + err := newCannotSetError("test") + _, ok := err.(CannotSetError) + assert.True(t, ok) +} + +func TestInvalidTypeForPathError(t *testing.T) { + err := newInvalidTypeForPathError("test", "test", "test") + _, ok := err.(InvalidTypeForPathError) + assert.True(t, ok) +} diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/get.go b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/get.go new file mode 100644 index 0000000..90c2b1c --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/get.go @@ -0,0 +1,74 @@ +// Copyright 2022 schukai GmbH +// SPDX-License-Identifier: AGPL-3.0 + +package pathfinder + +import ( + "reflect" + "strconv" + "strings" +) + +// GetValue returns the value of a field in a struct, given a path to the field. +// The path can contain dots to access nested fields. +// The object must be a pointer to a struct, a struct, a map, a slice or an array, +// otherwise an error is returned. +func GetValue[D any](obj D, keyWithDots string) (any, error) { + keySlice := strings.Split(keyWithDots, ".") + v := reflect.ValueOf(obj) + + for _, key := range keySlice[0:len(keySlice)] { + + if !v.IsValid() { + return nil, newInvalidPathError(keyWithDots) + } + + switch v.Kind() { + case reflect.Ptr, reflect.Interface: + v = v.Elem() + } + + switch v.Kind() { + case reflect.Map: + v = v.MapIndex(reflect.ValueOf(key)) + if !v.IsValid() { + return nil, newInvalidPathError(keyWithDots) + } + + case reflect.Slice, reflect.Array: + index, err := strconv.Atoi(key) + if err != nil { + return nil, newInvalidPathError(keyWithDots) + } + // check if index is in range + if index >= v.Len() { + return nil, newInvalidPathError(keyWithDots) + } + v = v.Index(index) + case reflect.Struct: + v = v.FieldByName(key) + if !v.IsValid() { + return nil, newInvalidPathError(keyWithDots) + } + default: + return nil, newInvalidPathError(keyWithDots) + } + + } + + if v.Kind() == reflect.Invalid { + return nil, newInvalidPathError(keyWithDots) + } + + for v.Kind() == reflect.Ptr { + v = v.Elem() + } + + // check if v can interface + if !v.CanInterface() { + return nil, newInvalidPathError(keyWithDots) + } + + return v.Interface(), nil + +} diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/get_test.go b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/get_test.go new file mode 100644 index 0000000..2340260 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/get_test.go @@ -0,0 +1,100 @@ +// Copyright 2022 schukai GmbH +// SPDX-License-Identifier: AGPL-3.0 + +package pathfinder + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestGetIndexFromArray(t *testing.T) { + m := map[string]any{ + "A": "true", + "B": []string{ + "1", + "2", + "3", + }, + } + + v, err := GetValue[map[string]any](m, "B.1") + if err != nil { + t.Error(err) + } + + assert.Equal(t, "2", v) + +} + +func TestGetValueFrom(t *testing.T) { + + m := map[string]string{ + "KeyA": "true", + "KeyB": "2", + "KeyC": "3", + "KeyD": "4.0", + } + + v, err := GetValue[map[string]string](m, "KeyA") + if err != nil { + t.Error(err) + } + + assert.Equal(t, v, "true") + +} + +func TestPathGetValueFromX(t *testing.T) { + m := map[string]any{ + "A": "true", + "B": map[string]any{ + "C": 2, + "D": map[string]any{ + "E": "3", + }, + }, + "X": "3", + "Y": "4.0", + } + + v, err := GetValue[map[string]any](m, "B.D.E") + if err != nil { + t.Error(err) + } + + assert.Equal(t, "3", v) + + v, err = GetValue[map[string]any](m, "B.C") + if err != nil { + t.Error(err) + } + + assert.Equal(t, 2, v) + +} + +func TestPathGetValueFrom(t *testing.T) { + + type PathfindTestStruct1 struct { + A bool + Sub1 struct { + B int + Sub2 struct { + C bool + } + D int + } + } + + var testData PathfindTestStruct1 + testData.A = true + testData.Sub1.B = 2 + testData.Sub1.Sub2.C = true + testData.Sub1.D = 4 + + GetValue[PathfindTestStruct1](testData, "Sub1.B") + GetValue[PathfindTestStruct1](testData, "Sub1.Sub2.C") + GetValue[PathfindTestStruct1](testData, "Sub1.D") + +} diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/go.mod b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/go.mod new file mode 100644 index 0000000..f1f0841 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/go.mod @@ -0,0 +1,11 @@ +module gitlab.schukai.com/oss/libraries/go/utilities/pathfinder + +go 1.20 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/testify v1.8.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/go.sum b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/go.sum new file mode 100644 index 0000000..0a45411 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/go.sum @@ -0,0 +1,16 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/issue_2_test.go b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/issue_2_test.go new file mode 100644 index 0000000..f9d652f --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/issue_2_test.go @@ -0,0 +1,151 @@ +// Copyright 2023 schukai GmbH +// SPDX-License-Identifier: AGPL-3.0 + +package pathfinder + +import ( + "fmt" + "github.com/google/gofuzz" + "github.com/stretchr/testify/assert" + "testing" +) + +type Issue2SubSubStruct struct { + Issue2SubSubField []string +} + +type Issue2SubStruct struct { + Issue2SubField Issue2SubSubStruct +} + +type Issue2Struct struct { + Issue1Sub1 Issue2SubStruct +} + +func TestGetValueWithArray(t *testing.T) { + testStructForGet := Issue2Struct{ + Issue1Sub1: Issue2SubStruct{ + Issue2SubField: Issue2SubSubStruct{ + Issue2SubSubField: []string{"test0", "test1", "test2"}, + }, + }, + } + + // iterate from 0 to 2 + for i := 0; i < 2; i++ { + result, err := GetValue(testStructForGet, "Issue1Sub1.Issue2SubField.Issue2SubSubField."+fmt.Sprintf("%d", i)) + + assert.Nil(t, err) + assert.Equal(t, result, "test"+fmt.Sprintf("%d", i)) + } + + i := 3 + result, err := GetValue(testStructForGet, "Issue1Sub1.Issue2SubField.Issue2SubSubField."+fmt.Sprintf("%d", i)) + + assert.NotNil(t, err) + assert.Nil(t, result) + +} + +func TestGetValueWithArrayFuzz(t *testing.T) { + f := fuzz.New() + + testStructForGet := Issue2Struct{ + Issue1Sub1: Issue2SubStruct{ + Issue2SubField: Issue2SubSubStruct{ + Issue2SubSubField: []string{"test0", "test1", "test2"}, + }, + }, + } + + for i := 0; i < 100; i++ { + var randomIndex int + f.Fuzz(&randomIndex) + randomIndex = randomIndex % len(testStructForGet.Issue1Sub1.Issue2SubField.Issue2SubSubField) + if randomIndex < 0 { + randomIndex = -randomIndex + } + + result, err := GetValue(testStructForGet, "Issue1Sub1.Issue2SubField.Issue2SubSubField."+fmt.Sprintf("%d", randomIndex)) + + if randomIndex < 3 { + assert.Nil(t, err) + assert.Equal(t, result, "test"+fmt.Sprintf("%d", randomIndex)) + } else { + assert.NotNil(t, err) + assert.Nil(t, result) + } + } +} + +func TestSetValueWithArray(t *testing.T) { + testStructForSet := Issue2Struct{ + Issue1Sub1: Issue2SubStruct{ + Issue2SubField: Issue2SubSubStruct{ + Issue2SubSubField: []string{"test0", "test1", "test2"}, + }, + }, + } + + // iterate from 0 to 2 + for i := 0; i < 2; i++ { + + newValue := "test~~" + fmt.Sprintf("%d", i) + k := "Issue1Sub1.Issue2SubField.Issue2SubSubField." + fmt.Sprintf("%d", i) + err := SetValue(&testStructForSet, k, newValue) + + assert.Nil(t, err) + + result, err := GetValue(testStructForSet, k) + assert.Equal(t, result, newValue) + } + + i := 3 + k := "Issue1Sub1.Issue2SubField.Issue2SubSubField." + fmt.Sprintf("%d", i) + err := SetValue(testStructForSet, k, "test3") + assert.NotNil(t, err) + +} + +type PathValue string + +type SubTestSubPaths struct { + Template PathValue + Definitions []PathValue +} + +type SubTest2Def struct { + Paths SubTestSubPaths +} + +type SubTestStruct1 map[string]SubTest2Def +type MainTestStruct struct { + Sub SubTestStruct1 +} + +func TestReplacePathForConfig(t *testing.T) { + config := MainTestStruct{ + Sub: SubTestStruct1{ + "Default": SubTest2Def{ + Paths: SubTestSubPaths{ + Template: "../../../default.html", + Definitions: []PathValue{"../../../legacy.yaml"}, + }, + }, + }, + } + + nn, err2 := GetValue[MainTestStruct](config, "Sub.Default.Paths.Template") + assert.Nil(t, err2) + + assert.Equal(t, nn, PathValue("../../../default.html")) + + err := SetValue[*MainTestStruct](&config, "Sub.Default.Paths.Template", "test") + assert.Nil(t, err) + + nn, err3 := GetValue[MainTestStruct](config, "Sub.Default.Paths.Template") + assert.Nil(t, err3) + + assert.Equal(t, nn, PathValue("test")) + +} diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/issue_7_test.go b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/issue_7_test.go new file mode 100644 index 0000000..5033b28 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/issue_7_test.go @@ -0,0 +1,126 @@ +package pathfinder + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +// Test für SetValue +func TestSetValue(t *testing.T) { + type Inner struct { + Field PathValue + } + type Outer struct { + InnerField *Inner + } + + obj := &Outer{ + InnerField: &Inner{ + Field: "oldValue", + }, + } + + v, err := GetValue[Outer](*obj, "InnerField.Field") + if err != nil { + t.Error(err) + } + + assert.Equal(t, PathValue("oldValue"), v) + + nv := PathValue("newValue") + err = SetValue[*Outer](obj, "InnerField.Field", nv) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if obj.InnerField.Field != "newValue" { + t.Fatalf("expected newValue, got: %s", obj.InnerField.Field) + } + + v, err = GetValue[Outer](*obj, "InnerField.Field") + if err != nil { + t.Error(err) + } + + assert.Equal(t, v, nv) + +} + +type Issue7Routing struct { + P PathValue `json:"p" yaml:"p"` + X string `json:"x" yaml:"x"` +} + +type Issue7Server struct { + Routing []Issue7Routing `json:"routing" yaml:"routing"` +} + +type Issue7Config struct { + Server Issue7Server `json:"server" yaml:"server"` +} + +func TestPathRewrite(t *testing.T) { + + obj := Issue7Config{ + Server: Issue7Server{ + Routing: []Issue7Routing{ + { + P: "./test", + X: "testX", + }, + }, + }, + } + + v, err := GetValue[*Issue7Config](&obj, "Server.Routing.0.P") + assert.Nil(t, err) + + assert.Equal(t, PathValue("./test"), v) + + nv := PathValue("newValue") + err = SetValue[*Issue7Config](&obj, "Server.Routing.0.P", nv) + assert.Nil(t, err) + + if obj.Server.Routing[0].P != "newValue" { + t.Fatalf("expected newValue, got: %s", obj.Server.Routing[0].P) + } + + v, err = GetValue[*Issue7Config](&obj, "Server.Routing.0.P") + assert.Nil(t, err) + assert.Equal(t, v, nv) + +} + +// Test data structs +type Issue7TestStruct2 struct { + D map[string]PathValue +} + +func TestPathRewrite2(t *testing.T) { + // Test case 2 + ts2 := Issue7TestStruct2{ + D: map[string]PathValue{ + "key1": "yyy", + "key2": "zzz", + }, + } + + v, err := GetValue[Issue7TestStruct2](ts2, "D.key1") + assert.Nil(t, err) + assert.Equal(t, PathValue("yyy"), v) + + v, err = GetValue[Issue7TestStruct2](ts2, "D.key2") + assert.Nil(t, err) + assert.Equal(t, PathValue("zzz"), v) + + err = SetValue[*Issue7TestStruct2](&ts2, "D.key1", "xxx") + assert.Nil(t, err) + + v, err = GetValue[Issue7TestStruct2](ts2, "D.key1") + assert.Nil(t, err) + assert.Equal(t, PathValue("xxx"), v) + + v, err = GetValue[Issue7TestStruct2](ts2, "D.key2") + assert.Nil(t, err) + assert.Equal(t, PathValue("zzz"), v) +} diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/pathfinder.iml b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/pathfinder.iml new file mode 100644 index 0000000..49df094 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/pathfinder.iml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="WEB_MODULE" version="4"> + <component name="Go" enabled="true" /> + <component name="NewModuleRootManager" inherit-compiler-output="true"> + <exclude-output /> + <content url="file://$MODULE_DIR$" /> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + </component> +</module> \ No newline at end of file diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/release.json b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/release.json new file mode 100644 index 0000000..ccd00c2 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/release.json @@ -0,0 +1 @@ +{"version":"0.5.2"} diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/set.go b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/set.go new file mode 100644 index 0000000..92b6509 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/set.go @@ -0,0 +1,332 @@ +// Copyright 2022 schukai GmbH +// SPDX-License-Identifier: AGPL-3.0 + +package pathfinder + +import ( + "bytes" + "encoding/gob" + "fmt" + "reflect" + "strconv" + "strings" +) + +func deepCopy(src, dst interface{}) error { + var buf bytes.Buffer + enc := gob.NewEncoder(&buf) + dec := gob.NewDecoder(&buf) + + if err := enc.Encode(src); err != nil { + return err + } + + return dec.Decode(dst) +} + +// SetValue sets the value of a field in a struct, given a path to the field. +// The object must be a pointer to a struct, otherwise an error is returned. +func SetValue[D any](obj D, keyWithDots string, newValue any) error { + + keySlice := strings.Split(keyWithDots, ".") + reflectionOfObject := reflect.ValueOf(obj) + + for keyIndex, key := range keySlice[0 : len(keySlice)-1] { + + if reflectionOfObject.Kind() == reflect.Map { + + if reflectionOfObject.IsNil() { + return newInvalidPathError(keyWithDots) + } + + currentValue := reflectionOfObject.MapIndex(reflect.ValueOf(key)).Interface() + newValueCopy := reflect.New(reflect.TypeOf(currentValue)).Interface() + if err := deepCopy(currentValue, newValueCopy); err != nil { + return err + } + + newValueCopyPtr := &newValueCopy + newValueCopyReflect := reflect.ValueOf(newValueCopyPtr).Elem() + if !newValueCopyReflect.CanAddr() { + return newCannotSetError("Wert ist nicht adressierbar") + } + newKey := strings.Join(keySlice[keyIndex+1:], ".") + err := SetValue(newValueCopyPtr, newKey, newValue) + if err != nil { + return err + } + + reflectionOfObject.SetMapIndex(reflect.ValueOf(key), reflect.ValueOf(newValueCopy).Elem()) + return nil + + } + + if reflectionOfObject.Kind() == reflect.Ptr && reflectionOfObject.Elem().Kind() == reflect.Interface { + reflectionOfObject = reflectionOfObject.Elem().Elem() + } + + for reflectionOfObject.Kind() != reflect.Ptr { + if reflectionOfObject.Kind() == reflect.Invalid { + return newInvalidPathError(keyWithDots) + } + + if reflectionOfObject.CanAddr() { + reflectionOfObject = reflectionOfObject.Addr() + } else { + return newCannotSetError(keyWithDots) + } + + } + + if reflectionOfObject.Kind() != reflect.Ptr { + return newUnsupportedTypePathError(keyWithDots, reflectionOfObject.Type()) + } + + switch reflectionOfObject.Elem().Kind() { + case reflect.Struct: + reflectionOfObject = reflectionOfObject.Elem().FieldByName(key) + + case reflect.Slice: + // index is a number and get reflectionOfObject from slice with index + index, err := strconv.Atoi(key) + if err != nil { + return newInvalidPathError(keyWithDots) + } + + if index >= reflectionOfObject.Elem().Len() { + return newInvalidPathError(keyWithDots) + } + + reflectionOfObject = reflectionOfObject.Elem().Index(index) + default: + return newUnsupportedTypePathError(keyWithDots, reflectionOfObject.Type()) + } + + } + + if reflectionOfObject.Kind() == reflect.Invalid { + return newInvalidPathError(keyWithDots) + } + + for reflectionOfObject.Kind() == reflect.Ptr { + reflectionOfObject = reflectionOfObject.Elem() + } + + // non-supporter type at the top of the path + switch reflectionOfObject.Kind() { + case reflect.Struct: + + reflectionOfObject = reflectionOfObject.FieldByName(keySlice[len(keySlice)-1]) + if !reflectionOfObject.IsValid() { + return newInvalidPathError(keyWithDots) + } + + if !reflectionOfObject.CanSet() { + return newCannotSetError(keyWithDots) + } + + case reflect.Map: + + key := keySlice[len(keySlice)-1] + m := reflectionOfObject + + keyVal := reflect.ValueOf(key) + newVal := reflect.ValueOf(newValue) + + if !keyVal.Type().ConvertibleTo(m.Type().Key()) { + return fmt.Errorf("key type mismatch") + } + + if !newVal.Type().ConvertibleTo(m.Type().Elem()) { + return fmt.Errorf("value type mismatch") + } + + keyValConverted := keyVal.Convert(m.Type().Key()) + newValConverted := newVal.Convert(m.Type().Elem()) + m.SetMapIndex(keyValConverted, newValConverted) + return nil + + //currentValue := reflectionOfObject.MapIndex(reflect.ValueOf(key)).Interface() + //newValueCopy := reflect.New(reflect.TypeOf(currentValue)).Interface() + //if err := deepCopy(currentValue, newValueCopy); err != nil { + // return err + //} + //newValueCopyPtr := &newValueCopy + //newValueCopyReflect := reflect.ValueOf(newValueCopyPtr).Elem() + //if !newValueCopyReflect.CanAddr() { + // return newCannotSetError("Wert ist nicht adressierbar") + //} + ////newKey := strings.Join(keySlice[keyIndex+1:], ".") + ////err := SetValue(newValueCopyPtr, newKey, newValue) + ////if err != nil { + //// return err + ////} + // + //reflectionOfObject.SetMapIndex(reflect.ValueOf(key), reflect.ValueOf(newValueCopy).Elem()) + //return nil + + //if reflectionOfObject.IsNil() { + // return newInvalidPathError(keyWithDots) + //} + // + //index := keySlice[len(keySlice)-1] + //reflectedIndex := reflect.ValueOf(index) + // + //if !reflectedIndex.Type().AssignableTo(reflectionOfObject.Type().Key()) { + // return newInvalidPathError(keyWithDots) + //} + // + //currentValue := reflectionOfObject.MapIndex(reflectedIndex).Interface() + //newValueCopy := reflect.New(reflect.TypeOf(currentValue)).Interface() + //if err := deepCopy(currentValue, newValueCopy); err != nil { + // return err + //} + // + //if !reflect.ValueOf(newValueCopy).Elem().Type().AssignableTo(reflectionOfObject.Type().Elem()) { + // return newInvalidPathError(keyWithDots) + //} + // + //newValueCopyX := reflect.ValueOf(newValueCopy).Elem() + //reflectionOfObject.SetMapIndex(reflectedIndex, newValueCopyX) + + case reflect.Slice: + + // index is a number and get reflectionOfObject from slice with index + index, err := strconv.Atoi(keySlice[len(keySlice)-1]) + if err != nil { + return newInvalidPathError(keyWithDots) + } + + // index out of range + if index >= reflectionOfObject.Len() { + return newInvalidPathError(keyWithDots) + } + + reflectionOfObject = reflectionOfObject.Index(index) + + case reflect.Array: + return newUnsupportedTypeAtTopOfPathError(keyWithDots, reflectionOfObject.Type()) + case reflect.Ptr: + if newValue == nil { + reflectionOfObject.Set(reflect.Zero(reflectionOfObject.Type())) + } else { + reflectionOfObject.Set(reflect.ValueOf(&newValue)) + } + return nil + case reflect.Interface: + return newUnsupportedTypeAtTopOfPathError(keyWithDots, reflectionOfObject.Type()) + case reflect.Chan: + return newUnsupportedTypeAtTopOfPathError(keyWithDots, reflectionOfObject.Type()) + case reflect.Func: + return newUnsupportedTypeAtTopOfPathError(keyWithDots, reflectionOfObject.Type()) + case reflect.UnsafePointer: + return newUnsupportedTypeAtTopOfPathError(keyWithDots, reflectionOfObject.Type()) + case reflect.Uintptr: + return newUnsupportedTypeAtTopOfPathError(keyWithDots, reflectionOfObject.Type()) + case reflect.Complex64: + return newUnsupportedTypeAtTopOfPathError(keyWithDots, reflectionOfObject.Type()) + case reflect.Complex128: + return newUnsupportedTypeAtTopOfPathError(keyWithDots, reflectionOfObject.Type()) + case reflect.Invalid: + return newUnsupportedTypeAtTopOfPathError(keyWithDots, reflectionOfObject.Type()) + default: + return newUnsupportedTypeAtTopOfPathError(keyWithDots, reflectionOfObject.Type()) + } + + newValueType := reflect.TypeOf(newValue) + if newValueType == nil { + return newUnsupportedTypePathError(keyWithDots, reflectionOfObject.Type()) + } + + newValueKind := reflect.TypeOf(newValue).Kind() + + switch reflectionOfObject.Kind() { + case reflect.String: + if reflectionOfObject.Kind() == reflect.Ptr || reflectionOfObject.Kind() == reflect.Interface { + if reflectionOfObject.Elem().CanSet() && reflectionOfObject.Elem().Kind() == reflect.String { + if newValueKind == reflect.String { + reflectionOfObject.Elem().SetString(newValue.(string)) + } else { + reflectionOfObject.Elem().SetString(fmt.Sprintf("%v", newValue)) + } + } + } else if newValueKind == reflect.String { + + if reflect.TypeOf(newValue).ConvertibleTo(reflect.TypeOf("")) { + newValueString := reflect.ValueOf(newValue).Convert(reflect.TypeOf("")).Interface().(string) + reflectionOfObject.SetString(newValueString) + } else { + return newUnsupportedTypePathError(keyWithDots, reflectionOfObject.Type()) + } + } else { + reflectionOfObject.SetString(fmt.Sprintf("%v", newValue)) + } + + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + + if newValueKind == reflect.Int { + reflectionOfObject.SetInt(int64(newValue.(int))) + } else { + s, err := strconv.ParseInt(fmt.Sprintf("%v", newValue), 10, 64) + if err != nil { + return err + } + reflectionOfObject.SetInt(s) + } + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + + if newValueKind == reflect.Int { + reflectionOfObject.SetUint(uint64(newValue.(int))) + } else { + s, err := strconv.ParseInt(fmt.Sprintf("%v", newValue), 10, 64) + if err != nil { + return err + } + reflectionOfObject.SetUint(uint64(s)) + } + + case reflect.Bool: + + if newValueKind == reflect.Bool { + reflectionOfObject.SetBool(newValue.(bool)) + } else { + b, err := strconv.ParseBool(fmt.Sprintf("%v", newValue)) + if err != nil { + return err + } + + reflectionOfObject.SetBool(b) + } + + case reflect.Float64, reflect.Float32: + + if newValueKind == reflect.Float64 { + reflectionOfObject.SetFloat(newValue.(float64)) + } else { + s, err := strconv.ParseFloat(fmt.Sprintf("%v", newValue), 64) + if err != nil { + return err + } + + reflectionOfObject.SetFloat(s) + } + + case reflect.Slice, reflect.Array: + + if newValueKind == reflect.Ptr { + newValue = reflect.ValueOf(newValue).Elem().Interface() + reflectionOfObject.Set(reflect.ValueOf(newValue)) + } else if newValueKind == reflect.Slice { + reflectionOfObject.Set(reflect.ValueOf(newValue)) + } else { + return newUnsupportedTypePathError(keyWithDots, reflectionOfObject.Type()) + } + + default: + return newInvalidTypeForPathError(keyWithDots, reflectionOfObject.Type().String(), newValueKind.String()) + } + + return nil + +} diff --git a/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/set_test.go b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/set_test.go new file mode 100644 index 0000000..37adcb7 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gitlab.schukai.com/oss/libraries/go/utilities/pathfinder@v0.8.1/set_test.go @@ -0,0 +1,278 @@ +// Copyright 2022 schukai GmbH +// SPDX-License-Identifier: AGPL-3.0 + +package pathfinder + +import "testing" + +type PathfindTestStruct1 struct { + A bool + Sub1 struct { + B bool + Bi int + Bs string + Bf float64 + Sub2 struct { + C bool + Ci int + Cs string + Cf float64 + + Sub3 struct { + D bool + Di int + Ds string + Df float64 + } + } + } +} + +func TestPathFindError(t *testing.T) { + + s := PathfindTestStruct1{} + + _, err := GetValue[PathfindTestStruct1](s, "Sub1.Sub2.Sub3.XX") + if err == nil { + t.Error("err == nil") + } + +} + +func TestPathFindSetValueString(t *testing.T) { + + testData := map[string]string{ + "Sub1.B": "true", + "Sub1.Bi": "2", + "Sub1.Bs": "3", + "Sub1.Bf": "4.0", + "Sub1.Sub2.C": "true", + "Sub1.Sub2.Ci": "2", + "Sub1.Sub2.Cs": "3", + "Sub1.Sub2.Cf": "4.0", + "Sub1.Sub2.Sub3.D": "true", + "Sub1.Sub2.Sub3.Di": "2", + "Sub1.Sub2.Sub3.Ds": "3", + "Sub1.Sub2.Sub3.Df": "4.0", + } + + for k, v := range testData { + s := &PathfindTestStruct1{} + err := SetValue[*PathfindTestStruct1](s, k, v) + if err != nil { + t.Error(err) + } + } +} + +func TestPathFindGetValueFrom(t *testing.T) { + + s := PathfindTestStruct1{} + + s.Sub1.B = true + s.Sub1.Bi = 2 + s.Sub1.Bs = "3" + s.Sub1.Bf = 4.0 + + v, err := GetValue[PathfindTestStruct1](s, "Sub1.B") + if err != nil { + t.Error(err) + } + + if v != true { + t.Error("v != true") + } + + v, err = GetValue[PathfindTestStruct1](s, "Sub1.Bi") + if err != nil { + t.Error(err) + } + + if v != 2 { + t.Error("v != 2") + } + + v, err = GetValue[PathfindTestStruct1](s, "Sub1.Bs") + if err != nil { + t.Error(err) + } + + if v != "3" { + t.Error("v != 3") + } + + v, err = GetValue[PathfindTestStruct1](s, "Sub1.Bf") + if err != nil { + t.Error(err) + } + + if v != 4.0 { + t.Error("v != 4.0") + } + + s.Sub1.Sub2.C = true + s.Sub1.Sub2.Ci = 2 + s.Sub1.Sub2.Cs = "3" + s.Sub1.Sub2.Cf = 4.0 + + v, err = GetValue[PathfindTestStruct1](s, "Sub1.Sub2.C") + if err != nil { + t.Error(err) + } + + if v != true { + t.Error("v != true") + } + + v, err = GetValue[PathfindTestStruct1](s, "Sub1.Sub2.Ci") + if err != nil { + t.Error(err) + } + + if v != 2 { + t.Error("v != 2") + } + + v, err = GetValue[PathfindTestStruct1](s, "Sub1.Sub2.Cs") + if err != nil { + t.Error(err) + } + + if v != "3" { + t.Error("v != 3") + } + + v, err = GetValue[PathfindTestStruct1](s, "Sub1.Sub2.Cf") + if err != nil { + t.Error(err) + } + + if v != 4.0 { + t.Error("v != 4.0") + } + + s.Sub1.Sub2.Sub3.D = true + s.Sub1.Sub2.Sub3.Di = 2 + s.Sub1.Sub2.Sub3.Ds = "3" + s.Sub1.Sub2.Sub3.Df = 4.0 + + v, err = GetValue[PathfindTestStruct1](s, "Sub1.Sub2.Sub3.D") + if err != nil { + t.Error(err) + + } + + if v != true { + t.Error("v != true") + } + + v, err = GetValue[PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Di") + if err != nil { + t.Error(err) + } + + if v != 2 { + t.Error("v != 2") + } + + v, err = GetValue[PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Ds") + if err != nil { + t.Error(err) + } + + if v != "3" { + t.Error("v != 3") + } + + v, err = GetValue[PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Df") + if err != nil { + t.Error(err) + } + + if v != 4.0 { + t.Error("v != 4.0") + } + +} + +func TestPathFindSetValueFrom(t *testing.T) { + s := &PathfindTestStruct1{} + + SetValue[*PathfindTestStruct1](s, "Sub1.B", "true") + SetValue[*PathfindTestStruct1](s, "Sub1.Bi", "2") + SetValue[*PathfindTestStruct1](s, "Sub1.Bs", "3") + SetValue[*PathfindTestStruct1](s, "Sub1.Bf", "4.0") + + if s.Sub1.B != true { + t.Error("s.Sub1.B != true") + } + + if s.Sub1.Bi != 2 { + t.Error("s.Sub1.Bi != 2") + + } + + if s.Sub1.Bs != "3" { + t.Error("s.Sub1.Bs != 3") + } + + if s.Sub1.Bf != 4.0 { + t.Error("s.Sub1.Bf != 4.0") + } + + SetValue[*PathfindTestStruct1](s, "Sub1.Sub2.C", "true") + SetValue[*PathfindTestStruct1](s, "Sub1.Sub2.Ci", "2") + SetValue[*PathfindTestStruct1](s, "Sub1.Sub2.Cs", "3") + SetValue[*PathfindTestStruct1](s, "Sub1.Sub2.Cf", "4.0") + + if s.Sub1.Sub2.C != true { + t.Error("s.Sub1.Sub2.C != true") + + } + + if s.Sub1.Sub2.Ci != 2 { + t.Error("s.Sub1.Sub2.Ci != 2") + + } + + if s.Sub1.Sub2.Cs != "3" { + t.Error("s.Sub1.Sub2.Cs != 3") + + } + + if s.Sub1.Sub2.Cf != 4.0 { + t.Error("s.Sub1.Sub2.Cf != 4.0") + + } + + if s.Sub1.Sub2.Sub3.D != false { + t.Error("s.Sub1.Sub2.Sub3.D != false") + + } + + SetValue[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.D", "true") + SetValue[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Di", "2") + SetValue[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Ds", "3") + SetValue[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Df", "4.0") + + if s.Sub1.Sub2.Sub3.D != true { + t.Error("s.Sub1.Sub2.Sub3.D != true") + + } + + if s.Sub1.Sub2.Sub3.Di != 2 { + t.Error("s.Sub1.Sub2.Sub3.Di != 2") + + } + + if s.Sub1.Sub2.Sub3.Ds != "3" { + t.Error("s.Sub1.Sub2.Sub3.Ds != 3") + + } + + if s.Sub1.Sub2.Sub3.Df != 4.0 { + t.Error("s.Sub1.Sub2.Sub3.Df != 4.0") + + } + +} diff --git a/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/.github/workflows/go.yaml b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/.github/workflows/go.yaml new file mode 100644 index 0000000..d2bb00b --- /dev/null +++ b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/.github/workflows/go.yaml @@ -0,0 +1,61 @@ +--- +name: Go +on: [push, pull_request] +jobs: + test: + name: Test + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + matrix: + go: + - "1.5" + - "1.6" + - "1.7" + - "1.8" + - "1.9" + - "1.10" + - "1.11" + - "1.12" + - "1.13" + - "1.14" + - "1.15" + - "1.16.0-beta1" + - "tip" + env: + GOPATH: ${{ github.workspace }}/go + steps: + - name: Check out code into the Go module directory + uses: actions/checkout@v2 + with: + path: ${{ github.workspace }}/go/src/gopkg.in/yaml.v3 + - name: Set up Go ${{ matrix.go }} + if: matrix.go != 'tip' + uses: actions/setup-go@v2 + with: + go-version: ${{ matrix.go }} + stable: false + - name: Set up Go ${{ matrix.go }} + if: matrix.go == 'tip' + run: | + export GOROOT_BOOTSTRAP=`go env GOROOT` + export GOROOT=$HOME/gotip + mkdir $HOME/gotip + cd $HOME/gotip + + curl -s 'https://go.googlesource.com/go/+/refs/heads/master?format=JSON' | awk '/"commit"/{print substr($2,2,40);exit}' >HEAD + awk '{printf("gotip-%s",substr($0,0,7))}' <HEAD >VERSION + + curl -s -o go.tar.gz https://go.googlesource.com/go/+archive/`cat HEAD`.tar.gz + tar xfz go.tar.gz + + cd src + bash make.bash + + echo "GOROOT=$GOROOT" >> $GITHUB_ENV + echo "$GOROOT/bin" >> $GITHUB_PATH + - run: go version + - run: go get -t ./... + working-directory: ${{ github.workspace }}/go/src/gopkg.in/yaml.v3 + - run: go test . + working-directory: ${{ github.workspace }}/go/src/gopkg.in/yaml.v3 diff --git a/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/LICENSE b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/LICENSE new file mode 100644 index 0000000..2683e4b --- /dev/null +++ b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/LICENSE @@ -0,0 +1,50 @@ + +This project is covered by two different licenses: MIT and Apache. + +#### MIT License #### + +The following files were ported to Go from C files of libyaml, and thus +are still covered by their original MIT license, with the additional +copyright staring in 2011 when the project was ported over: + + apic.go emitterc.go parserc.go readerc.go scannerc.go + writerc.go yamlh.go yamlprivateh.go + +Copyright (c) 2006-2010 Kirill Simonov +Copyright (c) 2006-2011 Kirill Simonov + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +### Apache License ### + +All the remaining project files are covered by the Apache license: + +Copyright (c) 2011-2019 Canonical Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/NOTICE b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/NOTICE new file mode 100644 index 0000000..866d74a --- /dev/null +++ b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/NOTICE @@ -0,0 +1,13 @@ +Copyright 2011-2016 Canonical Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/README.md b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/README.md new file mode 100644 index 0000000..08eb1ba --- /dev/null +++ b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/README.md @@ -0,0 +1,150 @@ +# YAML support for the Go language + +Introduction +------------ + +The yaml package enables Go programs to comfortably encode and decode YAML +values. It was developed within [Canonical](https://www.canonical.com) as +part of the [juju](https://juju.ubuntu.com) project, and is based on a +pure Go port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML) +C library to parse and generate YAML data quickly and reliably. + +Compatibility +------------- + +The yaml package supports most of YAML 1.2, but preserves some behavior +from 1.1 for backwards compatibility. + +Specifically, as of v3 of the yaml package: + + - YAML 1.1 bools (_yes/no, on/off_) are supported as long as they are being + decoded into a typed bool value. Otherwise they behave as a string. Booleans + in YAML 1.2 are _true/false_ only. + - Octals encode and decode as _0777_ per YAML 1.1, rather than _0o777_ + as specified in YAML 1.2, because most parsers still use the old format. + Octals in the _0o777_ format are supported though, so new files work. + - Does not support base-60 floats. These are gone from YAML 1.2, and were + actually never supported by this package as it's clearly a poor choice. + +and offers backwards +compatibility with YAML 1.1 in some cases. +1.2, including support for +anchors, tags, map merging, etc. Multi-document unmarshalling is not yet +implemented, and base-60 floats from YAML 1.1 are purposefully not +supported since they're a poor design and are gone in YAML 1.2. + +Installation and usage +---------------------- + +The import path for the package is *gopkg.in/yaml.v3*. + +To install it, run: + + go get gopkg.in/yaml.v3 + +API documentation +----------------- + +If opened in a browser, the import path itself leads to the API documentation: + + - [https://gopkg.in/yaml.v3](https://gopkg.in/yaml.v3) + +API stability +------------- + +The package API for yaml v3 will remain stable as described in [gopkg.in](https://gopkg.in). + + +License +------- + +The yaml package is licensed under the MIT and Apache License 2.0 licenses. +Please see the LICENSE file for details. + + +Example +------- + +```Go +package main + +import ( + "fmt" + "log" + + "gopkg.in/yaml.v3" +) + +var data = ` +a: Easy! +b: + c: 2 + d: [3, 4] +` + +// Note: struct fields must be public in order for unmarshal to +// correctly populate the data. +type T struct { + A string + B struct { + RenamedC int `yaml:"c"` + D []int `yaml:",flow"` + } +} + +func main() { + t := T{} + + err := yaml.Unmarshal([]byte(data), &t) + if err != nil { + log.Fatalf("error: %v", err) + } + fmt.Printf("--- t:\n%v\n\n", t) + + d, err := yaml.Marshal(&t) + if err != nil { + log.Fatalf("error: %v", err) + } + fmt.Printf("--- t dump:\n%s\n\n", string(d)) + + m := make(map[interface{}]interface{}) + + err = yaml.Unmarshal([]byte(data), &m) + if err != nil { + log.Fatalf("error: %v", err) + } + fmt.Printf("--- m:\n%v\n\n", m) + + d, err = yaml.Marshal(&m) + if err != nil { + log.Fatalf("error: %v", err) + } + fmt.Printf("--- m dump:\n%s\n\n", string(d)) +} +``` + +This example will generate the following output: + +``` +--- t: +{Easy! {2 [3 4]}} + +--- t dump: +a: Easy! +b: + c: 2 + d: [3, 4] + + +--- m: +map[a:Easy! b:map[c:2 d:[3 4]]] + +--- m dump: +a: Easy! +b: + c: 2 + d: + - 3 + - 4 +``` + diff --git a/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/apic.go b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/apic.go new file mode 100644 index 0000000..ae7d049 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/apic.go @@ -0,0 +1,747 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// Copyright (c) 2006-2010 Kirill Simonov +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package yaml + +import ( + "io" +) + +func yaml_insert_token(parser *yaml_parser_t, pos int, token *yaml_token_t) { + //fmt.Println("yaml_insert_token", "pos:", pos, "typ:", token.typ, "head:", parser.tokens_head, "len:", len(parser.tokens)) + + // Check if we can move the queue at the beginning of the buffer. + if parser.tokens_head > 0 && len(parser.tokens) == cap(parser.tokens) { + if parser.tokens_head != len(parser.tokens) { + copy(parser.tokens, parser.tokens[parser.tokens_head:]) + } + parser.tokens = parser.tokens[:len(parser.tokens)-parser.tokens_head] + parser.tokens_head = 0 + } + parser.tokens = append(parser.tokens, *token) + if pos < 0 { + return + } + copy(parser.tokens[parser.tokens_head+pos+1:], parser.tokens[parser.tokens_head+pos:]) + parser.tokens[parser.tokens_head+pos] = *token +} + +// Create a new parser object. +func yaml_parser_initialize(parser *yaml_parser_t) bool { + *parser = yaml_parser_t{ + raw_buffer: make([]byte, 0, input_raw_buffer_size), + buffer: make([]byte, 0, input_buffer_size), + } + return true +} + +// Destroy a parser object. +func yaml_parser_delete(parser *yaml_parser_t) { + *parser = yaml_parser_t{} +} + +// String read handler. +func yaml_string_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) { + if parser.input_pos == len(parser.input) { + return 0, io.EOF + } + n = copy(buffer, parser.input[parser.input_pos:]) + parser.input_pos += n + return n, nil +} + +// Reader read handler. +func yaml_reader_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) { + return parser.input_reader.Read(buffer) +} + +// Set a string input. +func yaml_parser_set_input_string(parser *yaml_parser_t, input []byte) { + if parser.read_handler != nil { + panic("must set the input source only once") + } + parser.read_handler = yaml_string_read_handler + parser.input = input + parser.input_pos = 0 +} + +// Set a file input. +func yaml_parser_set_input_reader(parser *yaml_parser_t, r io.Reader) { + if parser.read_handler != nil { + panic("must set the input source only once") + } + parser.read_handler = yaml_reader_read_handler + parser.input_reader = r +} + +// Set the source encoding. +func yaml_parser_set_encoding(parser *yaml_parser_t, encoding yaml_encoding_t) { + if parser.encoding != yaml_ANY_ENCODING { + panic("must set the encoding only once") + } + parser.encoding = encoding +} + +// Create a new emitter object. +func yaml_emitter_initialize(emitter *yaml_emitter_t) { + *emitter = yaml_emitter_t{ + buffer: make([]byte, output_buffer_size), + raw_buffer: make([]byte, 0, output_raw_buffer_size), + states: make([]yaml_emitter_state_t, 0, initial_stack_size), + events: make([]yaml_event_t, 0, initial_queue_size), + best_width: -1, + } +} + +// Destroy an emitter object. +func yaml_emitter_delete(emitter *yaml_emitter_t) { + *emitter = yaml_emitter_t{} +} + +// String write handler. +func yaml_string_write_handler(emitter *yaml_emitter_t, buffer []byte) error { + *emitter.output_buffer = append(*emitter.output_buffer, buffer...) + return nil +} + +// yaml_writer_write_handler uses emitter.output_writer to write the +// emitted text. +func yaml_writer_write_handler(emitter *yaml_emitter_t, buffer []byte) error { + _, err := emitter.output_writer.Write(buffer) + return err +} + +// Set a string output. +func yaml_emitter_set_output_string(emitter *yaml_emitter_t, output_buffer *[]byte) { + if emitter.write_handler != nil { + panic("must set the output target only once") + } + emitter.write_handler = yaml_string_write_handler + emitter.output_buffer = output_buffer +} + +// Set a file output. +func yaml_emitter_set_output_writer(emitter *yaml_emitter_t, w io.Writer) { + if emitter.write_handler != nil { + panic("must set the output target only once") + } + emitter.write_handler = yaml_writer_write_handler + emitter.output_writer = w +} + +// Set the output encoding. +func yaml_emitter_set_encoding(emitter *yaml_emitter_t, encoding yaml_encoding_t) { + if emitter.encoding != yaml_ANY_ENCODING { + panic("must set the output encoding only once") + } + emitter.encoding = encoding +} + +// Set the canonical output style. +func yaml_emitter_set_canonical(emitter *yaml_emitter_t, canonical bool) { + emitter.canonical = canonical +} + +// Set the indentation increment. +func yaml_emitter_set_indent(emitter *yaml_emitter_t, indent int) { + if indent < 2 || indent > 9 { + indent = 2 + } + emitter.best_indent = indent +} + +// Set the preferred line width. +func yaml_emitter_set_width(emitter *yaml_emitter_t, width int) { + if width < 0 { + width = -1 + } + emitter.best_width = width +} + +// Set if unescaped non-ASCII characters are allowed. +func yaml_emitter_set_unicode(emitter *yaml_emitter_t, unicode bool) { + emitter.unicode = unicode +} + +// Set the preferred line break character. +func yaml_emitter_set_break(emitter *yaml_emitter_t, line_break yaml_break_t) { + emitter.line_break = line_break +} + +///* +// * Destroy a token object. +// */ +// +//YAML_DECLARE(void) +//yaml_token_delete(yaml_token_t *token) +//{ +// assert(token); // Non-NULL token object expected. +// +// switch (token.type) +// { +// case YAML_TAG_DIRECTIVE_TOKEN: +// yaml_free(token.data.tag_directive.handle); +// yaml_free(token.data.tag_directive.prefix); +// break; +// +// case YAML_ALIAS_TOKEN: +// yaml_free(token.data.alias.value); +// break; +// +// case YAML_ANCHOR_TOKEN: +// yaml_free(token.data.anchor.value); +// break; +// +// case YAML_TAG_TOKEN: +// yaml_free(token.data.tag.handle); +// yaml_free(token.data.tag.suffix); +// break; +// +// case YAML_SCALAR_TOKEN: +// yaml_free(token.data.scalar.value); +// break; +// +// default: +// break; +// } +// +// memset(token, 0, sizeof(yaml_token_t)); +//} +// +///* +// * Check if a string is a valid UTF-8 sequence. +// * +// * Check 'reader.c' for more details on UTF-8 encoding. +// */ +// +//static int +//yaml_check_utf8(yaml_char_t *start, size_t length) +//{ +// yaml_char_t *end = start+length; +// yaml_char_t *pointer = start; +// +// while (pointer < end) { +// unsigned char octet; +// unsigned int width; +// unsigned int value; +// size_t k; +// +// octet = pointer[0]; +// width = (octet & 0x80) == 0x00 ? 1 : +// (octet & 0xE0) == 0xC0 ? 2 : +// (octet & 0xF0) == 0xE0 ? 3 : +// (octet & 0xF8) == 0xF0 ? 4 : 0; +// value = (octet & 0x80) == 0x00 ? octet & 0x7F : +// (octet & 0xE0) == 0xC0 ? octet & 0x1F : +// (octet & 0xF0) == 0xE0 ? octet & 0x0F : +// (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0; +// if (!width) return 0; +// if (pointer+width > end) return 0; +// for (k = 1; k < width; k ++) { +// octet = pointer[k]; +// if ((octet & 0xC0) != 0x80) return 0; +// value = (value << 6) + (octet & 0x3F); +// } +// if (!((width == 1) || +// (width == 2 && value >= 0x80) || +// (width == 3 && value >= 0x800) || +// (width == 4 && value >= 0x10000))) return 0; +// +// pointer += width; +// } +// +// return 1; +//} +// + +// Create STREAM-START. +func yaml_stream_start_event_initialize(event *yaml_event_t, encoding yaml_encoding_t) { + *event = yaml_event_t{ + typ: yaml_STREAM_START_EVENT, + encoding: encoding, + } +} + +// Create STREAM-END. +func yaml_stream_end_event_initialize(event *yaml_event_t) { + *event = yaml_event_t{ + typ: yaml_STREAM_END_EVENT, + } +} + +// Create DOCUMENT-START. +func yaml_document_start_event_initialize( + event *yaml_event_t, + version_directive *yaml_version_directive_t, + tag_directives []yaml_tag_directive_t, + implicit bool, +) { + *event = yaml_event_t{ + typ: yaml_DOCUMENT_START_EVENT, + version_directive: version_directive, + tag_directives: tag_directives, + implicit: implicit, + } +} + +// Create DOCUMENT-END. +func yaml_document_end_event_initialize(event *yaml_event_t, implicit bool) { + *event = yaml_event_t{ + typ: yaml_DOCUMENT_END_EVENT, + implicit: implicit, + } +} + +// Create ALIAS. +func yaml_alias_event_initialize(event *yaml_event_t, anchor []byte) bool { + *event = yaml_event_t{ + typ: yaml_ALIAS_EVENT, + anchor: anchor, + } + return true +} + +// Create SCALAR. +func yaml_scalar_event_initialize(event *yaml_event_t, anchor, tag, value []byte, plain_implicit, quoted_implicit bool, style yaml_scalar_style_t) bool { + *event = yaml_event_t{ + typ: yaml_SCALAR_EVENT, + anchor: anchor, + tag: tag, + value: value, + implicit: plain_implicit, + quoted_implicit: quoted_implicit, + style: yaml_style_t(style), + } + return true +} + +// Create SEQUENCE-START. +func yaml_sequence_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_sequence_style_t) bool { + *event = yaml_event_t{ + typ: yaml_SEQUENCE_START_EVENT, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(style), + } + return true +} + +// Create SEQUENCE-END. +func yaml_sequence_end_event_initialize(event *yaml_event_t) bool { + *event = yaml_event_t{ + typ: yaml_SEQUENCE_END_EVENT, + } + return true +} + +// Create MAPPING-START. +func yaml_mapping_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_mapping_style_t) { + *event = yaml_event_t{ + typ: yaml_MAPPING_START_EVENT, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(style), + } +} + +// Create MAPPING-END. +func yaml_mapping_end_event_initialize(event *yaml_event_t) { + *event = yaml_event_t{ + typ: yaml_MAPPING_END_EVENT, + } +} + +// Destroy an event object. +func yaml_event_delete(event *yaml_event_t) { + *event = yaml_event_t{} +} + +///* +// * Create a document object. +// */ +// +//YAML_DECLARE(int) +//yaml_document_initialize(document *yaml_document_t, +// version_directive *yaml_version_directive_t, +// tag_directives_start *yaml_tag_directive_t, +// tag_directives_end *yaml_tag_directive_t, +// start_implicit int, end_implicit int) +//{ +// struct { +// error yaml_error_type_t +// } context +// struct { +// start *yaml_node_t +// end *yaml_node_t +// top *yaml_node_t +// } nodes = { NULL, NULL, NULL } +// version_directive_copy *yaml_version_directive_t = NULL +// struct { +// start *yaml_tag_directive_t +// end *yaml_tag_directive_t +// top *yaml_tag_directive_t +// } tag_directives_copy = { NULL, NULL, NULL } +// value yaml_tag_directive_t = { NULL, NULL } +// mark yaml_mark_t = { 0, 0, 0 } +// +// assert(document) // Non-NULL document object is expected. +// assert((tag_directives_start && tag_directives_end) || +// (tag_directives_start == tag_directives_end)) +// // Valid tag directives are expected. +// +// if (!STACK_INIT(&context, nodes, INITIAL_STACK_SIZE)) goto error +// +// if (version_directive) { +// version_directive_copy = yaml_malloc(sizeof(yaml_version_directive_t)) +// if (!version_directive_copy) goto error +// version_directive_copy.major = version_directive.major +// version_directive_copy.minor = version_directive.minor +// } +// +// if (tag_directives_start != tag_directives_end) { +// tag_directive *yaml_tag_directive_t +// if (!STACK_INIT(&context, tag_directives_copy, INITIAL_STACK_SIZE)) +// goto error +// for (tag_directive = tag_directives_start +// tag_directive != tag_directives_end; tag_directive ++) { +// assert(tag_directive.handle) +// assert(tag_directive.prefix) +// if (!yaml_check_utf8(tag_directive.handle, +// strlen((char *)tag_directive.handle))) +// goto error +// if (!yaml_check_utf8(tag_directive.prefix, +// strlen((char *)tag_directive.prefix))) +// goto error +// value.handle = yaml_strdup(tag_directive.handle) +// value.prefix = yaml_strdup(tag_directive.prefix) +// if (!value.handle || !value.prefix) goto error +// if (!PUSH(&context, tag_directives_copy, value)) +// goto error +// value.handle = NULL +// value.prefix = NULL +// } +// } +// +// DOCUMENT_INIT(*document, nodes.start, nodes.end, version_directive_copy, +// tag_directives_copy.start, tag_directives_copy.top, +// start_implicit, end_implicit, mark, mark) +// +// return 1 +// +//error: +// STACK_DEL(&context, nodes) +// yaml_free(version_directive_copy) +// while (!STACK_EMPTY(&context, tag_directives_copy)) { +// value yaml_tag_directive_t = POP(&context, tag_directives_copy) +// yaml_free(value.handle) +// yaml_free(value.prefix) +// } +// STACK_DEL(&context, tag_directives_copy) +// yaml_free(value.handle) +// yaml_free(value.prefix) +// +// return 0 +//} +// +///* +// * Destroy a document object. +// */ +// +//YAML_DECLARE(void) +//yaml_document_delete(document *yaml_document_t) +//{ +// struct { +// error yaml_error_type_t +// } context +// tag_directive *yaml_tag_directive_t +// +// context.error = YAML_NO_ERROR // Eliminate a compiler warning. +// +// assert(document) // Non-NULL document object is expected. +// +// while (!STACK_EMPTY(&context, document.nodes)) { +// node yaml_node_t = POP(&context, document.nodes) +// yaml_free(node.tag) +// switch (node.type) { +// case YAML_SCALAR_NODE: +// yaml_free(node.data.scalar.value) +// break +// case YAML_SEQUENCE_NODE: +// STACK_DEL(&context, node.data.sequence.items) +// break +// case YAML_MAPPING_NODE: +// STACK_DEL(&context, node.data.mapping.pairs) +// break +// default: +// assert(0) // Should not happen. +// } +// } +// STACK_DEL(&context, document.nodes) +// +// yaml_free(document.version_directive) +// for (tag_directive = document.tag_directives.start +// tag_directive != document.tag_directives.end +// tag_directive++) { +// yaml_free(tag_directive.handle) +// yaml_free(tag_directive.prefix) +// } +// yaml_free(document.tag_directives.start) +// +// memset(document, 0, sizeof(yaml_document_t)) +//} +// +///** +// * Get a document node. +// */ +// +//YAML_DECLARE(yaml_node_t *) +//yaml_document_get_node(document *yaml_document_t, index int) +//{ +// assert(document) // Non-NULL document object is expected. +// +// if (index > 0 && document.nodes.start + index <= document.nodes.top) { +// return document.nodes.start + index - 1 +// } +// return NULL +//} +// +///** +// * Get the root object. +// */ +// +//YAML_DECLARE(yaml_node_t *) +//yaml_document_get_root_node(document *yaml_document_t) +//{ +// assert(document) // Non-NULL document object is expected. +// +// if (document.nodes.top != document.nodes.start) { +// return document.nodes.start +// } +// return NULL +//} +// +///* +// * Add a scalar node to a document. +// */ +// +//YAML_DECLARE(int) +//yaml_document_add_scalar(document *yaml_document_t, +// tag *yaml_char_t, value *yaml_char_t, length int, +// style yaml_scalar_style_t) +//{ +// struct { +// error yaml_error_type_t +// } context +// mark yaml_mark_t = { 0, 0, 0 } +// tag_copy *yaml_char_t = NULL +// value_copy *yaml_char_t = NULL +// node yaml_node_t +// +// assert(document) // Non-NULL document object is expected. +// assert(value) // Non-NULL value is expected. +// +// if (!tag) { +// tag = (yaml_char_t *)YAML_DEFAULT_SCALAR_TAG +// } +// +// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error +// tag_copy = yaml_strdup(tag) +// if (!tag_copy) goto error +// +// if (length < 0) { +// length = strlen((char *)value) +// } +// +// if (!yaml_check_utf8(value, length)) goto error +// value_copy = yaml_malloc(length+1) +// if (!value_copy) goto error +// memcpy(value_copy, value, length) +// value_copy[length] = '\0' +// +// SCALAR_NODE_INIT(node, tag_copy, value_copy, length, style, mark, mark) +// if (!PUSH(&context, document.nodes, node)) goto error +// +// return document.nodes.top - document.nodes.start +// +//error: +// yaml_free(tag_copy) +// yaml_free(value_copy) +// +// return 0 +//} +// +///* +// * Add a sequence node to a document. +// */ +// +//YAML_DECLARE(int) +//yaml_document_add_sequence(document *yaml_document_t, +// tag *yaml_char_t, style yaml_sequence_style_t) +//{ +// struct { +// error yaml_error_type_t +// } context +// mark yaml_mark_t = { 0, 0, 0 } +// tag_copy *yaml_char_t = NULL +// struct { +// start *yaml_node_item_t +// end *yaml_node_item_t +// top *yaml_node_item_t +// } items = { NULL, NULL, NULL } +// node yaml_node_t +// +// assert(document) // Non-NULL document object is expected. +// +// if (!tag) { +// tag = (yaml_char_t *)YAML_DEFAULT_SEQUENCE_TAG +// } +// +// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error +// tag_copy = yaml_strdup(tag) +// if (!tag_copy) goto error +// +// if (!STACK_INIT(&context, items, INITIAL_STACK_SIZE)) goto error +// +// SEQUENCE_NODE_INIT(node, tag_copy, items.start, items.end, +// style, mark, mark) +// if (!PUSH(&context, document.nodes, node)) goto error +// +// return document.nodes.top - document.nodes.start +// +//error: +// STACK_DEL(&context, items) +// yaml_free(tag_copy) +// +// return 0 +//} +// +///* +// * Add a mapping node to a document. +// */ +// +//YAML_DECLARE(int) +//yaml_document_add_mapping(document *yaml_document_t, +// tag *yaml_char_t, style yaml_mapping_style_t) +//{ +// struct { +// error yaml_error_type_t +// } context +// mark yaml_mark_t = { 0, 0, 0 } +// tag_copy *yaml_char_t = NULL +// struct { +// start *yaml_node_pair_t +// end *yaml_node_pair_t +// top *yaml_node_pair_t +// } pairs = { NULL, NULL, NULL } +// node yaml_node_t +// +// assert(document) // Non-NULL document object is expected. +// +// if (!tag) { +// tag = (yaml_char_t *)YAML_DEFAULT_MAPPING_TAG +// } +// +// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error +// tag_copy = yaml_strdup(tag) +// if (!tag_copy) goto error +// +// if (!STACK_INIT(&context, pairs, INITIAL_STACK_SIZE)) goto error +// +// MAPPING_NODE_INIT(node, tag_copy, pairs.start, pairs.end, +// style, mark, mark) +// if (!PUSH(&context, document.nodes, node)) goto error +// +// return document.nodes.top - document.nodes.start +// +//error: +// STACK_DEL(&context, pairs) +// yaml_free(tag_copy) +// +// return 0 +//} +// +///* +// * Append an item to a sequence node. +// */ +// +//YAML_DECLARE(int) +//yaml_document_append_sequence_item(document *yaml_document_t, +// sequence int, item int) +//{ +// struct { +// error yaml_error_type_t +// } context +// +// assert(document) // Non-NULL document is required. +// assert(sequence > 0 +// && document.nodes.start + sequence <= document.nodes.top) +// // Valid sequence id is required. +// assert(document.nodes.start[sequence-1].type == YAML_SEQUENCE_NODE) +// // A sequence node is required. +// assert(item > 0 && document.nodes.start + item <= document.nodes.top) +// // Valid item id is required. +// +// if (!PUSH(&context, +// document.nodes.start[sequence-1].data.sequence.items, item)) +// return 0 +// +// return 1 +//} +// +///* +// * Append a pair of a key and a value to a mapping node. +// */ +// +//YAML_DECLARE(int) +//yaml_document_append_mapping_pair(document *yaml_document_t, +// mapping int, key int, value int) +//{ +// struct { +// error yaml_error_type_t +// } context +// +// pair yaml_node_pair_t +// +// assert(document) // Non-NULL document is required. +// assert(mapping > 0 +// && document.nodes.start + mapping <= document.nodes.top) +// // Valid mapping id is required. +// assert(document.nodes.start[mapping-1].type == YAML_MAPPING_NODE) +// // A mapping node is required. +// assert(key > 0 && document.nodes.start + key <= document.nodes.top) +// // Valid key id is required. +// assert(value > 0 && document.nodes.start + value <= document.nodes.top) +// // Valid value id is required. +// +// pair.key = key +// pair.value = value +// +// if (!PUSH(&context, +// document.nodes.start[mapping-1].data.mapping.pairs, pair)) +// return 0 +// +// return 1 +//} +// +// diff --git a/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/decode.go b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/decode.go new file mode 100644 index 0000000..0173b69 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/decode.go @@ -0,0 +1,1000 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package yaml + +import ( + "encoding" + "encoding/base64" + "fmt" + "io" + "math" + "reflect" + "strconv" + "time" +) + +// ---------------------------------------------------------------------------- +// Parser, produces a node tree out of a libyaml event stream. + +type parser struct { + parser yaml_parser_t + event yaml_event_t + doc *Node + anchors map[string]*Node + doneInit bool + textless bool +} + +func newParser(b []byte) *parser { + p := parser{} + if !yaml_parser_initialize(&p.parser) { + panic("failed to initialize YAML emitter") + } + if len(b) == 0 { + b = []byte{'\n'} + } + yaml_parser_set_input_string(&p.parser, b) + return &p +} + +func newParserFromReader(r io.Reader) *parser { + p := parser{} + if !yaml_parser_initialize(&p.parser) { + panic("failed to initialize YAML emitter") + } + yaml_parser_set_input_reader(&p.parser, r) + return &p +} + +func (p *parser) init() { + if p.doneInit { + return + } + p.anchors = make(map[string]*Node) + p.expect(yaml_STREAM_START_EVENT) + p.doneInit = true +} + +func (p *parser) destroy() { + if p.event.typ != yaml_NO_EVENT { + yaml_event_delete(&p.event) + } + yaml_parser_delete(&p.parser) +} + +// expect consumes an event from the event stream and +// checks that it's of the expected type. +func (p *parser) expect(e yaml_event_type_t) { + if p.event.typ == yaml_NO_EVENT { + if !yaml_parser_parse(&p.parser, &p.event) { + p.fail() + } + } + if p.event.typ == yaml_STREAM_END_EVENT { + failf("attempted to go past the end of stream; corrupted value?") + } + if p.event.typ != e { + p.parser.problem = fmt.Sprintf("expected %s event but got %s", e, p.event.typ) + p.fail() + } + yaml_event_delete(&p.event) + p.event.typ = yaml_NO_EVENT +} + +// peek peeks at the next event in the event stream, +// puts the results into p.event and returns the event type. +func (p *parser) peek() yaml_event_type_t { + if p.event.typ != yaml_NO_EVENT { + return p.event.typ + } + // It's curious choice from the underlying API to generally return a + // positive result on success, but on this case return true in an error + // scenario. This was the source of bugs in the past (issue #666). + if !yaml_parser_parse(&p.parser, &p.event) || p.parser.error != yaml_NO_ERROR { + p.fail() + } + return p.event.typ +} + +func (p *parser) fail() { + var where string + var line int + if p.parser.context_mark.line != 0 { + line = p.parser.context_mark.line + // Scanner errors don't iterate line before returning error + if p.parser.error == yaml_SCANNER_ERROR { + line++ + } + } else if p.parser.problem_mark.line != 0 { + line = p.parser.problem_mark.line + // Scanner errors don't iterate line before returning error + if p.parser.error == yaml_SCANNER_ERROR { + line++ + } + } + if line != 0 { + where = "line " + strconv.Itoa(line) + ": " + } + var msg string + if len(p.parser.problem) > 0 { + msg = p.parser.problem + } else { + msg = "unknown problem parsing YAML content" + } + failf("%s%s", where, msg) +} + +func (p *parser) anchor(n *Node, anchor []byte) { + if anchor != nil { + n.Anchor = string(anchor) + p.anchors[n.Anchor] = n + } +} + +func (p *parser) parse() *Node { + p.init() + switch p.peek() { + case yaml_SCALAR_EVENT: + return p.scalar() + case yaml_ALIAS_EVENT: + return p.alias() + case yaml_MAPPING_START_EVENT: + return p.mapping() + case yaml_SEQUENCE_START_EVENT: + return p.sequence() + case yaml_DOCUMENT_START_EVENT: + return p.document() + case yaml_STREAM_END_EVENT: + // Happens when attempting to decode an empty buffer. + return nil + case yaml_TAIL_COMMENT_EVENT: + panic("internal error: unexpected tail comment event (please report)") + default: + panic("internal error: attempted to parse unknown event (please report): " + p.event.typ.String()) + } +} + +func (p *parser) node(kind Kind, defaultTag, tag, value string) *Node { + var style Style + if tag != "" && tag != "!" { + tag = shortTag(tag) + style = TaggedStyle + } else if defaultTag != "" { + tag = defaultTag + } else if kind == ScalarNode { + tag, _ = resolve("", value) + } + n := &Node{ + Kind: kind, + Tag: tag, + Value: value, + Style: style, + } + if !p.textless { + n.Line = p.event.start_mark.line + 1 + n.Column = p.event.start_mark.column + 1 + n.HeadComment = string(p.event.head_comment) + n.LineComment = string(p.event.line_comment) + n.FootComment = string(p.event.foot_comment) + } + return n +} + +func (p *parser) parseChild(parent *Node) *Node { + child := p.parse() + parent.Content = append(parent.Content, child) + return child +} + +func (p *parser) document() *Node { + n := p.node(DocumentNode, "", "", "") + p.doc = n + p.expect(yaml_DOCUMENT_START_EVENT) + p.parseChild(n) + if p.peek() == yaml_DOCUMENT_END_EVENT { + n.FootComment = string(p.event.foot_comment) + } + p.expect(yaml_DOCUMENT_END_EVENT) + return n +} + +func (p *parser) alias() *Node { + n := p.node(AliasNode, "", "", string(p.event.anchor)) + n.Alias = p.anchors[n.Value] + if n.Alias == nil { + failf("unknown anchor '%s' referenced", n.Value) + } + p.expect(yaml_ALIAS_EVENT) + return n +} + +func (p *parser) scalar() *Node { + var parsedStyle = p.event.scalar_style() + var nodeStyle Style + switch { + case parsedStyle&yaml_DOUBLE_QUOTED_SCALAR_STYLE != 0: + nodeStyle = DoubleQuotedStyle + case parsedStyle&yaml_SINGLE_QUOTED_SCALAR_STYLE != 0: + nodeStyle = SingleQuotedStyle + case parsedStyle&yaml_LITERAL_SCALAR_STYLE != 0: + nodeStyle = LiteralStyle + case parsedStyle&yaml_FOLDED_SCALAR_STYLE != 0: + nodeStyle = FoldedStyle + } + var nodeValue = string(p.event.value) + var nodeTag = string(p.event.tag) + var defaultTag string + if nodeStyle == 0 { + if nodeValue == "<<" { + defaultTag = mergeTag + } + } else { + defaultTag = strTag + } + n := p.node(ScalarNode, defaultTag, nodeTag, nodeValue) + n.Style |= nodeStyle + p.anchor(n, p.event.anchor) + p.expect(yaml_SCALAR_EVENT) + return n +} + +func (p *parser) sequence() *Node { + n := p.node(SequenceNode, seqTag, string(p.event.tag), "") + if p.event.sequence_style()&yaml_FLOW_SEQUENCE_STYLE != 0 { + n.Style |= FlowStyle + } + p.anchor(n, p.event.anchor) + p.expect(yaml_SEQUENCE_START_EVENT) + for p.peek() != yaml_SEQUENCE_END_EVENT { + p.parseChild(n) + } + n.LineComment = string(p.event.line_comment) + n.FootComment = string(p.event.foot_comment) + p.expect(yaml_SEQUENCE_END_EVENT) + return n +} + +func (p *parser) mapping() *Node { + n := p.node(MappingNode, mapTag, string(p.event.tag), "") + block := true + if p.event.mapping_style()&yaml_FLOW_MAPPING_STYLE != 0 { + block = false + n.Style |= FlowStyle + } + p.anchor(n, p.event.anchor) + p.expect(yaml_MAPPING_START_EVENT) + for p.peek() != yaml_MAPPING_END_EVENT { + k := p.parseChild(n) + if block && k.FootComment != "" { + // Must be a foot comment for the prior value when being dedented. + if len(n.Content) > 2 { + n.Content[len(n.Content)-3].FootComment = k.FootComment + k.FootComment = "" + } + } + v := p.parseChild(n) + if k.FootComment == "" && v.FootComment != "" { + k.FootComment = v.FootComment + v.FootComment = "" + } + if p.peek() == yaml_TAIL_COMMENT_EVENT { + if k.FootComment == "" { + k.FootComment = string(p.event.foot_comment) + } + p.expect(yaml_TAIL_COMMENT_EVENT) + } + } + n.LineComment = string(p.event.line_comment) + n.FootComment = string(p.event.foot_comment) + if n.Style&FlowStyle == 0 && n.FootComment != "" && len(n.Content) > 1 { + n.Content[len(n.Content)-2].FootComment = n.FootComment + n.FootComment = "" + } + p.expect(yaml_MAPPING_END_EVENT) + return n +} + +// ---------------------------------------------------------------------------- +// Decoder, unmarshals a node into a provided value. + +type decoder struct { + doc *Node + aliases map[*Node]bool + terrors []string + + stringMapType reflect.Type + generalMapType reflect.Type + + knownFields bool + uniqueKeys bool + decodeCount int + aliasCount int + aliasDepth int + + mergedFields map[interface{}]bool +} + +var ( + nodeType = reflect.TypeOf(Node{}) + durationType = reflect.TypeOf(time.Duration(0)) + stringMapType = reflect.TypeOf(map[string]interface{}{}) + generalMapType = reflect.TypeOf(map[interface{}]interface{}{}) + ifaceType = generalMapType.Elem() + timeType = reflect.TypeOf(time.Time{}) + ptrTimeType = reflect.TypeOf(&time.Time{}) +) + +func newDecoder() *decoder { + d := &decoder{ + stringMapType: stringMapType, + generalMapType: generalMapType, + uniqueKeys: true, + } + d.aliases = make(map[*Node]bool) + return d +} + +func (d *decoder) terror(n *Node, tag string, out reflect.Value) { + if n.Tag != "" { + tag = n.Tag + } + value := n.Value + if tag != seqTag && tag != mapTag { + if len(value) > 10 { + value = " `" + value[:7] + "...`" + } else { + value = " `" + value + "`" + } + } + d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.Line, shortTag(tag), value, out.Type())) +} + +func (d *decoder) callUnmarshaler(n *Node, u Unmarshaler) (good bool) { + err := u.UnmarshalYAML(n) + if e, ok := err.(*TypeError); ok { + d.terrors = append(d.terrors, e.Errors...) + return false + } + if err != nil { + fail(err) + } + return true +} + +func (d *decoder) callObsoleteUnmarshaler(n *Node, u obsoleteUnmarshaler) (good bool) { + terrlen := len(d.terrors) + err := u.UnmarshalYAML(func(v interface{}) (err error) { + defer handleErr(&err) + d.unmarshal(n, reflect.ValueOf(v)) + if len(d.terrors) > terrlen { + issues := d.terrors[terrlen:] + d.terrors = d.terrors[:terrlen] + return &TypeError{issues} + } + return nil + }) + if e, ok := err.(*TypeError); ok { + d.terrors = append(d.terrors, e.Errors...) + return false + } + if err != nil { + fail(err) + } + return true +} + +// d.prepare initializes and dereferences pointers and calls UnmarshalYAML +// if a value is found to implement it. +// It returns the initialized and dereferenced out value, whether +// unmarshalling was already done by UnmarshalYAML, and if so whether +// its types unmarshalled appropriately. +// +// If n holds a null value, prepare returns before doing anything. +func (d *decoder) prepare(n *Node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) { + if n.ShortTag() == nullTag { + return out, false, false + } + again := true + for again { + again = false + if out.Kind() == reflect.Ptr { + if out.IsNil() { + out.Set(reflect.New(out.Type().Elem())) + } + out = out.Elem() + again = true + } + if out.CanAddr() { + outi := out.Addr().Interface() + if u, ok := outi.(Unmarshaler); ok { + good = d.callUnmarshaler(n, u) + return out, true, good + } + if u, ok := outi.(obsoleteUnmarshaler); ok { + good = d.callObsoleteUnmarshaler(n, u) + return out, true, good + } + } + } + return out, false, false +} + +func (d *decoder) fieldByIndex(n *Node, v reflect.Value, index []int) (field reflect.Value) { + if n.ShortTag() == nullTag { + return reflect.Value{} + } + for _, num := range index { + for { + if v.Kind() == reflect.Ptr { + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + v = v.Elem() + continue + } + break + } + v = v.Field(num) + } + return v +} + +const ( + // 400,000 decode operations is ~500kb of dense object declarations, or + // ~5kb of dense object declarations with 10000% alias expansion + alias_ratio_range_low = 400000 + + // 4,000,000 decode operations is ~5MB of dense object declarations, or + // ~4.5MB of dense object declarations with 10% alias expansion + alias_ratio_range_high = 4000000 + + // alias_ratio_range is the range over which we scale allowed alias ratios + alias_ratio_range = float64(alias_ratio_range_high - alias_ratio_range_low) +) + +func allowedAliasRatio(decodeCount int) float64 { + switch { + case decodeCount <= alias_ratio_range_low: + // allow 99% to come from alias expansion for small-to-medium documents + return 0.99 + case decodeCount >= alias_ratio_range_high: + // allow 10% to come from alias expansion for very large documents + return 0.10 + default: + // scale smoothly from 99% down to 10% over the range. + // this maps to 396,000 - 400,000 allowed alias-driven decodes over the range. + // 400,000 decode operations is ~100MB of allocations in worst-case scenarios (single-item maps). + return 0.99 - 0.89*(float64(decodeCount-alias_ratio_range_low)/alias_ratio_range) + } +} + +func (d *decoder) unmarshal(n *Node, out reflect.Value) (good bool) { + d.decodeCount++ + if d.aliasDepth > 0 { + d.aliasCount++ + } + if d.aliasCount > 100 && d.decodeCount > 1000 && float64(d.aliasCount)/float64(d.decodeCount) > allowedAliasRatio(d.decodeCount) { + failf("document contains excessive aliasing") + } + if out.Type() == nodeType { + out.Set(reflect.ValueOf(n).Elem()) + return true + } + switch n.Kind { + case DocumentNode: + return d.document(n, out) + case AliasNode: + return d.alias(n, out) + } + out, unmarshaled, good := d.prepare(n, out) + if unmarshaled { + return good + } + switch n.Kind { + case ScalarNode: + good = d.scalar(n, out) + case MappingNode: + good = d.mapping(n, out) + case SequenceNode: + good = d.sequence(n, out) + case 0: + if n.IsZero() { + return d.null(out) + } + fallthrough + default: + failf("cannot decode node with unknown kind %d", n.Kind) + } + return good +} + +func (d *decoder) document(n *Node, out reflect.Value) (good bool) { + if len(n.Content) == 1 { + d.doc = n + d.unmarshal(n.Content[0], out) + return true + } + return false +} + +func (d *decoder) alias(n *Node, out reflect.Value) (good bool) { + if d.aliases[n] { + // TODO this could actually be allowed in some circumstances. + failf("anchor '%s' value contains itself", n.Value) + } + d.aliases[n] = true + d.aliasDepth++ + good = d.unmarshal(n.Alias, out) + d.aliasDepth-- + delete(d.aliases, n) + return good +} + +var zeroValue reflect.Value + +func resetMap(out reflect.Value) { + for _, k := range out.MapKeys() { + out.SetMapIndex(k, zeroValue) + } +} + +func (d *decoder) null(out reflect.Value) bool { + if out.CanAddr() { + switch out.Kind() { + case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice: + out.Set(reflect.Zero(out.Type())) + return true + } + } + return false +} + +func (d *decoder) scalar(n *Node, out reflect.Value) bool { + var tag string + var resolved interface{} + if n.indicatedString() { + tag = strTag + resolved = n.Value + } else { + tag, resolved = resolve(n.Tag, n.Value) + if tag == binaryTag { + data, err := base64.StdEncoding.DecodeString(resolved.(string)) + if err != nil { + failf("!!binary value contains invalid base64 data") + } + resolved = string(data) + } + } + if resolved == nil { + return d.null(out) + } + if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() { + // We've resolved to exactly the type we want, so use that. + out.Set(resolvedv) + return true + } + // Perhaps we can use the value as a TextUnmarshaler to + // set its value. + if out.CanAddr() { + u, ok := out.Addr().Interface().(encoding.TextUnmarshaler) + if ok { + var text []byte + if tag == binaryTag { + text = []byte(resolved.(string)) + } else { + // We let any value be unmarshaled into TextUnmarshaler. + // That might be more lax than we'd like, but the + // TextUnmarshaler itself should bowl out any dubious values. + text = []byte(n.Value) + } + err := u.UnmarshalText(text) + if err != nil { + fail(err) + } + return true + } + } + switch out.Kind() { + case reflect.String: + if tag == binaryTag { + out.SetString(resolved.(string)) + return true + } + out.SetString(n.Value) + return true + case reflect.Interface: + out.Set(reflect.ValueOf(resolved)) + return true + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + // This used to work in v2, but it's very unfriendly. + isDuration := out.Type() == durationType + + switch resolved := resolved.(type) { + case int: + if !isDuration && !out.OverflowInt(int64(resolved)) { + out.SetInt(int64(resolved)) + return true + } + case int64: + if !isDuration && !out.OverflowInt(resolved) { + out.SetInt(resolved) + return true + } + case uint64: + if !isDuration && resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { + out.SetInt(int64(resolved)) + return true + } + case float64: + if !isDuration && resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { + out.SetInt(int64(resolved)) + return true + } + case string: + if out.Type() == durationType { + d, err := time.ParseDuration(resolved) + if err == nil { + out.SetInt(int64(d)) + return true + } + } + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + switch resolved := resolved.(type) { + case int: + if resolved >= 0 && !out.OverflowUint(uint64(resolved)) { + out.SetUint(uint64(resolved)) + return true + } + case int64: + if resolved >= 0 && !out.OverflowUint(uint64(resolved)) { + out.SetUint(uint64(resolved)) + return true + } + case uint64: + if !out.OverflowUint(uint64(resolved)) { + out.SetUint(uint64(resolved)) + return true + } + case float64: + if resolved <= math.MaxUint64 && !out.OverflowUint(uint64(resolved)) { + out.SetUint(uint64(resolved)) + return true + } + } + case reflect.Bool: + switch resolved := resolved.(type) { + case bool: + out.SetBool(resolved) + return true + case string: + // This offers some compatibility with the 1.1 spec (https://yaml.org/type/bool.html). + // It only works if explicitly attempting to unmarshal into a typed bool value. + switch resolved { + case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON": + out.SetBool(true) + return true + case "n", "N", "no", "No", "NO", "off", "Off", "OFF": + out.SetBool(false) + return true + } + } + case reflect.Float32, reflect.Float64: + switch resolved := resolved.(type) { + case int: + out.SetFloat(float64(resolved)) + return true + case int64: + out.SetFloat(float64(resolved)) + return true + case uint64: + out.SetFloat(float64(resolved)) + return true + case float64: + out.SetFloat(resolved) + return true + } + case reflect.Struct: + if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() { + out.Set(resolvedv) + return true + } + case reflect.Ptr: + panic("yaml internal error: please report the issue") + } + d.terror(n, tag, out) + return false +} + +func settableValueOf(i interface{}) reflect.Value { + v := reflect.ValueOf(i) + sv := reflect.New(v.Type()).Elem() + sv.Set(v) + return sv +} + +func (d *decoder) sequence(n *Node, out reflect.Value) (good bool) { + l := len(n.Content) + + var iface reflect.Value + switch out.Kind() { + case reflect.Slice: + out.Set(reflect.MakeSlice(out.Type(), l, l)) + case reflect.Array: + if l != out.Len() { + failf("invalid array: want %d elements but got %d", out.Len(), l) + } + case reflect.Interface: + // No type hints. Will have to use a generic sequence. + iface = out + out = settableValueOf(make([]interface{}, l)) + default: + d.terror(n, seqTag, out) + return false + } + et := out.Type().Elem() + + j := 0 + for i := 0; i < l; i++ { + e := reflect.New(et).Elem() + if ok := d.unmarshal(n.Content[i], e); ok { + out.Index(j).Set(e) + j++ + } + } + if out.Kind() != reflect.Array { + out.Set(out.Slice(0, j)) + } + if iface.IsValid() { + iface.Set(out) + } + return true +} + +func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) { + l := len(n.Content) + if d.uniqueKeys { + nerrs := len(d.terrors) + for i := 0; i < l; i += 2 { + ni := n.Content[i] + for j := i + 2; j < l; j += 2 { + nj := n.Content[j] + if ni.Kind == nj.Kind && ni.Value == nj.Value { + d.terrors = append(d.terrors, fmt.Sprintf("line %d: mapping key %#v already defined at line %d", nj.Line, nj.Value, ni.Line)) + } + } + } + if len(d.terrors) > nerrs { + return false + } + } + switch out.Kind() { + case reflect.Struct: + return d.mappingStruct(n, out) + case reflect.Map: + // okay + case reflect.Interface: + iface := out + if isStringMap(n) { + out = reflect.MakeMap(d.stringMapType) + } else { + out = reflect.MakeMap(d.generalMapType) + } + iface.Set(out) + default: + d.terror(n, mapTag, out) + return false + } + + outt := out.Type() + kt := outt.Key() + et := outt.Elem() + + stringMapType := d.stringMapType + generalMapType := d.generalMapType + if outt.Elem() == ifaceType { + if outt.Key().Kind() == reflect.String { + d.stringMapType = outt + } else if outt.Key() == ifaceType { + d.generalMapType = outt + } + } + + mergedFields := d.mergedFields + d.mergedFields = nil + + var mergeNode *Node + + mapIsNew := false + if out.IsNil() { + out.Set(reflect.MakeMap(outt)) + mapIsNew = true + } + for i := 0; i < l; i += 2 { + if isMerge(n.Content[i]) { + mergeNode = n.Content[i+1] + continue + } + k := reflect.New(kt).Elem() + if d.unmarshal(n.Content[i], k) { + if mergedFields != nil { + ki := k.Interface() + if mergedFields[ki] { + continue + } + mergedFields[ki] = true + } + kkind := k.Kind() + if kkind == reflect.Interface { + kkind = k.Elem().Kind() + } + if kkind == reflect.Map || kkind == reflect.Slice { + failf("invalid map key: %#v", k.Interface()) + } + e := reflect.New(et).Elem() + if d.unmarshal(n.Content[i+1], e) || n.Content[i+1].ShortTag() == nullTag && (mapIsNew || !out.MapIndex(k).IsValid()) { + out.SetMapIndex(k, e) + } + } + } + + d.mergedFields = mergedFields + if mergeNode != nil { + d.merge(n, mergeNode, out) + } + + d.stringMapType = stringMapType + d.generalMapType = generalMapType + return true +} + +func isStringMap(n *Node) bool { + if n.Kind != MappingNode { + return false + } + l := len(n.Content) + for i := 0; i < l; i += 2 { + shortTag := n.Content[i].ShortTag() + if shortTag != strTag && shortTag != mergeTag { + return false + } + } + return true +} + +func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) { + sinfo, err := getStructInfo(out.Type()) + if err != nil { + panic(err) + } + + var inlineMap reflect.Value + var elemType reflect.Type + if sinfo.InlineMap != -1 { + inlineMap = out.Field(sinfo.InlineMap) + elemType = inlineMap.Type().Elem() + } + + for _, index := range sinfo.InlineUnmarshalers { + field := d.fieldByIndex(n, out, index) + d.prepare(n, field) + } + + mergedFields := d.mergedFields + d.mergedFields = nil + var mergeNode *Node + var doneFields []bool + if d.uniqueKeys { + doneFields = make([]bool, len(sinfo.FieldsList)) + } + name := settableValueOf("") + l := len(n.Content) + for i := 0; i < l; i += 2 { + ni := n.Content[i] + if isMerge(ni) { + mergeNode = n.Content[i+1] + continue + } + if !d.unmarshal(ni, name) { + continue + } + sname := name.String() + if mergedFields != nil { + if mergedFields[sname] { + continue + } + mergedFields[sname] = true + } + if info, ok := sinfo.FieldsMap[sname]; ok { + if d.uniqueKeys { + if doneFields[info.Id] { + d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.Line, name.String(), out.Type())) + continue + } + doneFields[info.Id] = true + } + var field reflect.Value + if info.Inline == nil { + field = out.Field(info.Num) + } else { + field = d.fieldByIndex(n, out, info.Inline) + } + d.unmarshal(n.Content[i+1], field) + } else if sinfo.InlineMap != -1 { + if inlineMap.IsNil() { + inlineMap.Set(reflect.MakeMap(inlineMap.Type())) + } + value := reflect.New(elemType).Elem() + d.unmarshal(n.Content[i+1], value) + inlineMap.SetMapIndex(name, value) + } else if d.knownFields { + d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.Line, name.String(), out.Type())) + } + } + + d.mergedFields = mergedFields + if mergeNode != nil { + d.merge(n, mergeNode, out) + } + return true +} + +func failWantMap() { + failf("map merge requires map or sequence of maps as the value") +} + +func (d *decoder) merge(parent *Node, merge *Node, out reflect.Value) { + mergedFields := d.mergedFields + if mergedFields == nil { + d.mergedFields = make(map[interface{}]bool) + for i := 0; i < len(parent.Content); i += 2 { + k := reflect.New(ifaceType).Elem() + if d.unmarshal(parent.Content[i], k) { + d.mergedFields[k.Interface()] = true + } + } + } + + switch merge.Kind { + case MappingNode: + d.unmarshal(merge, out) + case AliasNode: + if merge.Alias != nil && merge.Alias.Kind != MappingNode { + failWantMap() + } + d.unmarshal(merge, out) + case SequenceNode: + for i := 0; i < len(merge.Content); i++ { + ni := merge.Content[i] + if ni.Kind == AliasNode { + if ni.Alias != nil && ni.Alias.Kind != MappingNode { + failWantMap() + } + } else if ni.Kind != MappingNode { + failWantMap() + } + d.unmarshal(ni, out) + } + default: + failWantMap() + } + + d.mergedFields = mergedFields +} + +func isMerge(n *Node) bool { + return n.Kind == ScalarNode && n.Value == "<<" && (n.Tag == "" || n.Tag == "!" || shortTag(n.Tag) == mergeTag) +} diff --git a/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/decode_test.go b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/decode_test.go new file mode 100644 index 0000000..0364b0b --- /dev/null +++ b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/decode_test.go @@ -0,0 +1,1771 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package yaml_test + +import ( + "bytes" + "errors" + "fmt" + "io" + "math" + "reflect" + "strings" + "time" + + . "gopkg.in/check.v1" + "gopkg.in/yaml.v3" +) + +var unmarshalIntTest = 123 + +var unmarshalTests = []struct { + data string + value interface{} +}{ + { + "", + (*struct{})(nil), + }, + { + "{}", &struct{}{}, + }, { + "v: hi", + map[string]string{"v": "hi"}, + }, { + "v: hi", map[string]interface{}{"v": "hi"}, + }, { + "v: true", + map[string]string{"v": "true"}, + }, { + "v: true", + map[string]interface{}{"v": true}, + }, { + "v: 10", + map[string]interface{}{"v": 10}, + }, { + "v: 0b10", + map[string]interface{}{"v": 2}, + }, { + "v: 0xA", + map[string]interface{}{"v": 10}, + }, { + "v: 4294967296", + map[string]int64{"v": 4294967296}, + }, { + "v: 0.1", + map[string]interface{}{"v": 0.1}, + }, { + "v: .1", + map[string]interface{}{"v": 0.1}, + }, { + "v: .Inf", + map[string]interface{}{"v": math.Inf(+1)}, + }, { + "v: -.Inf", + map[string]interface{}{"v": math.Inf(-1)}, + }, { + "v: -10", + map[string]interface{}{"v": -10}, + }, { + "v: -.1", + map[string]interface{}{"v": -0.1}, + }, + + // Simple values. + { + "123", + &unmarshalIntTest, + }, + + // Floats from spec + { + "canonical: 6.8523e+5", + map[string]interface{}{"canonical": 6.8523e+5}, + }, { + "expo: 685.230_15e+03", + map[string]interface{}{"expo": 685.23015e+03}, + }, { + "fixed: 685_230.15", + map[string]interface{}{"fixed": 685230.15}, + }, { + "neginf: -.inf", + map[string]interface{}{"neginf": math.Inf(-1)}, + }, { + "fixed: 685_230.15", + map[string]float64{"fixed": 685230.15}, + }, + //{"sexa: 190:20:30.15", map[string]interface{}{"sexa": 0}}, // Unsupported + //{"notanum: .NaN", map[string]interface{}{"notanum": math.NaN()}}, // Equality of NaN fails. + + // Bools are per 1.2 spec. + { + "canonical: true", + map[string]interface{}{"canonical": true}, + }, { + "canonical: false", + map[string]interface{}{"canonical": false}, + }, { + "bool: True", + map[string]interface{}{"bool": true}, + }, { + "bool: False", + map[string]interface{}{"bool": false}, + }, { + "bool: TRUE", + map[string]interface{}{"bool": true}, + }, { + "bool: FALSE", + map[string]interface{}{"bool": false}, + }, + // For backwards compatibility with 1.1, decoding old strings into typed values still works. + { + "option: on", + map[string]bool{"option": true}, + }, { + "option: y", + map[string]bool{"option": true}, + }, { + "option: Off", + map[string]bool{"option": false}, + }, { + "option: No", + map[string]bool{"option": false}, + }, { + "option: other", + map[string]bool{}, + }, + // Ints from spec + { + "canonical: 685230", + map[string]interface{}{"canonical": 685230}, + }, { + "decimal: +685_230", + map[string]interface{}{"decimal": 685230}, + }, { + "octal: 02472256", + map[string]interface{}{"octal": 685230}, + }, { + "octal: -02472256", + map[string]interface{}{"octal": -685230}, + }, { + "octal: 0o2472256", + map[string]interface{}{"octal": 685230}, + }, { + "octal: -0o2472256", + map[string]interface{}{"octal": -685230}, + }, { + "hexa: 0x_0A_74_AE", + map[string]interface{}{"hexa": 685230}, + }, { + "bin: 0b1010_0111_0100_1010_1110", + map[string]interface{}{"bin": 685230}, + }, { + "bin: -0b101010", + map[string]interface{}{"bin": -42}, + }, { + "bin: -0b1000000000000000000000000000000000000000000000000000000000000000", + map[string]interface{}{"bin": -9223372036854775808}, + }, { + "decimal: +685_230", + map[string]int{"decimal": 685230}, + }, + + //{"sexa: 190:20:30", map[string]interface{}{"sexa": 0}}, // Unsupported + + // Nulls from spec + { + "empty:", + map[string]interface{}{"empty": nil}, + }, { + "canonical: ~", + map[string]interface{}{"canonical": nil}, + }, { + "english: null", + map[string]interface{}{"english": nil}, + }, { + "~: null key", + map[interface{}]string{nil: "null key"}, + }, { + "empty:", + map[string]*bool{"empty": nil}, + }, + + // Flow sequence + { + "seq: [A,B]", + map[string]interface{}{"seq": []interface{}{"A", "B"}}, + }, { + "seq: [A,B,C,]", + map[string][]string{"seq": []string{"A", "B", "C"}}, + }, { + "seq: [A,1,C]", + map[string][]string{"seq": []string{"A", "1", "C"}}, + }, { + "seq: [A,1,C]", + map[string][]int{"seq": []int{1}}, + }, { + "seq: [A,1,C]", + map[string]interface{}{"seq": []interface{}{"A", 1, "C"}}, + }, + // Block sequence + { + "seq:\n - A\n - B", + map[string]interface{}{"seq": []interface{}{"A", "B"}}, + }, { + "seq:\n - A\n - B\n - C", + map[string][]string{"seq": []string{"A", "B", "C"}}, + }, { + "seq:\n - A\n - 1\n - C", + map[string][]string{"seq": []string{"A", "1", "C"}}, + }, { + "seq:\n - A\n - 1\n - C", + map[string][]int{"seq": []int{1}}, + }, { + "seq:\n - A\n - 1\n - C", + map[string]interface{}{"seq": []interface{}{"A", 1, "C"}}, + }, + + // Literal block scalar + { + "scalar: | # Comment\n\n literal\n\n \ttext\n\n", + map[string]string{"scalar": "\nliteral\n\n\ttext\n"}, + }, + + // Folded block scalar + { + "scalar: > # Comment\n\n folded\n line\n \n next\n line\n * one\n * two\n\n last\n line\n\n", + map[string]string{"scalar": "\nfolded line\nnext line\n * one\n * two\n\nlast line\n"}, + }, + + // Map inside interface with no type hints. + { + "a: {b: c}", + map[interface{}]interface{}{"a": map[string]interface{}{"b": "c"}}, + }, + // Non-string map inside interface with no type hints. + { + "a: {b: c, 1: d}", + map[interface{}]interface{}{"a": map[interface{}]interface{}{"b": "c", 1: "d"}}, + }, + + // Structs and type conversions. + { + "hello: world", + &struct{ Hello string }{"world"}, + }, { + "a: {b: c}", + &struct{ A struct{ B string } }{struct{ B string }{"c"}}, + }, { + "a: {b: c}", + &struct{ A *struct{ B string } }{&struct{ B string }{"c"}}, + }, { + "a: 'null'", + &struct{ A *unmarshalerType }{&unmarshalerType{"null"}}, + }, { + "a: {b: c}", + &struct{ A map[string]string }{map[string]string{"b": "c"}}, + }, { + "a: {b: c}", + &struct{ A *map[string]string }{&map[string]string{"b": "c"}}, + }, { + "a:", + &struct{ A map[string]string }{}, + }, { + "a: 1", + &struct{ A int }{1}, + }, { + "a: 1", + &struct{ A float64 }{1}, + }, { + "a: 1.0", + &struct{ A int }{1}, + }, { + "a: 1.0", + &struct{ A uint }{1}, + }, { + "a: [1, 2]", + &struct{ A []int }{[]int{1, 2}}, + }, { + "a: [1, 2]", + &struct{ A [2]int }{[2]int{1, 2}}, + }, { + "a: 1", + &struct{ B int }{0}, + }, { + "a: 1", + &struct { + B int "a" + }{1}, + }, { + // Some limited backwards compatibility with the 1.1 spec. + "a: YES", + &struct{ A bool }{true}, + }, + + // Some cross type conversions + { + "v: 42", + map[string]uint{"v": 42}, + }, { + "v: -42", + map[string]uint{}, + }, { + "v: 4294967296", + map[string]uint64{"v": 4294967296}, + }, { + "v: -4294967296", + map[string]uint64{}, + }, + + // int + { + "int_max: 2147483647", + map[string]int{"int_max": math.MaxInt32}, + }, + { + "int_min: -2147483648", + map[string]int{"int_min": math.MinInt32}, + }, + { + "int_overflow: 9223372036854775808", // math.MaxInt64 + 1 + map[string]int{}, + }, + + // int64 + { + "int64_max: 9223372036854775807", + map[string]int64{"int64_max": math.MaxInt64}, + }, + { + "int64_max_base2: 0b111111111111111111111111111111111111111111111111111111111111111", + map[string]int64{"int64_max_base2": math.MaxInt64}, + }, + { + "int64_min: -9223372036854775808", + map[string]int64{"int64_min": math.MinInt64}, + }, + { + "int64_neg_base2: -0b111111111111111111111111111111111111111111111111111111111111111", + map[string]int64{"int64_neg_base2": -math.MaxInt64}, + }, + { + "int64_overflow: 9223372036854775808", // math.MaxInt64 + 1 + map[string]int64{}, + }, + + // uint + { + "uint_min: 0", + map[string]uint{"uint_min": 0}, + }, + { + "uint_max: 4294967295", + map[string]uint{"uint_max": math.MaxUint32}, + }, + { + "uint_underflow: -1", + map[string]uint{}, + }, + + // uint64 + { + "uint64_min: 0", + map[string]uint{"uint64_min": 0}, + }, + { + "uint64_max: 18446744073709551615", + map[string]uint64{"uint64_max": math.MaxUint64}, + }, + { + "uint64_max_base2: 0b1111111111111111111111111111111111111111111111111111111111111111", + map[string]uint64{"uint64_max_base2": math.MaxUint64}, + }, + { + "uint64_maxint64: 9223372036854775807", + map[string]uint64{"uint64_maxint64": math.MaxInt64}, + }, + { + "uint64_underflow: -1", + map[string]uint64{}, + }, + + // float32 + { + "float32_max: 3.40282346638528859811704183484516925440e+38", + map[string]float32{"float32_max": math.MaxFloat32}, + }, + { + "float32_nonzero: 1.401298464324817070923729583289916131280e-45", + map[string]float32{"float32_nonzero": math.SmallestNonzeroFloat32}, + }, + { + "float32_maxuint64: 18446744073709551615", + map[string]float32{"float32_maxuint64": float32(math.MaxUint64)}, + }, + { + "float32_maxuint64+1: 18446744073709551616", + map[string]float32{"float32_maxuint64+1": float32(math.MaxUint64 + 1)}, + }, + + // float64 + { + "float64_max: 1.797693134862315708145274237317043567981e+308", + map[string]float64{"float64_max": math.MaxFloat64}, + }, + { + "float64_nonzero: 4.940656458412465441765687928682213723651e-324", + map[string]float64{"float64_nonzero": math.SmallestNonzeroFloat64}, + }, + { + "float64_maxuint64: 18446744073709551615", + map[string]float64{"float64_maxuint64": float64(math.MaxUint64)}, + }, + { + "float64_maxuint64+1: 18446744073709551616", + map[string]float64{"float64_maxuint64+1": float64(math.MaxUint64 + 1)}, + }, + + // Overflow cases. + { + "v: 4294967297", + map[string]int32{}, + }, { + "v: 128", + map[string]int8{}, + }, + + // Quoted values. + { + "'1': '\"2\"'", + map[interface{}]interface{}{"1": "\"2\""}, + }, { + "v:\n- A\n- 'B\n\n C'\n", + map[string][]string{"v": []string{"A", "B\nC"}}, + }, + + // Explicit tags. + { + "v: !!float '1.1'", + map[string]interface{}{"v": 1.1}, + }, { + "v: !!float 0", + map[string]interface{}{"v": float64(0)}, + }, { + "v: !!float -1", + map[string]interface{}{"v": float64(-1)}, + }, { + "v: !!null ''", + map[string]interface{}{"v": nil}, + }, { + "%TAG !y! tag:yaml.org,2002:\n---\nv: !y!int '1'", + map[string]interface{}{"v": 1}, + }, + + // Non-specific tag (Issue #75) + { + "v: ! test", + map[string]interface{}{"v": "test"}, + }, + + // Anchors and aliases. + { + "a: &x 1\nb: &y 2\nc: *x\nd: *y\n", + &struct{ A, B, C, D int }{1, 2, 1, 2}, + }, { + "a: &a {c: 1}\nb: *a", + &struct { + A, B struct { + C int + } + }{struct{ C int }{1}, struct{ C int }{1}}, + }, { + "a: &a [1, 2]\nb: *a", + &struct{ B []int }{[]int{1, 2}}, + }, + + // Bug #1133337 + { + "foo: ''", + map[string]*string{"foo": new(string)}, + }, { + "foo: null", + map[string]*string{"foo": nil}, + }, { + "foo: null", + map[string]string{"foo": ""}, + }, { + "foo: null", + map[string]interface{}{"foo": nil}, + }, + + // Support for ~ + { + "foo: ~", + map[string]*string{"foo": nil}, + }, { + "foo: ~", + map[string]string{"foo": ""}, + }, { + "foo: ~", + map[string]interface{}{"foo": nil}, + }, + + // Ignored field + { + "a: 1\nb: 2\n", + &struct { + A int + B int "-" + }{1, 0}, + }, + + // Bug #1191981 + { + "" + + "%YAML 1.1\n" + + "--- !!str\n" + + `"Generic line break (no glyph)\n\` + "\n" + + ` Generic line break (glyphed)\n\` + "\n" + + ` Line separator\u2028\` + "\n" + + ` Paragraph separator\u2029"` + "\n", + "" + + "Generic line break (no glyph)\n" + + "Generic line break (glyphed)\n" + + "Line separator\u2028Paragraph separator\u2029", + }, + + // Struct inlining + { + "a: 1\nb: 2\nc: 3\n", + &struct { + A int + C inlineB `yaml:",inline"` + }{1, inlineB{2, inlineC{3}}}, + }, + + // Struct inlining as a pointer. + { + "a: 1\nb: 2\nc: 3\n", + &struct { + A int + C *inlineB `yaml:",inline"` + }{1, &inlineB{2, inlineC{3}}}, + }, { + "a: 1\n", + &struct { + A int + C *inlineB `yaml:",inline"` + }{1, nil}, + }, { + "a: 1\nc: 3\nd: 4\n", + &struct { + A int + C *inlineD `yaml:",inline"` + }{1, &inlineD{&inlineC{3}, 4}}, + }, + + // Map inlining + { + "a: 1\nb: 2\nc: 3\n", + &struct { + A int + C map[string]int `yaml:",inline"` + }{1, map[string]int{"b": 2, "c": 3}}, + }, + + // bug 1243827 + { + "a: -b_c", + map[string]interface{}{"a": "-b_c"}, + }, + { + "a: +b_c", + map[string]interface{}{"a": "+b_c"}, + }, + { + "a: 50cent_of_dollar", + map[string]interface{}{"a": "50cent_of_dollar"}, + }, + + // issue #295 (allow scalars with colons in flow mappings and sequences) + { + "a: {b: https://github.com/go-yaml/yaml}", + map[string]interface{}{"a": map[string]interface{}{ + "b": "https://github.com/go-yaml/yaml", + }}, + }, + { + "a: [https://github.com/go-yaml/yaml]", + map[string]interface{}{"a": []interface{}{"https://github.com/go-yaml/yaml"}}, + }, + + // Duration + { + "a: 3s", + map[string]time.Duration{"a": 3 * time.Second}, + }, + + // Issue #24. + { + "a: <foo>", + map[string]string{"a": "<foo>"}, + }, + + // Base 60 floats are obsolete and unsupported. + { + "a: 1:1\n", + map[string]string{"a": "1:1"}, + }, + + // Binary data. + { + "a: !!binary gIGC\n", + map[string]string{"a": "\x80\x81\x82"}, + }, { + "a: !!binary |\n " + strings.Repeat("kJCQ", 17) + "kJ\n CQ\n", + map[string]string{"a": strings.Repeat("\x90", 54)}, + }, { + "a: !!binary |\n " + strings.Repeat("A", 70) + "\n ==\n", + map[string]string{"a": strings.Repeat("\x00", 52)}, + }, + + // Issue #39. + { + "a:\n b:\n c: d\n", + map[string]struct{ B interface{} }{"a": {map[string]interface{}{"c": "d"}}}, + }, + + // Custom map type. + { + "a: {b: c}", + M{"a": M{"b": "c"}}, + }, + + // Support encoding.TextUnmarshaler. + { + "a: 1.2.3.4\n", + map[string]textUnmarshaler{"a": textUnmarshaler{S: "1.2.3.4"}}, + }, + { + "a: 2015-02-24T18:19:39Z\n", + map[string]textUnmarshaler{"a": textUnmarshaler{"2015-02-24T18:19:39Z"}}, + }, + + // Timestamps + { + // Date only. + "a: 2015-01-01\n", + map[string]time.Time{"a": time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)}, + }, + { + // RFC3339 + "a: 2015-02-24T18:19:39.12Z\n", + map[string]time.Time{"a": time.Date(2015, 2, 24, 18, 19, 39, .12e9, time.UTC)}, + }, + { + // RFC3339 with short dates. + "a: 2015-2-3T3:4:5Z", + map[string]time.Time{"a": time.Date(2015, 2, 3, 3, 4, 5, 0, time.UTC)}, + }, + { + // ISO8601 lower case t + "a: 2015-02-24t18:19:39Z\n", + map[string]time.Time{"a": time.Date(2015, 2, 24, 18, 19, 39, 0, time.UTC)}, + }, + { + // space separate, no time zone + "a: 2015-02-24 18:19:39\n", + map[string]time.Time{"a": time.Date(2015, 2, 24, 18, 19, 39, 0, time.UTC)}, + }, + // Some cases not currently handled. Uncomment these when + // the code is fixed. + // { + // // space separated with time zone + // "a: 2001-12-14 21:59:43.10 -5", + // map[string]interface{}{"a": time.Date(2001, 12, 14, 21, 59, 43, .1e9, time.UTC)}, + // }, + // { + // // arbitrary whitespace between fields + // "a: 2001-12-14 \t\t \t21:59:43.10 \t Z", + // map[string]interface{}{"a": time.Date(2001, 12, 14, 21, 59, 43, .1e9, time.UTC)}, + // }, + { + // explicit string tag + "a: !!str 2015-01-01", + map[string]interface{}{"a": "2015-01-01"}, + }, + { + // explicit timestamp tag on quoted string + "a: !!timestamp \"2015-01-01\"", + map[string]time.Time{"a": time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)}, + }, + { + // explicit timestamp tag on unquoted string + "a: !!timestamp 2015-01-01", + map[string]time.Time{"a": time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)}, + }, + { + // quoted string that's a valid timestamp + "a: \"2015-01-01\"", + map[string]interface{}{"a": "2015-01-01"}, + }, + { + // explicit timestamp tag into interface. + "a: !!timestamp \"2015-01-01\"", + map[string]interface{}{"a": time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)}, + }, + { + // implicit timestamp tag into interface. + "a: 2015-01-01", + map[string]interface{}{"a": time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)}, + }, + + // Encode empty lists as zero-length slices. + { + "a: []", + &struct{ A []int }{[]int{}}, + }, + + // UTF-16-LE + { + "\xff\xfe\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00\n\x00", + M{"ñoño": "very yes"}, + }, + // UTF-16-LE with surrogate. + { + "\xff\xfe\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00 \x00=\xd8\xd4\xdf\n\x00", + M{"ñoño": "very yes 🟔"}, + }, + + // UTF-16-BE + { + "\xfe\xff\x00\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00\n", + M{"ñoño": "very yes"}, + }, + // UTF-16-BE with surrogate. + { + "\xfe\xff\x00\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00 \xd8=\xdf\xd4\x00\n", + M{"ñoño": "very yes 🟔"}, + }, + + // This *is* in fact a float number, per the spec. #171 was a mistake. + { + "a: 123456e1\n", + M{"a": 123456e1}, + }, { + "a: 123456E1\n", + M{"a": 123456e1}, + }, + // yaml-test-suite 3GZX: Spec Example 7.1. Alias Nodes + { + "First occurrence: &anchor Foo\nSecond occurrence: *anchor\nOverride anchor: &anchor Bar\nReuse anchor: *anchor\n", + map[string]interface{}{ + "First occurrence": "Foo", + "Second occurrence": "Foo", + "Override anchor": "Bar", + "Reuse anchor": "Bar", + }, + }, + // Single document with garbage following it. + { + "---\nhello\n...\n}not yaml", + "hello", + }, + + // Comment scan exhausting the input buffer (issue #469). + { + "true\n#" + strings.Repeat(" ", 512*3), + "true", + }, { + "true #" + strings.Repeat(" ", 512*3), + "true", + }, + + // CRLF + { + "a: b\r\nc:\r\n- d\r\n- e\r\n", + map[string]interface{}{ + "a": "b", + "c": []interface{}{"d", "e"}, + }, + }, +} + +type M map[string]interface{} + +type inlineB struct { + B int + inlineC `yaml:",inline"` +} + +type inlineC struct { + C int +} + +type inlineD struct { + C *inlineC `yaml:",inline"` + D int +} + +func (s *S) TestUnmarshal(c *C) { + for i, item := range unmarshalTests { + c.Logf("test %d: %q", i, item.data) + t := reflect.ValueOf(item.value).Type() + value := reflect.New(t) + err := yaml.Unmarshal([]byte(item.data), value.Interface()) + if _, ok := err.(*yaml.TypeError); !ok { + c.Assert(err, IsNil) + } + c.Assert(value.Elem().Interface(), DeepEquals, item.value, Commentf("error: %v", err)) + } +} + +func (s *S) TestUnmarshalFullTimestamp(c *C) { + // Full timestamp in same format as encoded. This is confirmed to be + // properly decoded by Python as a timestamp as well. + var str = "2015-02-24T18:19:39.123456789-03:00" + var t interface{} + err := yaml.Unmarshal([]byte(str), &t) + c.Assert(err, IsNil) + c.Assert(t, Equals, time.Date(2015, 2, 24, 18, 19, 39, 123456789, t.(time.Time).Location())) + c.Assert(t.(time.Time).In(time.UTC), Equals, time.Date(2015, 2, 24, 21, 19, 39, 123456789, time.UTC)) +} + +func (s *S) TestDecoderSingleDocument(c *C) { + // Test that Decoder.Decode works as expected on + // all the unmarshal tests. + for i, item := range unmarshalTests { + c.Logf("test %d: %q", i, item.data) + if item.data == "" { + // Behaviour differs when there's no YAML. + continue + } + t := reflect.ValueOf(item.value).Type() + value := reflect.New(t) + err := yaml.NewDecoder(strings.NewReader(item.data)).Decode(value.Interface()) + if _, ok := err.(*yaml.TypeError); !ok { + c.Assert(err, IsNil) + } + c.Assert(value.Elem().Interface(), DeepEquals, item.value) + } +} + +var decoderTests = []struct { + data string + values []interface{} +}{{ + "", + nil, +}, { + "a: b", + []interface{}{ + map[string]interface{}{"a": "b"}, + }, +}, { + "---\na: b\n...\n", + []interface{}{ + map[string]interface{}{"a": "b"}, + }, +}, { + "---\n'hello'\n...\n---\ngoodbye\n...\n", + []interface{}{ + "hello", + "goodbye", + }, +}} + +func (s *S) TestDecoder(c *C) { + for i, item := range decoderTests { + c.Logf("test %d: %q", i, item.data) + var values []interface{} + dec := yaml.NewDecoder(strings.NewReader(item.data)) + for { + var value interface{} + err := dec.Decode(&value) + if err == io.EOF { + break + } + c.Assert(err, IsNil) + values = append(values, value) + } + c.Assert(values, DeepEquals, item.values) + } +} + +type errReader struct{} + +func (errReader) Read([]byte) (int, error) { + return 0, errors.New("some read error") +} + +func (s *S) TestDecoderReadError(c *C) { + err := yaml.NewDecoder(errReader{}).Decode(&struct{}{}) + c.Assert(err, ErrorMatches, `yaml: input error: some read error`) +} + +func (s *S) TestUnmarshalNaN(c *C) { + value := map[string]interface{}{} + err := yaml.Unmarshal([]byte("notanum: .NaN"), &value) + c.Assert(err, IsNil) + c.Assert(math.IsNaN(value["notanum"].(float64)), Equals, true) +} + +func (s *S) TestUnmarshalDurationInt(c *C) { + // Don't accept plain ints as durations as it's unclear (issue #200). + var d time.Duration + err := yaml.Unmarshal([]byte("123"), &d) + c.Assert(err, ErrorMatches, "(?s).* line 1: cannot unmarshal !!int `123` into time.Duration") +} + +var unmarshalErrorTests = []struct { + data, error string +}{ + {"v: !!float 'error'", "yaml: cannot decode !!str `error` as a !!float"}, + {"v: [A,", "yaml: line 1: did not find expected node content"}, + {"v:\n- [A,", "yaml: line 2: did not find expected node content"}, + {"a:\n- b: *,", "yaml: line 2: did not find expected alphabetic or numeric character"}, + {"a: *b\n", "yaml: unknown anchor 'b' referenced"}, + {"a: &a\n b: *a\n", "yaml: anchor 'a' value contains itself"}, + {"value: -", "yaml: block sequence entries are not allowed in this context"}, + {"a: !!binary ==", "yaml: !!binary value contains invalid base64 data"}, + {"{[.]}", `yaml: invalid map key: \[\]interface \{\}\{"\."\}`}, + {"{{.}}", `yaml: invalid map key: map\[string]interface \{\}\{".":interface \{\}\(nil\)\}`}, + {"b: *a\na: &a {c: 1}", `yaml: unknown anchor 'a' referenced`}, + {"%TAG !%79! tag:yaml.org,2002:\n---\nv: !%79!int '1'", "yaml: did not find expected whitespace"}, + {"a:\n 1:\nb\n 2:", ".*could not find expected ':'"}, + {"a: 1\nb: 2\nc 2\nd: 3\n", "^yaml: line 3: could not find expected ':'$"}, + {"#\n-\n{", "yaml: line 3: could not find expected ':'"}, // Issue #665 + {"0: [:!00 \xef", "yaml: incomplete UTF-8 octet sequence"}, // Issue #666 + { + "a: &a [00,00,00,00,00,00,00,00,00]\n" + + "b: &b [*a,*a,*a,*a,*a,*a,*a,*a,*a]\n" + + "c: &c [*b,*b,*b,*b,*b,*b,*b,*b,*b]\n" + + "d: &d [*c,*c,*c,*c,*c,*c,*c,*c,*c]\n" + + "e: &e [*d,*d,*d,*d,*d,*d,*d,*d,*d]\n" + + "f: &f [*e,*e,*e,*e,*e,*e,*e,*e,*e]\n" + + "g: &g [*f,*f,*f,*f,*f,*f,*f,*f,*f]\n" + + "h: &h [*g,*g,*g,*g,*g,*g,*g,*g,*g]\n" + + "i: &i [*h,*h,*h,*h,*h,*h,*h,*h,*h]\n", + "yaml: document contains excessive aliasing", + }, +} + +func (s *S) TestUnmarshalErrors(c *C) { + for i, item := range unmarshalErrorTests { + c.Logf("test %d: %q", i, item.data) + var value interface{} + err := yaml.Unmarshal([]byte(item.data), &value) + c.Assert(err, ErrorMatches, item.error, Commentf("Partial unmarshal: %#v", value)) + } +} + +func (s *S) TestDecoderErrors(c *C) { + for _, item := range unmarshalErrorTests { + var value interface{} + err := yaml.NewDecoder(strings.NewReader(item.data)).Decode(&value) + c.Assert(err, ErrorMatches, item.error, Commentf("Partial unmarshal: %#v", value)) + } +} + +var unmarshalerTests = []struct { + data, tag string + value interface{} +}{ + {"_: {hi: there}", "!!map", map[string]interface{}{"hi": "there"}}, + {"_: [1,A]", "!!seq", []interface{}{1, "A"}}, + {"_: 10", "!!int", 10}, + {"_: null", "!!null", nil}, + {`_: BAR!`, "!!str", "BAR!"}, + {`_: "BAR!"`, "!!str", "BAR!"}, + {"_: !!foo 'BAR!'", "!!foo", "BAR!"}, + {`_: ""`, "!!str", ""}, +} + +var unmarshalerResult = map[int]error{} + +type unmarshalerType struct { + value interface{} +} + +func (o *unmarshalerType) UnmarshalYAML(value *yaml.Node) error { + if err := value.Decode(&o.value); err != nil { + return err + } + if i, ok := o.value.(int); ok { + if result, ok := unmarshalerResult[i]; ok { + return result + } + } + return nil +} + +type unmarshalerPointer struct { + Field *unmarshalerType "_" +} + +type unmarshalerValue struct { + Field unmarshalerType "_" +} + +type unmarshalerInlined struct { + Field *unmarshalerType "_" + Inlined unmarshalerType `yaml:",inline"` +} + +type unmarshalerInlinedTwice struct { + InlinedTwice unmarshalerInlined `yaml:",inline"` +} + +type obsoleteUnmarshalerType struct { + value interface{} +} + +func (o *obsoleteUnmarshalerType) UnmarshalYAML(unmarshal func(v interface{}) error) error { + if err := unmarshal(&o.value); err != nil { + return err + } + if i, ok := o.value.(int); ok { + if result, ok := unmarshalerResult[i]; ok { + return result + } + } + return nil +} + +type obsoleteUnmarshalerPointer struct { + Field *obsoleteUnmarshalerType "_" +} + +type obsoleteUnmarshalerValue struct { + Field obsoleteUnmarshalerType "_" +} + +func (s *S) TestUnmarshalerPointerField(c *C) { + for _, item := range unmarshalerTests { + obj := &unmarshalerPointer{} + err := yaml.Unmarshal([]byte(item.data), obj) + c.Assert(err, IsNil) + if item.value == nil { + c.Assert(obj.Field, IsNil) + } else { + c.Assert(obj.Field, NotNil, Commentf("Pointer not initialized (%#v)", item.value)) + c.Assert(obj.Field.value, DeepEquals, item.value) + } + } + for _, item := range unmarshalerTests { + obj := &obsoleteUnmarshalerPointer{} + err := yaml.Unmarshal([]byte(item.data), obj) + c.Assert(err, IsNil) + if item.value == nil { + c.Assert(obj.Field, IsNil) + } else { + c.Assert(obj.Field, NotNil, Commentf("Pointer not initialized (%#v)", item.value)) + c.Assert(obj.Field.value, DeepEquals, item.value) + } + } +} + +func (s *S) TestUnmarshalerValueField(c *C) { + for _, item := range unmarshalerTests { + obj := &obsoleteUnmarshalerValue{} + err := yaml.Unmarshal([]byte(item.data), obj) + c.Assert(err, IsNil) + c.Assert(obj.Field, NotNil, Commentf("Pointer not initialized (%#v)", item.value)) + c.Assert(obj.Field.value, DeepEquals, item.value) + } +} + +func (s *S) TestUnmarshalerInlinedField(c *C) { + obj := &unmarshalerInlined{} + err := yaml.Unmarshal([]byte("_: a\ninlined: b\n"), obj) + c.Assert(err, IsNil) + c.Assert(obj.Field, DeepEquals, &unmarshalerType{"a"}) + c.Assert(obj.Inlined, DeepEquals, unmarshalerType{map[string]interface{}{"_": "a", "inlined": "b"}}) + + twc := &unmarshalerInlinedTwice{} + err = yaml.Unmarshal([]byte("_: a\ninlined: b\n"), twc) + c.Assert(err, IsNil) + c.Assert(twc.InlinedTwice.Field, DeepEquals, &unmarshalerType{"a"}) + c.Assert(twc.InlinedTwice.Inlined, DeepEquals, unmarshalerType{map[string]interface{}{"_": "a", "inlined": "b"}}) +} + +func (s *S) TestUnmarshalerWholeDocument(c *C) { + obj := &obsoleteUnmarshalerType{} + err := yaml.Unmarshal([]byte(unmarshalerTests[0].data), obj) + c.Assert(err, IsNil) + value, ok := obj.value.(map[string]interface{}) + c.Assert(ok, Equals, true, Commentf("value: %#v", obj.value)) + c.Assert(value["_"], DeepEquals, unmarshalerTests[0].value) +} + +func (s *S) TestUnmarshalerTypeError(c *C) { + unmarshalerResult[2] = &yaml.TypeError{[]string{"foo"}} + unmarshalerResult[4] = &yaml.TypeError{[]string{"bar"}} + defer func() { + delete(unmarshalerResult, 2) + delete(unmarshalerResult, 4) + }() + + type T struct { + Before int + After int + M map[string]*unmarshalerType + } + var v T + data := `{before: A, m: {abc: 1, def: 2, ghi: 3, jkl: 4}, after: B}` + err := yaml.Unmarshal([]byte(data), &v) + c.Assert(err, ErrorMatches, ""+ + "yaml: unmarshal errors:\n"+ + " line 1: cannot unmarshal !!str `A` into int\n"+ + " foo\n"+ + " bar\n"+ + " line 1: cannot unmarshal !!str `B` into int") + c.Assert(v.M["abc"], NotNil) + c.Assert(v.M["def"], IsNil) + c.Assert(v.M["ghi"], NotNil) + c.Assert(v.M["jkl"], IsNil) + + c.Assert(v.M["abc"].value, Equals, 1) + c.Assert(v.M["ghi"].value, Equals, 3) +} + +func (s *S) TestObsoleteUnmarshalerTypeError(c *C) { + unmarshalerResult[2] = &yaml.TypeError{[]string{"foo"}} + unmarshalerResult[4] = &yaml.TypeError{[]string{"bar"}} + defer func() { + delete(unmarshalerResult, 2) + delete(unmarshalerResult, 4) + }() + + type T struct { + Before int + After int + M map[string]*obsoleteUnmarshalerType + } + var v T + data := `{before: A, m: {abc: 1, def: 2, ghi: 3, jkl: 4}, after: B}` + err := yaml.Unmarshal([]byte(data), &v) + c.Assert(err, ErrorMatches, ""+ + "yaml: unmarshal errors:\n"+ + " line 1: cannot unmarshal !!str `A` into int\n"+ + " foo\n"+ + " bar\n"+ + " line 1: cannot unmarshal !!str `B` into int") + c.Assert(v.M["abc"], NotNil) + c.Assert(v.M["def"], IsNil) + c.Assert(v.M["ghi"], NotNil) + c.Assert(v.M["jkl"], IsNil) + + c.Assert(v.M["abc"].value, Equals, 1) + c.Assert(v.M["ghi"].value, Equals, 3) +} + +type proxyTypeError struct{} + +func (v *proxyTypeError) UnmarshalYAML(node *yaml.Node) error { + var s string + var a int32 + var b int64 + if err := node.Decode(&s); err != nil { + panic(err) + } + if s == "a" { + if err := node.Decode(&b); err == nil { + panic("should have failed") + } + return node.Decode(&a) + } + if err := node.Decode(&a); err == nil { + panic("should have failed") + } + return node.Decode(&b) +} + +func (s *S) TestUnmarshalerTypeErrorProxying(c *C) { + type T struct { + Before int + After int + M map[string]*proxyTypeError + } + var v T + data := `{before: A, m: {abc: a, def: b}, after: B}` + err := yaml.Unmarshal([]byte(data), &v) + c.Assert(err, ErrorMatches, ""+ + "yaml: unmarshal errors:\n"+ + " line 1: cannot unmarshal !!str `A` into int\n"+ + " line 1: cannot unmarshal !!str `a` into int32\n"+ + " line 1: cannot unmarshal !!str `b` into int64\n"+ + " line 1: cannot unmarshal !!str `B` into int") +} + +type obsoleteProxyTypeError struct{} + +func (v *obsoleteProxyTypeError) UnmarshalYAML(unmarshal func(interface{}) error) error { + var s string + var a int32 + var b int64 + if err := unmarshal(&s); err != nil { + panic(err) + } + if s == "a" { + if err := unmarshal(&b); err == nil { + panic("should have failed") + } + return unmarshal(&a) + } + if err := unmarshal(&a); err == nil { + panic("should have failed") + } + return unmarshal(&b) +} + +func (s *S) TestObsoleteUnmarshalerTypeErrorProxying(c *C) { + type T struct { + Before int + After int + M map[string]*obsoleteProxyTypeError + } + var v T + data := `{before: A, m: {abc: a, def: b}, after: B}` + err := yaml.Unmarshal([]byte(data), &v) + c.Assert(err, ErrorMatches, ""+ + "yaml: unmarshal errors:\n"+ + " line 1: cannot unmarshal !!str `A` into int\n"+ + " line 1: cannot unmarshal !!str `a` into int32\n"+ + " line 1: cannot unmarshal !!str `b` into int64\n"+ + " line 1: cannot unmarshal !!str `B` into int") +} + +var failingErr = errors.New("failingErr") + +type failingUnmarshaler struct{} + +func (ft *failingUnmarshaler) UnmarshalYAML(node *yaml.Node) error { + return failingErr +} + +func (s *S) TestUnmarshalerError(c *C) { + err := yaml.Unmarshal([]byte("a: b"), &failingUnmarshaler{}) + c.Assert(err, Equals, failingErr) +} + +type obsoleteFailingUnmarshaler struct{} + +func (ft *obsoleteFailingUnmarshaler) UnmarshalYAML(unmarshal func(interface{}) error) error { + return failingErr +} + +func (s *S) TestObsoleteUnmarshalerError(c *C) { + err := yaml.Unmarshal([]byte("a: b"), &obsoleteFailingUnmarshaler{}) + c.Assert(err, Equals, failingErr) +} + +type sliceUnmarshaler []int + +func (su *sliceUnmarshaler) UnmarshalYAML(node *yaml.Node) error { + var slice []int + err := node.Decode(&slice) + if err == nil { + *su = slice + return nil + } + + var intVal int + err = node.Decode(&intVal) + if err == nil { + *su = []int{intVal} + return nil + } + + return err +} + +func (s *S) TestUnmarshalerRetry(c *C) { + var su sliceUnmarshaler + err := yaml.Unmarshal([]byte("[1, 2, 3]"), &su) + c.Assert(err, IsNil) + c.Assert(su, DeepEquals, sliceUnmarshaler([]int{1, 2, 3})) + + err = yaml.Unmarshal([]byte("1"), &su) + c.Assert(err, IsNil) + c.Assert(su, DeepEquals, sliceUnmarshaler([]int{1})) +} + +type obsoleteSliceUnmarshaler []int + +func (su *obsoleteSliceUnmarshaler) UnmarshalYAML(unmarshal func(interface{}) error) error { + var slice []int + err := unmarshal(&slice) + if err == nil { + *su = slice + return nil + } + + var intVal int + err = unmarshal(&intVal) + if err == nil { + *su = []int{intVal} + return nil + } + + return err +} + +func (s *S) TestObsoleteUnmarshalerRetry(c *C) { + var su obsoleteSliceUnmarshaler + err := yaml.Unmarshal([]byte("[1, 2, 3]"), &su) + c.Assert(err, IsNil) + c.Assert(su, DeepEquals, obsoleteSliceUnmarshaler([]int{1, 2, 3})) + + err = yaml.Unmarshal([]byte("1"), &su) + c.Assert(err, IsNil) + c.Assert(su, DeepEquals, obsoleteSliceUnmarshaler([]int{1})) +} + +// From http://yaml.org/type/merge.html +var mergeTests = ` +anchors: + list: + - &CENTER { "x": 1, "y": 2 } + - &LEFT { "x": 0, "y": 2 } + - &BIG { "r": 10 } + - &SMALL { "r": 1 } + +# All the following maps are equal: + +plain: + # Explicit keys + "x": 1 + "y": 2 + "r": 10 + label: center/big + +mergeOne: + # Merge one map + << : *CENTER + "r": 10 + label: center/big + +mergeMultiple: + # Merge multiple maps + << : [ *CENTER, *BIG ] + label: center/big + +override: + # Override + << : [ *BIG, *LEFT, *SMALL ] + "x": 1 + label: center/big + +shortTag: + # Explicit short merge tag + !!merge "<<" : [ *CENTER, *BIG ] + label: center/big + +longTag: + # Explicit merge long tag + !<tag:yaml.org,2002:merge> "<<" : [ *CENTER, *BIG ] + label: center/big + +inlineMap: + # Inlined map + << : {"x": 1, "y": 2, "r": 10} + label: center/big + +inlineSequenceMap: + # Inlined map in sequence + << : [ *CENTER, {"r": 10} ] + label: center/big +` + +func (s *S) TestMerge(c *C) { + var want = map[string]interface{}{ + "x": 1, + "y": 2, + "r": 10, + "label": "center/big", + } + + wantStringMap := make(map[string]interface{}) + for k, v := range want { + wantStringMap[fmt.Sprintf("%v", k)] = v + } + + var m map[interface{}]interface{} + err := yaml.Unmarshal([]byte(mergeTests), &m) + c.Assert(err, IsNil) + for name, test := range m { + if name == "anchors" { + continue + } + if name == "plain" { + c.Assert(test, DeepEquals, wantStringMap, Commentf("test %q failed", name)) + continue + } + c.Assert(test, DeepEquals, want, Commentf("test %q failed", name)) + } +} + +func (s *S) TestMergeStruct(c *C) { + type Data struct { + X, Y, R int + Label string + } + want := Data{1, 2, 10, "center/big"} + + var m map[string]Data + err := yaml.Unmarshal([]byte(mergeTests), &m) + c.Assert(err, IsNil) + for name, test := range m { + if name == "anchors" { + continue + } + c.Assert(test, Equals, want, Commentf("test %q failed", name)) + } +} + +var mergeTestsNested = ` +mergeouter1: &mergeouter1 + d: 40 + e: 50 + +mergeouter2: &mergeouter2 + e: 5 + f: 6 + g: 70 + +mergeinner1: &mergeinner1 + <<: *mergeouter1 + inner: + a: 1 + b: 2 + +mergeinner2: &mergeinner2 + <<: *mergeouter2 + inner: + a: -1 + b: -2 + +outer: + <<: [*mergeinner1, *mergeinner2] + f: 60 + inner: + a: 10 +` + +func (s *S) TestMergeNestedStruct(c *C) { + // Issue #818: Merging used to just unmarshal twice on the target + // value, which worked for maps as these were replaced by the new map, + // but not on struct values as these are preserved. This resulted in + // the nested data from the merged map to be mixed up with the data + // from the map being merged into. + // + // This test also prevents two potential bugs from showing up: + // + // 1) A simple implementation might just zero out the nested value + // before unmarshaling the second time, but this would clobber previous + // data that is usually respected ({C: 30} below). + // + // 2) A simple implementation might attempt to handle the key skipping + // directly by iterating over the merging map without recursion, but + // there are more complex cases that require recursion. + // + // Quick summary of the fields: + // + // - A must come from outer and not overriden + // - B must not be set as its in the ignored merge + // - C should still be set as it's preset in the value + // - D should be set from the recursive merge + // - E should be set from the first recursive merge, ignored on the second + // - F should be set in the inlined map from outer, ignored later + // - G should be set in the inlined map from the second recursive merge + // + + type Inner struct { + A, B, C int + } + type Outer struct { + D, E int + Inner Inner + Inline map[string]int `yaml:",inline"` + } + type Data struct { + Outer Outer + } + + test := Data{Outer{0, 0, Inner{C: 30}, nil}} + want := Data{Outer{40, 50, Inner{A: 10, C: 30}, map[string]int{"f": 60, "g": 70}}} + + err := yaml.Unmarshal([]byte(mergeTestsNested), &test) + c.Assert(err, IsNil) + c.Assert(test, DeepEquals, want) + + // Repeat test with a map. + + var testm map[string]interface{} + var wantm = map[string]interface {} { + "f": 60, + "inner": map[string]interface{}{ + "a": 10, + }, + "d": 40, + "e": 50, + "g": 70, + } + err = yaml.Unmarshal([]byte(mergeTestsNested), &testm) + c.Assert(err, IsNil) + c.Assert(testm["outer"], DeepEquals, wantm) +} + +var unmarshalNullTests = []struct { + input string + pristine, expected func() interface{} +}{{ + "null", + func() interface{} { var v interface{}; v = "v"; return &v }, + func() interface{} { var v interface{}; v = nil; return &v }, +}, { + "null", + func() interface{} { var s = "s"; return &s }, + func() interface{} { var s = "s"; return &s }, +}, { + "null", + func() interface{} { var s = "s"; sptr := &s; return &sptr }, + func() interface{} { var sptr *string; return &sptr }, +}, { + "null", + func() interface{} { var i = 1; return &i }, + func() interface{} { var i = 1; return &i }, +}, { + "null", + func() interface{} { var i = 1; iptr := &i; return &iptr }, + func() interface{} { var iptr *int; return &iptr }, +}, { + "null", + func() interface{} { var m = map[string]int{"s": 1}; return &m }, + func() interface{} { var m map[string]int; return &m }, +}, { + "null", + func() interface{} { var m = map[string]int{"s": 1}; return m }, + func() interface{} { var m = map[string]int{"s": 1}; return m }, +}, { + "s2: null\ns3: null", + func() interface{} { var m = map[string]int{"s1": 1, "s2": 2}; return m }, + func() interface{} { var m = map[string]int{"s1": 1, "s2": 2, "s3": 0}; return m }, +}, { + "s2: null\ns3: null", + func() interface{} { var m = map[string]interface{}{"s1": 1, "s2": 2}; return m }, + func() interface{} { var m = map[string]interface{}{"s1": 1, "s2": nil, "s3": nil}; return m }, +}} + +func (s *S) TestUnmarshalNull(c *C) { + for _, test := range unmarshalNullTests { + pristine := test.pristine() + expected := test.expected() + err := yaml.Unmarshal([]byte(test.input), pristine) + c.Assert(err, IsNil) + c.Assert(pristine, DeepEquals, expected) + } +} + +func (s *S) TestUnmarshalPreservesData(c *C) { + var v struct { + A, B int + C int `yaml:"-"` + } + v.A = 42 + v.C = 88 + err := yaml.Unmarshal([]byte("---"), &v) + c.Assert(err, IsNil) + c.Assert(v.A, Equals, 42) + c.Assert(v.B, Equals, 0) + c.Assert(v.C, Equals, 88) + + err = yaml.Unmarshal([]byte("b: 21\nc: 99"), &v) + c.Assert(err, IsNil) + c.Assert(v.A, Equals, 42) + c.Assert(v.B, Equals, 21) + c.Assert(v.C, Equals, 88) +} + +func (s *S) TestUnmarshalSliceOnPreset(c *C) { + // Issue #48. + v := struct{ A []int }{[]int{1}} + yaml.Unmarshal([]byte("a: [2]"), &v) + c.Assert(v.A, DeepEquals, []int{2}) +} + +var unmarshalStrictTests = []struct { + known bool + unique bool + data string + value interface{} + error string +}{{ + known: true, + data: "a: 1\nc: 2\n", + value: struct{ A, B int }{A: 1}, + error: `yaml: unmarshal errors:\n line 2: field c not found in type struct { A int; B int }`, +}, { + unique: true, + data: "a: 1\nb: 2\na: 3\n", + value: struct{ A, B int }{A: 3, B: 2}, + error: `yaml: unmarshal errors:\n line 3: mapping key "a" already defined at line 1`, +}, { + unique: true, + data: "c: 3\na: 1\nb: 2\nc: 4\n", + value: struct { + A int + inlineB `yaml:",inline"` + }{ + A: 1, + inlineB: inlineB{ + B: 2, + inlineC: inlineC{ + C: 4, + }, + }, + }, + error: `yaml: unmarshal errors:\n line 4: mapping key "c" already defined at line 1`, +}, { + unique: true, + data: "c: 0\na: 1\nb: 2\nc: 1\n", + value: struct { + A int + inlineB `yaml:",inline"` + }{ + A: 1, + inlineB: inlineB{ + B: 2, + inlineC: inlineC{ + C: 1, + }, + }, + }, + error: `yaml: unmarshal errors:\n line 4: mapping key "c" already defined at line 1`, +}, { + unique: true, + data: "c: 1\na: 1\nb: 2\nc: 3\n", + value: struct { + A int + M map[string]interface{} `yaml:",inline"` + }{ + A: 1, + M: map[string]interface{}{ + "b": 2, + "c": 3, + }, + }, + error: `yaml: unmarshal errors:\n line 4: mapping key "c" already defined at line 1`, +}, { + unique: true, + data: "a: 1\n9: 2\nnull: 3\n9: 4", + value: map[interface{}]interface{}{ + "a": 1, + nil: 3, + 9: 4, + }, + error: `yaml: unmarshal errors:\n line 4: mapping key "9" already defined at line 2`, +}} + +func (s *S) TestUnmarshalKnownFields(c *C) { + for i, item := range unmarshalStrictTests { + c.Logf("test %d: %q", i, item.data) + // First test that normal Unmarshal unmarshals to the expected value. + if !item.unique { + t := reflect.ValueOf(item.value).Type() + value := reflect.New(t) + err := yaml.Unmarshal([]byte(item.data), value.Interface()) + c.Assert(err, Equals, nil) + c.Assert(value.Elem().Interface(), DeepEquals, item.value) + } + + // Then test that it fails on the same thing with KnownFields on. + t := reflect.ValueOf(item.value).Type() + value := reflect.New(t) + dec := yaml.NewDecoder(bytes.NewBuffer([]byte(item.data))) + dec.KnownFields(item.known) + err := dec.Decode(value.Interface()) + c.Assert(err, ErrorMatches, item.error) + } +} + +type textUnmarshaler struct { + S string +} + +func (t *textUnmarshaler) UnmarshalText(s []byte) error { + t.S = string(s) + return nil +} + +func (s *S) TestFuzzCrashers(c *C) { + cases := []string{ + // runtime error: index out of range + "\"\\0\\\r\n", + + // should not happen + " 0: [\n] 0", + "? ? \"\n\" 0", + " - {\n000}0", + "0:\n 0: [0\n] 0", + " - \"\n000\"0", + " - \"\n000\"\"", + "0:\n - {\n000}0", + "0:\n - \"\n000\"0", + "0:\n - \"\n000\"\"", + + // runtime error: index out of range + " \ufeff\n", + "? \ufeff\n", + "? \ufeff:\n", + "0: \ufeff\n", + "? \ufeff: \ufeff\n", + } + for _, data := range cases { + var v interface{} + _ = yaml.Unmarshal([]byte(data), &v) + } +} + +//var data []byte +//func init() { +// var err error +// data, err = ioutil.ReadFile("/tmp/file.yaml") +// if err != nil { +// panic(err) +// } +//} +// +//func (s *S) BenchmarkUnmarshal(c *C) { +// var err error +// for i := 0; i < c.N; i++ { +// var v map[string]interface{} +// err = yaml.Unmarshal(data, &v) +// } +// if err != nil { +// panic(err) +// } +//} +// +//func (s *S) BenchmarkMarshal(c *C) { +// var v map[string]interface{} +// yaml.Unmarshal(data, &v) +// c.ResetTimer() +// for i := 0; i < c.N; i++ { +// yaml.Marshal(&v) +// } +//} diff --git a/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/emitterc.go b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/emitterc.go new file mode 100644 index 0000000..0f47c9c --- /dev/null +++ b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/emitterc.go @@ -0,0 +1,2020 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// Copyright (c) 2006-2010 Kirill Simonov +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package yaml + +import ( + "bytes" + "fmt" +) + +// Flush the buffer if needed. +func flush(emitter *yaml_emitter_t) bool { + if emitter.buffer_pos+5 >= len(emitter.buffer) { + return yaml_emitter_flush(emitter) + } + return true +} + +// Put a character to the output buffer. +func put(emitter *yaml_emitter_t, value byte) bool { + if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) { + return false + } + emitter.buffer[emitter.buffer_pos] = value + emitter.buffer_pos++ + emitter.column++ + return true +} + +// Put a line break to the output buffer. +func put_break(emitter *yaml_emitter_t) bool { + if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) { + return false + } + switch emitter.line_break { + case yaml_CR_BREAK: + emitter.buffer[emitter.buffer_pos] = '\r' + emitter.buffer_pos += 1 + case yaml_LN_BREAK: + emitter.buffer[emitter.buffer_pos] = '\n' + emitter.buffer_pos += 1 + case yaml_CRLN_BREAK: + emitter.buffer[emitter.buffer_pos+0] = '\r' + emitter.buffer[emitter.buffer_pos+1] = '\n' + emitter.buffer_pos += 2 + default: + panic("unknown line break setting") + } + if emitter.column == 0 { + emitter.space_above = true + } + emitter.column = 0 + emitter.line++ + // [Go] Do this here and below and drop from everywhere else (see commented lines). + emitter.indention = true + return true +} + +// Copy a character from a string into buffer. +func write(emitter *yaml_emitter_t, s []byte, i *int) bool { + if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) { + return false + } + p := emitter.buffer_pos + w := width(s[*i]) + switch w { + case 4: + emitter.buffer[p+3] = s[*i+3] + fallthrough + case 3: + emitter.buffer[p+2] = s[*i+2] + fallthrough + case 2: + emitter.buffer[p+1] = s[*i+1] + fallthrough + case 1: + emitter.buffer[p+0] = s[*i+0] + default: + panic("unknown character width") + } + emitter.column++ + emitter.buffer_pos += w + *i += w + return true +} + +// Write a whole string into buffer. +func write_all(emitter *yaml_emitter_t, s []byte) bool { + for i := 0; i < len(s); { + if !write(emitter, s, &i) { + return false + } + } + return true +} + +// Copy a line break character from a string into buffer. +func write_break(emitter *yaml_emitter_t, s []byte, i *int) bool { + if s[*i] == '\n' { + if !put_break(emitter) { + return false + } + *i++ + } else { + if !write(emitter, s, i) { + return false + } + if emitter.column == 0 { + emitter.space_above = true + } + emitter.column = 0 + emitter.line++ + // [Go] Do this here and above and drop from everywhere else (see commented lines). + emitter.indention = true + } + return true +} + +// Set an emitter error and return false. +func yaml_emitter_set_emitter_error(emitter *yaml_emitter_t, problem string) bool { + emitter.error = yaml_EMITTER_ERROR + emitter.problem = problem + return false +} + +// Emit an event. +func yaml_emitter_emit(emitter *yaml_emitter_t, event *yaml_event_t) bool { + emitter.events = append(emitter.events, *event) + for !yaml_emitter_need_more_events(emitter) { + event := &emitter.events[emitter.events_head] + if !yaml_emitter_analyze_event(emitter, event) { + return false + } + if !yaml_emitter_state_machine(emitter, event) { + return false + } + yaml_event_delete(event) + emitter.events_head++ + } + return true +} + +// Check if we need to accumulate more events before emitting. +// +// We accumulate extra +// - 1 event for DOCUMENT-START +// - 2 events for SEQUENCE-START +// - 3 events for MAPPING-START +// +func yaml_emitter_need_more_events(emitter *yaml_emitter_t) bool { + if emitter.events_head == len(emitter.events) { + return true + } + var accumulate int + switch emitter.events[emitter.events_head].typ { + case yaml_DOCUMENT_START_EVENT: + accumulate = 1 + break + case yaml_SEQUENCE_START_EVENT: + accumulate = 2 + break + case yaml_MAPPING_START_EVENT: + accumulate = 3 + break + default: + return false + } + if len(emitter.events)-emitter.events_head > accumulate { + return false + } + var level int + for i := emitter.events_head; i < len(emitter.events); i++ { + switch emitter.events[i].typ { + case yaml_STREAM_START_EVENT, yaml_DOCUMENT_START_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT: + level++ + case yaml_STREAM_END_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_END_EVENT, yaml_MAPPING_END_EVENT: + level-- + } + if level == 0 { + return false + } + } + return true +} + +// Append a directive to the directives stack. +func yaml_emitter_append_tag_directive(emitter *yaml_emitter_t, value *yaml_tag_directive_t, allow_duplicates bool) bool { + for i := 0; i < len(emitter.tag_directives); i++ { + if bytes.Equal(value.handle, emitter.tag_directives[i].handle) { + if allow_duplicates { + return true + } + return yaml_emitter_set_emitter_error(emitter, "duplicate %TAG directive") + } + } + + // [Go] Do we actually need to copy this given garbage collection + // and the lack of deallocating destructors? + tag_copy := yaml_tag_directive_t{ + handle: make([]byte, len(value.handle)), + prefix: make([]byte, len(value.prefix)), + } + copy(tag_copy.handle, value.handle) + copy(tag_copy.prefix, value.prefix) + emitter.tag_directives = append(emitter.tag_directives, tag_copy) + return true +} + +// Increase the indentation level. +func yaml_emitter_increase_indent(emitter *yaml_emitter_t, flow, indentless bool) bool { + emitter.indents = append(emitter.indents, emitter.indent) + if emitter.indent < 0 { + if flow { + emitter.indent = emitter.best_indent + } else { + emitter.indent = 0 + } + } else if !indentless { + // [Go] This was changed so that indentations are more regular. + if emitter.states[len(emitter.states)-1] == yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE { + // The first indent inside a sequence will just skip the "- " indicator. + emitter.indent += 2 + } else { + // Everything else aligns to the chosen indentation. + emitter.indent = emitter.best_indent*((emitter.indent+emitter.best_indent)/emitter.best_indent) + } + } + return true +} + +// State dispatcher. +func yaml_emitter_state_machine(emitter *yaml_emitter_t, event *yaml_event_t) bool { + switch emitter.state { + default: + case yaml_EMIT_STREAM_START_STATE: + return yaml_emitter_emit_stream_start(emitter, event) + + case yaml_EMIT_FIRST_DOCUMENT_START_STATE: + return yaml_emitter_emit_document_start(emitter, event, true) + + case yaml_EMIT_DOCUMENT_START_STATE: + return yaml_emitter_emit_document_start(emitter, event, false) + + case yaml_EMIT_DOCUMENT_CONTENT_STATE: + return yaml_emitter_emit_document_content(emitter, event) + + case yaml_EMIT_DOCUMENT_END_STATE: + return yaml_emitter_emit_document_end(emitter, event) + + case yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE: + return yaml_emitter_emit_flow_sequence_item(emitter, event, true, false) + + case yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE: + return yaml_emitter_emit_flow_sequence_item(emitter, event, false, true) + + case yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE: + return yaml_emitter_emit_flow_sequence_item(emitter, event, false, false) + + case yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE: + return yaml_emitter_emit_flow_mapping_key(emitter, event, true, false) + + case yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE: + return yaml_emitter_emit_flow_mapping_key(emitter, event, false, true) + + case yaml_EMIT_FLOW_MAPPING_KEY_STATE: + return yaml_emitter_emit_flow_mapping_key(emitter, event, false, false) + + case yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE: + return yaml_emitter_emit_flow_mapping_value(emitter, event, true) + + case yaml_EMIT_FLOW_MAPPING_VALUE_STATE: + return yaml_emitter_emit_flow_mapping_value(emitter, event, false) + + case yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE: + return yaml_emitter_emit_block_sequence_item(emitter, event, true) + + case yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE: + return yaml_emitter_emit_block_sequence_item(emitter, event, false) + + case yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE: + return yaml_emitter_emit_block_mapping_key(emitter, event, true) + + case yaml_EMIT_BLOCK_MAPPING_KEY_STATE: + return yaml_emitter_emit_block_mapping_key(emitter, event, false) + + case yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE: + return yaml_emitter_emit_block_mapping_value(emitter, event, true) + + case yaml_EMIT_BLOCK_MAPPING_VALUE_STATE: + return yaml_emitter_emit_block_mapping_value(emitter, event, false) + + case yaml_EMIT_END_STATE: + return yaml_emitter_set_emitter_error(emitter, "expected nothing after STREAM-END") + } + panic("invalid emitter state") +} + +// Expect STREAM-START. +func yaml_emitter_emit_stream_start(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if event.typ != yaml_STREAM_START_EVENT { + return yaml_emitter_set_emitter_error(emitter, "expected STREAM-START") + } + if emitter.encoding == yaml_ANY_ENCODING { + emitter.encoding = event.encoding + if emitter.encoding == yaml_ANY_ENCODING { + emitter.encoding = yaml_UTF8_ENCODING + } + } + if emitter.best_indent < 2 || emitter.best_indent > 9 { + emitter.best_indent = 2 + } + if emitter.best_width >= 0 && emitter.best_width <= emitter.best_indent*2 { + emitter.best_width = 80 + } + if emitter.best_width < 0 { + emitter.best_width = 1<<31 - 1 + } + if emitter.line_break == yaml_ANY_BREAK { + emitter.line_break = yaml_LN_BREAK + } + + emitter.indent = -1 + emitter.line = 0 + emitter.column = 0 + emitter.whitespace = true + emitter.indention = true + emitter.space_above = true + emitter.foot_indent = -1 + + if emitter.encoding != yaml_UTF8_ENCODING { + if !yaml_emitter_write_bom(emitter) { + return false + } + } + emitter.state = yaml_EMIT_FIRST_DOCUMENT_START_STATE + return true +} + +// Expect DOCUMENT-START or STREAM-END. +func yaml_emitter_emit_document_start(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { + + if event.typ == yaml_DOCUMENT_START_EVENT { + + if event.version_directive != nil { + if !yaml_emitter_analyze_version_directive(emitter, event.version_directive) { + return false + } + } + + for i := 0; i < len(event.tag_directives); i++ { + tag_directive := &event.tag_directives[i] + if !yaml_emitter_analyze_tag_directive(emitter, tag_directive) { + return false + } + if !yaml_emitter_append_tag_directive(emitter, tag_directive, false) { + return false + } + } + + for i := 0; i < len(default_tag_directives); i++ { + tag_directive := &default_tag_directives[i] + if !yaml_emitter_append_tag_directive(emitter, tag_directive, true) { + return false + } + } + + implicit := event.implicit + if !first || emitter.canonical { + implicit = false + } + + if emitter.open_ended && (event.version_directive != nil || len(event.tag_directives) > 0) { + if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + + if event.version_directive != nil { + implicit = false + if !yaml_emitter_write_indicator(emitter, []byte("%YAML"), true, false, false) { + return false + } + if !yaml_emitter_write_indicator(emitter, []byte("1.1"), true, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + + if len(event.tag_directives) > 0 { + implicit = false + for i := 0; i < len(event.tag_directives); i++ { + tag_directive := &event.tag_directives[i] + if !yaml_emitter_write_indicator(emitter, []byte("%TAG"), true, false, false) { + return false + } + if !yaml_emitter_write_tag_handle(emitter, tag_directive.handle) { + return false + } + if !yaml_emitter_write_tag_content(emitter, tag_directive.prefix, true) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + } + + if yaml_emitter_check_empty_document(emitter) { + implicit = false + } + if !implicit { + if !yaml_emitter_write_indent(emitter) { + return false + } + if !yaml_emitter_write_indicator(emitter, []byte("---"), true, false, false) { + return false + } + if emitter.canonical || true { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + } + + if len(emitter.head_comment) > 0 { + if !yaml_emitter_process_head_comment(emitter) { + return false + } + if !put_break(emitter) { + return false + } + } + + emitter.state = yaml_EMIT_DOCUMENT_CONTENT_STATE + return true + } + + if event.typ == yaml_STREAM_END_EVENT { + if emitter.open_ended { + if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !yaml_emitter_flush(emitter) { + return false + } + emitter.state = yaml_EMIT_END_STATE + return true + } + + return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-START or STREAM-END") +} + +// Expect the root node. +func yaml_emitter_emit_document_content(emitter *yaml_emitter_t, event *yaml_event_t) bool { + emitter.states = append(emitter.states, yaml_EMIT_DOCUMENT_END_STATE) + + if !yaml_emitter_process_head_comment(emitter) { + return false + } + if !yaml_emitter_emit_node(emitter, event, true, false, false, false) { + return false + } + if !yaml_emitter_process_line_comment(emitter) { + return false + } + if !yaml_emitter_process_foot_comment(emitter) { + return false + } + return true +} + +// Expect DOCUMENT-END. +func yaml_emitter_emit_document_end(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if event.typ != yaml_DOCUMENT_END_EVENT { + return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-END") + } + // [Go] Force document foot separation. + emitter.foot_indent = 0 + if !yaml_emitter_process_foot_comment(emitter) { + return false + } + emitter.foot_indent = -1 + if !yaml_emitter_write_indent(emitter) { + return false + } + if !event.implicit { + // [Go] Allocate the slice elsewhere. + if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !yaml_emitter_flush(emitter) { + return false + } + emitter.state = yaml_EMIT_DOCUMENT_START_STATE + emitter.tag_directives = emitter.tag_directives[:0] + return true +} + +// Expect a flow item node. +func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first, trail bool) bool { + if first { + if !yaml_emitter_write_indicator(emitter, []byte{'['}, true, true, false) { + return false + } + if !yaml_emitter_increase_indent(emitter, true, false) { + return false + } + emitter.flow_level++ + } + + if event.typ == yaml_SEQUENCE_END_EVENT { + if emitter.canonical && !first && !trail { + if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { + return false + } + } + emitter.flow_level-- + emitter.indent = emitter.indents[len(emitter.indents)-1] + emitter.indents = emitter.indents[:len(emitter.indents)-1] + if emitter.column == 0 || emitter.canonical && !first { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !yaml_emitter_write_indicator(emitter, []byte{']'}, false, false, false) { + return false + } + if !yaml_emitter_process_line_comment(emitter) { + return false + } + if !yaml_emitter_process_foot_comment(emitter) { + return false + } + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + + return true + } + + if !first && !trail { + if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { + return false + } + } + + if !yaml_emitter_process_head_comment(emitter) { + return false + } + if emitter.column == 0 { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + + if emitter.canonical || emitter.column > emitter.best_width { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 { + emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE) + } else { + emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE) + } + if !yaml_emitter_emit_node(emitter, event, false, true, false, false) { + return false + } + if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 { + if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { + return false + } + } + if !yaml_emitter_process_line_comment(emitter) { + return false + } + if !yaml_emitter_process_foot_comment(emitter) { + return false + } + return true +} + +// Expect a flow key node. +func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first, trail bool) bool { + if first { + if !yaml_emitter_write_indicator(emitter, []byte{'{'}, true, true, false) { + return false + } + if !yaml_emitter_increase_indent(emitter, true, false) { + return false + } + emitter.flow_level++ + } + + if event.typ == yaml_MAPPING_END_EVENT { + if (emitter.canonical || len(emitter.head_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0) && !first && !trail { + if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { + return false + } + } + if !yaml_emitter_process_head_comment(emitter) { + return false + } + emitter.flow_level-- + emitter.indent = emitter.indents[len(emitter.indents)-1] + emitter.indents = emitter.indents[:len(emitter.indents)-1] + if emitter.canonical && !first { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !yaml_emitter_write_indicator(emitter, []byte{'}'}, false, false, false) { + return false + } + if !yaml_emitter_process_line_comment(emitter) { + return false + } + if !yaml_emitter_process_foot_comment(emitter) { + return false + } + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + return true + } + + if !first && !trail { + if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { + return false + } + } + + if !yaml_emitter_process_head_comment(emitter) { + return false + } + + if emitter.column == 0 { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + + if emitter.canonical || emitter.column > emitter.best_width { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + + if !emitter.canonical && yaml_emitter_check_simple_key(emitter) { + emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, true) + } + if !yaml_emitter_write_indicator(emitter, []byte{'?'}, true, false, false) { + return false + } + emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_VALUE_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, false) +} + +// Expect a flow value node. +func yaml_emitter_emit_flow_mapping_value(emitter *yaml_emitter_t, event *yaml_event_t, simple bool) bool { + if simple { + if !yaml_emitter_write_indicator(emitter, []byte{':'}, false, false, false) { + return false + } + } else { + if emitter.canonical || emitter.column > emitter.best_width { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !yaml_emitter_write_indicator(emitter, []byte{':'}, true, false, false) { + return false + } + } + if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 { + emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE) + } else { + emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_KEY_STATE) + } + if !yaml_emitter_emit_node(emitter, event, false, false, true, false) { + return false + } + if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 { + if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { + return false + } + } + if !yaml_emitter_process_line_comment(emitter) { + return false + } + if !yaml_emitter_process_foot_comment(emitter) { + return false + } + return true +} + +// Expect a block item node. +func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { + if first { + if !yaml_emitter_increase_indent(emitter, false, false) { + return false + } + } + if event.typ == yaml_SEQUENCE_END_EVENT { + emitter.indent = emitter.indents[len(emitter.indents)-1] + emitter.indents = emitter.indents[:len(emitter.indents)-1] + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + return true + } + if !yaml_emitter_process_head_comment(emitter) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + if !yaml_emitter_write_indicator(emitter, []byte{'-'}, true, false, true) { + return false + } + emitter.states = append(emitter.states, yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE) + if !yaml_emitter_emit_node(emitter, event, false, true, false, false) { + return false + } + if !yaml_emitter_process_line_comment(emitter) { + return false + } + if !yaml_emitter_process_foot_comment(emitter) { + return false + } + return true +} + +// Expect a block key node. +func yaml_emitter_emit_block_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { + if first { + if !yaml_emitter_increase_indent(emitter, false, false) { + return false + } + } + if !yaml_emitter_process_head_comment(emitter) { + return false + } + if event.typ == yaml_MAPPING_END_EVENT { + emitter.indent = emitter.indents[len(emitter.indents)-1] + emitter.indents = emitter.indents[:len(emitter.indents)-1] + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + return true + } + if !yaml_emitter_write_indent(emitter) { + return false + } + if len(emitter.line_comment) > 0 { + // [Go] A line comment was provided for the key. That's unusual as the + // scanner associates line comments with the value. Either way, + // save the line comment and render it appropriately later. + emitter.key_line_comment = emitter.line_comment + emitter.line_comment = nil + } + if yaml_emitter_check_simple_key(emitter) { + emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, true) + } + if !yaml_emitter_write_indicator(emitter, []byte{'?'}, true, false, true) { + return false + } + emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_VALUE_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, false) +} + +// Expect a block value node. +func yaml_emitter_emit_block_mapping_value(emitter *yaml_emitter_t, event *yaml_event_t, simple bool) bool { + if simple { + if !yaml_emitter_write_indicator(emitter, []byte{':'}, false, false, false) { + return false + } + } else { + if !yaml_emitter_write_indent(emitter) { + return false + } + if !yaml_emitter_write_indicator(emitter, []byte{':'}, true, false, true) { + return false + } + } + if len(emitter.key_line_comment) > 0 { + // [Go] Line comments are generally associated with the value, but when there's + // no value on the same line as a mapping key they end up attached to the + // key itself. + if event.typ == yaml_SCALAR_EVENT { + if len(emitter.line_comment) == 0 { + // A scalar is coming and it has no line comments by itself yet, + // so just let it handle the line comment as usual. If it has a + // line comment, we can't have both so the one from the key is lost. + emitter.line_comment = emitter.key_line_comment + emitter.key_line_comment = nil + } + } else if event.sequence_style() != yaml_FLOW_SEQUENCE_STYLE && (event.typ == yaml_MAPPING_START_EVENT || event.typ == yaml_SEQUENCE_START_EVENT) { + // An indented block follows, so write the comment right now. + emitter.line_comment, emitter.key_line_comment = emitter.key_line_comment, emitter.line_comment + if !yaml_emitter_process_line_comment(emitter) { + return false + } + emitter.line_comment, emitter.key_line_comment = emitter.key_line_comment, emitter.line_comment + } + } + emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_KEY_STATE) + if !yaml_emitter_emit_node(emitter, event, false, false, true, false) { + return false + } + if !yaml_emitter_process_line_comment(emitter) { + return false + } + if !yaml_emitter_process_foot_comment(emitter) { + return false + } + return true +} + +func yaml_emitter_silent_nil_event(emitter *yaml_emitter_t, event *yaml_event_t) bool { + return event.typ == yaml_SCALAR_EVENT && event.implicit && !emitter.canonical && len(emitter.scalar_data.value) == 0 +} + +// Expect a node. +func yaml_emitter_emit_node(emitter *yaml_emitter_t, event *yaml_event_t, + root bool, sequence bool, mapping bool, simple_key bool) bool { + + emitter.root_context = root + emitter.sequence_context = sequence + emitter.mapping_context = mapping + emitter.simple_key_context = simple_key + + switch event.typ { + case yaml_ALIAS_EVENT: + return yaml_emitter_emit_alias(emitter, event) + case yaml_SCALAR_EVENT: + return yaml_emitter_emit_scalar(emitter, event) + case yaml_SEQUENCE_START_EVENT: + return yaml_emitter_emit_sequence_start(emitter, event) + case yaml_MAPPING_START_EVENT: + return yaml_emitter_emit_mapping_start(emitter, event) + default: + return yaml_emitter_set_emitter_error(emitter, + fmt.Sprintf("expected SCALAR, SEQUENCE-START, MAPPING-START, or ALIAS, but got %v", event.typ)) + } +} + +// Expect ALIAS. +func yaml_emitter_emit_alias(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if !yaml_emitter_process_anchor(emitter) { + return false + } + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + return true +} + +// Expect SCALAR. +func yaml_emitter_emit_scalar(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if !yaml_emitter_select_scalar_style(emitter, event) { + return false + } + if !yaml_emitter_process_anchor(emitter) { + return false + } + if !yaml_emitter_process_tag(emitter) { + return false + } + if !yaml_emitter_increase_indent(emitter, true, false) { + return false + } + if !yaml_emitter_process_scalar(emitter) { + return false + } + emitter.indent = emitter.indents[len(emitter.indents)-1] + emitter.indents = emitter.indents[:len(emitter.indents)-1] + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + return true +} + +// Expect SEQUENCE-START. +func yaml_emitter_emit_sequence_start(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if !yaml_emitter_process_anchor(emitter) { + return false + } + if !yaml_emitter_process_tag(emitter) { + return false + } + if emitter.flow_level > 0 || emitter.canonical || event.sequence_style() == yaml_FLOW_SEQUENCE_STYLE || + yaml_emitter_check_empty_sequence(emitter) { + emitter.state = yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE + } else { + emitter.state = yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE + } + return true +} + +// Expect MAPPING-START. +func yaml_emitter_emit_mapping_start(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if !yaml_emitter_process_anchor(emitter) { + return false + } + if !yaml_emitter_process_tag(emitter) { + return false + } + if emitter.flow_level > 0 || emitter.canonical || event.mapping_style() == yaml_FLOW_MAPPING_STYLE || + yaml_emitter_check_empty_mapping(emitter) { + emitter.state = yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE + } else { + emitter.state = yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE + } + return true +} + +// Check if the document content is an empty scalar. +func yaml_emitter_check_empty_document(emitter *yaml_emitter_t) bool { + return false // [Go] Huh? +} + +// Check if the next events represent an empty sequence. +func yaml_emitter_check_empty_sequence(emitter *yaml_emitter_t) bool { + if len(emitter.events)-emitter.events_head < 2 { + return false + } + return emitter.events[emitter.events_head].typ == yaml_SEQUENCE_START_EVENT && + emitter.events[emitter.events_head+1].typ == yaml_SEQUENCE_END_EVENT +} + +// Check if the next events represent an empty mapping. +func yaml_emitter_check_empty_mapping(emitter *yaml_emitter_t) bool { + if len(emitter.events)-emitter.events_head < 2 { + return false + } + return emitter.events[emitter.events_head].typ == yaml_MAPPING_START_EVENT && + emitter.events[emitter.events_head+1].typ == yaml_MAPPING_END_EVENT +} + +// Check if the next node can be expressed as a simple key. +func yaml_emitter_check_simple_key(emitter *yaml_emitter_t) bool { + length := 0 + switch emitter.events[emitter.events_head].typ { + case yaml_ALIAS_EVENT: + length += len(emitter.anchor_data.anchor) + case yaml_SCALAR_EVENT: + if emitter.scalar_data.multiline { + return false + } + length += len(emitter.anchor_data.anchor) + + len(emitter.tag_data.handle) + + len(emitter.tag_data.suffix) + + len(emitter.scalar_data.value) + case yaml_SEQUENCE_START_EVENT: + if !yaml_emitter_check_empty_sequence(emitter) { + return false + } + length += len(emitter.anchor_data.anchor) + + len(emitter.tag_data.handle) + + len(emitter.tag_data.suffix) + case yaml_MAPPING_START_EVENT: + if !yaml_emitter_check_empty_mapping(emitter) { + return false + } + length += len(emitter.anchor_data.anchor) + + len(emitter.tag_data.handle) + + len(emitter.tag_data.suffix) + default: + return false + } + return length <= 128 +} + +// Determine an acceptable scalar style. +func yaml_emitter_select_scalar_style(emitter *yaml_emitter_t, event *yaml_event_t) bool { + + no_tag := len(emitter.tag_data.handle) == 0 && len(emitter.tag_data.suffix) == 0 + if no_tag && !event.implicit && !event.quoted_implicit { + return yaml_emitter_set_emitter_error(emitter, "neither tag nor implicit flags are specified") + } + + style := event.scalar_style() + if style == yaml_ANY_SCALAR_STYLE { + style = yaml_PLAIN_SCALAR_STYLE + } + if emitter.canonical { + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + if emitter.simple_key_context && emitter.scalar_data.multiline { + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + + if style == yaml_PLAIN_SCALAR_STYLE { + if emitter.flow_level > 0 && !emitter.scalar_data.flow_plain_allowed || + emitter.flow_level == 0 && !emitter.scalar_data.block_plain_allowed { + style = yaml_SINGLE_QUOTED_SCALAR_STYLE + } + if len(emitter.scalar_data.value) == 0 && (emitter.flow_level > 0 || emitter.simple_key_context) { + style = yaml_SINGLE_QUOTED_SCALAR_STYLE + } + if no_tag && !event.implicit { + style = yaml_SINGLE_QUOTED_SCALAR_STYLE + } + } + if style == yaml_SINGLE_QUOTED_SCALAR_STYLE { + if !emitter.scalar_data.single_quoted_allowed { + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + } + if style == yaml_LITERAL_SCALAR_STYLE || style == yaml_FOLDED_SCALAR_STYLE { + if !emitter.scalar_data.block_allowed || emitter.flow_level > 0 || emitter.simple_key_context { + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + } + + if no_tag && !event.quoted_implicit && style != yaml_PLAIN_SCALAR_STYLE { + emitter.tag_data.handle = []byte{'!'} + } + emitter.scalar_data.style = style + return true +} + +// Write an anchor. +func yaml_emitter_process_anchor(emitter *yaml_emitter_t) bool { + if emitter.anchor_data.anchor == nil { + return true + } + c := []byte{'&'} + if emitter.anchor_data.alias { + c[0] = '*' + } + if !yaml_emitter_write_indicator(emitter, c, true, false, false) { + return false + } + return yaml_emitter_write_anchor(emitter, emitter.anchor_data.anchor) +} + +// Write a tag. +func yaml_emitter_process_tag(emitter *yaml_emitter_t) bool { + if len(emitter.tag_data.handle) == 0 && len(emitter.tag_data.suffix) == 0 { + return true + } + if len(emitter.tag_data.handle) > 0 { + if !yaml_emitter_write_tag_handle(emitter, emitter.tag_data.handle) { + return false + } + if len(emitter.tag_data.suffix) > 0 { + if !yaml_emitter_write_tag_content(emitter, emitter.tag_data.suffix, false) { + return false + } + } + } else { + // [Go] Allocate these slices elsewhere. + if !yaml_emitter_write_indicator(emitter, []byte("!<"), true, false, false) { + return false + } + if !yaml_emitter_write_tag_content(emitter, emitter.tag_data.suffix, false) { + return false + } + if !yaml_emitter_write_indicator(emitter, []byte{'>'}, false, false, false) { + return false + } + } + return true +} + +// Write a scalar. +func yaml_emitter_process_scalar(emitter *yaml_emitter_t) bool { + switch emitter.scalar_data.style { + case yaml_PLAIN_SCALAR_STYLE: + return yaml_emitter_write_plain_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context) + + case yaml_SINGLE_QUOTED_SCALAR_STYLE: + return yaml_emitter_write_single_quoted_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context) + + case yaml_DOUBLE_QUOTED_SCALAR_STYLE: + return yaml_emitter_write_double_quoted_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context) + + case yaml_LITERAL_SCALAR_STYLE: + return yaml_emitter_write_literal_scalar(emitter, emitter.scalar_data.value) + + case yaml_FOLDED_SCALAR_STYLE: + return yaml_emitter_write_folded_scalar(emitter, emitter.scalar_data.value) + } + panic("unknown scalar style") +} + +// Write a head comment. +func yaml_emitter_process_head_comment(emitter *yaml_emitter_t) bool { + if len(emitter.tail_comment) > 0 { + if !yaml_emitter_write_indent(emitter) { + return false + } + if !yaml_emitter_write_comment(emitter, emitter.tail_comment) { + return false + } + emitter.tail_comment = emitter.tail_comment[:0] + emitter.foot_indent = emitter.indent + if emitter.foot_indent < 0 { + emitter.foot_indent = 0 + } + } + + if len(emitter.head_comment) == 0 { + return true + } + if !yaml_emitter_write_indent(emitter) { + return false + } + if !yaml_emitter_write_comment(emitter, emitter.head_comment) { + return false + } + emitter.head_comment = emitter.head_comment[:0] + return true +} + +// Write an line comment. +func yaml_emitter_process_line_comment(emitter *yaml_emitter_t) bool { + if len(emitter.line_comment) == 0 { + return true + } + if !emitter.whitespace { + if !put(emitter, ' ') { + return false + } + } + if !yaml_emitter_write_comment(emitter, emitter.line_comment) { + return false + } + emitter.line_comment = emitter.line_comment[:0] + return true +} + +// Write a foot comment. +func yaml_emitter_process_foot_comment(emitter *yaml_emitter_t) bool { + if len(emitter.foot_comment) == 0 { + return true + } + if !yaml_emitter_write_indent(emitter) { + return false + } + if !yaml_emitter_write_comment(emitter, emitter.foot_comment) { + return false + } + emitter.foot_comment = emitter.foot_comment[:0] + emitter.foot_indent = emitter.indent + if emitter.foot_indent < 0 { + emitter.foot_indent = 0 + } + return true +} + +// Check if a %YAML directive is valid. +func yaml_emitter_analyze_version_directive(emitter *yaml_emitter_t, version_directive *yaml_version_directive_t) bool { + if version_directive.major != 1 || version_directive.minor != 1 { + return yaml_emitter_set_emitter_error(emitter, "incompatible %YAML directive") + } + return true +} + +// Check if a %TAG directive is valid. +func yaml_emitter_analyze_tag_directive(emitter *yaml_emitter_t, tag_directive *yaml_tag_directive_t) bool { + handle := tag_directive.handle + prefix := tag_directive.prefix + if len(handle) == 0 { + return yaml_emitter_set_emitter_error(emitter, "tag handle must not be empty") + } + if handle[0] != '!' { + return yaml_emitter_set_emitter_error(emitter, "tag handle must start with '!'") + } + if handle[len(handle)-1] != '!' { + return yaml_emitter_set_emitter_error(emitter, "tag handle must end with '!'") + } + for i := 1; i < len(handle)-1; i += width(handle[i]) { + if !is_alpha(handle, i) { + return yaml_emitter_set_emitter_error(emitter, "tag handle must contain alphanumerical characters only") + } + } + if len(prefix) == 0 { + return yaml_emitter_set_emitter_error(emitter, "tag prefix must not be empty") + } + return true +} + +// Check if an anchor is valid. +func yaml_emitter_analyze_anchor(emitter *yaml_emitter_t, anchor []byte, alias bool) bool { + if len(anchor) == 0 { + problem := "anchor value must not be empty" + if alias { + problem = "alias value must not be empty" + } + return yaml_emitter_set_emitter_error(emitter, problem) + } + for i := 0; i < len(anchor); i += width(anchor[i]) { + if !is_alpha(anchor, i) { + problem := "anchor value must contain alphanumerical characters only" + if alias { + problem = "alias value must contain alphanumerical characters only" + } + return yaml_emitter_set_emitter_error(emitter, problem) + } + } + emitter.anchor_data.anchor = anchor + emitter.anchor_data.alias = alias + return true +} + +// Check if a tag is valid. +func yaml_emitter_analyze_tag(emitter *yaml_emitter_t, tag []byte) bool { + if len(tag) == 0 { + return yaml_emitter_set_emitter_error(emitter, "tag value must not be empty") + } + for i := 0; i < len(emitter.tag_directives); i++ { + tag_directive := &emitter.tag_directives[i] + if bytes.HasPrefix(tag, tag_directive.prefix) { + emitter.tag_data.handle = tag_directive.handle + emitter.tag_data.suffix = tag[len(tag_directive.prefix):] + return true + } + } + emitter.tag_data.suffix = tag + return true +} + +// Check if a scalar is valid. +func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool { + var ( + block_indicators = false + flow_indicators = false + line_breaks = false + special_characters = false + tab_characters = false + + leading_space = false + leading_break = false + trailing_space = false + trailing_break = false + break_space = false + space_break = false + + preceded_by_whitespace = false + followed_by_whitespace = false + previous_space = false + previous_break = false + ) + + emitter.scalar_data.value = value + + if len(value) == 0 { + emitter.scalar_data.multiline = false + emitter.scalar_data.flow_plain_allowed = false + emitter.scalar_data.block_plain_allowed = true + emitter.scalar_data.single_quoted_allowed = true + emitter.scalar_data.block_allowed = false + return true + } + + if len(value) >= 3 && ((value[0] == '-' && value[1] == '-' && value[2] == '-') || (value[0] == '.' && value[1] == '.' && value[2] == '.')) { + block_indicators = true + flow_indicators = true + } + + preceded_by_whitespace = true + for i, w := 0, 0; i < len(value); i += w { + w = width(value[i]) + followed_by_whitespace = i+w >= len(value) || is_blank(value, i+w) + + if i == 0 { + switch value[i] { + case '#', ',', '[', ']', '{', '}', '&', '*', '!', '|', '>', '\'', '"', '%', '@', '`': + flow_indicators = true + block_indicators = true + case '?', ':': + flow_indicators = true + if followed_by_whitespace { + block_indicators = true + } + case '-': + if followed_by_whitespace { + flow_indicators = true + block_indicators = true + } + } + } else { + switch value[i] { + case ',', '?', '[', ']', '{', '}': + flow_indicators = true + case ':': + flow_indicators = true + if followed_by_whitespace { + block_indicators = true + } + case '#': + if preceded_by_whitespace { + flow_indicators = true + block_indicators = true + } + } + } + + if value[i] == '\t' { + tab_characters = true + } else if !is_printable(value, i) || !is_ascii(value, i) && !emitter.unicode { + special_characters = true + } + if is_space(value, i) { + if i == 0 { + leading_space = true + } + if i+width(value[i]) == len(value) { + trailing_space = true + } + if previous_break { + break_space = true + } + previous_space = true + previous_break = false + } else if is_break(value, i) { + line_breaks = true + if i == 0 { + leading_break = true + } + if i+width(value[i]) == len(value) { + trailing_break = true + } + if previous_space { + space_break = true + } + previous_space = false + previous_break = true + } else { + previous_space = false + previous_break = false + } + + // [Go]: Why 'z'? Couldn't be the end of the string as that's the loop condition. + preceded_by_whitespace = is_blankz(value, i) + } + + emitter.scalar_data.multiline = line_breaks + emitter.scalar_data.flow_plain_allowed = true + emitter.scalar_data.block_plain_allowed = true + emitter.scalar_data.single_quoted_allowed = true + emitter.scalar_data.block_allowed = true + + if leading_space || leading_break || trailing_space || trailing_break { + emitter.scalar_data.flow_plain_allowed = false + emitter.scalar_data.block_plain_allowed = false + } + if trailing_space { + emitter.scalar_data.block_allowed = false + } + if break_space { + emitter.scalar_data.flow_plain_allowed = false + emitter.scalar_data.block_plain_allowed = false + emitter.scalar_data.single_quoted_allowed = false + } + if space_break || tab_characters || special_characters { + emitter.scalar_data.flow_plain_allowed = false + emitter.scalar_data.block_plain_allowed = false + emitter.scalar_data.single_quoted_allowed = false + } + if space_break || special_characters { + emitter.scalar_data.block_allowed = false + } + if line_breaks { + emitter.scalar_data.flow_plain_allowed = false + emitter.scalar_data.block_plain_allowed = false + } + if flow_indicators { + emitter.scalar_data.flow_plain_allowed = false + } + if block_indicators { + emitter.scalar_data.block_plain_allowed = false + } + return true +} + +// Check if the event data is valid. +func yaml_emitter_analyze_event(emitter *yaml_emitter_t, event *yaml_event_t) bool { + + emitter.anchor_data.anchor = nil + emitter.tag_data.handle = nil + emitter.tag_data.suffix = nil + emitter.scalar_data.value = nil + + if len(event.head_comment) > 0 { + emitter.head_comment = event.head_comment + } + if len(event.line_comment) > 0 { + emitter.line_comment = event.line_comment + } + if len(event.foot_comment) > 0 { + emitter.foot_comment = event.foot_comment + } + if len(event.tail_comment) > 0 { + emitter.tail_comment = event.tail_comment + } + + switch event.typ { + case yaml_ALIAS_EVENT: + if !yaml_emitter_analyze_anchor(emitter, event.anchor, true) { + return false + } + + case yaml_SCALAR_EVENT: + if len(event.anchor) > 0 { + if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) { + return false + } + } + if len(event.tag) > 0 && (emitter.canonical || (!event.implicit && !event.quoted_implicit)) { + if !yaml_emitter_analyze_tag(emitter, event.tag) { + return false + } + } + if !yaml_emitter_analyze_scalar(emitter, event.value) { + return false + } + + case yaml_SEQUENCE_START_EVENT: + if len(event.anchor) > 0 { + if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) { + return false + } + } + if len(event.tag) > 0 && (emitter.canonical || !event.implicit) { + if !yaml_emitter_analyze_tag(emitter, event.tag) { + return false + } + } + + case yaml_MAPPING_START_EVENT: + if len(event.anchor) > 0 { + if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) { + return false + } + } + if len(event.tag) > 0 && (emitter.canonical || !event.implicit) { + if !yaml_emitter_analyze_tag(emitter, event.tag) { + return false + } + } + } + return true +} + +// Write the BOM character. +func yaml_emitter_write_bom(emitter *yaml_emitter_t) bool { + if !flush(emitter) { + return false + } + pos := emitter.buffer_pos + emitter.buffer[pos+0] = '\xEF' + emitter.buffer[pos+1] = '\xBB' + emitter.buffer[pos+2] = '\xBF' + emitter.buffer_pos += 3 + return true +} + +func yaml_emitter_write_indent(emitter *yaml_emitter_t) bool { + indent := emitter.indent + if indent < 0 { + indent = 0 + } + if !emitter.indention || emitter.column > indent || (emitter.column == indent && !emitter.whitespace) { + if !put_break(emitter) { + return false + } + } + if emitter.foot_indent == indent { + if !put_break(emitter) { + return false + } + } + for emitter.column < indent { + if !put(emitter, ' ') { + return false + } + } + emitter.whitespace = true + //emitter.indention = true + emitter.space_above = false + emitter.foot_indent = -1 + return true +} + +func yaml_emitter_write_indicator(emitter *yaml_emitter_t, indicator []byte, need_whitespace, is_whitespace, is_indention bool) bool { + if need_whitespace && !emitter.whitespace { + if !put(emitter, ' ') { + return false + } + } + if !write_all(emitter, indicator) { + return false + } + emitter.whitespace = is_whitespace + emitter.indention = (emitter.indention && is_indention) + emitter.open_ended = false + return true +} + +func yaml_emitter_write_anchor(emitter *yaml_emitter_t, value []byte) bool { + if !write_all(emitter, value) { + return false + } + emitter.whitespace = false + emitter.indention = false + return true +} + +func yaml_emitter_write_tag_handle(emitter *yaml_emitter_t, value []byte) bool { + if !emitter.whitespace { + if !put(emitter, ' ') { + return false + } + } + if !write_all(emitter, value) { + return false + } + emitter.whitespace = false + emitter.indention = false + return true +} + +func yaml_emitter_write_tag_content(emitter *yaml_emitter_t, value []byte, need_whitespace bool) bool { + if need_whitespace && !emitter.whitespace { + if !put(emitter, ' ') { + return false + } + } + for i := 0; i < len(value); { + var must_write bool + switch value[i] { + case ';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '_', '.', '~', '*', '\'', '(', ')', '[', ']': + must_write = true + default: + must_write = is_alpha(value, i) + } + if must_write { + if !write(emitter, value, &i) { + return false + } + } else { + w := width(value[i]) + for k := 0; k < w; k++ { + octet := value[i] + i++ + if !put(emitter, '%') { + return false + } + + c := octet >> 4 + if c < 10 { + c += '0' + } else { + c += 'A' - 10 + } + if !put(emitter, c) { + return false + } + + c = octet & 0x0f + if c < 10 { + c += '0' + } else { + c += 'A' - 10 + } + if !put(emitter, c) { + return false + } + } + } + } + emitter.whitespace = false + emitter.indention = false + return true +} + +func yaml_emitter_write_plain_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { + if len(value) > 0 && !emitter.whitespace { + if !put(emitter, ' ') { + return false + } + } + + spaces := false + breaks := false + for i := 0; i < len(value); { + if is_space(value, i) { + if allow_breaks && !spaces && emitter.column > emitter.best_width && !is_space(value, i+1) { + if !yaml_emitter_write_indent(emitter) { + return false + } + i += width(value[i]) + } else { + if !write(emitter, value, &i) { + return false + } + } + spaces = true + } else if is_break(value, i) { + if !breaks && value[i] == '\n' { + if !put_break(emitter) { + return false + } + } + if !write_break(emitter, value, &i) { + return false + } + //emitter.indention = true + breaks = true + } else { + if breaks { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !write(emitter, value, &i) { + return false + } + emitter.indention = false + spaces = false + breaks = false + } + } + + if len(value) > 0 { + emitter.whitespace = false + } + emitter.indention = false + if emitter.root_context { + emitter.open_ended = true + } + + return true +} + +func yaml_emitter_write_single_quoted_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { + + if !yaml_emitter_write_indicator(emitter, []byte{'\''}, true, false, false) { + return false + } + + spaces := false + breaks := false + for i := 0; i < len(value); { + if is_space(value, i) { + if allow_breaks && !spaces && emitter.column > emitter.best_width && i > 0 && i < len(value)-1 && !is_space(value, i+1) { + if !yaml_emitter_write_indent(emitter) { + return false + } + i += width(value[i]) + } else { + if !write(emitter, value, &i) { + return false + } + } + spaces = true + } else if is_break(value, i) { + if !breaks && value[i] == '\n' { + if !put_break(emitter) { + return false + } + } + if !write_break(emitter, value, &i) { + return false + } + //emitter.indention = true + breaks = true + } else { + if breaks { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if value[i] == '\'' { + if !put(emitter, '\'') { + return false + } + } + if !write(emitter, value, &i) { + return false + } + emitter.indention = false + spaces = false + breaks = false + } + } + if !yaml_emitter_write_indicator(emitter, []byte{'\''}, false, false, false) { + return false + } + emitter.whitespace = false + emitter.indention = false + return true +} + +func yaml_emitter_write_double_quoted_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { + spaces := false + if !yaml_emitter_write_indicator(emitter, []byte{'"'}, true, false, false) { + return false + } + + for i := 0; i < len(value); { + if !is_printable(value, i) || (!emitter.unicode && !is_ascii(value, i)) || + is_bom(value, i) || is_break(value, i) || + value[i] == '"' || value[i] == '\\' { + + octet := value[i] + + var w int + var v rune + switch { + case octet&0x80 == 0x00: + w, v = 1, rune(octet&0x7F) + case octet&0xE0 == 0xC0: + w, v = 2, rune(octet&0x1F) + case octet&0xF0 == 0xE0: + w, v = 3, rune(octet&0x0F) + case octet&0xF8 == 0xF0: + w, v = 4, rune(octet&0x07) + } + for k := 1; k < w; k++ { + octet = value[i+k] + v = (v << 6) + (rune(octet) & 0x3F) + } + i += w + + if !put(emitter, '\\') { + return false + } + + var ok bool + switch v { + case 0x00: + ok = put(emitter, '0') + case 0x07: + ok = put(emitter, 'a') + case 0x08: + ok = put(emitter, 'b') + case 0x09: + ok = put(emitter, 't') + case 0x0A: + ok = put(emitter, 'n') + case 0x0b: + ok = put(emitter, 'v') + case 0x0c: + ok = put(emitter, 'f') + case 0x0d: + ok = put(emitter, 'r') + case 0x1b: + ok = put(emitter, 'e') + case 0x22: + ok = put(emitter, '"') + case 0x5c: + ok = put(emitter, '\\') + case 0x85: + ok = put(emitter, 'N') + case 0xA0: + ok = put(emitter, '_') + case 0x2028: + ok = put(emitter, 'L') + case 0x2029: + ok = put(emitter, 'P') + default: + if v <= 0xFF { + ok = put(emitter, 'x') + w = 2 + } else if v <= 0xFFFF { + ok = put(emitter, 'u') + w = 4 + } else { + ok = put(emitter, 'U') + w = 8 + } + for k := (w - 1) * 4; ok && k >= 0; k -= 4 { + digit := byte((v >> uint(k)) & 0x0F) + if digit < 10 { + ok = put(emitter, digit+'0') + } else { + ok = put(emitter, digit+'A'-10) + } + } + } + if !ok { + return false + } + spaces = false + } else if is_space(value, i) { + if allow_breaks && !spaces && emitter.column > emitter.best_width && i > 0 && i < len(value)-1 { + if !yaml_emitter_write_indent(emitter) { + return false + } + if is_space(value, i+1) { + if !put(emitter, '\\') { + return false + } + } + i += width(value[i]) + } else if !write(emitter, value, &i) { + return false + } + spaces = true + } else { + if !write(emitter, value, &i) { + return false + } + spaces = false + } + } + if !yaml_emitter_write_indicator(emitter, []byte{'"'}, false, false, false) { + return false + } + emitter.whitespace = false + emitter.indention = false + return true +} + +func yaml_emitter_write_block_scalar_hints(emitter *yaml_emitter_t, value []byte) bool { + if is_space(value, 0) || is_break(value, 0) { + indent_hint := []byte{'0' + byte(emitter.best_indent)} + if !yaml_emitter_write_indicator(emitter, indent_hint, false, false, false) { + return false + } + } + + emitter.open_ended = false + + var chomp_hint [1]byte + if len(value) == 0 { + chomp_hint[0] = '-' + } else { + i := len(value) - 1 + for value[i]&0xC0 == 0x80 { + i-- + } + if !is_break(value, i) { + chomp_hint[0] = '-' + } else if i == 0 { + chomp_hint[0] = '+' + emitter.open_ended = true + } else { + i-- + for value[i]&0xC0 == 0x80 { + i-- + } + if is_break(value, i) { + chomp_hint[0] = '+' + emitter.open_ended = true + } + } + } + if chomp_hint[0] != 0 { + if !yaml_emitter_write_indicator(emitter, chomp_hint[:], false, false, false) { + return false + } + } + return true +} + +func yaml_emitter_write_literal_scalar(emitter *yaml_emitter_t, value []byte) bool { + if !yaml_emitter_write_indicator(emitter, []byte{'|'}, true, false, false) { + return false + } + if !yaml_emitter_write_block_scalar_hints(emitter, value) { + return false + } + if !yaml_emitter_process_line_comment(emitter) { + return false + } + //emitter.indention = true + emitter.whitespace = true + breaks := true + for i := 0; i < len(value); { + if is_break(value, i) { + if !write_break(emitter, value, &i) { + return false + } + //emitter.indention = true + breaks = true + } else { + if breaks { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !write(emitter, value, &i) { + return false + } + emitter.indention = false + breaks = false + } + } + + return true +} + +func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) bool { + if !yaml_emitter_write_indicator(emitter, []byte{'>'}, true, false, false) { + return false + } + if !yaml_emitter_write_block_scalar_hints(emitter, value) { + return false + } + if !yaml_emitter_process_line_comment(emitter) { + return false + } + + //emitter.indention = true + emitter.whitespace = true + + breaks := true + leading_spaces := true + for i := 0; i < len(value); { + if is_break(value, i) { + if !breaks && !leading_spaces && value[i] == '\n' { + k := 0 + for is_break(value, k) { + k += width(value[k]) + } + if !is_blankz(value, k) { + if !put_break(emitter) { + return false + } + } + } + if !write_break(emitter, value, &i) { + return false + } + //emitter.indention = true + breaks = true + } else { + if breaks { + if !yaml_emitter_write_indent(emitter) { + return false + } + leading_spaces = is_blank(value, i) + } + if !breaks && is_space(value, i) && !is_space(value, i+1) && emitter.column > emitter.best_width { + if !yaml_emitter_write_indent(emitter) { + return false + } + i += width(value[i]) + } else { + if !write(emitter, value, &i) { + return false + } + } + emitter.indention = false + breaks = false + } + } + return true +} + +func yaml_emitter_write_comment(emitter *yaml_emitter_t, comment []byte) bool { + breaks := false + pound := false + for i := 0; i < len(comment); { + if is_break(comment, i) { + if !write_break(emitter, comment, &i) { + return false + } + //emitter.indention = true + breaks = true + pound = false + } else { + if breaks && !yaml_emitter_write_indent(emitter) { + return false + } + if !pound { + if comment[i] != '#' && (!put(emitter, '#') || !put(emitter, ' ')) { + return false + } + pound = true + } + if !write(emitter, comment, &i) { + return false + } + emitter.indention = false + breaks = false + } + } + if !breaks && !put_break(emitter) { + return false + } + + emitter.whitespace = true + //emitter.indention = true + return true +} diff --git a/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/encode.go b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/encode.go new file mode 100644 index 0000000..de9e72a --- /dev/null +++ b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/encode.go @@ -0,0 +1,577 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package yaml + +import ( + "encoding" + "fmt" + "io" + "reflect" + "regexp" + "sort" + "strconv" + "strings" + "time" + "unicode/utf8" +) + +type encoder struct { + emitter yaml_emitter_t + event yaml_event_t + out []byte + flow bool + indent int + doneInit bool +} + +func newEncoder() *encoder { + e := &encoder{} + yaml_emitter_initialize(&e.emitter) + yaml_emitter_set_output_string(&e.emitter, &e.out) + yaml_emitter_set_unicode(&e.emitter, true) + return e +} + +func newEncoderWithWriter(w io.Writer) *encoder { + e := &encoder{} + yaml_emitter_initialize(&e.emitter) + yaml_emitter_set_output_writer(&e.emitter, w) + yaml_emitter_set_unicode(&e.emitter, true) + return e +} + +func (e *encoder) init() { + if e.doneInit { + return + } + if e.indent == 0 { + e.indent = 4 + } + e.emitter.best_indent = e.indent + yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING) + e.emit() + e.doneInit = true +} + +func (e *encoder) finish() { + e.emitter.open_ended = false + yaml_stream_end_event_initialize(&e.event) + e.emit() +} + +func (e *encoder) destroy() { + yaml_emitter_delete(&e.emitter) +} + +func (e *encoder) emit() { + // This will internally delete the e.event value. + e.must(yaml_emitter_emit(&e.emitter, &e.event)) +} + +func (e *encoder) must(ok bool) { + if !ok { + msg := e.emitter.problem + if msg == "" { + msg = "unknown problem generating YAML content" + } + failf("%s", msg) + } +} + +func (e *encoder) marshalDoc(tag string, in reflect.Value) { + e.init() + var node *Node + if in.IsValid() { + node, _ = in.Interface().(*Node) + } + if node != nil && node.Kind == DocumentNode { + e.nodev(in) + } else { + yaml_document_start_event_initialize(&e.event, nil, nil, true) + e.emit() + e.marshal(tag, in) + yaml_document_end_event_initialize(&e.event, true) + e.emit() + } +} + +func (e *encoder) marshal(tag string, in reflect.Value) { + tag = shortTag(tag) + if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() { + e.nilv() + return + } + iface := in.Interface() + switch value := iface.(type) { + case *Node: + e.nodev(in) + return + case Node: + if !in.CanAddr() { + var n = reflect.New(in.Type()).Elem() + n.Set(in) + in = n + } + e.nodev(in.Addr()) + return + case time.Time: + e.timev(tag, in) + return + case *time.Time: + e.timev(tag, in.Elem()) + return + case time.Duration: + e.stringv(tag, reflect.ValueOf(value.String())) + return + case Marshaler: + v, err := value.MarshalYAML() + if err != nil { + fail(err) + } + if v == nil { + e.nilv() + return + } + e.marshal(tag, reflect.ValueOf(v)) + return + case encoding.TextMarshaler: + text, err := value.MarshalText() + if err != nil { + fail(err) + } + in = reflect.ValueOf(string(text)) + case nil: + e.nilv() + return + } + switch in.Kind() { + case reflect.Interface: + e.marshal(tag, in.Elem()) + case reflect.Map: + e.mapv(tag, in) + case reflect.Ptr: + e.marshal(tag, in.Elem()) + case reflect.Struct: + e.structv(tag, in) + case reflect.Slice, reflect.Array: + e.slicev(tag, in) + case reflect.String: + e.stringv(tag, in) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + e.intv(tag, in) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + e.uintv(tag, in) + case reflect.Float32, reflect.Float64: + e.floatv(tag, in) + case reflect.Bool: + e.boolv(tag, in) + default: + panic("cannot marshal type: " + in.Type().String()) + } +} + +func (e *encoder) mapv(tag string, in reflect.Value) { + e.mappingv(tag, func() { + keys := keyList(in.MapKeys()) + sort.Sort(keys) + for _, k := range keys { + e.marshal("", k) + e.marshal("", in.MapIndex(k)) + } + }) +} + +func (e *encoder) fieldByIndex(v reflect.Value, index []int) (field reflect.Value) { + for _, num := range index { + for { + if v.Kind() == reflect.Ptr { + if v.IsNil() { + return reflect.Value{} + } + v = v.Elem() + continue + } + break + } + v = v.Field(num) + } + return v +} + +func (e *encoder) structv(tag string, in reflect.Value) { + sinfo, err := getStructInfo(in.Type()) + if err != nil { + panic(err) + } + e.mappingv(tag, func() { + for _, info := range sinfo.FieldsList { + var value reflect.Value + if info.Inline == nil { + value = in.Field(info.Num) + } else { + value = e.fieldByIndex(in, info.Inline) + if !value.IsValid() { + continue + } + } + if info.OmitEmpty && isZero(value) { + continue + } + e.marshal("", reflect.ValueOf(info.Key)) + e.flow = info.Flow + e.marshal("", value) + } + if sinfo.InlineMap >= 0 { + m := in.Field(sinfo.InlineMap) + if m.Len() > 0 { + e.flow = false + keys := keyList(m.MapKeys()) + sort.Sort(keys) + for _, k := range keys { + if _, found := sinfo.FieldsMap[k.String()]; found { + panic(fmt.Sprintf("cannot have key %q in inlined map: conflicts with struct field", k.String())) + } + e.marshal("", k) + e.flow = false + e.marshal("", m.MapIndex(k)) + } + } + } + }) +} + +func (e *encoder) mappingv(tag string, f func()) { + implicit := tag == "" + style := yaml_BLOCK_MAPPING_STYLE + if e.flow { + e.flow = false + style = yaml_FLOW_MAPPING_STYLE + } + yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style) + e.emit() + f() + yaml_mapping_end_event_initialize(&e.event) + e.emit() +} + +func (e *encoder) slicev(tag string, in reflect.Value) { + implicit := tag == "" + style := yaml_BLOCK_SEQUENCE_STYLE + if e.flow { + e.flow = false + style = yaml_FLOW_SEQUENCE_STYLE + } + e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) + e.emit() + n := in.Len() + for i := 0; i < n; i++ { + e.marshal("", in.Index(i)) + } + e.must(yaml_sequence_end_event_initialize(&e.event)) + e.emit() +} + +// isBase60 returns whether s is in base 60 notation as defined in YAML 1.1. +// +// The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported +// in YAML 1.2 and by this package, but these should be marshalled quoted for +// the time being for compatibility with other parsers. +func isBase60Float(s string) (result bool) { + // Fast path. + if s == "" { + return false + } + c := s[0] + if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 { + return false + } + // Do the full match. + return base60float.MatchString(s) +} + +// From http://yaml.org/type/float.html, except the regular expression there +// is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix. +var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`) + +// isOldBool returns whether s is bool notation as defined in YAML 1.1. +// +// We continue to force strings that YAML 1.1 would interpret as booleans to be +// rendered as quotes strings so that the marshalled output valid for YAML 1.1 +// parsing. +func isOldBool(s string) (result bool) { + switch s { + case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON", + "n", "N", "no", "No", "NO", "off", "Off", "OFF": + return true + default: + return false + } +} + +func (e *encoder) stringv(tag string, in reflect.Value) { + var style yaml_scalar_style_t + s := in.String() + canUsePlain := true + switch { + case !utf8.ValidString(s): + if tag == binaryTag { + failf("explicitly tagged !!binary data must be base64-encoded") + } + if tag != "" { + failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag)) + } + // It can't be encoded directly as YAML so use a binary tag + // and encode it as base64. + tag = binaryTag + s = encodeBase64(s) + case tag == "": + // Check to see if it would resolve to a specific + // tag when encoded unquoted. If it doesn't, + // there's no need to quote it. + rtag, _ := resolve("", s) + canUsePlain = rtag == strTag && !(isBase60Float(s) || isOldBool(s)) + } + // Note: it's possible for user code to emit invalid YAML + // if they explicitly specify a tag and a string containing + // text that's incompatible with that tag. + switch { + case strings.Contains(s, "\n"): + if e.flow { + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } else { + style = yaml_LITERAL_SCALAR_STYLE + } + case canUsePlain: + style = yaml_PLAIN_SCALAR_STYLE + default: + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + e.emitScalar(s, "", tag, style, nil, nil, nil, nil) +} + +func (e *encoder) boolv(tag string, in reflect.Value) { + var s string + if in.Bool() { + s = "true" + } else { + s = "false" + } + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) +} + +func (e *encoder) intv(tag string, in reflect.Value) { + s := strconv.FormatInt(in.Int(), 10) + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) +} + +func (e *encoder) uintv(tag string, in reflect.Value) { + s := strconv.FormatUint(in.Uint(), 10) + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) +} + +func (e *encoder) timev(tag string, in reflect.Value) { + t := in.Interface().(time.Time) + s := t.Format(time.RFC3339Nano) + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) +} + +func (e *encoder) floatv(tag string, in reflect.Value) { + // Issue #352: When formatting, use the precision of the underlying value + precision := 64 + if in.Kind() == reflect.Float32 { + precision = 32 + } + + s := strconv.FormatFloat(in.Float(), 'g', -1, precision) + switch s { + case "+Inf": + s = ".inf" + case "-Inf": + s = "-.inf" + case "NaN": + s = ".nan" + } + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) +} + +func (e *encoder) nilv() { + e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) +} + +func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t, head, line, foot, tail []byte) { + // TODO Kill this function. Replace all initialize calls by their underlining Go literals. + implicit := tag == "" + if !implicit { + tag = longTag(tag) + } + e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style)) + e.event.head_comment = head + e.event.line_comment = line + e.event.foot_comment = foot + e.event.tail_comment = tail + e.emit() +} + +func (e *encoder) nodev(in reflect.Value) { + e.node(in.Interface().(*Node), "") +} + +func (e *encoder) node(node *Node, tail string) { + // Zero nodes behave as nil. + if node.Kind == 0 && node.IsZero() { + e.nilv() + return + } + + // If the tag was not explicitly requested, and dropping it won't change the + // implicit tag of the value, don't include it in the presentation. + var tag = node.Tag + var stag = shortTag(tag) + var forceQuoting bool + if tag != "" && node.Style&TaggedStyle == 0 { + if node.Kind == ScalarNode { + if stag == strTag && node.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0 { + tag = "" + } else { + rtag, _ := resolve("", node.Value) + if rtag == stag { + tag = "" + } else if stag == strTag { + tag = "" + forceQuoting = true + } + } + } else { + var rtag string + switch node.Kind { + case MappingNode: + rtag = mapTag + case SequenceNode: + rtag = seqTag + } + if rtag == stag { + tag = "" + } + } + } + + switch node.Kind { + case DocumentNode: + yaml_document_start_event_initialize(&e.event, nil, nil, true) + e.event.head_comment = []byte(node.HeadComment) + e.emit() + for _, node := range node.Content { + e.node(node, "") + } + yaml_document_end_event_initialize(&e.event, true) + e.event.foot_comment = []byte(node.FootComment) + e.emit() + + case SequenceNode: + style := yaml_BLOCK_SEQUENCE_STYLE + if node.Style&FlowStyle != 0 { + style = yaml_FLOW_SEQUENCE_STYLE + } + e.must(yaml_sequence_start_event_initialize(&e.event, []byte(node.Anchor), []byte(longTag(tag)), tag == "", style)) + e.event.head_comment = []byte(node.HeadComment) + e.emit() + for _, node := range node.Content { + e.node(node, "") + } + e.must(yaml_sequence_end_event_initialize(&e.event)) + e.event.line_comment = []byte(node.LineComment) + e.event.foot_comment = []byte(node.FootComment) + e.emit() + + case MappingNode: + style := yaml_BLOCK_MAPPING_STYLE + if node.Style&FlowStyle != 0 { + style = yaml_FLOW_MAPPING_STYLE + } + yaml_mapping_start_event_initialize(&e.event, []byte(node.Anchor), []byte(longTag(tag)), tag == "", style) + e.event.tail_comment = []byte(tail) + e.event.head_comment = []byte(node.HeadComment) + e.emit() + + // The tail logic below moves the foot comment of prior keys to the following key, + // since the value for each key may be a nested structure and the foot needs to be + // processed only the entirety of the value is streamed. The last tail is processed + // with the mapping end event. + var tail string + for i := 0; i+1 < len(node.Content); i += 2 { + k := node.Content[i] + foot := k.FootComment + if foot != "" { + kopy := *k + kopy.FootComment = "" + k = &kopy + } + e.node(k, tail) + tail = foot + + v := node.Content[i+1] + e.node(v, "") + } + + yaml_mapping_end_event_initialize(&e.event) + e.event.tail_comment = []byte(tail) + e.event.line_comment = []byte(node.LineComment) + e.event.foot_comment = []byte(node.FootComment) + e.emit() + + case AliasNode: + yaml_alias_event_initialize(&e.event, []byte(node.Value)) + e.event.head_comment = []byte(node.HeadComment) + e.event.line_comment = []byte(node.LineComment) + e.event.foot_comment = []byte(node.FootComment) + e.emit() + + case ScalarNode: + value := node.Value + if !utf8.ValidString(value) { + if stag == binaryTag { + failf("explicitly tagged !!binary data must be base64-encoded") + } + if stag != "" { + failf("cannot marshal invalid UTF-8 data as %s", stag) + } + // It can't be encoded directly as YAML so use a binary tag + // and encode it as base64. + tag = binaryTag + value = encodeBase64(value) + } + + style := yaml_PLAIN_SCALAR_STYLE + switch { + case node.Style&DoubleQuotedStyle != 0: + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + case node.Style&SingleQuotedStyle != 0: + style = yaml_SINGLE_QUOTED_SCALAR_STYLE + case node.Style&LiteralStyle != 0: + style = yaml_LITERAL_SCALAR_STYLE + case node.Style&FoldedStyle != 0: + style = yaml_FOLDED_SCALAR_STYLE + case strings.Contains(value, "\n"): + style = yaml_LITERAL_SCALAR_STYLE + case forceQuoting: + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + + e.emitScalar(value, node.Anchor, tag, style, []byte(node.HeadComment), []byte(node.LineComment), []byte(node.FootComment), []byte(tail)) + default: + failf("cannot encode node with unknown kind %d", node.Kind) + } +} diff --git a/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/encode_test.go b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/encode_test.go new file mode 100644 index 0000000..4a8bf2e --- /dev/null +++ b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/encode_test.go @@ -0,0 +1,736 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package yaml_test + +import ( + "bytes" + "fmt" + "math" + "strconv" + "strings" + "time" + + "net" + "os" + + . "gopkg.in/check.v1" + "gopkg.in/yaml.v3" +) + +var marshalIntTest = 123 + +var marshalTests = []struct { + value interface{} + data string +}{ + { + nil, + "null\n", + }, { + (*marshalerType)(nil), + "null\n", + }, { + &struct{}{}, + "{}\n", + }, { + map[string]string{"v": "hi"}, + "v: hi\n", + }, { + map[string]interface{}{"v": "hi"}, + "v: hi\n", + }, { + map[string]string{"v": "true"}, + "v: \"true\"\n", + }, { + map[string]string{"v": "false"}, + "v: \"false\"\n", + }, { + map[string]interface{}{"v": true}, + "v: true\n", + }, { + map[string]interface{}{"v": false}, + "v: false\n", + }, { + map[string]interface{}{"v": 10}, + "v: 10\n", + }, { + map[string]interface{}{"v": -10}, + "v: -10\n", + }, { + map[string]uint{"v": 42}, + "v: 42\n", + }, { + map[string]interface{}{"v": int64(4294967296)}, + "v: 4294967296\n", + }, { + map[string]int64{"v": int64(4294967296)}, + "v: 4294967296\n", + }, { + map[string]uint64{"v": 4294967296}, + "v: 4294967296\n", + }, { + map[string]interface{}{"v": "10"}, + "v: \"10\"\n", + }, { + map[string]interface{}{"v": 0.1}, + "v: 0.1\n", + }, { + map[string]interface{}{"v": float64(0.1)}, + "v: 0.1\n", + }, { + map[string]interface{}{"v": float32(0.99)}, + "v: 0.99\n", + }, { + map[string]interface{}{"v": -0.1}, + "v: -0.1\n", + }, { + map[string]interface{}{"v": math.Inf(+1)}, + "v: .inf\n", + }, { + map[string]interface{}{"v": math.Inf(-1)}, + "v: -.inf\n", + }, { + map[string]interface{}{"v": math.NaN()}, + "v: .nan\n", + }, { + map[string]interface{}{"v": nil}, + "v: null\n", + }, { + map[string]interface{}{"v": ""}, + "v: \"\"\n", + }, { + map[string][]string{"v": []string{"A", "B"}}, + "v:\n - A\n - B\n", + }, { + map[string][]string{"v": []string{"A", "B\nC"}}, + "v:\n - A\n - |-\n B\n C\n", + }, { + map[string][]interface{}{"v": []interface{}{"A", 1, map[string][]int{"B": []int{2, 3}}}}, + "v:\n - A\n - 1\n - B:\n - 2\n - 3\n", + }, { + map[string]interface{}{"a": map[interface{}]interface{}{"b": "c"}}, + "a:\n b: c\n", + }, { + map[string]interface{}{"a": "-"}, + "a: '-'\n", + }, + + // Simple values. + { + &marshalIntTest, + "123\n", + }, + + // Structures + { + &struct{ Hello string }{"world"}, + "hello: world\n", + }, { + &struct { + A struct { + B string + } + }{struct{ B string }{"c"}}, + "a:\n b: c\n", + }, { + &struct { + A *struct { + B string + } + }{&struct{ B string }{"c"}}, + "a:\n b: c\n", + }, { + &struct { + A *struct { + B string + } + }{}, + "a: null\n", + }, { + &struct{ A int }{1}, + "a: 1\n", + }, { + &struct{ A []int }{[]int{1, 2}}, + "a:\n - 1\n - 2\n", + }, { + &struct{ A [2]int }{[2]int{1, 2}}, + "a:\n - 1\n - 2\n", + }, { + &struct { + B int "a" + }{1}, + "a: 1\n", + }, { + &struct{ A bool }{true}, + "a: true\n", + }, { + &struct{ A string }{"true"}, + "a: \"true\"\n", + }, { + &struct{ A string }{"off"}, + "a: \"off\"\n", + }, + + // Conditional flag + { + &struct { + A int "a,omitempty" + B int "b,omitempty" + }{1, 0}, + "a: 1\n", + }, { + &struct { + A int "a,omitempty" + B int "b,omitempty" + }{0, 0}, + "{}\n", + }, { + &struct { + A *struct{ X, y int } "a,omitempty,flow" + }{&struct{ X, y int }{1, 2}}, + "a: {x: 1}\n", + }, { + &struct { + A *struct{ X, y int } "a,omitempty,flow" + }{nil}, + "{}\n", + }, { + &struct { + A *struct{ X, y int } "a,omitempty,flow" + }{&struct{ X, y int }{}}, + "a: {x: 0}\n", + }, { + &struct { + A struct{ X, y int } "a,omitempty,flow" + }{struct{ X, y int }{1, 2}}, + "a: {x: 1}\n", + }, { + &struct { + A struct{ X, y int } "a,omitempty,flow" + }{struct{ X, y int }{0, 1}}, + "{}\n", + }, { + &struct { + A float64 "a,omitempty" + B float64 "b,omitempty" + }{1, 0}, + "a: 1\n", + }, + { + &struct { + T1 time.Time "t1,omitempty" + T2 time.Time "t2,omitempty" + T3 *time.Time "t3,omitempty" + T4 *time.Time "t4,omitempty" + }{ + T2: time.Date(2018, 1, 9, 10, 40, 47, 0, time.UTC), + T4: newTime(time.Date(2098, 1, 9, 10, 40, 47, 0, time.UTC)), + }, + "t2: 2018-01-09T10:40:47Z\nt4: 2098-01-09T10:40:47Z\n", + }, + // Nil interface that implements Marshaler. + { + map[string]yaml.Marshaler{ + "a": nil, + }, + "a: null\n", + }, + + // Flow flag + { + &struct { + A []int "a,flow" + }{[]int{1, 2}}, + "a: [1, 2]\n", + }, { + &struct { + A map[string]string "a,flow" + }{map[string]string{"b": "c", "d": "e"}}, + "a: {b: c, d: e}\n", + }, { + &struct { + A struct { + B, D string + } "a,flow" + }{struct{ B, D string }{"c", "e"}}, + "a: {b: c, d: e}\n", + }, { + &struct { + A string "a,flow" + }{"b\nc"}, + "a: \"b\\nc\"\n", + }, + + // Unexported field + { + &struct { + u int + A int + }{0, 1}, + "a: 1\n", + }, + + // Ignored field + { + &struct { + A int + B int "-" + }{1, 2}, + "a: 1\n", + }, + + // Struct inlining + { + &struct { + A int + C inlineB `yaml:",inline"` + }{1, inlineB{2, inlineC{3}}}, + "a: 1\nb: 2\nc: 3\n", + }, + // Struct inlining as a pointer + { + &struct { + A int + C *inlineB `yaml:",inline"` + }{1, &inlineB{2, inlineC{3}}}, + "a: 1\nb: 2\nc: 3\n", + }, { + &struct { + A int + C *inlineB `yaml:",inline"` + }{1, nil}, + "a: 1\n", + }, { + &struct { + A int + D *inlineD `yaml:",inline"` + }{1, &inlineD{&inlineC{3}, 4}}, + "a: 1\nc: 3\nd: 4\n", + }, + + // Map inlining + { + &struct { + A int + C map[string]int `yaml:",inline"` + }{1, map[string]int{"b": 2, "c": 3}}, + "a: 1\nb: 2\nc: 3\n", + }, + + // Duration + { + map[string]time.Duration{"a": 3 * time.Second}, + "a: 3s\n", + }, + + // Issue #24: bug in map merging logic. + { + map[string]string{"a": "<foo>"}, + "a: <foo>\n", + }, + + // Issue #34: marshal unsupported base 60 floats quoted for compatibility + // with old YAML 1.1 parsers. + { + map[string]string{"a": "1:1"}, + "a: \"1:1\"\n", + }, + + // Binary data. + { + map[string]string{"a": "\x00"}, + "a: \"\\0\"\n", + }, { + map[string]string{"a": "\x80\x81\x82"}, + "a: !!binary gIGC\n", + }, { + map[string]string{"a": strings.Repeat("\x90", 54)}, + "a: !!binary |\n " + strings.Repeat("kJCQ", 17) + "kJ\n CQ\n", + }, + + // Encode unicode as utf-8 rather than in escaped form. + { + map[string]string{"a": "ä½ å¥½"}, + "a: ä½ å¥½\n", + }, + + // Support encoding.TextMarshaler. + { + map[string]net.IP{"a": net.IPv4(1, 2, 3, 4)}, + "a: 1.2.3.4\n", + }, + // time.Time gets a timestamp tag. + { + map[string]time.Time{"a": time.Date(2015, 2, 24, 18, 19, 39, 0, time.UTC)}, + "a: 2015-02-24T18:19:39Z\n", + }, + { + map[string]*time.Time{"a": newTime(time.Date(2015, 2, 24, 18, 19, 39, 0, time.UTC))}, + "a: 2015-02-24T18:19:39Z\n", + }, + { + // This is confirmed to be properly decoded in Python (libyaml) without a timestamp tag. + map[string]time.Time{"a": time.Date(2015, 2, 24, 18, 19, 39, 123456789, time.FixedZone("FOO", -3*60*60))}, + "a: 2015-02-24T18:19:39.123456789-03:00\n", + }, + // Ensure timestamp-like strings are quoted. + { + map[string]string{"a": "2015-02-24T18:19:39Z"}, + "a: \"2015-02-24T18:19:39Z\"\n", + }, + + // Ensure strings containing ": " are quoted (reported as PR #43, but not reproducible). + { + map[string]string{"a": "b: c"}, + "a: 'b: c'\n", + }, + + // Containing hash mark ('#') in string should be quoted + { + map[string]string{"a": "Hello #comment"}, + "a: 'Hello #comment'\n", + }, + { + map[string]string{"a": "ä½ å¥½ #comment"}, + "a: 'ä½ å¥½ #comment'\n", + }, + + // Ensure MarshalYAML also gets called on the result of MarshalYAML itself. + { + &marshalerType{marshalerType{true}}, + "true\n", + }, { + &marshalerType{&marshalerType{true}}, + "true\n", + }, + + // Check indentation of maps inside sequences inside maps. + { + map[string]interface{}{"a": map[string]interface{}{"b": []map[string]int{{"c": 1, "d": 2}}}}, + "a:\n b:\n - c: 1\n d: 2\n", + }, + + // Strings with tabs were disallowed as literals (issue #471). + { + map[string]string{"a": "\tB\n\tC\n"}, + "a: |\n \tB\n \tC\n", + }, + + // Ensure that strings do not wrap + { + map[string]string{"a": "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 "}, + "a: 'abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 '\n", + }, + + // yaml.Node + { + &struct { + Value yaml.Node + }{ + yaml.Node{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "foo", + Style: yaml.SingleQuotedStyle, + }, + }, + "value: 'foo'\n", + }, { + yaml.Node{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "foo", + Style: yaml.SingleQuotedStyle, + }, + "'foo'\n", + }, + + // Enforced tagging with shorthand notation (issue #616). + { + &struct { + Value yaml.Node + }{ + yaml.Node{ + Kind: yaml.ScalarNode, + Style: yaml.TaggedStyle, + Value: "foo", + Tag: "!!str", + }, + }, + "value: !!str foo\n", + }, { + &struct { + Value yaml.Node + }{ + yaml.Node{ + Kind: yaml.MappingNode, + Style: yaml.TaggedStyle, + Tag: "!!map", + }, + }, + "value: !!map {}\n", + }, { + &struct { + Value yaml.Node + }{ + yaml.Node{ + Kind: yaml.SequenceNode, + Style: yaml.TaggedStyle, + Tag: "!!seq", + }, + }, + "value: !!seq []\n", + }, +} + +func (s *S) TestMarshal(c *C) { + defer os.Setenv("TZ", os.Getenv("TZ")) + os.Setenv("TZ", "UTC") + for i, item := range marshalTests { + c.Logf("test %d: %q", i, item.data) + data, err := yaml.Marshal(item.value) + c.Assert(err, IsNil) + c.Assert(string(data), Equals, item.data) + } +} + +func (s *S) TestEncoderSingleDocument(c *C) { + for i, item := range marshalTests { + c.Logf("test %d. %q", i, item.data) + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf) + err := enc.Encode(item.value) + c.Assert(err, Equals, nil) + err = enc.Close() + c.Assert(err, Equals, nil) + c.Assert(buf.String(), Equals, item.data) + } +} + +func (s *S) TestEncoderMultipleDocuments(c *C) { + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf) + err := enc.Encode(map[string]string{"a": "b"}) + c.Assert(err, Equals, nil) + err = enc.Encode(map[string]string{"c": "d"}) + c.Assert(err, Equals, nil) + err = enc.Close() + c.Assert(err, Equals, nil) + c.Assert(buf.String(), Equals, "a: b\n---\nc: d\n") +} + +func (s *S) TestEncoderWriteError(c *C) { + enc := yaml.NewEncoder(errorWriter{}) + err := enc.Encode(map[string]string{"a": "b"}) + c.Assert(err, ErrorMatches, `yaml: write error: some write error`) // Data not flushed yet +} + +type errorWriter struct{} + +func (errorWriter) Write([]byte) (int, error) { + return 0, fmt.Errorf("some write error") +} + +var marshalErrorTests = []struct { + value interface{} + error string + panic string +}{{ + value: &struct { + B int + inlineB ",inline" + }{1, inlineB{2, inlineC{3}}}, + panic: `duplicated key 'b' in struct struct \{ B int; .*`, +}, { + value: &struct { + A int + B map[string]int ",inline" + }{1, map[string]int{"a": 2}}, + panic: `cannot have key "a" in inlined map: conflicts with struct field`, +}} + +func (s *S) TestMarshalErrors(c *C) { + for _, item := range marshalErrorTests { + if item.panic != "" { + c.Assert(func() { yaml.Marshal(item.value) }, PanicMatches, item.panic) + } else { + _, err := yaml.Marshal(item.value) + c.Assert(err, ErrorMatches, item.error) + } + } +} + +func (s *S) TestMarshalTypeCache(c *C) { + var data []byte + var err error + func() { + type T struct{ A int } + data, err = yaml.Marshal(&T{}) + c.Assert(err, IsNil) + }() + func() { + type T struct{ B int } + data, err = yaml.Marshal(&T{}) + c.Assert(err, IsNil) + }() + c.Assert(string(data), Equals, "b: 0\n") +} + +var marshalerTests = []struct { + data string + value interface{} +}{ + {"_:\n hi: there\n", map[interface{}]interface{}{"hi": "there"}}, + {"_:\n - 1\n - A\n", []interface{}{1, "A"}}, + {"_: 10\n", 10}, + {"_: null\n", nil}, + {"_: BAR!\n", "BAR!"}, +} + +type marshalerType struct { + value interface{} +} + +func (o marshalerType) MarshalText() ([]byte, error) { + panic("MarshalText called on type with MarshalYAML") +} + +func (o marshalerType) MarshalYAML() (interface{}, error) { + return o.value, nil +} + +type marshalerValue struct { + Field marshalerType "_" +} + +func (s *S) TestMarshaler(c *C) { + for _, item := range marshalerTests { + obj := &marshalerValue{} + obj.Field.value = item.value + data, err := yaml.Marshal(obj) + c.Assert(err, IsNil) + c.Assert(string(data), Equals, string(item.data)) + } +} + +func (s *S) TestMarshalerWholeDocument(c *C) { + obj := &marshalerType{} + obj.value = map[string]string{"hello": "world!"} + data, err := yaml.Marshal(obj) + c.Assert(err, IsNil) + c.Assert(string(data), Equals, "hello: world!\n") +} + +type failingMarshaler struct{} + +func (ft *failingMarshaler) MarshalYAML() (interface{}, error) { + return nil, failingErr +} + +func (s *S) TestMarshalerError(c *C) { + _, err := yaml.Marshal(&failingMarshaler{}) + c.Assert(err, Equals, failingErr) +} + +func (s *S) TestSetIndent(c *C) { + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf) + enc.SetIndent(8) + err := enc.Encode(map[string]interface{}{"a": map[string]interface{}{"b": map[string]string{"c": "d"}}}) + c.Assert(err, Equals, nil) + err = enc.Close() + c.Assert(err, Equals, nil) + c.Assert(buf.String(), Equals, "a:\n b:\n c: d\n") +} + +func (s *S) TestSortedOutput(c *C) { + order := []interface{}{ + false, + true, + 1, + uint(1), + 1.0, + 1.1, + 1.2, + 2, + uint(2), + 2.0, + 2.1, + "", + ".1", + ".2", + ".a", + "1", + "2", + "a!10", + "a/0001", + "a/002", + "a/3", + "a/10", + "a/11", + "a/0012", + "a/100", + "a~10", + "ab/1", + "b/1", + "b/01", + "b/2", + "b/02", + "b/3", + "b/03", + "b1", + "b01", + "b3", + "c2.10", + "c10.2", + "d1", + "d7", + "d7abc", + "d12", + "d12a", + "e2b", + "e4b", + "e21a", + } + m := make(map[interface{}]int) + for _, k := range order { + m[k] = 1 + } + data, err := yaml.Marshal(m) + c.Assert(err, IsNil) + out := "\n" + string(data) + last := 0 + for i, k := range order { + repr := fmt.Sprint(k) + if s, ok := k.(string); ok { + if _, err = strconv.ParseFloat(repr, 32); s == "" || err == nil { + repr = `"` + repr + `"` + } + } + index := strings.Index(out, "\n"+repr+":") + if index == -1 { + c.Fatalf("%#v is not in the output: %#v", k, out) + } + if index < last { + c.Fatalf("%#v was generated before %#v: %q", k, order[i-1], out) + } + last = index + } +} + +func newTime(t time.Time) *time.Time { + return &t +} diff --git a/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/example_embedded_test.go b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/example_embedded_test.go new file mode 100644 index 0000000..9d17398 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/example_embedded_test.go @@ -0,0 +1,56 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package yaml_test + +import ( + "fmt" + "log" + + "gopkg.in/yaml.v3" +) + +// An example showing how to unmarshal embedded +// structs from YAML. + +type StructA struct { + A string `yaml:"a"` +} + +type StructB struct { + // Embedded structs are not treated as embedded in YAML by default. To do that, + // add the ",inline" annotation below + StructA `yaml:",inline"` + B string `yaml:"b"` +} + +var data = ` +a: a string from struct A +b: a string from struct B +` + +func ExampleUnmarshal_embedded() { + var b StructB + + err := yaml.Unmarshal([]byte(data), &b) + if err != nil { + log.Fatalf("cannot unmarshal data: %v", err) + } + fmt.Println(b.A) + fmt.Println(b.B) + // Output: + // a string from struct A + // a string from struct B +} diff --git a/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/go.mod b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/go.mod new file mode 100644 index 0000000..f407ea3 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/go.mod @@ -0,0 +1,5 @@ +module "gopkg.in/yaml.v3" + +require ( + "gopkg.in/check.v1" v0.0.0-20161208181325-20d25e280405 +) diff --git a/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/limit_test.go b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/limit_test.go new file mode 100644 index 0000000..07a3cbd --- /dev/null +++ b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/limit_test.go @@ -0,0 +1,128 @@ +package yaml_test + +import ( + "strings" + "testing" + + . "gopkg.in/check.v1" + "gopkg.in/yaml.v3" +) + +var limitTests = []struct { + name string + data []byte + error string +}{ + { + name: "1000kb of maps with 100 aliases", + data: []byte(`{a: &a [{a}` + strings.Repeat(`,{a}`, 1000*1024/4-100) + `], b: &b [*a` + strings.Repeat(`,*a`, 99) + `]}`), + error: "yaml: document contains excessive aliasing", + }, { + name: "1000kb of deeply nested slices", + data: []byte(strings.Repeat(`[`, 1000*1024)), + error: "yaml: exceeded max depth of 10000", + }, { + name: "1000kb of deeply nested maps", + data: []byte("x: " + strings.Repeat(`{`, 1000*1024)), + error: "yaml: exceeded max depth of 10000", + }, { + name: "1000kb of deeply nested indents", + data: []byte(strings.Repeat(`- `, 1000*1024)), + error: "yaml: exceeded max depth of 10000", + }, { + name: "1000kb of 1000-indent lines", + data: []byte(strings.Repeat(strings.Repeat(`- `, 1000)+"\n", 1024/2)), + }, + {name: "1kb of maps", data: []byte(`a: &a [{a}` + strings.Repeat(`,{a}`, 1*1024/4-1) + `]`)}, + {name: "10kb of maps", data: []byte(`a: &a [{a}` + strings.Repeat(`,{a}`, 10*1024/4-1) + `]`)}, + {name: "100kb of maps", data: []byte(`a: &a [{a}` + strings.Repeat(`,{a}`, 100*1024/4-1) + `]`)}, + {name: "1000kb of maps", data: []byte(`a: &a [{a}` + strings.Repeat(`,{a}`, 1000*1024/4-1) + `]`)}, + {name: "1000kb slice nested at max-depth", data: []byte(strings.Repeat(`[`, 10000) + `1` + strings.Repeat(`,1`, 1000*1024/2-20000-1) + strings.Repeat(`]`, 10000))}, + {name: "1000kb slice nested in maps at max-depth", data: []byte("{a,b:\n" + strings.Repeat(" {a,b:", 10000-2) + ` [1` + strings.Repeat(",1", 1000*1024/2-6*10000-1) + `]` + strings.Repeat(`}`, 10000-1))}, + {name: "1000kb of 10000-nested lines", data: []byte(strings.Repeat(`- `+strings.Repeat(`[`, 10000)+strings.Repeat(`]`, 10000)+"\n", 1000*1024/20000))}, +} + +func (s *S) TestLimits(c *C) { + if testing.Short() { + return + } + for _, tc := range limitTests { + var v interface{} + err := yaml.Unmarshal(tc.data, &v) + if len(tc.error) > 0 { + c.Assert(err, ErrorMatches, tc.error, Commentf("testcase: %s", tc.name)) + } else { + c.Assert(err, IsNil, Commentf("testcase: %s", tc.name)) + } + } +} + +func Benchmark1000KB100Aliases(b *testing.B) { + benchmark(b, "1000kb of maps with 100 aliases") +} +func Benchmark1000KBDeeplyNestedSlices(b *testing.B) { + benchmark(b, "1000kb of deeply nested slices") +} +func Benchmark1000KBDeeplyNestedMaps(b *testing.B) { + benchmark(b, "1000kb of deeply nested maps") +} +func Benchmark1000KBDeeplyNestedIndents(b *testing.B) { + benchmark(b, "1000kb of deeply nested indents") +} +func Benchmark1000KB1000IndentLines(b *testing.B) { + benchmark(b, "1000kb of 1000-indent lines") +} +func Benchmark1KBMaps(b *testing.B) { + benchmark(b, "1kb of maps") +} +func Benchmark10KBMaps(b *testing.B) { + benchmark(b, "10kb of maps") +} +func Benchmark100KBMaps(b *testing.B) { + benchmark(b, "100kb of maps") +} +func Benchmark1000KBMaps(b *testing.B) { + benchmark(b, "1000kb of maps") +} + +func BenchmarkDeepSlice(b *testing.B) { + benchmark(b, "1000kb slice nested at max-depth") +} + +func BenchmarkDeepFlow(b *testing.B) { + benchmark(b, "1000kb slice nested in maps at max-depth") +} + +func Benchmark1000KBMaxDepthNested(b *testing.B) { + benchmark(b, "1000kb of 10000-nested lines") +} + +func benchmark(b *testing.B, name string) { + for _, t := range limitTests { + if t.name != name { + continue + } + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + var v interface{} + err := yaml.Unmarshal(t.data, &v) + if len(t.error) > 0 { + if err == nil { + b.Errorf("expected error, got none") + } else if err.Error() != t.error { + b.Errorf("expected error '%s', got '%s'", t.error, err.Error()) + } + } else { + if err != nil { + b.Errorf("unexpected error: %v", err) + } + } + } + + return + } + + b.Errorf("testcase %q not found", name) +} diff --git a/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/node_test.go b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/node_test.go new file mode 100644 index 0000000..b7d0c9a --- /dev/null +++ b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/node_test.go @@ -0,0 +1,2886 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package yaml_test + +import ( + "bytes" + "fmt" + "os" + + . "gopkg.in/check.v1" + "gopkg.in/yaml.v3" + "io" + "strings" +) + +var nodeTests = []struct { + yaml string + node yaml.Node +}{ + { + "null\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: "null", + Tag: "!!null", + Line: 1, + Column: 1, + }}, + }, + }, { + "[encode]null\n", + yaml.Node{}, + }, { + "foo\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: "foo", + Tag: "!!str", + Line: 1, + Column: 1, + }}, + }, + }, { + "\"foo\"\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Style: yaml.DoubleQuotedStyle, + Value: "foo", + Tag: "!!str", + Line: 1, + Column: 1, + }}, + }, + }, { + "'foo'\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Style: yaml.SingleQuotedStyle, + Value: "foo", + Tag: "!!str", + Line: 1, + Column: 1, + }}, + }, + }, { + "!!str 123\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Style: yaml.TaggedStyle, + Value: "123", + Tag: "!!str", + Line: 1, + Column: 1, + }}, + }, + }, { + // Although the node isn't TaggedStyle, dropping the tag would change the value. + "[encode]!!binary gIGC\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: "gIGC", + Tag: "!!binary", + Line: 1, + Column: 1, + }}, + }, + }, { + // Item doesn't have a tag, but needs to be binary encoded due to its content. + "[encode]!!binary gIGC\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: "\x80\x81\x82", + Line: 1, + Column: 1, + }}, + }, + }, { + // Same, but with strings we can just quote them. + "[encode]\"123\"\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: "123", + Tag: "!!str", + Line: 1, + Column: 1, + }}, + }, + }, { + "!tag:something 123\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Style: yaml.TaggedStyle, + Value: "123", + Tag: "!tag:something", + Line: 1, + Column: 1, + }}, + }, + }, { + "[encode]!tag:something 123\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: "123", + Tag: "!tag:something", + Line: 1, + Column: 1, + }}, + }, + }, { + "!tag:something {}\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Style: yaml.TaggedStyle | yaml.FlowStyle, + Tag: "!tag:something", + Line: 1, + Column: 1, + }}, + }, + }, { + "[encode]!tag:something {}\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Style: yaml.FlowStyle, + Tag: "!tag:something", + Line: 1, + Column: 1, + }}, + }, + }, { + "!tag:something []\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.SequenceNode, + Style: yaml.TaggedStyle | yaml.FlowStyle, + Tag: "!tag:something", + Line: 1, + Column: 1, + }}, + }, + }, { + "[encode]!tag:something []\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.SequenceNode, + Style: yaml.FlowStyle, + Tag: "!tag:something", + Line: 1, + Column: 1, + }}, + }, + }, { + "''\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Style: yaml.SingleQuotedStyle, + Value: "", + Tag: "!!str", + Line: 1, + Column: 1, + }}, + }, + }, { + "|\n foo\n bar\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Style: yaml.LiteralStyle, + Value: "foo\nbar\n", + Tag: "!!str", + Line: 1, + Column: 1, + }}, + }, + }, { + "true\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: "true", + Tag: "!!bool", + Line: 1, + Column: 1, + }}, + }, + }, { + "-10\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: "-10", + Tag: "!!int", + Line: 1, + Column: 1, + }}, + }, + }, { + "4294967296\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: "4294967296", + Tag: "!!int", + Line: 1, + Column: 1, + }}, + }, + }, { + "0.1000\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: "0.1000", + Tag: "!!float", + Line: 1, + Column: 1, + }}, + }, + }, { + "-.inf\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: "-.inf", + Tag: "!!float", + Line: 1, + Column: 1, + }}, + }, + }, { + ".nan\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: ".nan", + Tag: "!!float", + Line: 1, + Column: 1, + }}, + }, + }, { + "{}\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Style: yaml.FlowStyle, + Value: "", + Tag: "!!map", + Line: 1, + Column: 1, + }}, + }, + }, { + "a: b c\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Value: "", + Tag: "!!map", + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: "a", + Tag: "!!str", + Line: 1, + Column: 1, + }, { + Kind: yaml.ScalarNode, + Value: "b c", + Tag: "!!str", + Line: 1, + Column: 4, + }}, + }}, + }, + }, { + "a:\n b: c\n d: e\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: "a", + Tag: "!!str", + Line: 1, + Column: 1, + }, { + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 2, + Column: 3, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: "b", + Tag: "!!str", + Line: 2, + Column: 3, + }, { + Kind: yaml.ScalarNode, + Value: "c", + Tag: "!!str", + Line: 2, + Column: 6, + }, { + Kind: yaml.ScalarNode, + Value: "d", + Tag: "!!str", + Line: 3, + Column: 3, + }, { + Kind: yaml.ScalarNode, + Value: "e", + Tag: "!!str", + Line: 3, + Column: 6, + }}, + }}, + }}, + }, + }, { + "a:\n - b: c\n d: e\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: "a", + Tag: "!!str", + Line: 1, + Column: 1, + }, { + Kind: yaml.SequenceNode, + Tag: "!!seq", + Line: 2, + Column: 3, + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 2, + Column: 5, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: "b", + Tag: "!!str", + Line: 2, + Column: 5, + }, { + Kind: yaml.ScalarNode, + Value: "c", + Tag: "!!str", + Line: 2, + Column: 8, + }, { + Kind: yaml.ScalarNode, + Value: "d", + Tag: "!!str", + Line: 3, + Column: 5, + }, { + Kind: yaml.ScalarNode, + Value: "e", + Tag: "!!str", + Line: 3, + Column: 8, + }}, + }}, + }}, + }}, + }, + }, { + "a: # AI\n - b\nc:\n - d\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "a", + LineComment: "# AI", + Line: 1, + Column: 1, + }, { + Kind: yaml.SequenceNode, + Tag: "!!seq", + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "b", + Line: 2, + Column: 5, + }}, + Line: 2, + Column: 3, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "c", + Line: 3, + Column: 1, + }, { + Kind: yaml.SequenceNode, + Tag: "!!seq", + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "d", + Line: 4, + Column: 5, + }}, + Line: 4, + Column: 3, + }}, + }}, + }, + }, { + "[decode]a:\n # HM\n - # HB1\n # HB2\n b: # IB\n c # IC\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Style: 0x0, + Tag: "!!str", + Value: "a", + Line: 1, + Column: 1, + }, { + Kind: yaml.SequenceNode, + Tag: "!!seq", + Line: 3, + Column: 3, + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Tag: "!!map", + HeadComment: "# HM", + Line: 5, + Column: 5, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "b", + HeadComment: "# HB1\n# HB2", + LineComment: "# IB", + Line: 5, + Column: 5, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "c", + LineComment: "# IC", + Line: 6, + Column: 7, + }}, + }}, + }}, + }}, + }, + }, { + // When encoding the value above, it loses b's inline comment. + "[encode]a:\n # HM\n - # HB1\n # HB2\n b: c # IC\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Style: 0x0, + Tag: "!!str", + Value: "a", + Line: 1, + Column: 1, + }, { + Kind: yaml.SequenceNode, + Tag: "!!seq", + Line: 3, + Column: 3, + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Tag: "!!map", + HeadComment: "# HM", + Line: 5, + Column: 5, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "b", + HeadComment: "# HB1\n# HB2", + LineComment: "# IB", + Line: 5, + Column: 5, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "c", + LineComment: "# IC", + Line: 6, + Column: 7, + }}, + }}, + }}, + }}, + }, + }, { + // Multiple cases of comment inlining next to mapping keys. + "a: | # IA\n str\nb: >- # IB\n str\nc: # IC\n - str\nd: # ID\n str:\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "a", + Line: 1, + Column: 1, + }, { + Kind: yaml.ScalarNode, + Style: yaml.LiteralStyle, + Tag: "!!str", + Value: "str\n", + LineComment: "# IA", + Line: 1, + Column: 4, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "b", + Line: 3, + Column: 1, + }, { + Kind: yaml.ScalarNode, + Style: yaml.FoldedStyle, + Tag: "!!str", + Value: "str", + LineComment: "# IB", + Line: 3, + Column: 4, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "c", + LineComment: "# IC", + Line: 5, + Column: 1, + }, { + Kind: yaml.SequenceNode, + Tag: "!!seq", + Line: 6, + Column: 3, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "str", + Line: 6, + Column: 5, + }}, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "d", + LineComment: "# ID", + Line: 7, + Column: 1, + }, { + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 8, + Column: 3, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "str", + Line: 8, + Column: 3, + }, { + Kind: yaml.ScalarNode, + Tag: "!!null", + Line: 8, + Column: 7, + }}, + }}, + }}, + }, + }, { + // Indentless sequence. + "[decode]a:\n# HM\n- # HB1\n # HB2\n b: # IB\n c # IC\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "a", + Line: 1, + Column: 1, + }, { + Kind: yaml.SequenceNode, + Tag: "!!seq", + Line: 3, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Tag: "!!map", + HeadComment: "# HM", + Line: 5, + Column: 3, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "b", + HeadComment: "# HB1\n# HB2", + LineComment: "# IB", + Line: 5, + Column: 3, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "c", + LineComment: "# IC", + Line: 6, + Column: 5, + }}, + }}, + }}, + }}, + }, + }, { + "- a\n- b\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.SequenceNode, + Value: "", + Tag: "!!seq", + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: "a", + Tag: "!!str", + Line: 1, + Column: 3, + }, { + Kind: yaml.ScalarNode, + Value: "b", + Tag: "!!str", + Line: 2, + Column: 3, + }}, + }}, + }, + }, { + "- a\n- - b\n - c\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.SequenceNode, + Tag: "!!seq", + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: "a", + Tag: "!!str", + Line: 1, + Column: 3, + }, { + Kind: yaml.SequenceNode, + Tag: "!!seq", + Line: 2, + Column: 3, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: "b", + Tag: "!!str", + Line: 2, + Column: 5, + }, { + Kind: yaml.ScalarNode, + Value: "c", + Tag: "!!str", + Line: 3, + Column: 5, + }}, + }}, + }}, + }, + }, { + "[a, b]\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.SequenceNode, + Style: yaml.FlowStyle, + Value: "", + Tag: "!!seq", + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: "a", + Tag: "!!str", + Line: 1, + Column: 2, + }, { + Kind: yaml.ScalarNode, + Value: "b", + Tag: "!!str", + Line: 1, + Column: 5, + }}, + }}, + }, + }, { + "- a\n- [b, c]\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.SequenceNode, + Tag: "!!seq", + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: "a", + Tag: "!!str", + Line: 1, + Column: 3, + }, { + Kind: yaml.SequenceNode, + Tag: "!!seq", + Style: yaml.FlowStyle, + Line: 2, + Column: 3, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: "b", + Tag: "!!str", + Line: 2, + Column: 4, + }, { + Kind: yaml.ScalarNode, + Value: "c", + Tag: "!!str", + Line: 2, + Column: 7, + }}, + }}, + }}, + }, + }, { + "a: &x 1\nb: &y 2\nc: *x\nd: *y\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Line: 1, + Column: 1, + Tag: "!!map", + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: "a", + Tag: "!!str", + Line: 1, + Column: 1, + }, + saveNode("x", &yaml.Node{ + Kind: yaml.ScalarNode, + Value: "1", + Tag: "!!int", + Anchor: "x", + Line: 1, + Column: 4, + }), + { + Kind: yaml.ScalarNode, + Value: "b", + Tag: "!!str", + Line: 2, + Column: 1, + }, + saveNode("y", &yaml.Node{ + Kind: yaml.ScalarNode, + Value: "2", + Tag: "!!int", + Anchor: "y", + Line: 2, + Column: 4, + }), + { + Kind: yaml.ScalarNode, + Value: "c", + Tag: "!!str", + Line: 3, + Column: 1, + }, { + Kind: yaml.AliasNode, + Value: "x", + Alias: dropNode("x"), + Line: 3, + Column: 4, + }, { + Kind: yaml.ScalarNode, + Value: "d", + Tag: "!!str", + Line: 4, + Column: 1, + }, { + Kind: yaml.AliasNode, + Value: "y", + Tag: "", + Alias: dropNode("y"), + Line: 4, + Column: 4, + }}, + }}, + }, + }, { + + "# One\n# Two\ntrue # Three\n# Four\n# Five\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 3, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: "true", + Tag: "!!bool", + Line: 3, + Column: 1, + HeadComment: "# One\n# Two", + LineComment: "# Three", + FootComment: "# Four\n# Five", + }}, + }, + }, { + + "# Å¡\ntrue # Å¡\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 2, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: "true", + Tag: "!!bool", + Line: 2, + Column: 1, + HeadComment: "# Å¡", + LineComment: "# Å¡", + }}, + }, + }, { + + "[decode]\n# One\n\n# Two\n\n# Three\ntrue # Four\n# Five\n\n# Six\n\n# Seven\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 7, + Column: 1, + HeadComment: "# One\n\n# Two", + FootComment: "# Six\n\n# Seven", + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: "true", + Tag: "!!bool", + Line: 7, + Column: 1, + HeadComment: "# Three", + LineComment: "# Four", + FootComment: "# Five", + }}, + }, + }, { + // Write out the pound character if missing from comments. + "[encode]# One\n# Two\ntrue # Three\n# Four\n# Five\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 3, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: "true", + Tag: "!!bool", + Line: 3, + Column: 1, + HeadComment: "One\nTwo\n", + LineComment: "Three\n", + FootComment: "Four\nFive\n", + }}, + }, + }, { + "[encode]# One\n# Two\ntrue # Three\n# Four\n# Five\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 3, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: "true", + Tag: "!!bool", + Line: 3, + Column: 1, + HeadComment: " One\n Two", + LineComment: " Three", + FootComment: " Four\n Five", + }}, + }, + }, { + "# DH1\n\n# DH2\n\n# H1\n# H2\ntrue # I\n# F1\n# F2\n\n# DF1\n\n# DF2\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 7, + Column: 1, + HeadComment: "# DH1\n\n# DH2", + FootComment: "# DF1\n\n# DF2", + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: "true", + Tag: "!!bool", + Line: 7, + Column: 1, + HeadComment: "# H1\n# H2", + LineComment: "# I", + FootComment: "# F1\n# F2", + }}, + }, + }, { + "# DH1\n\n# DH2\n\n# HA1\n# HA2\nka: va # IA\n# FA1\n# FA2\n\n# HB1\n# HB2\nkb: vb # IB\n# FB1\n# FB2\n\n# DF1\n\n# DF2\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 7, + Column: 1, + HeadComment: "# DH1\n\n# DH2", + FootComment: "# DF1\n\n# DF2", + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 7, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Line: 7, + Column: 1, + Tag: "!!str", + Value: "ka", + HeadComment: "# HA1\n# HA2", + FootComment: "# FA1\n# FA2", + }, { + Kind: yaml.ScalarNode, + Line: 7, + Column: 5, + Tag: "!!str", + Value: "va", + LineComment: "# IA", + }, { + Kind: yaml.ScalarNode, + Line: 13, + Column: 1, + Tag: "!!str", + Value: "kb", + HeadComment: "# HB1\n# HB2", + FootComment: "# FB1\n# FB2", + }, { + Kind: yaml.ScalarNode, + Line: 13, + Column: 5, + Tag: "!!str", + Value: "vb", + LineComment: "# IB", + }}, + }}, + }, + }, { + "# DH1\n\n# DH2\n\n# HA1\n# HA2\n- la # IA\n# FA1\n# FA2\n\n# HB1\n# HB2\n- lb # IB\n# FB1\n# FB2\n\n# DF1\n\n# DF2\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 7, + Column: 1, + HeadComment: "# DH1\n\n# DH2", + FootComment: "# DF1\n\n# DF2", + Content: []*yaml.Node{{ + Kind: yaml.SequenceNode, + Tag: "!!seq", + Line: 7, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 7, + Column: 3, + Value: "la", + HeadComment: "# HA1\n# HA2", + LineComment: "# IA", + FootComment: "# FA1\n# FA2", + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 13, + Column: 3, + Value: "lb", + HeadComment: "# HB1\n# HB2", + LineComment: "# IB", + FootComment: "# FB1\n# FB2", + }}, + }}, + }, + }, { + "# DH1\n\n- la # IA\n# HB1\n- lb\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 3, + Column: 1, + HeadComment: "# DH1", + Content: []*yaml.Node{{ + Kind: yaml.SequenceNode, + Tag: "!!seq", + Line: 3, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 3, + Column: 3, + Value: "la", + LineComment: "# IA", + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 5, + Column: 3, + Value: "lb", + HeadComment: "# HB1", + }}, + }}, + }, + }, { + "- la # IA\n- lb # IB\n- lc # IC\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.SequenceNode, + Tag: "!!seq", + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 1, + Column: 3, + Value: "la", + LineComment: "# IA", + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 2, + Column: 3, + Value: "lb", + LineComment: "# IB", + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 3, + Column: 3, + Value: "lc", + LineComment: "# IC", + }}, + }}, + }, + }, { + "# DH1\n\n# HL1\n- - la\n # HB1\n - lb\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 4, + Column: 1, + HeadComment: "# DH1", + Content: []*yaml.Node{{ + Kind: yaml.SequenceNode, + Tag: "!!seq", + Line: 4, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.SequenceNode, + Tag: "!!seq", + Line: 4, + Column: 3, + HeadComment: "# HL1", + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 4, + Column: 5, + Value: "la", + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 6, + Column: 5, + Value: "lb", + HeadComment: "# HB1", + }}, + }}, + }}, + }, + }, { + "# DH1\n\n# HL1\n- # HA1\n - la\n # HB1\n - lb\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 4, + Column: 1, + HeadComment: "# DH1", + Content: []*yaml.Node{{ + Kind: yaml.SequenceNode, + Tag: "!!seq", + Line: 4, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.SequenceNode, + Tag: "!!seq", + Line: 5, + Column: 3, + HeadComment: "# HL1", + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 5, + Column: 5, + Value: "la", + HeadComment: "# HA1", + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 7, + Column: 5, + Value: "lb", + HeadComment: "# HB1", + }}, + }}, + }}, + }, + }, { + "[decode]# DH1\n\n# HL1\n- # HA1\n\n - la\n # HB1\n - lb\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 4, + Column: 1, + HeadComment: "# DH1", + Content: []*yaml.Node{{ + Kind: yaml.SequenceNode, + Tag: "!!seq", + Line: 4, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.SequenceNode, + Tag: "!!seq", + Line: 6, + Column: 3, + HeadComment: "# HL1", + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 6, + Column: 5, + Value: "la", + HeadComment: "# HA1\n", + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 8, + Column: 5, + Value: "lb", + HeadComment: "# HB1", + }}, + }}, + }}, + }, + }, { + "# DH1\n\n# HA1\nka:\n # HB1\n kb:\n # HC1\n # HC2\n - lc # IC\n # FC1\n # FC2\n\n # HD1\n - ld # ID\n # FD1\n\n# DF1\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 4, + Column: 1, + HeadComment: "# DH1", + FootComment: "# DF1", + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 4, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 4, + Column: 1, + Value: "ka", + HeadComment: "# HA1", + }, { + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 6, + Column: 3, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 6, + Column: 3, + Value: "kb", + HeadComment: "# HB1", + }, { + Kind: yaml.SequenceNode, + Line: 9, + Column: 5, + Tag: "!!seq", + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 9, + Column: 7, + Value: "lc", + HeadComment: "# HC1\n# HC2", + LineComment: "# IC", + FootComment: "# FC1\n# FC2", + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 14, + Column: 7, + Value: "ld", + HeadComment: "# HD1", + + LineComment: "# ID", + FootComment: "# FD1", + }}, + }}, + }}, + }}, + }, + }, { + "# DH1\n\n# HA1\nka:\n # HB1\n kb:\n # HC1\n # HC2\n - lc # IC\n # FC1\n # FC2\n\n # HD1\n - ld # ID\n # FD1\nke: ve\n\n# DF1\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 4, + Column: 1, + HeadComment: "# DH1", + FootComment: "# DF1", + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 4, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 4, + Column: 1, + Value: "ka", + HeadComment: "# HA1", + }, { + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 6, + Column: 3, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 6, + Column: 3, + Value: "kb", + HeadComment: "# HB1", + }, { + Kind: yaml.SequenceNode, + Line: 9, + Column: 5, + Tag: "!!seq", + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 9, + Column: 7, + Value: "lc", + HeadComment: "# HC1\n# HC2", + LineComment: "# IC", + FootComment: "# FC1\n# FC2", + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 14, + Column: 7, + Value: "ld", + HeadComment: "# HD1", + LineComment: "# ID", + FootComment: "# FD1", + }}, + }}, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 16, + Column: 1, + Value: "ke", + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 16, + Column: 5, + Value: "ve", + }}, + }}, + }, + }, { + "# DH1\n\n# DH2\n\n# HA1\n# HA2\nka:\n # HB1\n # HB2\n kb:\n" + + " # HC1\n # HC2\n kc:\n # HD1\n # HD2\n kd: vd\n # FD1\n # FD2\n" + + " # FC1\n # FC2\n # FB1\n # FB2\n# FA1\n# FA2\n\n# HE1\n# HE2\nke: ve\n# FE1\n# FE2\n\n# DF1\n\n# DF2\n", + yaml.Node{ + Kind: yaml.DocumentNode, + HeadComment: "# DH1\n\n# DH2", + FootComment: "# DF1\n\n# DF2", + Line: 7, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 7, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "ka", + HeadComment: "# HA1\n# HA2", + FootComment: "# FA1\n# FA2", + Line: 7, + Column: 1, + }, { + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 10, + Column: 3, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "kb", + HeadComment: "# HB1\n# HB2", + FootComment: "# FB1\n# FB2", + Line: 10, + Column: 3, + }, { + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 13, + Column: 5, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "kc", + HeadComment: "# HC1\n# HC2", + FootComment: "# FC1\n# FC2", + Line: 13, + Column: 5, + }, { + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 16, + Column: 7, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "kd", + HeadComment: "# HD1\n# HD2", + FootComment: "# FD1\n# FD2", + Line: 16, + Column: 7, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "vd", + Line: 16, + Column: 11, + }}, + }}, + }}, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "ke", + HeadComment: "# HE1\n# HE2", + FootComment: "# FE1\n# FE2", + Line: 28, + Column: 1, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "ve", + Line: 28, + Column: 5, + }}, + }}, + }, + }, { + // Same as above but indenting ke in so it's also part of ka's value. + "# DH1\n\n# DH2\n\n# HA1\n# HA2\nka:\n # HB1\n # HB2\n kb:\n" + + " # HC1\n # HC2\n kc:\n # HD1\n # HD2\n kd: vd\n # FD1\n # FD2\n" + + " # FC1\n # FC2\n # FB1\n # FB2\n\n # HE1\n # HE2\n ke: ve\n # FE1\n # FE2\n# FA1\n# FA2\n\n# DF1\n\n# DF2\n", + yaml.Node{ + Kind: yaml.DocumentNode, + HeadComment: "# DH1\n\n# DH2", + FootComment: "# DF1\n\n# DF2", + Line: 7, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 7, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "ka", + HeadComment: "# HA1\n# HA2", + FootComment: "# FA1\n# FA2", + Line: 7, + Column: 1, + }, { + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 10, + Column: 3, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "kb", + HeadComment: "# HB1\n# HB2", + FootComment: "# FB1\n# FB2", + Line: 10, + Column: 3, + }, { + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 13, + Column: 5, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "kc", + HeadComment: "# HC1\n# HC2", + FootComment: "# FC1\n# FC2", + Line: 13, + Column: 5, + }, { + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 16, + Column: 7, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "kd", + HeadComment: "# HD1\n# HD2", + FootComment: "# FD1\n# FD2", + Line: 16, + Column: 7, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "vd", + Line: 16, + Column: 11, + }}, + }}, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "ke", + HeadComment: "# HE1\n# HE2", + FootComment: "# FE1\n# FE2", + Line: 26, + Column: 3, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "ve", + Line: 26, + Column: 7, + }}, + }}, + }}, + }, + }, { + // Decode only due to lack of newline at the end. + "[decode]# HA1\nka:\n # HB1\n kb: vb\n # FB1\n# FA1", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 2, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 2, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "ka", + HeadComment: "# HA1", + FootComment: "# FA1", + Line: 2, + Column: 1, + }, { + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 4, + Column: 3, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "kb", + HeadComment: "# HB1", + FootComment: "# FB1", + Line: 4, + Column: 3, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "vb", + Line: 4, + Column: 7, + }}, + }}, + }}, + }, + }, { + // Same as above, but with newline at the end. + "# HA1\nka:\n # HB1\n kb: vb\n # FB1\n# FA1\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 2, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 2, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "ka", + HeadComment: "# HA1", + FootComment: "# FA1", + Line: 2, + Column: 1, + }, { + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 4, + Column: 3, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "kb", + HeadComment: "# HB1", + FootComment: "# FB1", + Line: 4, + Column: 3, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "vb", + Line: 4, + Column: 7, + }}, + }}, + }}, + }, + }, { + // Same as above, but without FB1. + "# HA1\nka:\n # HB1\n kb: vb\n# FA1\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 2, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 2, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "ka", + HeadComment: "# HA1", + FootComment: "# FA1", + Line: 2, + Column: 1, + }, { + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 4, + Column: 3, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "kb", + HeadComment: "# HB1", + Line: 4, + Column: 3, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "vb", + Line: 4, + Column: 7, + }}, + }}, + }}, + }, + }, { + // Same as above, but with two newlines at the end. Decode-only for that. + "[decode]# HA1\nka:\n # HB1\n kb: vb\n # FB1\n# FA1\n\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 2, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 2, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "ka", + HeadComment: "# HA1", + FootComment: "# FA1", + Line: 2, + Column: 1, + }, { + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 4, + Column: 3, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "kb", + HeadComment: "# HB1", + FootComment: "# FB1", + Line: 4, + Column: 3, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "vb", + Line: 4, + Column: 7, + }}, + }}, + }}, + }, + }, { + // Similar to above, but make HB1 look more like a footer of ka. + "[decode]# HA1\nka:\n# HB1\n\n kb: vb\n# FA1\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 2, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 2, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "ka", + HeadComment: "# HA1", + FootComment: "# FA1", + Line: 2, + Column: 1, + }, { + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 5, + Column: 3, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "kb", + HeadComment: "# HB1\n", + Line: 5, + Column: 3, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "vb", + Line: 5, + Column: 7, + }}, + }}, + }}, + }, + }, { + "ka:\n kb: vb\n# FA1\n\nkc: vc\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "ka", + Line: 1, + Column: 1, + FootComment: "# FA1", + }, { + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 2, + Column: 3, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "kb", + Line: 2, + Column: 3, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "vb", + Line: 2, + Column: 7, + }}, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "kc", + Line: 5, + Column: 1, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "vc", + Line: 5, + Column: 5, + }}, + }}, + }, + }, { + "ka:\n kb: vb\n# HC1\nkc: vc\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "ka", + Line: 1, + Column: 1, + }, { + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 2, + Column: 3, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "kb", + Line: 2, + Column: 3, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "vb", + Line: 2, + Column: 7, + }}, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "kc", + HeadComment: "# HC1", + Line: 4, + Column: 1, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "vc", + Line: 4, + Column: 5, + }}, + }}, + }, + }, { + // Decode only due to empty line before HC1. + "[decode]ka:\n kb: vb\n\n# HC1\nkc: vc\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "ka", + Line: 1, + Column: 1, + }, { + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 2, + Column: 3, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "kb", + Line: 2, + Column: 3, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "vb", + Line: 2, + Column: 7, + }}, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "kc", + HeadComment: "# HC1", + Line: 5, + Column: 1, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "vc", + Line: 5, + Column: 5, + }}, + }}, + }, + }, { + // Decode-only due to empty lines around HC1. + "[decode]ka:\n kb: vb\n\n# HC1\n\nkc: vc\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "ka", + Line: 1, + Column: 1, + }, { + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 2, + Column: 3, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "kb", + Line: 2, + Column: 3, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "vb", + Line: 2, + Column: 7, + }}, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "kc", + HeadComment: "# HC1\n", + Line: 6, + Column: 1, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "vc", + Line: 6, + Column: 5, + }}, + }}, + }, + }, { + "ka: # IA\n kb: # IB\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "ka", + Line: 1, + Column: 1, + LineComment: "# IA", + }, { + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 2, + Column: 3, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "kb", + Line: 2, + Column: 3, + LineComment: "# IB", + }, { + Kind: yaml.ScalarNode, + Tag: "!!null", + Line: 2, + Column: 6, + }}, + }}, + }}, + }, + }, { + "# HA1\nka:\n # HB1\n kb: vb\n # FB1\n# HC1\n# HC2\nkc: vc\n# FC1\n# FC2\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 2, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 2, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "ka", + HeadComment: "# HA1", + Line: 2, + Column: 1, + }, { + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 4, + Column: 3, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "kb", + HeadComment: "# HB1", + FootComment: "# FB1", + Line: 4, + Column: 3, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "vb", + Line: 4, + Column: 7, + }}, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "kc", + HeadComment: "# HC1\n# HC2", + FootComment: "# FC1\n# FC2", + Line: 8, + Column: 1, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "vc", + Line: 8, + Column: 5, + }}, + }}, + }, + }, { + // Same as above, but decode only due to empty line between ka's value and kc's headers. + "[decode]# HA1\nka:\n # HB1\n kb: vb\n # FB1\n\n# HC1\n# HC2\nkc: vc\n# FC1\n# FC2\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 2, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 2, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "ka", + HeadComment: "# HA1", + Line: 2, + Column: 1, + }, { + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 4, + Column: 3, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "kb", + HeadComment: "# HB1", + FootComment: "# FB1", + Line: 4, + Column: 3, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "vb", + Line: 4, + Column: 7, + }}, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "kc", + HeadComment: "# HC1\n# HC2", + FootComment: "# FC1\n# FC2", + Line: 9, + Column: 1, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "vc", + Line: 9, + Column: 5, + }}, + }}, + }, + }, { + "# H1\n[la, lb] # I\n# F1\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 2, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.SequenceNode, + Tag: "!!seq", + Style: yaml.FlowStyle, + Line: 2, + Column: 1, + HeadComment: "# H1", + LineComment: "# I", + FootComment: "# F1", + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 2, + Column: 2, + Value: "la", + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 2, + Column: 6, + Value: "lb", + }}, + }}, + }, + }, { + "# DH1\n\n# SH1\n[\n # HA1\n la, # IA\n # FA1\n\n # HB1\n lb, # IB\n # FB1\n]\n# SF1\n\n# DF1\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 4, + Column: 1, + HeadComment: "# DH1", + FootComment: "# DF1", + Content: []*yaml.Node{{ + Kind: yaml.SequenceNode, + Tag: "!!seq", + Style: yaml.FlowStyle, + Line: 4, + Column: 1, + HeadComment: "# SH1", + FootComment: "# SF1", + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 6, + Column: 3, + Value: "la", + HeadComment: "# HA1", + LineComment: "# IA", + FootComment: "# FA1", + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 10, + Column: 3, + Value: "lb", + HeadComment: "# HB1", + LineComment: "# IB", + FootComment: "# FB1", + }}, + }}, + }, + }, { + // Same as above, but with extra newlines before FB1 and FB2 + "[decode]# DH1\n\n# SH1\n[\n # HA1\n la, # IA\n # FA1\n\n # HB1\n lb, # IB\n\n\n # FB1\n\n# FB2\n]\n# SF1\n\n# DF1\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 4, + Column: 1, + HeadComment: "# DH1", + FootComment: "# DF1", + Content: []*yaml.Node{{ + Kind: yaml.SequenceNode, + Tag: "!!seq", + Style: yaml.FlowStyle, + Line: 4, + Column: 1, + HeadComment: "# SH1", + FootComment: "# SF1", + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 6, + Column: 3, + Value: "la", + HeadComment: "# HA1", + LineComment: "# IA", + FootComment: "# FA1", + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 10, + Column: 3, + Value: "lb", + HeadComment: "# HB1", + LineComment: "# IB", + FootComment: "# FB1\n\n# FB2", + }}, + }}, + }, + }, { + "# DH1\n\n# SH1\n[\n # HA1\n la,\n # FA1\n\n # HB1\n lb,\n # FB1\n]\n# SF1\n\n# DF1\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 4, + Column: 1, + HeadComment: "# DH1", + FootComment: "# DF1", + Content: []*yaml.Node{{ + Kind: yaml.SequenceNode, + Tag: "!!seq", + Style: yaml.FlowStyle, + Line: 4, + Column: 1, + HeadComment: "# SH1", + FootComment: "# SF1", + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 6, + Column: 3, + Value: "la", + HeadComment: "# HA1", + FootComment: "# FA1", + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 10, + Column: 3, + Value: "lb", + HeadComment: "# HB1", + FootComment: "# FB1", + }}, + }}, + }, + }, { + "ka:\n kb: [\n # HA1\n la,\n # FA1\n\n # HB1\n lb,\n # FB1\n ]\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Tag: "!!map", + Line: 1, + Column: 1, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "ka", + Line: 1, + Column: 1, + }, { + Kind: 0x4, + Tag: "!!map", + Line: 2, + Column: 3, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "kb", + Line: 2, + Column: 3, + }, { + Kind: yaml.SequenceNode, + Style: 0x20, + Tag: "!!seq", + Line: 2, + Column: 7, + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "la", + HeadComment: "# HA1", + FootComment: "# FA1", + Line: 4, + Column: 5, + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "lb", + HeadComment: "# HB1", + FootComment: "# FB1", + Line: 8, + Column: 5, + }}, + }}, + }}, + }}, + }, + }, { + "# DH1\n\n# MH1\n{\n # HA1\n ka: va, # IA\n # FA1\n\n # HB1\n kb: vb, # IB\n # FB1\n}\n# MF1\n\n# DF1\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 4, + Column: 1, + HeadComment: "# DH1", + FootComment: "# DF1", + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Tag: "!!map", + Style: yaml.FlowStyle, + Line: 4, + Column: 1, + HeadComment: "# MH1", + FootComment: "# MF1", + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 6, + Column: 3, + Value: "ka", + HeadComment: "# HA1", + FootComment: "# FA1", + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 6, + Column: 7, + Value: "va", + LineComment: "# IA", + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 10, + Column: 3, + Value: "kb", + HeadComment: "# HB1", + FootComment: "# FB1", + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 10, + Column: 7, + Value: "vb", + LineComment: "# IB", + }}, + }}, + }, + }, { + "# DH1\n\n# MH1\n{\n # HA1\n ka: va,\n # FA1\n\n # HB1\n kb: vb,\n # FB1\n}\n# MF1\n\n# DF1\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 4, + Column: 1, + HeadComment: "# DH1", + FootComment: "# DF1", + Content: []*yaml.Node{{ + Kind: yaml.MappingNode, + Tag: "!!map", + Style: yaml.FlowStyle, + Line: 4, + Column: 1, + HeadComment: "# MH1", + FootComment: "# MF1", + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 6, + Column: 3, + Value: "ka", + HeadComment: "# HA1", + FootComment: "# FA1", + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 6, + Column: 7, + Value: "va", + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 10, + Column: 3, + Value: "kb", + HeadComment: "# HB1", + FootComment: "# FB1", + }, { + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 10, + Column: 7, + Value: "vb", + }}, + }}, + }, + }, { + "# DH1\n\n# DH2\n\n# HA1\n# HA2\n- &x la # IA\n# FA1\n# FA2\n\n# HB1\n# HB2\n- *x # IB\n# FB1\n# FB2\n\n# DF1\n\n# DF2\n", + yaml.Node{ + Kind: yaml.DocumentNode, + Line: 7, + Column: 1, + HeadComment: "# DH1\n\n# DH2", + FootComment: "# DF1\n\n# DF2", + Content: []*yaml.Node{{ + Kind: yaml.SequenceNode, + Tag: "!!seq", + Line: 7, + Column: 1, + Content: []*yaml.Node{ + saveNode("x", &yaml.Node{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Line: 7, + Column: 3, + Value: "la", + HeadComment: "# HA1\n# HA2", + LineComment: "# IA", + FootComment: "# FA1\n# FA2", + Anchor: "x", + }), { + Kind: yaml.AliasNode, + Line: 13, + Column: 3, + Value: "x", + Alias: dropNode("x"), + HeadComment: "# HB1\n# HB2", + LineComment: "# IB", + FootComment: "# FB1\n# FB2", + }, + }, + }}, + }, + }, +} + +func (s *S) TestNodeRoundtrip(c *C) { + defer os.Setenv("TZ", os.Getenv("TZ")) + os.Setenv("TZ", "UTC") + for i, item := range nodeTests { + c.Logf("test %d: %q", i, item.yaml) + + if strings.Contains(item.yaml, "#") { + var buf bytes.Buffer + fprintComments(&buf, &item.node, " ") + c.Logf(" expected comments:\n%s", buf.Bytes()) + } + + decode := true + encode := true + + testYaml := item.yaml + if s := strings.TrimPrefix(testYaml, "[decode]"); s != testYaml { + encode = false + testYaml = s + } + if s := strings.TrimPrefix(testYaml, "[encode]"); s != testYaml { + decode = false + testYaml = s + } + + if decode { + var node yaml.Node + err := yaml.Unmarshal([]byte(testYaml), &node) + c.Assert(err, IsNil) + if strings.Contains(item.yaml, "#") { + var buf bytes.Buffer + fprintComments(&buf, &node, " ") + c.Logf(" obtained comments:\n%s", buf.Bytes()) + } + c.Assert(&node, DeepEquals, &item.node) + } + if encode { + node := deepCopyNode(&item.node, nil) + buf := bytes.Buffer{} + enc := yaml.NewEncoder(&buf) + enc.SetIndent(2) + err := enc.Encode(node) + c.Assert(err, IsNil) + err = enc.Close() + c.Assert(err, IsNil) + c.Assert(buf.String(), Equals, testYaml) + + // Ensure there were no mutations to the tree. + c.Assert(node, DeepEquals, &item.node) + } + } +} + +func deepCopyNode(node *yaml.Node, cache map[*yaml.Node]*yaml.Node) *yaml.Node { + if n, ok := cache[node]; ok { + return n + } + if cache == nil { + cache = make(map[*yaml.Node]*yaml.Node) + } + copy := *node + cache[node] = © + copy.Content = nil + for _, elem := range node.Content { + copy.Content = append(copy.Content, deepCopyNode(elem, cache)) + } + if node.Alias != nil { + copy.Alias = deepCopyNode(node.Alias, cache) + } + return © +} + +var savedNodes = make(map[string]*yaml.Node) + +func saveNode(name string, node *yaml.Node) *yaml.Node { + savedNodes[name] = node + return node +} + +func peekNode(name string) *yaml.Node { + return savedNodes[name] +} + +func dropNode(name string) *yaml.Node { + node := savedNodes[name] + delete(savedNodes, name) + return node +} + +var setStringTests = []struct { + str string + yaml string + node yaml.Node +}{ + { + "something simple", + "something simple\n", + yaml.Node{ + Kind: yaml.ScalarNode, + Value: "something simple", + Tag: "!!str", + }, + }, { + `"quoted value"`, + "'\"quoted value\"'\n", + yaml.Node{ + Kind: yaml.ScalarNode, + Value: `"quoted value"`, + Tag: "!!str", + }, + }, { + "multi\nline", + "|-\n multi\n line\n", + yaml.Node{ + Kind: yaml.ScalarNode, + Value: "multi\nline", + Tag: "!!str", + Style: yaml.LiteralStyle, + }, + }, { + "123", + "\"123\"\n", + yaml.Node{ + Kind: yaml.ScalarNode, + Value: "123", + Tag: "!!str", + }, + }, { + "multi\nline\n", + "|\n multi\n line\n", + yaml.Node{ + Kind: yaml.ScalarNode, + Value: "multi\nline\n", + Tag: "!!str", + Style: yaml.LiteralStyle, + }, + }, { + "\x80\x81\x82", + "!!binary gIGC\n", + yaml.Node{ + Kind: yaml.ScalarNode, + Value: "gIGC", + Tag: "!!binary", + }, + }, +} + +func (s *S) TestSetString(c *C) { + defer os.Setenv("TZ", os.Getenv("TZ")) + os.Setenv("TZ", "UTC") + for i, item := range setStringTests { + c.Logf("test %d: %q", i, item.str) + + var node yaml.Node + + node.SetString(item.str) + + c.Assert(node, DeepEquals, item.node) + + buf := bytes.Buffer{} + enc := yaml.NewEncoder(&buf) + enc.SetIndent(2) + err := enc.Encode(&item.node) + c.Assert(err, IsNil) + err = enc.Close() + c.Assert(err, IsNil) + c.Assert(buf.String(), Equals, item.yaml) + + var doc yaml.Node + err = yaml.Unmarshal([]byte(item.yaml), &doc) + c.Assert(err, IsNil) + + var str string + err = node.Decode(&str) + c.Assert(err, IsNil) + c.Assert(str, Equals, item.str) + } +} + +var nodeEncodeDecodeTests = []struct { + value interface{} + yaml string + node yaml.Node +}{{ + "something simple", + "something simple\n", + yaml.Node{ + Kind: yaml.ScalarNode, + Value: "something simple", + Tag: "!!str", + }, +}, { + `"quoted value"`, + "'\"quoted value\"'\n", + yaml.Node{ + Kind: yaml.ScalarNode, + Style: yaml.SingleQuotedStyle, + Value: `"quoted value"`, + Tag: "!!str", + }, +}, { + 123, + "123", + yaml.Node{ + Kind: yaml.ScalarNode, + Value: `123`, + Tag: "!!int", + }, +}, { + []interface{}{1, 2}, + "[1, 2]", + yaml.Node{ + Kind: yaml.SequenceNode, + Tag: "!!seq", + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: "1", + Tag: "!!int", + }, { + Kind: yaml.ScalarNode, + Value: "2", + Tag: "!!int", + }}, + }, +}, { + map[string]interface{}{"a": "b"}, + "a: b", + yaml.Node{ + Kind: yaml.MappingNode, + Tag: "!!map", + Content: []*yaml.Node{{ + Kind: yaml.ScalarNode, + Value: "a", + Tag: "!!str", + }, { + Kind: yaml.ScalarNode, + Value: "b", + Tag: "!!str", + }}, + }, +}} + +func (s *S) TestNodeEncodeDecode(c *C) { + for i, item := range nodeEncodeDecodeTests { + c.Logf("Encode/Decode test value #%d: %#v", i, item.value) + + var v interface{} + err := item.node.Decode(&v) + c.Assert(err, IsNil) + c.Assert(v, DeepEquals, item.value) + + var n yaml.Node + err = n.Encode(item.value) + c.Assert(err, IsNil) + c.Assert(n, DeepEquals, item.node) + } +} + +func (s *S) TestNodeZeroEncodeDecode(c *C) { + // Zero node value behaves as nil when encoding... + var n yaml.Node + data, err := yaml.Marshal(&n) + c.Assert(err, IsNil) + c.Assert(string(data), Equals, "null\n") + + // ... and decoding. + var v *struct{} = &struct{}{} + c.Assert(n.Decode(&v), IsNil) + c.Assert(v, IsNil) + + // ... and even when looking for its tag. + c.Assert(n.ShortTag(), Equals, "!!null") + + // Kind zero is still unknown, though. + n.Line = 1 + _, err = yaml.Marshal(&n) + c.Assert(err, ErrorMatches, "yaml: cannot encode node with unknown kind 0") + c.Assert(n.Decode(&v), ErrorMatches, "yaml: cannot decode node with unknown kind 0") +} + +func (s *S) TestNodeOmitEmpty(c *C) { + var v struct { + A int + B yaml.Node ",omitempty" + } + v.A = 1 + data, err := yaml.Marshal(&v) + c.Assert(err, IsNil) + c.Assert(string(data), Equals, "a: 1\n") + + v.B.Line = 1 + _, err = yaml.Marshal(&v) + c.Assert(err, ErrorMatches, "yaml: cannot encode node with unknown kind 0") +} + +func fprintComments(out io.Writer, node *yaml.Node, indent string) { + switch node.Kind { + case yaml.ScalarNode: + fmt.Fprintf(out, "%s<%s> ", indent, node.Value) + fprintCommentSet(out, node) + fmt.Fprintf(out, "\n") + case yaml.DocumentNode: + fmt.Fprintf(out, "%s<DOC> ", indent) + fprintCommentSet(out, node) + fmt.Fprintf(out, "\n") + for i := 0; i < len(node.Content); i++ { + fprintComments(out, node.Content[i], indent+" ") + } + case yaml.MappingNode: + fmt.Fprintf(out, "%s<MAP> ", indent) + fprintCommentSet(out, node) + fmt.Fprintf(out, "\n") + for i := 0; i < len(node.Content); i += 2 { + fprintComments(out, node.Content[i], indent+" ") + fprintComments(out, node.Content[i+1], indent+" ") + } + case yaml.SequenceNode: + fmt.Fprintf(out, "%s<SEQ> ", indent) + fprintCommentSet(out, node) + fmt.Fprintf(out, "\n") + for i := 0; i < len(node.Content); i++ { + fprintComments(out, node.Content[i], indent+" ") + } + } +} + +func fprintCommentSet(out io.Writer, node *yaml.Node) { + if len(node.HeadComment)+len(node.LineComment)+len(node.FootComment) > 0 { + fmt.Fprintf(out, "%q / %q / %q", node.HeadComment, node.LineComment, node.FootComment) + } +} diff --git a/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/parserc.go b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/parserc.go new file mode 100644 index 0000000..268558a --- /dev/null +++ b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/parserc.go @@ -0,0 +1,1258 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// Copyright (c) 2006-2010 Kirill Simonov +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package yaml + +import ( + "bytes" +) + +// The parser implements the following grammar: +// +// stream ::= STREAM-START implicit_document? explicit_document* STREAM-END +// implicit_document ::= block_node DOCUMENT-END* +// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// block_node_or_indentless_sequence ::= +// ALIAS +// | properties (block_content | indentless_block_sequence)? +// | block_content +// | indentless_block_sequence +// block_node ::= ALIAS +// | properties block_content? +// | block_content +// flow_node ::= ALIAS +// | properties flow_content? +// | flow_content +// properties ::= TAG ANCHOR? | ANCHOR TAG? +// block_content ::= block_collection | flow_collection | SCALAR +// flow_content ::= flow_collection | SCALAR +// block_collection ::= block_sequence | block_mapping +// flow_collection ::= flow_sequence | flow_mapping +// block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END +// indentless_sequence ::= (BLOCK-ENTRY block_node?)+ +// block_mapping ::= BLOCK-MAPPING_START +// ((KEY block_node_or_indentless_sequence?)? +// (VALUE block_node_or_indentless_sequence?)?)* +// BLOCK-END +// flow_sequence ::= FLOW-SEQUENCE-START +// (flow_sequence_entry FLOW-ENTRY)* +// flow_sequence_entry? +// FLOW-SEQUENCE-END +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// flow_mapping ::= FLOW-MAPPING-START +// (flow_mapping_entry FLOW-ENTRY)* +// flow_mapping_entry? +// FLOW-MAPPING-END +// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? + +// Peek the next token in the token queue. +func peek_token(parser *yaml_parser_t) *yaml_token_t { + if parser.token_available || yaml_parser_fetch_more_tokens(parser) { + token := &parser.tokens[parser.tokens_head] + yaml_parser_unfold_comments(parser, token) + return token + } + return nil +} + +// yaml_parser_unfold_comments walks through the comments queue and joins all +// comments behind the position of the provided token into the respective +// top-level comment slices in the parser. +func yaml_parser_unfold_comments(parser *yaml_parser_t, token *yaml_token_t) { + for parser.comments_head < len(parser.comments) && token.start_mark.index >= parser.comments[parser.comments_head].token_mark.index { + comment := &parser.comments[parser.comments_head] + if len(comment.head) > 0 { + if token.typ == yaml_BLOCK_END_TOKEN { + // No heads on ends, so keep comment.head for a follow up token. + break + } + if len(parser.head_comment) > 0 { + parser.head_comment = append(parser.head_comment, '\n') + } + parser.head_comment = append(parser.head_comment, comment.head...) + } + if len(comment.foot) > 0 { + if len(parser.foot_comment) > 0 { + parser.foot_comment = append(parser.foot_comment, '\n') + } + parser.foot_comment = append(parser.foot_comment, comment.foot...) + } + if len(comment.line) > 0 { + if len(parser.line_comment) > 0 { + parser.line_comment = append(parser.line_comment, '\n') + } + parser.line_comment = append(parser.line_comment, comment.line...) + } + *comment = yaml_comment_t{} + parser.comments_head++ + } +} + +// Remove the next token from the queue (must be called after peek_token). +func skip_token(parser *yaml_parser_t) { + parser.token_available = false + parser.tokens_parsed++ + parser.stream_end_produced = parser.tokens[parser.tokens_head].typ == yaml_STREAM_END_TOKEN + parser.tokens_head++ +} + +// Get the next event. +func yaml_parser_parse(parser *yaml_parser_t, event *yaml_event_t) bool { + // Erase the event object. + *event = yaml_event_t{} + + // No events after the end of the stream or error. + if parser.stream_end_produced || parser.error != yaml_NO_ERROR || parser.state == yaml_PARSE_END_STATE { + return true + } + + // Generate the next event. + return yaml_parser_state_machine(parser, event) +} + +// Set parser error. +func yaml_parser_set_parser_error(parser *yaml_parser_t, problem string, problem_mark yaml_mark_t) bool { + parser.error = yaml_PARSER_ERROR + parser.problem = problem + parser.problem_mark = problem_mark + return false +} + +func yaml_parser_set_parser_error_context(parser *yaml_parser_t, context string, context_mark yaml_mark_t, problem string, problem_mark yaml_mark_t) bool { + parser.error = yaml_PARSER_ERROR + parser.context = context + parser.context_mark = context_mark + parser.problem = problem + parser.problem_mark = problem_mark + return false +} + +// State dispatcher. +func yaml_parser_state_machine(parser *yaml_parser_t, event *yaml_event_t) bool { + //trace("yaml_parser_state_machine", "state:", parser.state.String()) + + switch parser.state { + case yaml_PARSE_STREAM_START_STATE: + return yaml_parser_parse_stream_start(parser, event) + + case yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE: + return yaml_parser_parse_document_start(parser, event, true) + + case yaml_PARSE_DOCUMENT_START_STATE: + return yaml_parser_parse_document_start(parser, event, false) + + case yaml_PARSE_DOCUMENT_CONTENT_STATE: + return yaml_parser_parse_document_content(parser, event) + + case yaml_PARSE_DOCUMENT_END_STATE: + return yaml_parser_parse_document_end(parser, event) + + case yaml_PARSE_BLOCK_NODE_STATE: + return yaml_parser_parse_node(parser, event, true, false) + + case yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE: + return yaml_parser_parse_node(parser, event, true, true) + + case yaml_PARSE_FLOW_NODE_STATE: + return yaml_parser_parse_node(parser, event, false, false) + + case yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE: + return yaml_parser_parse_block_sequence_entry(parser, event, true) + + case yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE: + return yaml_parser_parse_block_sequence_entry(parser, event, false) + + case yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE: + return yaml_parser_parse_indentless_sequence_entry(parser, event) + + case yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE: + return yaml_parser_parse_block_mapping_key(parser, event, true) + + case yaml_PARSE_BLOCK_MAPPING_KEY_STATE: + return yaml_parser_parse_block_mapping_key(parser, event, false) + + case yaml_PARSE_BLOCK_MAPPING_VALUE_STATE: + return yaml_parser_parse_block_mapping_value(parser, event) + + case yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE: + return yaml_parser_parse_flow_sequence_entry(parser, event, true) + + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE: + return yaml_parser_parse_flow_sequence_entry(parser, event, false) + + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE: + return yaml_parser_parse_flow_sequence_entry_mapping_key(parser, event) + + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE: + return yaml_parser_parse_flow_sequence_entry_mapping_value(parser, event) + + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE: + return yaml_parser_parse_flow_sequence_entry_mapping_end(parser, event) + + case yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE: + return yaml_parser_parse_flow_mapping_key(parser, event, true) + + case yaml_PARSE_FLOW_MAPPING_KEY_STATE: + return yaml_parser_parse_flow_mapping_key(parser, event, false) + + case yaml_PARSE_FLOW_MAPPING_VALUE_STATE: + return yaml_parser_parse_flow_mapping_value(parser, event, false) + + case yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE: + return yaml_parser_parse_flow_mapping_value(parser, event, true) + + default: + panic("invalid parser state") + } +} + +// Parse the production: +// stream ::= STREAM-START implicit_document? explicit_document* STREAM-END +// ************ +func yaml_parser_parse_stream_start(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_STREAM_START_TOKEN { + return yaml_parser_set_parser_error(parser, "did not find expected <stream-start>", token.start_mark) + } + parser.state = yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE + *event = yaml_event_t{ + typ: yaml_STREAM_START_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + encoding: token.encoding, + } + skip_token(parser) + return true +} + +// Parse the productions: +// implicit_document ::= block_node DOCUMENT-END* +// * +// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// ************************* +func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t, implicit bool) bool { + + token := peek_token(parser) + if token == nil { + return false + } + + // Parse extra document end indicators. + if !implicit { + for token.typ == yaml_DOCUMENT_END_TOKEN { + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } + } + + if implicit && token.typ != yaml_VERSION_DIRECTIVE_TOKEN && + token.typ != yaml_TAG_DIRECTIVE_TOKEN && + token.typ != yaml_DOCUMENT_START_TOKEN && + token.typ != yaml_STREAM_END_TOKEN { + // Parse an implicit document. + if !yaml_parser_process_directives(parser, nil, nil) { + return false + } + parser.states = append(parser.states, yaml_PARSE_DOCUMENT_END_STATE) + parser.state = yaml_PARSE_BLOCK_NODE_STATE + + var head_comment []byte + if len(parser.head_comment) > 0 { + // [Go] Scan the header comment backwards, and if an empty line is found, break + // the header so the part before the last empty line goes into the + // document header, while the bottom of it goes into a follow up event. + for i := len(parser.head_comment) - 1; i > 0; i-- { + if parser.head_comment[i] == '\n' { + if i == len(parser.head_comment)-1 { + head_comment = parser.head_comment[:i] + parser.head_comment = parser.head_comment[i+1:] + break + } else if parser.head_comment[i-1] == '\n' { + head_comment = parser.head_comment[:i-1] + parser.head_comment = parser.head_comment[i+1:] + break + } + } + } + } + + *event = yaml_event_t{ + typ: yaml_DOCUMENT_START_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + + head_comment: head_comment, + } + + } else if token.typ != yaml_STREAM_END_TOKEN { + // Parse an explicit document. + var version_directive *yaml_version_directive_t + var tag_directives []yaml_tag_directive_t + start_mark := token.start_mark + if !yaml_parser_process_directives(parser, &version_directive, &tag_directives) { + return false + } + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_DOCUMENT_START_TOKEN { + yaml_parser_set_parser_error(parser, + "did not find expected <document start>", token.start_mark) + return false + } + parser.states = append(parser.states, yaml_PARSE_DOCUMENT_END_STATE) + parser.state = yaml_PARSE_DOCUMENT_CONTENT_STATE + end_mark := token.end_mark + + *event = yaml_event_t{ + typ: yaml_DOCUMENT_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + version_directive: version_directive, + tag_directives: tag_directives, + implicit: false, + } + skip_token(parser) + + } else { + // Parse the stream end. + parser.state = yaml_PARSE_END_STATE + *event = yaml_event_t{ + typ: yaml_STREAM_END_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + skip_token(parser) + } + + return true +} + +// Parse the productions: +// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// *********** +// +func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + + if token.typ == yaml_VERSION_DIRECTIVE_TOKEN || + token.typ == yaml_TAG_DIRECTIVE_TOKEN || + token.typ == yaml_DOCUMENT_START_TOKEN || + token.typ == yaml_DOCUMENT_END_TOKEN || + token.typ == yaml_STREAM_END_TOKEN { + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + return yaml_parser_process_empty_scalar(parser, event, + token.start_mark) + } + return yaml_parser_parse_node(parser, event, true, false) +} + +// Parse the productions: +// implicit_document ::= block_node DOCUMENT-END* +// ************* +// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// +func yaml_parser_parse_document_end(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + + start_mark := token.start_mark + end_mark := token.start_mark + + implicit := true + if token.typ == yaml_DOCUMENT_END_TOKEN { + end_mark = token.end_mark + skip_token(parser) + implicit = false + } + + parser.tag_directives = parser.tag_directives[:0] + + parser.state = yaml_PARSE_DOCUMENT_START_STATE + *event = yaml_event_t{ + typ: yaml_DOCUMENT_END_EVENT, + start_mark: start_mark, + end_mark: end_mark, + implicit: implicit, + } + yaml_parser_set_event_comments(parser, event) + if len(event.head_comment) > 0 && len(event.foot_comment) == 0 { + event.foot_comment = event.head_comment + event.head_comment = nil + } + return true +} + +func yaml_parser_set_event_comments(parser *yaml_parser_t, event *yaml_event_t) { + event.head_comment = parser.head_comment + event.line_comment = parser.line_comment + event.foot_comment = parser.foot_comment + parser.head_comment = nil + parser.line_comment = nil + parser.foot_comment = nil + parser.tail_comment = nil + parser.stem_comment = nil +} + +// Parse the productions: +// block_node_or_indentless_sequence ::= +// ALIAS +// ***** +// | properties (block_content | indentless_block_sequence)? +// ********** * +// | block_content | indentless_block_sequence +// * +// block_node ::= ALIAS +// ***** +// | properties block_content? +// ********** * +// | block_content +// * +// flow_node ::= ALIAS +// ***** +// | properties flow_content? +// ********** * +// | flow_content +// * +// properties ::= TAG ANCHOR? | ANCHOR TAG? +// ************************* +// block_content ::= block_collection | flow_collection | SCALAR +// ****** +// flow_content ::= flow_collection | SCALAR +// ****** +func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, indentless_sequence bool) bool { + //defer trace("yaml_parser_parse_node", "block:", block, "indentless_sequence:", indentless_sequence)() + + token := peek_token(parser) + if token == nil { + return false + } + + if token.typ == yaml_ALIAS_TOKEN { + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + *event = yaml_event_t{ + typ: yaml_ALIAS_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + anchor: token.value, + } + yaml_parser_set_event_comments(parser, event) + skip_token(parser) + return true + } + + start_mark := token.start_mark + end_mark := token.start_mark + + var tag_token bool + var tag_handle, tag_suffix, anchor []byte + var tag_mark yaml_mark_t + if token.typ == yaml_ANCHOR_TOKEN { + anchor = token.value + start_mark = token.start_mark + end_mark = token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ == yaml_TAG_TOKEN { + tag_token = true + tag_handle = token.value + tag_suffix = token.suffix + tag_mark = token.start_mark + end_mark = token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } + } else if token.typ == yaml_TAG_TOKEN { + tag_token = true + tag_handle = token.value + tag_suffix = token.suffix + start_mark = token.start_mark + tag_mark = token.start_mark + end_mark = token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ == yaml_ANCHOR_TOKEN { + anchor = token.value + end_mark = token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } + } + + var tag []byte + if tag_token { + if len(tag_handle) == 0 { + tag = tag_suffix + tag_suffix = nil + } else { + for i := range parser.tag_directives { + if bytes.Equal(parser.tag_directives[i].handle, tag_handle) { + tag = append([]byte(nil), parser.tag_directives[i].prefix...) + tag = append(tag, tag_suffix...) + break + } + } + if len(tag) == 0 { + yaml_parser_set_parser_error_context(parser, + "while parsing a node", start_mark, + "found undefined tag handle", tag_mark) + return false + } + } + } + + implicit := len(tag) == 0 + if indentless_sequence && token.typ == yaml_BLOCK_ENTRY_TOKEN { + end_mark = token.end_mark + parser.state = yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE + *event = yaml_event_t{ + typ: yaml_SEQUENCE_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(yaml_BLOCK_SEQUENCE_STYLE), + } + return true + } + if token.typ == yaml_SCALAR_TOKEN { + var plain_implicit, quoted_implicit bool + end_mark = token.end_mark + if (len(tag) == 0 && token.style == yaml_PLAIN_SCALAR_STYLE) || (len(tag) == 1 && tag[0] == '!') { + plain_implicit = true + } else if len(tag) == 0 { + quoted_implicit = true + } + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + + *event = yaml_event_t{ + typ: yaml_SCALAR_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + value: token.value, + implicit: plain_implicit, + quoted_implicit: quoted_implicit, + style: yaml_style_t(token.style), + } + yaml_parser_set_event_comments(parser, event) + skip_token(parser) + return true + } + if token.typ == yaml_FLOW_SEQUENCE_START_TOKEN { + // [Go] Some of the events below can be merged as they differ only on style. + end_mark = token.end_mark + parser.state = yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE + *event = yaml_event_t{ + typ: yaml_SEQUENCE_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(yaml_FLOW_SEQUENCE_STYLE), + } + yaml_parser_set_event_comments(parser, event) + return true + } + if token.typ == yaml_FLOW_MAPPING_START_TOKEN { + end_mark = token.end_mark + parser.state = yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE + *event = yaml_event_t{ + typ: yaml_MAPPING_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(yaml_FLOW_MAPPING_STYLE), + } + yaml_parser_set_event_comments(parser, event) + return true + } + if block && token.typ == yaml_BLOCK_SEQUENCE_START_TOKEN { + end_mark = token.end_mark + parser.state = yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE + *event = yaml_event_t{ + typ: yaml_SEQUENCE_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(yaml_BLOCK_SEQUENCE_STYLE), + } + if parser.stem_comment != nil { + event.head_comment = parser.stem_comment + parser.stem_comment = nil + } + return true + } + if block && token.typ == yaml_BLOCK_MAPPING_START_TOKEN { + end_mark = token.end_mark + parser.state = yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE + *event = yaml_event_t{ + typ: yaml_MAPPING_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(yaml_BLOCK_MAPPING_STYLE), + } + if parser.stem_comment != nil { + event.head_comment = parser.stem_comment + parser.stem_comment = nil + } + return true + } + if len(anchor) > 0 || len(tag) > 0 { + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + + *event = yaml_event_t{ + typ: yaml_SCALAR_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + quoted_implicit: false, + style: yaml_style_t(yaml_PLAIN_SCALAR_STYLE), + } + return true + } + + context := "while parsing a flow node" + if block { + context = "while parsing a block node" + } + yaml_parser_set_parser_error_context(parser, context, start_mark, + "did not find expected node content", token.start_mark) + return false +} + +// Parse the productions: +// block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END +// ******************** *********** * ********* +// +func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { + if first { + token := peek_token(parser) + if token == nil { + return false + } + parser.marks = append(parser.marks, token.start_mark) + skip_token(parser) + } + + token := peek_token(parser) + if token == nil { + return false + } + + if token.typ == yaml_BLOCK_ENTRY_TOKEN { + mark := token.end_mark + prior_head_len := len(parser.head_comment) + skip_token(parser) + yaml_parser_split_stem_comment(parser, prior_head_len) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_BLOCK_ENTRY_TOKEN && token.typ != yaml_BLOCK_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE) + return yaml_parser_parse_node(parser, event, true, false) + } else { + parser.state = yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE + return yaml_parser_process_empty_scalar(parser, event, mark) + } + } + if token.typ == yaml_BLOCK_END_TOKEN { + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + + *event = yaml_event_t{ + typ: yaml_SEQUENCE_END_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + + skip_token(parser) + return true + } + + context_mark := parser.marks[len(parser.marks)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + return yaml_parser_set_parser_error_context(parser, + "while parsing a block collection", context_mark, + "did not find expected '-' indicator", token.start_mark) +} + +// Parse the productions: +// indentless_sequence ::= (BLOCK-ENTRY block_node?)+ +// *********** * +func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + + if token.typ == yaml_BLOCK_ENTRY_TOKEN { + mark := token.end_mark + prior_head_len := len(parser.head_comment) + skip_token(parser) + yaml_parser_split_stem_comment(parser, prior_head_len) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_BLOCK_ENTRY_TOKEN && + token.typ != yaml_KEY_TOKEN && + token.typ != yaml_VALUE_TOKEN && + token.typ != yaml_BLOCK_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE) + return yaml_parser_parse_node(parser, event, true, false) + } + parser.state = yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE + return yaml_parser_process_empty_scalar(parser, event, mark) + } + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + + *event = yaml_event_t{ + typ: yaml_SEQUENCE_END_EVENT, + start_mark: token.start_mark, + end_mark: token.start_mark, // [Go] Shouldn't this be token.end_mark? + } + return true +} + +// Split stem comment from head comment. +// +// When a sequence or map is found under a sequence entry, the former head comment +// is assigned to the underlying sequence or map as a whole, not the individual +// sequence or map entry as would be expected otherwise. To handle this case the +// previous head comment is moved aside as the stem comment. +func yaml_parser_split_stem_comment(parser *yaml_parser_t, stem_len int) { + if stem_len == 0 { + return + } + + token := peek_token(parser) + if token == nil || token.typ != yaml_BLOCK_SEQUENCE_START_TOKEN && token.typ != yaml_BLOCK_MAPPING_START_TOKEN { + return + } + + parser.stem_comment = parser.head_comment[:stem_len] + if len(parser.head_comment) == stem_len { + parser.head_comment = nil + } else { + // Copy suffix to prevent very strange bugs if someone ever appends + // further bytes to the prefix in the stem_comment slice above. + parser.head_comment = append([]byte(nil), parser.head_comment[stem_len+1:]...) + } +} + +// Parse the productions: +// block_mapping ::= BLOCK-MAPPING_START +// ******************* +// ((KEY block_node_or_indentless_sequence?)? +// *** * +// (VALUE block_node_or_indentless_sequence?)?)* +// +// BLOCK-END +// ********* +// +func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { + if first { + token := peek_token(parser) + if token == nil { + return false + } + parser.marks = append(parser.marks, token.start_mark) + skip_token(parser) + } + + token := peek_token(parser) + if token == nil { + return false + } + + // [Go] A tail comment was left from the prior mapping value processed. Emit an event + // as it needs to be processed with that value and not the following key. + if len(parser.tail_comment) > 0 { + *event = yaml_event_t{ + typ: yaml_TAIL_COMMENT_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + foot_comment: parser.tail_comment, + } + parser.tail_comment = nil + return true + } + + if token.typ == yaml_KEY_TOKEN { + mark := token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_KEY_TOKEN && + token.typ != yaml_VALUE_TOKEN && + token.typ != yaml_BLOCK_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_BLOCK_MAPPING_VALUE_STATE) + return yaml_parser_parse_node(parser, event, true, true) + } else { + parser.state = yaml_PARSE_BLOCK_MAPPING_VALUE_STATE + return yaml_parser_process_empty_scalar(parser, event, mark) + } + } else if token.typ == yaml_BLOCK_END_TOKEN { + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + *event = yaml_event_t{ + typ: yaml_MAPPING_END_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + yaml_parser_set_event_comments(parser, event) + skip_token(parser) + return true + } + + context_mark := parser.marks[len(parser.marks)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + return yaml_parser_set_parser_error_context(parser, + "while parsing a block mapping", context_mark, + "did not find expected key", token.start_mark) +} + +// Parse the productions: +// block_mapping ::= BLOCK-MAPPING_START +// +// ((KEY block_node_or_indentless_sequence?)? +// +// (VALUE block_node_or_indentless_sequence?)?)* +// ***** * +// BLOCK-END +// +// +func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + if token.typ == yaml_VALUE_TOKEN { + mark := token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_KEY_TOKEN && + token.typ != yaml_VALUE_TOKEN && + token.typ != yaml_BLOCK_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_BLOCK_MAPPING_KEY_STATE) + return yaml_parser_parse_node(parser, event, true, true) + } + parser.state = yaml_PARSE_BLOCK_MAPPING_KEY_STATE + return yaml_parser_process_empty_scalar(parser, event, mark) + } + parser.state = yaml_PARSE_BLOCK_MAPPING_KEY_STATE + return yaml_parser_process_empty_scalar(parser, event, token.start_mark) +} + +// Parse the productions: +// flow_sequence ::= FLOW-SEQUENCE-START +// ******************* +// (flow_sequence_entry FLOW-ENTRY)* +// * ********** +// flow_sequence_entry? +// * +// FLOW-SEQUENCE-END +// ***************** +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * +// +func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { + if first { + token := peek_token(parser) + if token == nil { + return false + } + parser.marks = append(parser.marks, token.start_mark) + skip_token(parser) + } + token := peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { + if !first { + if token.typ == yaml_FLOW_ENTRY_TOKEN { + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } else { + context_mark := parser.marks[len(parser.marks)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + return yaml_parser_set_parser_error_context(parser, + "while parsing a flow sequence", context_mark, + "did not find expected ',' or ']'", token.start_mark) + } + } + + if token.typ == yaml_KEY_TOKEN { + parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE + *event = yaml_event_t{ + typ: yaml_MAPPING_START_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + implicit: true, + style: yaml_style_t(yaml_FLOW_MAPPING_STYLE), + } + skip_token(parser) + return true + } else if token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } + } + + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + + *event = yaml_event_t{ + typ: yaml_SEQUENCE_END_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + yaml_parser_set_event_comments(parser, event) + + skip_token(parser) + return true +} + +// +// Parse the productions: +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// *** * +// +func yaml_parser_parse_flow_sequence_entry_mapping_key(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_VALUE_TOKEN && + token.typ != yaml_FLOW_ENTRY_TOKEN && + token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } + mark := token.end_mark + skip_token(parser) + parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE + return yaml_parser_process_empty_scalar(parser, event, mark) +} + +// Parse the productions: +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// ***** * +// +func yaml_parser_parse_flow_sequence_entry_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + if token.typ == yaml_VALUE_TOKEN { + skip_token(parser) + token := peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_FLOW_ENTRY_TOKEN && token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } + } + parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE + return yaml_parser_process_empty_scalar(parser, event, token.start_mark) +} + +// Parse the productions: +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * +// +func yaml_parser_parse_flow_sequence_entry_mapping_end(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE + *event = yaml_event_t{ + typ: yaml_MAPPING_END_EVENT, + start_mark: token.start_mark, + end_mark: token.start_mark, // [Go] Shouldn't this be end_mark? + } + return true +} + +// Parse the productions: +// flow_mapping ::= FLOW-MAPPING-START +// ****************** +// (flow_mapping_entry FLOW-ENTRY)* +// * ********** +// flow_mapping_entry? +// ****************** +// FLOW-MAPPING-END +// **************** +// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * *** * +// +func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { + if first { + token := peek_token(parser) + parser.marks = append(parser.marks, token.start_mark) + skip_token(parser) + } + + token := peek_token(parser) + if token == nil { + return false + } + + if token.typ != yaml_FLOW_MAPPING_END_TOKEN { + if !first { + if token.typ == yaml_FLOW_ENTRY_TOKEN { + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } else { + context_mark := parser.marks[len(parser.marks)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + return yaml_parser_set_parser_error_context(parser, + "while parsing a flow mapping", context_mark, + "did not find expected ',' or '}'", token.start_mark) + } + } + + if token.typ == yaml_KEY_TOKEN { + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_VALUE_TOKEN && + token.typ != yaml_FLOW_ENTRY_TOKEN && + token.typ != yaml_FLOW_MAPPING_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_VALUE_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } else { + parser.state = yaml_PARSE_FLOW_MAPPING_VALUE_STATE + return yaml_parser_process_empty_scalar(parser, event, token.start_mark) + } + } else if token.typ != yaml_FLOW_MAPPING_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } + } + + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + *event = yaml_event_t{ + typ: yaml_MAPPING_END_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + yaml_parser_set_event_comments(parser, event) + skip_token(parser) + return true +} + +// Parse the productions: +// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * ***** * +// +func yaml_parser_parse_flow_mapping_value(parser *yaml_parser_t, event *yaml_event_t, empty bool) bool { + token := peek_token(parser) + if token == nil { + return false + } + if empty { + parser.state = yaml_PARSE_FLOW_MAPPING_KEY_STATE + return yaml_parser_process_empty_scalar(parser, event, token.start_mark) + } + if token.typ == yaml_VALUE_TOKEN { + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_FLOW_ENTRY_TOKEN && token.typ != yaml_FLOW_MAPPING_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_KEY_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } + } + parser.state = yaml_PARSE_FLOW_MAPPING_KEY_STATE + return yaml_parser_process_empty_scalar(parser, event, token.start_mark) +} + +// Generate an empty scalar event. +func yaml_parser_process_empty_scalar(parser *yaml_parser_t, event *yaml_event_t, mark yaml_mark_t) bool { + *event = yaml_event_t{ + typ: yaml_SCALAR_EVENT, + start_mark: mark, + end_mark: mark, + value: nil, // Empty + implicit: true, + style: yaml_style_t(yaml_PLAIN_SCALAR_STYLE), + } + return true +} + +var default_tag_directives = []yaml_tag_directive_t{ + {[]byte("!"), []byte("!")}, + {[]byte("!!"), []byte("tag:yaml.org,2002:")}, +} + +// Parse directives. +func yaml_parser_process_directives(parser *yaml_parser_t, + version_directive_ref **yaml_version_directive_t, + tag_directives_ref *[]yaml_tag_directive_t) bool { + + var version_directive *yaml_version_directive_t + var tag_directives []yaml_tag_directive_t + + token := peek_token(parser) + if token == nil { + return false + } + + for token.typ == yaml_VERSION_DIRECTIVE_TOKEN || token.typ == yaml_TAG_DIRECTIVE_TOKEN { + if token.typ == yaml_VERSION_DIRECTIVE_TOKEN { + if version_directive != nil { + yaml_parser_set_parser_error(parser, + "found duplicate %YAML directive", token.start_mark) + return false + } + if token.major != 1 || token.minor != 1 { + yaml_parser_set_parser_error(parser, + "found incompatible YAML document", token.start_mark) + return false + } + version_directive = &yaml_version_directive_t{ + major: token.major, + minor: token.minor, + } + } else if token.typ == yaml_TAG_DIRECTIVE_TOKEN { + value := yaml_tag_directive_t{ + handle: token.value, + prefix: token.prefix, + } + if !yaml_parser_append_tag_directive(parser, value, false, token.start_mark) { + return false + } + tag_directives = append(tag_directives, value) + } + + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } + + for i := range default_tag_directives { + if !yaml_parser_append_tag_directive(parser, default_tag_directives[i], true, token.start_mark) { + return false + } + } + + if version_directive_ref != nil { + *version_directive_ref = version_directive + } + if tag_directives_ref != nil { + *tag_directives_ref = tag_directives + } + return true +} + +// Append a tag directive to the directives stack. +func yaml_parser_append_tag_directive(parser *yaml_parser_t, value yaml_tag_directive_t, allow_duplicates bool, mark yaml_mark_t) bool { + for i := range parser.tag_directives { + if bytes.Equal(value.handle, parser.tag_directives[i].handle) { + if allow_duplicates { + return true + } + return yaml_parser_set_parser_error(parser, "found duplicate %TAG directive", mark) + } + } + + // [Go] I suspect the copy is unnecessary. This was likely done + // because there was no way to track ownership of the data. + value_copy := yaml_tag_directive_t{ + handle: make([]byte, len(value.handle)), + prefix: make([]byte, len(value.prefix)), + } + copy(value_copy.handle, value.handle) + copy(value_copy.prefix, value.prefix) + parser.tag_directives = append(parser.tag_directives, value_copy) + return true +} diff --git a/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/readerc.go b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/readerc.go new file mode 100644 index 0000000..b7de0a8 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/readerc.go @@ -0,0 +1,434 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// Copyright (c) 2006-2010 Kirill Simonov +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package yaml + +import ( + "io" +) + +// Set the reader error and return 0. +func yaml_parser_set_reader_error(parser *yaml_parser_t, problem string, offset int, value int) bool { + parser.error = yaml_READER_ERROR + parser.problem = problem + parser.problem_offset = offset + parser.problem_value = value + return false +} + +// Byte order marks. +const ( + bom_UTF8 = "\xef\xbb\xbf" + bom_UTF16LE = "\xff\xfe" + bom_UTF16BE = "\xfe\xff" +) + +// Determine the input stream encoding by checking the BOM symbol. If no BOM is +// found, the UTF-8 encoding is assumed. Return 1 on success, 0 on failure. +func yaml_parser_determine_encoding(parser *yaml_parser_t) bool { + // Ensure that we had enough bytes in the raw buffer. + for !parser.eof && len(parser.raw_buffer)-parser.raw_buffer_pos < 3 { + if !yaml_parser_update_raw_buffer(parser) { + return false + } + } + + // Determine the encoding. + buf := parser.raw_buffer + pos := parser.raw_buffer_pos + avail := len(buf) - pos + if avail >= 2 && buf[pos] == bom_UTF16LE[0] && buf[pos+1] == bom_UTF16LE[1] { + parser.encoding = yaml_UTF16LE_ENCODING + parser.raw_buffer_pos += 2 + parser.offset += 2 + } else if avail >= 2 && buf[pos] == bom_UTF16BE[0] && buf[pos+1] == bom_UTF16BE[1] { + parser.encoding = yaml_UTF16BE_ENCODING + parser.raw_buffer_pos += 2 + parser.offset += 2 + } else if avail >= 3 && buf[pos] == bom_UTF8[0] && buf[pos+1] == bom_UTF8[1] && buf[pos+2] == bom_UTF8[2] { + parser.encoding = yaml_UTF8_ENCODING + parser.raw_buffer_pos += 3 + parser.offset += 3 + } else { + parser.encoding = yaml_UTF8_ENCODING + } + return true +} + +// Update the raw buffer. +func yaml_parser_update_raw_buffer(parser *yaml_parser_t) bool { + size_read := 0 + + // Return if the raw buffer is full. + if parser.raw_buffer_pos == 0 && len(parser.raw_buffer) == cap(parser.raw_buffer) { + return true + } + + // Return on EOF. + if parser.eof { + return true + } + + // Move the remaining bytes in the raw buffer to the beginning. + if parser.raw_buffer_pos > 0 && parser.raw_buffer_pos < len(parser.raw_buffer) { + copy(parser.raw_buffer, parser.raw_buffer[parser.raw_buffer_pos:]) + } + parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)-parser.raw_buffer_pos] + parser.raw_buffer_pos = 0 + + // Call the read handler to fill the buffer. + size_read, err := parser.read_handler(parser, parser.raw_buffer[len(parser.raw_buffer):cap(parser.raw_buffer)]) + parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)+size_read] + if err == io.EOF { + parser.eof = true + } else if err != nil { + return yaml_parser_set_reader_error(parser, "input error: "+err.Error(), parser.offset, -1) + } + return true +} + +// Ensure that the buffer contains at least `length` characters. +// Return true on success, false on failure. +// +// The length is supposed to be significantly less that the buffer size. +func yaml_parser_update_buffer(parser *yaml_parser_t, length int) bool { + if parser.read_handler == nil { + panic("read handler must be set") + } + + // [Go] This function was changed to guarantee the requested length size at EOF. + // The fact we need to do this is pretty awful, but the description above implies + // for that to be the case, and there are tests + + // If the EOF flag is set and the raw buffer is empty, do nothing. + if parser.eof && parser.raw_buffer_pos == len(parser.raw_buffer) { + // [Go] ACTUALLY! Read the documentation of this function above. + // This is just broken. To return true, we need to have the + // given length in the buffer. Not doing that means every single + // check that calls this function to make sure the buffer has a + // given length is Go) panicking; or C) accessing invalid memory. + //return true + } + + // Return if the buffer contains enough characters. + if parser.unread >= length { + return true + } + + // Determine the input encoding if it is not known yet. + if parser.encoding == yaml_ANY_ENCODING { + if !yaml_parser_determine_encoding(parser) { + return false + } + } + + // Move the unread characters to the beginning of the buffer. + buffer_len := len(parser.buffer) + if parser.buffer_pos > 0 && parser.buffer_pos < buffer_len { + copy(parser.buffer, parser.buffer[parser.buffer_pos:]) + buffer_len -= parser.buffer_pos + parser.buffer_pos = 0 + } else if parser.buffer_pos == buffer_len { + buffer_len = 0 + parser.buffer_pos = 0 + } + + // Open the whole buffer for writing, and cut it before returning. + parser.buffer = parser.buffer[:cap(parser.buffer)] + + // Fill the buffer until it has enough characters. + first := true + for parser.unread < length { + + // Fill the raw buffer if necessary. + if !first || parser.raw_buffer_pos == len(parser.raw_buffer) { + if !yaml_parser_update_raw_buffer(parser) { + parser.buffer = parser.buffer[:buffer_len] + return false + } + } + first = false + + // Decode the raw buffer. + inner: + for parser.raw_buffer_pos != len(parser.raw_buffer) { + var value rune + var width int + + raw_unread := len(parser.raw_buffer) - parser.raw_buffer_pos + + // Decode the next character. + switch parser.encoding { + case yaml_UTF8_ENCODING: + // Decode a UTF-8 character. Check RFC 3629 + // (http://www.ietf.org/rfc/rfc3629.txt) for more details. + // + // The following table (taken from the RFC) is used for + // decoding. + // + // Char. number range | UTF-8 octet sequence + // (hexadecimal) | (binary) + // --------------------+------------------------------------ + // 0000 0000-0000 007F | 0xxxxxxx + // 0000 0080-0000 07FF | 110xxxxx 10xxxxxx + // 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx + // 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + // + // Additionally, the characters in the range 0xD800-0xDFFF + // are prohibited as they are reserved for use with UTF-16 + // surrogate pairs. + + // Determine the length of the UTF-8 sequence. + octet := parser.raw_buffer[parser.raw_buffer_pos] + switch { + case octet&0x80 == 0x00: + width = 1 + case octet&0xE0 == 0xC0: + width = 2 + case octet&0xF0 == 0xE0: + width = 3 + case octet&0xF8 == 0xF0: + width = 4 + default: + // The leading octet is invalid. + return yaml_parser_set_reader_error(parser, + "invalid leading UTF-8 octet", + parser.offset, int(octet)) + } + + // Check if the raw buffer contains an incomplete character. + if width > raw_unread { + if parser.eof { + return yaml_parser_set_reader_error(parser, + "incomplete UTF-8 octet sequence", + parser.offset, -1) + } + break inner + } + + // Decode the leading octet. + switch { + case octet&0x80 == 0x00: + value = rune(octet & 0x7F) + case octet&0xE0 == 0xC0: + value = rune(octet & 0x1F) + case octet&0xF0 == 0xE0: + value = rune(octet & 0x0F) + case octet&0xF8 == 0xF0: + value = rune(octet & 0x07) + default: + value = 0 + } + + // Check and decode the trailing octets. + for k := 1; k < width; k++ { + octet = parser.raw_buffer[parser.raw_buffer_pos+k] + + // Check if the octet is valid. + if (octet & 0xC0) != 0x80 { + return yaml_parser_set_reader_error(parser, + "invalid trailing UTF-8 octet", + parser.offset+k, int(octet)) + } + + // Decode the octet. + value = (value << 6) + rune(octet&0x3F) + } + + // Check the length of the sequence against the value. + switch { + case width == 1: + case width == 2 && value >= 0x80: + case width == 3 && value >= 0x800: + case width == 4 && value >= 0x10000: + default: + return yaml_parser_set_reader_error(parser, + "invalid length of a UTF-8 sequence", + parser.offset, -1) + } + + // Check the range of the value. + if value >= 0xD800 && value <= 0xDFFF || value > 0x10FFFF { + return yaml_parser_set_reader_error(parser, + "invalid Unicode character", + parser.offset, int(value)) + } + + case yaml_UTF16LE_ENCODING, yaml_UTF16BE_ENCODING: + var low, high int + if parser.encoding == yaml_UTF16LE_ENCODING { + low, high = 0, 1 + } else { + low, high = 1, 0 + } + + // The UTF-16 encoding is not as simple as one might + // naively think. Check RFC 2781 + // (http://www.ietf.org/rfc/rfc2781.txt). + // + // Normally, two subsequent bytes describe a Unicode + // character. However a special technique (called a + // surrogate pair) is used for specifying character + // values larger than 0xFFFF. + // + // A surrogate pair consists of two pseudo-characters: + // high surrogate area (0xD800-0xDBFF) + // low surrogate area (0xDC00-0xDFFF) + // + // The following formulas are used for decoding + // and encoding characters using surrogate pairs: + // + // U = U' + 0x10000 (0x01 00 00 <= U <= 0x10 FF FF) + // U' = yyyyyyyyyyxxxxxxxxxx (0 <= U' <= 0x0F FF FF) + // W1 = 110110yyyyyyyyyy + // W2 = 110111xxxxxxxxxx + // + // where U is the character value, W1 is the high surrogate + // area, W2 is the low surrogate area. + + // Check for incomplete UTF-16 character. + if raw_unread < 2 { + if parser.eof { + return yaml_parser_set_reader_error(parser, + "incomplete UTF-16 character", + parser.offset, -1) + } + break inner + } + + // Get the character. + value = rune(parser.raw_buffer[parser.raw_buffer_pos+low]) + + (rune(parser.raw_buffer[parser.raw_buffer_pos+high]) << 8) + + // Check for unexpected low surrogate area. + if value&0xFC00 == 0xDC00 { + return yaml_parser_set_reader_error(parser, + "unexpected low surrogate area", + parser.offset, int(value)) + } + + // Check for a high surrogate area. + if value&0xFC00 == 0xD800 { + width = 4 + + // Check for incomplete surrogate pair. + if raw_unread < 4 { + if parser.eof { + return yaml_parser_set_reader_error(parser, + "incomplete UTF-16 surrogate pair", + parser.offset, -1) + } + break inner + } + + // Get the next character. + value2 := rune(parser.raw_buffer[parser.raw_buffer_pos+low+2]) + + (rune(parser.raw_buffer[parser.raw_buffer_pos+high+2]) << 8) + + // Check for a low surrogate area. + if value2&0xFC00 != 0xDC00 { + return yaml_parser_set_reader_error(parser, + "expected low surrogate area", + parser.offset+2, int(value2)) + } + + // Generate the value of the surrogate pair. + value = 0x10000 + ((value & 0x3FF) << 10) + (value2 & 0x3FF) + } else { + width = 2 + } + + default: + panic("impossible") + } + + // Check if the character is in the allowed range: + // #x9 | #xA | #xD | [#x20-#x7E] (8 bit) + // | #x85 | [#xA0-#xD7FF] | [#xE000-#xFFFD] (16 bit) + // | [#x10000-#x10FFFF] (32 bit) + switch { + case value == 0x09: + case value == 0x0A: + case value == 0x0D: + case value >= 0x20 && value <= 0x7E: + case value == 0x85: + case value >= 0xA0 && value <= 0xD7FF: + case value >= 0xE000 && value <= 0xFFFD: + case value >= 0x10000 && value <= 0x10FFFF: + default: + return yaml_parser_set_reader_error(parser, + "control characters are not allowed", + parser.offset, int(value)) + } + + // Move the raw pointers. + parser.raw_buffer_pos += width + parser.offset += width + + // Finally put the character into the buffer. + if value <= 0x7F { + // 0000 0000-0000 007F . 0xxxxxxx + parser.buffer[buffer_len+0] = byte(value) + buffer_len += 1 + } else if value <= 0x7FF { + // 0000 0080-0000 07FF . 110xxxxx 10xxxxxx + parser.buffer[buffer_len+0] = byte(0xC0 + (value >> 6)) + parser.buffer[buffer_len+1] = byte(0x80 + (value & 0x3F)) + buffer_len += 2 + } else if value <= 0xFFFF { + // 0000 0800-0000 FFFF . 1110xxxx 10xxxxxx 10xxxxxx + parser.buffer[buffer_len+0] = byte(0xE0 + (value >> 12)) + parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 6) & 0x3F)) + parser.buffer[buffer_len+2] = byte(0x80 + (value & 0x3F)) + buffer_len += 3 + } else { + // 0001 0000-0010 FFFF . 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + parser.buffer[buffer_len+0] = byte(0xF0 + (value >> 18)) + parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 12) & 0x3F)) + parser.buffer[buffer_len+2] = byte(0x80 + ((value >> 6) & 0x3F)) + parser.buffer[buffer_len+3] = byte(0x80 + (value & 0x3F)) + buffer_len += 4 + } + + parser.unread++ + } + + // On EOF, put NUL into the buffer and return. + if parser.eof { + parser.buffer[buffer_len] = 0 + buffer_len++ + parser.unread++ + break + } + } + // [Go] Read the documentation of this function above. To return true, + // we need to have the given length in the buffer. Not doing that means + // every single check that calls this function to make sure the buffer + // has a given length is Go) panicking; or C) accessing invalid memory. + // This happens here due to the EOF above breaking early. + for buffer_len < length { + parser.buffer[buffer_len] = 0 + buffer_len++ + } + parser.buffer = parser.buffer[:buffer_len] + return true +} diff --git a/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/resolve.go b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/resolve.go new file mode 100644 index 0000000..64ae888 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/resolve.go @@ -0,0 +1,326 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package yaml + +import ( + "encoding/base64" + "math" + "regexp" + "strconv" + "strings" + "time" +) + +type resolveMapItem struct { + value interface{} + tag string +} + +var resolveTable = make([]byte, 256) +var resolveMap = make(map[string]resolveMapItem) + +func init() { + t := resolveTable + t[int('+')] = 'S' // Sign + t[int('-')] = 'S' + for _, c := range "0123456789" { + t[int(c)] = 'D' // Digit + } + for _, c := range "yYnNtTfFoO~" { + t[int(c)] = 'M' // In map + } + t[int('.')] = '.' // Float (potentially in map) + + var resolveMapList = []struct { + v interface{} + tag string + l []string + }{ + {true, boolTag, []string{"true", "True", "TRUE"}}, + {false, boolTag, []string{"false", "False", "FALSE"}}, + {nil, nullTag, []string{"", "~", "null", "Null", "NULL"}}, + {math.NaN(), floatTag, []string{".nan", ".NaN", ".NAN"}}, + {math.Inf(+1), floatTag, []string{".inf", ".Inf", ".INF"}}, + {math.Inf(+1), floatTag, []string{"+.inf", "+.Inf", "+.INF"}}, + {math.Inf(-1), floatTag, []string{"-.inf", "-.Inf", "-.INF"}}, + {"<<", mergeTag, []string{"<<"}}, + } + + m := resolveMap + for _, item := range resolveMapList { + for _, s := range item.l { + m[s] = resolveMapItem{item.v, item.tag} + } + } +} + +const ( + nullTag = "!!null" + boolTag = "!!bool" + strTag = "!!str" + intTag = "!!int" + floatTag = "!!float" + timestampTag = "!!timestamp" + seqTag = "!!seq" + mapTag = "!!map" + binaryTag = "!!binary" + mergeTag = "!!merge" +) + +var longTags = make(map[string]string) +var shortTags = make(map[string]string) + +func init() { + for _, stag := range []string{nullTag, boolTag, strTag, intTag, floatTag, timestampTag, seqTag, mapTag, binaryTag, mergeTag} { + ltag := longTag(stag) + longTags[stag] = ltag + shortTags[ltag] = stag + } +} + +const longTagPrefix = "tag:yaml.org,2002:" + +func shortTag(tag string) string { + if strings.HasPrefix(tag, longTagPrefix) { + if stag, ok := shortTags[tag]; ok { + return stag + } + return "!!" + tag[len(longTagPrefix):] + } + return tag +} + +func longTag(tag string) string { + if strings.HasPrefix(tag, "!!") { + if ltag, ok := longTags[tag]; ok { + return ltag + } + return longTagPrefix + tag[2:] + } + return tag +} + +func resolvableTag(tag string) bool { + switch tag { + case "", strTag, boolTag, intTag, floatTag, nullTag, timestampTag: + return true + } + return false +} + +var yamlStyleFloat = regexp.MustCompile(`^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$`) + +func resolve(tag string, in string) (rtag string, out interface{}) { + tag = shortTag(tag) + if !resolvableTag(tag) { + return tag, in + } + + defer func() { + switch tag { + case "", rtag, strTag, binaryTag: + return + case floatTag: + if rtag == intTag { + switch v := out.(type) { + case int64: + rtag = floatTag + out = float64(v) + return + case int: + rtag = floatTag + out = float64(v) + return + } + } + } + failf("cannot decode %s `%s` as a %s", shortTag(rtag), in, shortTag(tag)) + }() + + // Any data is accepted as a !!str or !!binary. + // Otherwise, the prefix is enough of a hint about what it might be. + hint := byte('N') + if in != "" { + hint = resolveTable[in[0]] + } + if hint != 0 && tag != strTag && tag != binaryTag { + // Handle things we can lookup in a map. + if item, ok := resolveMap[in]; ok { + return item.tag, item.value + } + + // Base 60 floats are a bad idea, were dropped in YAML 1.2, and + // are purposefully unsupported here. They're still quoted on + // the way out for compatibility with other parser, though. + + switch hint { + case 'M': + // We've already checked the map above. + + case '.': + // Not in the map, so maybe a normal float. + floatv, err := strconv.ParseFloat(in, 64) + if err == nil { + return floatTag, floatv + } + + case 'D', 'S': + // Int, float, or timestamp. + // Only try values as a timestamp if the value is unquoted or there's an explicit + // !!timestamp tag. + if tag == "" || tag == timestampTag { + t, ok := parseTimestamp(in) + if ok { + return timestampTag, t + } + } + + plain := strings.Replace(in, "_", "", -1) + intv, err := strconv.ParseInt(plain, 0, 64) + if err == nil { + if intv == int64(int(intv)) { + return intTag, int(intv) + } else { + return intTag, intv + } + } + uintv, err := strconv.ParseUint(plain, 0, 64) + if err == nil { + return intTag, uintv + } + if yamlStyleFloat.MatchString(plain) { + floatv, err := strconv.ParseFloat(plain, 64) + if err == nil { + return floatTag, floatv + } + } + if strings.HasPrefix(plain, "0b") { + intv, err := strconv.ParseInt(plain[2:], 2, 64) + if err == nil { + if intv == int64(int(intv)) { + return intTag, int(intv) + } else { + return intTag, intv + } + } + uintv, err := strconv.ParseUint(plain[2:], 2, 64) + if err == nil { + return intTag, uintv + } + } else if strings.HasPrefix(plain, "-0b") { + intv, err := strconv.ParseInt("-"+plain[3:], 2, 64) + if err == nil { + if true || intv == int64(int(intv)) { + return intTag, int(intv) + } else { + return intTag, intv + } + } + } + // Octals as introduced in version 1.2 of the spec. + // Octals from the 1.1 spec, spelled as 0777, are still + // decoded by default in v3 as well for compatibility. + // May be dropped in v4 depending on how usage evolves. + if strings.HasPrefix(plain, "0o") { + intv, err := strconv.ParseInt(plain[2:], 8, 64) + if err == nil { + if intv == int64(int(intv)) { + return intTag, int(intv) + } else { + return intTag, intv + } + } + uintv, err := strconv.ParseUint(plain[2:], 8, 64) + if err == nil { + return intTag, uintv + } + } else if strings.HasPrefix(plain, "-0o") { + intv, err := strconv.ParseInt("-"+plain[3:], 8, 64) + if err == nil { + if true || intv == int64(int(intv)) { + return intTag, int(intv) + } else { + return intTag, intv + } + } + } + default: + panic("internal error: missing handler for resolver table: " + string(rune(hint)) + " (with " + in + ")") + } + } + return strTag, in +} + +// encodeBase64 encodes s as base64 that is broken up into multiple lines +// as appropriate for the resulting length. +func encodeBase64(s string) string { + const lineLen = 70 + encLen := base64.StdEncoding.EncodedLen(len(s)) + lines := encLen/lineLen + 1 + buf := make([]byte, encLen*2+lines) + in := buf[0:encLen] + out := buf[encLen:] + base64.StdEncoding.Encode(in, []byte(s)) + k := 0 + for i := 0; i < len(in); i += lineLen { + j := i + lineLen + if j > len(in) { + j = len(in) + } + k += copy(out[k:], in[i:j]) + if lines > 1 { + out[k] = '\n' + k++ + } + } + return string(out[:k]) +} + +// This is a subset of the formats allowed by the regular expression +// defined at http://yaml.org/type/timestamp.html. +var allowedTimestampFormats = []string{ + "2006-1-2T15:4:5.999999999Z07:00", // RCF3339Nano with short date fields. + "2006-1-2t15:4:5.999999999Z07:00", // RFC3339Nano with short date fields and lower-case "t". + "2006-1-2 15:4:5.999999999", // space separated with no time zone + "2006-1-2", // date only + // Notable exception: time.Parse cannot handle: "2001-12-14 21:59:43.10 -5" + // from the set of examples. +} + +// parseTimestamp parses s as a timestamp string and +// returns the timestamp and reports whether it succeeded. +// Timestamp formats are defined at http://yaml.org/type/timestamp.html +func parseTimestamp(s string) (time.Time, bool) { + // TODO write code to check all the formats supported by + // http://yaml.org/type/timestamp.html instead of using time.Parse. + + // Quick check: all date formats start with YYYY-. + i := 0 + for ; i < len(s); i++ { + if c := s[i]; c < '0' || c > '9' { + break + } + } + if i != 4 || i == len(s) || s[i] != '-' { + return time.Time{}, false + } + for _, format := range allowedTimestampFormats { + if t, err := time.Parse(format, s); err == nil { + return t, true + } + } + return time.Time{}, false +} diff --git a/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/scannerc.go b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/scannerc.go new file mode 100644 index 0000000..ca00701 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/scannerc.go @@ -0,0 +1,3038 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// Copyright (c) 2006-2010 Kirill Simonov +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package yaml + +import ( + "bytes" + "fmt" +) + +// Introduction +// ************ +// +// The following notes assume that you are familiar with the YAML specification +// (http://yaml.org/spec/1.2/spec.html). We mostly follow it, although in +// some cases we are less restrictive that it requires. +// +// The process of transforming a YAML stream into a sequence of events is +// divided on two steps: Scanning and Parsing. +// +// The Scanner transforms the input stream into a sequence of tokens, while the +// parser transform the sequence of tokens produced by the Scanner into a +// sequence of parsing events. +// +// The Scanner is rather clever and complicated. The Parser, on the contrary, +// is a straightforward implementation of a recursive-descendant parser (or, +// LL(1) parser, as it is usually called). +// +// Actually there are two issues of Scanning that might be called "clever", the +// rest is quite straightforward. The issues are "block collection start" and +// "simple keys". Both issues are explained below in details. +// +// Here the Scanning step is explained and implemented. We start with the list +// of all the tokens produced by the Scanner together with short descriptions. +// +// Now, tokens: +// +// STREAM-START(encoding) # The stream start. +// STREAM-END # The stream end. +// VERSION-DIRECTIVE(major,minor) # The '%YAML' directive. +// TAG-DIRECTIVE(handle,prefix) # The '%TAG' directive. +// DOCUMENT-START # '---' +// DOCUMENT-END # '...' +// BLOCK-SEQUENCE-START # Indentation increase denoting a block +// BLOCK-MAPPING-START # sequence or a block mapping. +// BLOCK-END # Indentation decrease. +// FLOW-SEQUENCE-START # '[' +// FLOW-SEQUENCE-END # ']' +// BLOCK-SEQUENCE-START # '{' +// BLOCK-SEQUENCE-END # '}' +// BLOCK-ENTRY # '-' +// FLOW-ENTRY # ',' +// KEY # '?' or nothing (simple keys). +// VALUE # ':' +// ALIAS(anchor) # '*anchor' +// ANCHOR(anchor) # '&anchor' +// TAG(handle,suffix) # '!handle!suffix' +// SCALAR(value,style) # A scalar. +// +// The following two tokens are "virtual" tokens denoting the beginning and the +// end of the stream: +// +// STREAM-START(encoding) +// STREAM-END +// +// We pass the information about the input stream encoding with the +// STREAM-START token. +// +// The next two tokens are responsible for tags: +// +// VERSION-DIRECTIVE(major,minor) +// TAG-DIRECTIVE(handle,prefix) +// +// Example: +// +// %YAML 1.1 +// %TAG ! !foo +// %TAG !yaml! tag:yaml.org,2002: +// --- +// +// The correspoding sequence of tokens: +// +// STREAM-START(utf-8) +// VERSION-DIRECTIVE(1,1) +// TAG-DIRECTIVE("!","!foo") +// TAG-DIRECTIVE("!yaml","tag:yaml.org,2002:") +// DOCUMENT-START +// STREAM-END +// +// Note that the VERSION-DIRECTIVE and TAG-DIRECTIVE tokens occupy a whole +// line. +// +// The document start and end indicators are represented by: +// +// DOCUMENT-START +// DOCUMENT-END +// +// Note that if a YAML stream contains an implicit document (without '---' +// and '...' indicators), no DOCUMENT-START and DOCUMENT-END tokens will be +// produced. +// +// In the following examples, we present whole documents together with the +// produced tokens. +// +// 1. An implicit document: +// +// 'a scalar' +// +// Tokens: +// +// STREAM-START(utf-8) +// SCALAR("a scalar",single-quoted) +// STREAM-END +// +// 2. An explicit document: +// +// --- +// 'a scalar' +// ... +// +// Tokens: +// +// STREAM-START(utf-8) +// DOCUMENT-START +// SCALAR("a scalar",single-quoted) +// DOCUMENT-END +// STREAM-END +// +// 3. Several documents in a stream: +// +// 'a scalar' +// --- +// 'another scalar' +// --- +// 'yet another scalar' +// +// Tokens: +// +// STREAM-START(utf-8) +// SCALAR("a scalar",single-quoted) +// DOCUMENT-START +// SCALAR("another scalar",single-quoted) +// DOCUMENT-START +// SCALAR("yet another scalar",single-quoted) +// STREAM-END +// +// We have already introduced the SCALAR token above. The following tokens are +// used to describe aliases, anchors, tag, and scalars: +// +// ALIAS(anchor) +// ANCHOR(anchor) +// TAG(handle,suffix) +// SCALAR(value,style) +// +// The following series of examples illustrate the usage of these tokens: +// +// 1. A recursive sequence: +// +// &A [ *A ] +// +// Tokens: +// +// STREAM-START(utf-8) +// ANCHOR("A") +// FLOW-SEQUENCE-START +// ALIAS("A") +// FLOW-SEQUENCE-END +// STREAM-END +// +// 2. A tagged scalar: +// +// !!float "3.14" # A good approximation. +// +// Tokens: +// +// STREAM-START(utf-8) +// TAG("!!","float") +// SCALAR("3.14",double-quoted) +// STREAM-END +// +// 3. Various scalar styles: +// +// --- # Implicit empty plain scalars do not produce tokens. +// --- a plain scalar +// --- 'a single-quoted scalar' +// --- "a double-quoted scalar" +// --- |- +// a literal scalar +// --- >- +// a folded +// scalar +// +// Tokens: +// +// STREAM-START(utf-8) +// DOCUMENT-START +// DOCUMENT-START +// SCALAR("a plain scalar",plain) +// DOCUMENT-START +// SCALAR("a single-quoted scalar",single-quoted) +// DOCUMENT-START +// SCALAR("a double-quoted scalar",double-quoted) +// DOCUMENT-START +// SCALAR("a literal scalar",literal) +// DOCUMENT-START +// SCALAR("a folded scalar",folded) +// STREAM-END +// +// Now it's time to review collection-related tokens. We will start with +// flow collections: +// +// FLOW-SEQUENCE-START +// FLOW-SEQUENCE-END +// FLOW-MAPPING-START +// FLOW-MAPPING-END +// FLOW-ENTRY +// KEY +// VALUE +// +// The tokens FLOW-SEQUENCE-START, FLOW-SEQUENCE-END, FLOW-MAPPING-START, and +// FLOW-MAPPING-END represent the indicators '[', ']', '{', and '}' +// correspondingly. FLOW-ENTRY represent the ',' indicator. Finally the +// indicators '?' and ':', which are used for denoting mapping keys and values, +// are represented by the KEY and VALUE tokens. +// +// The following examples show flow collections: +// +// 1. A flow sequence: +// +// [item 1, item 2, item 3] +// +// Tokens: +// +// STREAM-START(utf-8) +// FLOW-SEQUENCE-START +// SCALAR("item 1",plain) +// FLOW-ENTRY +// SCALAR("item 2",plain) +// FLOW-ENTRY +// SCALAR("item 3",plain) +// FLOW-SEQUENCE-END +// STREAM-END +// +// 2. A flow mapping: +// +// { +// a simple key: a value, # Note that the KEY token is produced. +// ? a complex key: another value, +// } +// +// Tokens: +// +// STREAM-START(utf-8) +// FLOW-MAPPING-START +// KEY +// SCALAR("a simple key",plain) +// VALUE +// SCALAR("a value",plain) +// FLOW-ENTRY +// KEY +// SCALAR("a complex key",plain) +// VALUE +// SCALAR("another value",plain) +// FLOW-ENTRY +// FLOW-MAPPING-END +// STREAM-END +// +// A simple key is a key which is not denoted by the '?' indicator. Note that +// the Scanner still produce the KEY token whenever it encounters a simple key. +// +// For scanning block collections, the following tokens are used (note that we +// repeat KEY and VALUE here): +// +// BLOCK-SEQUENCE-START +// BLOCK-MAPPING-START +// BLOCK-END +// BLOCK-ENTRY +// KEY +// VALUE +// +// The tokens BLOCK-SEQUENCE-START and BLOCK-MAPPING-START denote indentation +// increase that precedes a block collection (cf. the INDENT token in Python). +// The token BLOCK-END denote indentation decrease that ends a block collection +// (cf. the DEDENT token in Python). However YAML has some syntax pecularities +// that makes detections of these tokens more complex. +// +// The tokens BLOCK-ENTRY, KEY, and VALUE are used to represent the indicators +// '-', '?', and ':' correspondingly. +// +// The following examples show how the tokens BLOCK-SEQUENCE-START, +// BLOCK-MAPPING-START, and BLOCK-END are emitted by the Scanner: +// +// 1. Block sequences: +// +// - item 1 +// - item 2 +// - +// - item 3.1 +// - item 3.2 +// - +// key 1: value 1 +// key 2: value 2 +// +// Tokens: +// +// STREAM-START(utf-8) +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// SCALAR("item 1",plain) +// BLOCK-ENTRY +// SCALAR("item 2",plain) +// BLOCK-ENTRY +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// SCALAR("item 3.1",plain) +// BLOCK-ENTRY +// SCALAR("item 3.2",plain) +// BLOCK-END +// BLOCK-ENTRY +// BLOCK-MAPPING-START +// KEY +// SCALAR("key 1",plain) +// VALUE +// SCALAR("value 1",plain) +// KEY +// SCALAR("key 2",plain) +// VALUE +// SCALAR("value 2",plain) +// BLOCK-END +// BLOCK-END +// STREAM-END +// +// 2. Block mappings: +// +// a simple key: a value # The KEY token is produced here. +// ? a complex key +// : another value +// a mapping: +// key 1: value 1 +// key 2: value 2 +// a sequence: +// - item 1 +// - item 2 +// +// Tokens: +// +// STREAM-START(utf-8) +// BLOCK-MAPPING-START +// KEY +// SCALAR("a simple key",plain) +// VALUE +// SCALAR("a value",plain) +// KEY +// SCALAR("a complex key",plain) +// VALUE +// SCALAR("another value",plain) +// KEY +// SCALAR("a mapping",plain) +// BLOCK-MAPPING-START +// KEY +// SCALAR("key 1",plain) +// VALUE +// SCALAR("value 1",plain) +// KEY +// SCALAR("key 2",plain) +// VALUE +// SCALAR("value 2",plain) +// BLOCK-END +// KEY +// SCALAR("a sequence",plain) +// VALUE +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// SCALAR("item 1",plain) +// BLOCK-ENTRY +// SCALAR("item 2",plain) +// BLOCK-END +// BLOCK-END +// STREAM-END +// +// YAML does not always require to start a new block collection from a new +// line. If the current line contains only '-', '?', and ':' indicators, a new +// block collection may start at the current line. The following examples +// illustrate this case: +// +// 1. Collections in a sequence: +// +// - - item 1 +// - item 2 +// - key 1: value 1 +// key 2: value 2 +// - ? complex key +// : complex value +// +// Tokens: +// +// STREAM-START(utf-8) +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// SCALAR("item 1",plain) +// BLOCK-ENTRY +// SCALAR("item 2",plain) +// BLOCK-END +// BLOCK-ENTRY +// BLOCK-MAPPING-START +// KEY +// SCALAR("key 1",plain) +// VALUE +// SCALAR("value 1",plain) +// KEY +// SCALAR("key 2",plain) +// VALUE +// SCALAR("value 2",plain) +// BLOCK-END +// BLOCK-ENTRY +// BLOCK-MAPPING-START +// KEY +// SCALAR("complex key") +// VALUE +// SCALAR("complex value") +// BLOCK-END +// BLOCK-END +// STREAM-END +// +// 2. Collections in a mapping: +// +// ? a sequence +// : - item 1 +// - item 2 +// ? a mapping +// : key 1: value 1 +// key 2: value 2 +// +// Tokens: +// +// STREAM-START(utf-8) +// BLOCK-MAPPING-START +// KEY +// SCALAR("a sequence",plain) +// VALUE +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// SCALAR("item 1",plain) +// BLOCK-ENTRY +// SCALAR("item 2",plain) +// BLOCK-END +// KEY +// SCALAR("a mapping",plain) +// VALUE +// BLOCK-MAPPING-START +// KEY +// SCALAR("key 1",plain) +// VALUE +// SCALAR("value 1",plain) +// KEY +// SCALAR("key 2",plain) +// VALUE +// SCALAR("value 2",plain) +// BLOCK-END +// BLOCK-END +// STREAM-END +// +// YAML also permits non-indented sequences if they are included into a block +// mapping. In this case, the token BLOCK-SEQUENCE-START is not produced: +// +// key: +// - item 1 # BLOCK-SEQUENCE-START is NOT produced here. +// - item 2 +// +// Tokens: +// +// STREAM-START(utf-8) +// BLOCK-MAPPING-START +// KEY +// SCALAR("key",plain) +// VALUE +// BLOCK-ENTRY +// SCALAR("item 1",plain) +// BLOCK-ENTRY +// SCALAR("item 2",plain) +// BLOCK-END +// + +// Ensure that the buffer contains the required number of characters. +// Return true on success, false on failure (reader error or memory error). +func cache(parser *yaml_parser_t, length int) bool { + // [Go] This was inlined: !cache(A, B) -> unread < B && !update(A, B) + return parser.unread >= length || yaml_parser_update_buffer(parser, length) +} + +// Advance the buffer pointer. +func skip(parser *yaml_parser_t) { + if !is_blank(parser.buffer, parser.buffer_pos) { + parser.newlines = 0 + } + parser.mark.index++ + parser.mark.column++ + parser.unread-- + parser.buffer_pos += width(parser.buffer[parser.buffer_pos]) +} + +func skip_line(parser *yaml_parser_t) { + if is_crlf(parser.buffer, parser.buffer_pos) { + parser.mark.index += 2 + parser.mark.column = 0 + parser.mark.line++ + parser.unread -= 2 + parser.buffer_pos += 2 + parser.newlines++ + } else if is_break(parser.buffer, parser.buffer_pos) { + parser.mark.index++ + parser.mark.column = 0 + parser.mark.line++ + parser.unread-- + parser.buffer_pos += width(parser.buffer[parser.buffer_pos]) + parser.newlines++ + } +} + +// Copy a character to a string buffer and advance pointers. +func read(parser *yaml_parser_t, s []byte) []byte { + if !is_blank(parser.buffer, parser.buffer_pos) { + parser.newlines = 0 + } + w := width(parser.buffer[parser.buffer_pos]) + if w == 0 { + panic("invalid character sequence") + } + if len(s) == 0 { + s = make([]byte, 0, 32) + } + if w == 1 && len(s)+w <= cap(s) { + s = s[:len(s)+1] + s[len(s)-1] = parser.buffer[parser.buffer_pos] + parser.buffer_pos++ + } else { + s = append(s, parser.buffer[parser.buffer_pos:parser.buffer_pos+w]...) + parser.buffer_pos += w + } + parser.mark.index++ + parser.mark.column++ + parser.unread-- + return s +} + +// Copy a line break character to a string buffer and advance pointers. +func read_line(parser *yaml_parser_t, s []byte) []byte { + buf := parser.buffer + pos := parser.buffer_pos + switch { + case buf[pos] == '\r' && buf[pos+1] == '\n': + // CR LF . LF + s = append(s, '\n') + parser.buffer_pos += 2 + parser.mark.index++ + parser.unread-- + case buf[pos] == '\r' || buf[pos] == '\n': + // CR|LF . LF + s = append(s, '\n') + parser.buffer_pos += 1 + case buf[pos] == '\xC2' && buf[pos+1] == '\x85': + // NEL . LF + s = append(s, '\n') + parser.buffer_pos += 2 + case buf[pos] == '\xE2' && buf[pos+1] == '\x80' && (buf[pos+2] == '\xA8' || buf[pos+2] == '\xA9'): + // LS|PS . LS|PS + s = append(s, buf[parser.buffer_pos:pos+3]...) + parser.buffer_pos += 3 + default: + return s + } + parser.mark.index++ + parser.mark.column = 0 + parser.mark.line++ + parser.unread-- + parser.newlines++ + return s +} + +// Get the next token. +func yaml_parser_scan(parser *yaml_parser_t, token *yaml_token_t) bool { + // Erase the token object. + *token = yaml_token_t{} // [Go] Is this necessary? + + // No tokens after STREAM-END or error. + if parser.stream_end_produced || parser.error != yaml_NO_ERROR { + return true + } + + // Ensure that the tokens queue contains enough tokens. + if !parser.token_available { + if !yaml_parser_fetch_more_tokens(parser) { + return false + } + } + + // Fetch the next token from the queue. + *token = parser.tokens[parser.tokens_head] + parser.tokens_head++ + parser.tokens_parsed++ + parser.token_available = false + + if token.typ == yaml_STREAM_END_TOKEN { + parser.stream_end_produced = true + } + return true +} + +// Set the scanner error and return false. +func yaml_parser_set_scanner_error(parser *yaml_parser_t, context string, context_mark yaml_mark_t, problem string) bool { + parser.error = yaml_SCANNER_ERROR + parser.context = context + parser.context_mark = context_mark + parser.problem = problem + parser.problem_mark = parser.mark + return false +} + +func yaml_parser_set_scanner_tag_error(parser *yaml_parser_t, directive bool, context_mark yaml_mark_t, problem string) bool { + context := "while parsing a tag" + if directive { + context = "while parsing a %TAG directive" + } + return yaml_parser_set_scanner_error(parser, context, context_mark, problem) +} + +func trace(args ...interface{}) func() { + pargs := append([]interface{}{"+++"}, args...) + fmt.Println(pargs...) + pargs = append([]interface{}{"---"}, args...) + return func() { fmt.Println(pargs...) } +} + +// Ensure that the tokens queue contains at least one token which can be +// returned to the Parser. +func yaml_parser_fetch_more_tokens(parser *yaml_parser_t) bool { + // While we need more tokens to fetch, do it. + for { + // [Go] The comment parsing logic requires a lookahead of two tokens + // so that foot comments may be parsed in time of associating them + // with the tokens that are parsed before them, and also for line + // comments to be transformed into head comments in some edge cases. + if parser.tokens_head < len(parser.tokens)-2 { + // If a potential simple key is at the head position, we need to fetch + // the next token to disambiguate it. + head_tok_idx, ok := parser.simple_keys_by_tok[parser.tokens_parsed] + if !ok { + break + } else if valid, ok := yaml_simple_key_is_valid(parser, &parser.simple_keys[head_tok_idx]); !ok { + return false + } else if !valid { + break + } + } + // Fetch the next token. + if !yaml_parser_fetch_next_token(parser) { + return false + } + } + + parser.token_available = true + return true +} + +// The dispatcher for token fetchers. +func yaml_parser_fetch_next_token(parser *yaml_parser_t) (ok bool) { + // Ensure that the buffer is initialized. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + // Check if we just started scanning. Fetch STREAM-START then. + if !parser.stream_start_produced { + return yaml_parser_fetch_stream_start(parser) + } + + scan_mark := parser.mark + + // Eat whitespaces and comments until we reach the next token. + if !yaml_parser_scan_to_next_token(parser) { + return false + } + + // [Go] While unrolling indents, transform the head comments of prior + // indentation levels observed after scan_start into foot comments at + // the respective indexes. + + // Check the indentation level against the current column. + if !yaml_parser_unroll_indent(parser, parser.mark.column, scan_mark) { + return false + } + + // Ensure that the buffer contains at least 4 characters. 4 is the length + // of the longest indicators ('--- ' and '... '). + if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) { + return false + } + + // Is it the end of the stream? + if is_z(parser.buffer, parser.buffer_pos) { + return yaml_parser_fetch_stream_end(parser) + } + + // Is it a directive? + if parser.mark.column == 0 && parser.buffer[parser.buffer_pos] == '%' { + return yaml_parser_fetch_directive(parser) + } + + buf := parser.buffer + pos := parser.buffer_pos + + // Is it the document start indicator? + if parser.mark.column == 0 && buf[pos] == '-' && buf[pos+1] == '-' && buf[pos+2] == '-' && is_blankz(buf, pos+3) { + return yaml_parser_fetch_document_indicator(parser, yaml_DOCUMENT_START_TOKEN) + } + + // Is it the document end indicator? + if parser.mark.column == 0 && buf[pos] == '.' && buf[pos+1] == '.' && buf[pos+2] == '.' && is_blankz(buf, pos+3) { + return yaml_parser_fetch_document_indicator(parser, yaml_DOCUMENT_END_TOKEN) + } + + comment_mark := parser.mark + if len(parser.tokens) > 0 && (parser.flow_level == 0 && buf[pos] == ':' || parser.flow_level > 0 && buf[pos] == ',') { + // Associate any following comments with the prior token. + comment_mark = parser.tokens[len(parser.tokens)-1].start_mark + } + defer func() { + if !ok { + return + } + if len(parser.tokens) > 0 && parser.tokens[len(parser.tokens)-1].typ == yaml_BLOCK_ENTRY_TOKEN { + // Sequence indicators alone have no line comments. It becomes + // a head comment for whatever follows. + return + } + if !yaml_parser_scan_line_comment(parser, comment_mark) { + ok = false + return + } + }() + + // Is it the flow sequence start indicator? + if buf[pos] == '[' { + return yaml_parser_fetch_flow_collection_start(parser, yaml_FLOW_SEQUENCE_START_TOKEN) + } + + // Is it the flow mapping start indicator? + if parser.buffer[parser.buffer_pos] == '{' { + return yaml_parser_fetch_flow_collection_start(parser, yaml_FLOW_MAPPING_START_TOKEN) + } + + // Is it the flow sequence end indicator? + if parser.buffer[parser.buffer_pos] == ']' { + return yaml_parser_fetch_flow_collection_end(parser, + yaml_FLOW_SEQUENCE_END_TOKEN) + } + + // Is it the flow mapping end indicator? + if parser.buffer[parser.buffer_pos] == '}' { + return yaml_parser_fetch_flow_collection_end(parser, + yaml_FLOW_MAPPING_END_TOKEN) + } + + // Is it the flow entry indicator? + if parser.buffer[parser.buffer_pos] == ',' { + return yaml_parser_fetch_flow_entry(parser) + } + + // Is it the block entry indicator? + if parser.buffer[parser.buffer_pos] == '-' && is_blankz(parser.buffer, parser.buffer_pos+1) { + return yaml_parser_fetch_block_entry(parser) + } + + // Is it the key indicator? + if parser.buffer[parser.buffer_pos] == '?' && (parser.flow_level > 0 || is_blankz(parser.buffer, parser.buffer_pos+1)) { + return yaml_parser_fetch_key(parser) + } + + // Is it the value indicator? + if parser.buffer[parser.buffer_pos] == ':' && (parser.flow_level > 0 || is_blankz(parser.buffer, parser.buffer_pos+1)) { + return yaml_parser_fetch_value(parser) + } + + // Is it an alias? + if parser.buffer[parser.buffer_pos] == '*' { + return yaml_parser_fetch_anchor(parser, yaml_ALIAS_TOKEN) + } + + // Is it an anchor? + if parser.buffer[parser.buffer_pos] == '&' { + return yaml_parser_fetch_anchor(parser, yaml_ANCHOR_TOKEN) + } + + // Is it a tag? + if parser.buffer[parser.buffer_pos] == '!' { + return yaml_parser_fetch_tag(parser) + } + + // Is it a literal scalar? + if parser.buffer[parser.buffer_pos] == '|' && parser.flow_level == 0 { + return yaml_parser_fetch_block_scalar(parser, true) + } + + // Is it a folded scalar? + if parser.buffer[parser.buffer_pos] == '>' && parser.flow_level == 0 { + return yaml_parser_fetch_block_scalar(parser, false) + } + + // Is it a single-quoted scalar? + if parser.buffer[parser.buffer_pos] == '\'' { + return yaml_parser_fetch_flow_scalar(parser, true) + } + + // Is it a double-quoted scalar? + if parser.buffer[parser.buffer_pos] == '"' { + return yaml_parser_fetch_flow_scalar(parser, false) + } + + // Is it a plain scalar? + // + // A plain scalar may start with any non-blank characters except + // + // '-', '?', ':', ',', '[', ']', '{', '}', + // '#', '&', '*', '!', '|', '>', '\'', '\"', + // '%', '@', '`'. + // + // In the block context (and, for the '-' indicator, in the flow context + // too), it may also start with the characters + // + // '-', '?', ':' + // + // if it is followed by a non-space character. + // + // The last rule is more restrictive than the specification requires. + // [Go] TODO Make this logic more reasonable. + //switch parser.buffer[parser.buffer_pos] { + //case '-', '?', ':', ',', '?', '-', ',', ':', ']', '[', '}', '{', '&', '#', '!', '*', '>', '|', '"', '\'', '@', '%', '-', '`': + //} + if !(is_blankz(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == '-' || + parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':' || + parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == '[' || + parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '{' || + parser.buffer[parser.buffer_pos] == '}' || parser.buffer[parser.buffer_pos] == '#' || + parser.buffer[parser.buffer_pos] == '&' || parser.buffer[parser.buffer_pos] == '*' || + parser.buffer[parser.buffer_pos] == '!' || parser.buffer[parser.buffer_pos] == '|' || + parser.buffer[parser.buffer_pos] == '>' || parser.buffer[parser.buffer_pos] == '\'' || + parser.buffer[parser.buffer_pos] == '"' || parser.buffer[parser.buffer_pos] == '%' || + parser.buffer[parser.buffer_pos] == '@' || parser.buffer[parser.buffer_pos] == '`') || + (parser.buffer[parser.buffer_pos] == '-' && !is_blank(parser.buffer, parser.buffer_pos+1)) || + (parser.flow_level == 0 && + (parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':') && + !is_blankz(parser.buffer, parser.buffer_pos+1)) { + return yaml_parser_fetch_plain_scalar(parser) + } + + // If we don't determine the token type so far, it is an error. + return yaml_parser_set_scanner_error(parser, + "while scanning for the next token", parser.mark, + "found character that cannot start any token") +} + +func yaml_simple_key_is_valid(parser *yaml_parser_t, simple_key *yaml_simple_key_t) (valid, ok bool) { + if !simple_key.possible { + return false, true + } + + // The 1.2 specification says: + // + // "If the ? indicator is omitted, parsing needs to see past the + // implicit key to recognize it as such. To limit the amount of + // lookahead required, the “:†indicator must appear at most 1024 + // Unicode characters beyond the start of the key. In addition, the key + // is restricted to a single line." + // + if simple_key.mark.line < parser.mark.line || simple_key.mark.index+1024 < parser.mark.index { + // Check if the potential simple key to be removed is required. + if simple_key.required { + return false, yaml_parser_set_scanner_error(parser, + "while scanning a simple key", simple_key.mark, + "could not find expected ':'") + } + simple_key.possible = false + return false, true + } + return true, true +} + +// Check if a simple key may start at the current position and add it if +// needed. +func yaml_parser_save_simple_key(parser *yaml_parser_t) bool { + // A simple key is required at the current position if the scanner is in + // the block context and the current column coincides with the indentation + // level. + + required := parser.flow_level == 0 && parser.indent == parser.mark.column + + // + // If the current position may start a simple key, save it. + // + if parser.simple_key_allowed { + simple_key := yaml_simple_key_t{ + possible: true, + required: required, + token_number: parser.tokens_parsed + (len(parser.tokens) - parser.tokens_head), + mark: parser.mark, + } + + if !yaml_parser_remove_simple_key(parser) { + return false + } + parser.simple_keys[len(parser.simple_keys)-1] = simple_key + parser.simple_keys_by_tok[simple_key.token_number] = len(parser.simple_keys) - 1 + } + return true +} + +// Remove a potential simple key at the current flow level. +func yaml_parser_remove_simple_key(parser *yaml_parser_t) bool { + i := len(parser.simple_keys) - 1 + if parser.simple_keys[i].possible { + // If the key is required, it is an error. + if parser.simple_keys[i].required { + return yaml_parser_set_scanner_error(parser, + "while scanning a simple key", parser.simple_keys[i].mark, + "could not find expected ':'") + } + // Remove the key from the stack. + parser.simple_keys[i].possible = false + delete(parser.simple_keys_by_tok, parser.simple_keys[i].token_number) + } + return true +} + +// max_flow_level limits the flow_level +const max_flow_level = 10000 + +// Increase the flow level and resize the simple key list if needed. +func yaml_parser_increase_flow_level(parser *yaml_parser_t) bool { + // Reset the simple key on the next level. + parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{ + possible: false, + required: false, + token_number: parser.tokens_parsed + (len(parser.tokens) - parser.tokens_head), + mark: parser.mark, + }) + + // Increase the flow level. + parser.flow_level++ + if parser.flow_level > max_flow_level { + return yaml_parser_set_scanner_error(parser, + "while increasing flow level", parser.simple_keys[len(parser.simple_keys)-1].mark, + fmt.Sprintf("exceeded max depth of %d", max_flow_level)) + } + return true +} + +// Decrease the flow level. +func yaml_parser_decrease_flow_level(parser *yaml_parser_t) bool { + if parser.flow_level > 0 { + parser.flow_level-- + last := len(parser.simple_keys) - 1 + delete(parser.simple_keys_by_tok, parser.simple_keys[last].token_number) + parser.simple_keys = parser.simple_keys[:last] + } + return true +} + +// max_indents limits the indents stack size +const max_indents = 10000 + +// Push the current indentation level to the stack and set the new level +// the current column is greater than the indentation level. In this case, +// append or insert the specified token into the token queue. +func yaml_parser_roll_indent(parser *yaml_parser_t, column, number int, typ yaml_token_type_t, mark yaml_mark_t) bool { + // In the flow context, do nothing. + if parser.flow_level > 0 { + return true + } + + if parser.indent < column { + // Push the current indentation level to the stack and set the new + // indentation level. + parser.indents = append(parser.indents, parser.indent) + parser.indent = column + if len(parser.indents) > max_indents { + return yaml_parser_set_scanner_error(parser, + "while increasing indent level", parser.simple_keys[len(parser.simple_keys)-1].mark, + fmt.Sprintf("exceeded max depth of %d", max_indents)) + } + + // Create a token and insert it into the queue. + token := yaml_token_t{ + typ: typ, + start_mark: mark, + end_mark: mark, + } + if number > -1 { + number -= parser.tokens_parsed + } + yaml_insert_token(parser, number, &token) + } + return true +} + +// Pop indentation levels from the indents stack until the current level +// becomes less or equal to the column. For each indentation level, append +// the BLOCK-END token. +func yaml_parser_unroll_indent(parser *yaml_parser_t, column int, scan_mark yaml_mark_t) bool { + // In the flow context, do nothing. + if parser.flow_level > 0 { + return true + } + + block_mark := scan_mark + block_mark.index-- + + // Loop through the indentation levels in the stack. + for parser.indent > column { + + // [Go] Reposition the end token before potential following + // foot comments of parent blocks. For that, search + // backwards for recent comments that were at the same + // indent as the block that is ending now. + stop_index := block_mark.index + for i := len(parser.comments) - 1; i >= 0; i-- { + comment := &parser.comments[i] + + if comment.end_mark.index < stop_index { + // Don't go back beyond the start of the comment/whitespace scan, unless column < 0. + // If requested indent column is < 0, then the document is over and everything else + // is a foot anyway. + break + } + if comment.start_mark.column == parser.indent+1 { + // This is a good match. But maybe there's a former comment + // at that same indent level, so keep searching. + block_mark = comment.start_mark + } + + // While the end of the former comment matches with + // the start of the following one, we know there's + // nothing in between and scanning is still safe. + stop_index = comment.scan_mark.index + } + + // Create a token and append it to the queue. + token := yaml_token_t{ + typ: yaml_BLOCK_END_TOKEN, + start_mark: block_mark, + end_mark: block_mark, + } + yaml_insert_token(parser, -1, &token) + + // Pop the indentation level. + parser.indent = parser.indents[len(parser.indents)-1] + parser.indents = parser.indents[:len(parser.indents)-1] + } + return true +} + +// Initialize the scanner and produce the STREAM-START token. +func yaml_parser_fetch_stream_start(parser *yaml_parser_t) bool { + + // Set the initial indentation. + parser.indent = -1 + + // Initialize the simple key stack. + parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{}) + + parser.simple_keys_by_tok = make(map[int]int) + + // A simple key is allowed at the beginning of the stream. + parser.simple_key_allowed = true + + // We have started. + parser.stream_start_produced = true + + // Create the STREAM-START token and append it to the queue. + token := yaml_token_t{ + typ: yaml_STREAM_START_TOKEN, + start_mark: parser.mark, + end_mark: parser.mark, + encoding: parser.encoding, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the STREAM-END token and shut down the scanner. +func yaml_parser_fetch_stream_end(parser *yaml_parser_t) bool { + + // Force new line. + if parser.mark.column != 0 { + parser.mark.column = 0 + parser.mark.line++ + } + + // Reset the indentation level. + if !yaml_parser_unroll_indent(parser, -1, parser.mark) { + return false + } + + // Reset simple keys. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + parser.simple_key_allowed = false + + // Create the STREAM-END token and append it to the queue. + token := yaml_token_t{ + typ: yaml_STREAM_END_TOKEN, + start_mark: parser.mark, + end_mark: parser.mark, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce a VERSION-DIRECTIVE or TAG-DIRECTIVE token. +func yaml_parser_fetch_directive(parser *yaml_parser_t) bool { + // Reset the indentation level. + if !yaml_parser_unroll_indent(parser, -1, parser.mark) { + return false + } + + // Reset simple keys. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + parser.simple_key_allowed = false + + // Create the YAML-DIRECTIVE or TAG-DIRECTIVE token. + token := yaml_token_t{} + if !yaml_parser_scan_directive(parser, &token) { + return false + } + // Append the token to the queue. + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the DOCUMENT-START or DOCUMENT-END token. +func yaml_parser_fetch_document_indicator(parser *yaml_parser_t, typ yaml_token_type_t) bool { + // Reset the indentation level. + if !yaml_parser_unroll_indent(parser, -1, parser.mark) { + return false + } + + // Reset simple keys. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + parser.simple_key_allowed = false + + // Consume the token. + start_mark := parser.mark + + skip(parser) + skip(parser) + skip(parser) + + end_mark := parser.mark + + // Create the DOCUMENT-START or DOCUMENT-END token. + token := yaml_token_t{ + typ: typ, + start_mark: start_mark, + end_mark: end_mark, + } + // Append the token to the queue. + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the FLOW-SEQUENCE-START or FLOW-MAPPING-START token. +func yaml_parser_fetch_flow_collection_start(parser *yaml_parser_t, typ yaml_token_type_t) bool { + + // The indicators '[' and '{' may start a simple key. + if !yaml_parser_save_simple_key(parser) { + return false + } + + // Increase the flow level. + if !yaml_parser_increase_flow_level(parser) { + return false + } + + // A simple key may follow the indicators '[' and '{'. + parser.simple_key_allowed = true + + // Consume the token. + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the FLOW-SEQUENCE-START of FLOW-MAPPING-START token. + token := yaml_token_t{ + typ: typ, + start_mark: start_mark, + end_mark: end_mark, + } + // Append the token to the queue. + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the FLOW-SEQUENCE-END or FLOW-MAPPING-END token. +func yaml_parser_fetch_flow_collection_end(parser *yaml_parser_t, typ yaml_token_type_t) bool { + // Reset any potential simple key on the current flow level. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + // Decrease the flow level. + if !yaml_parser_decrease_flow_level(parser) { + return false + } + + // No simple keys after the indicators ']' and '}'. + parser.simple_key_allowed = false + + // Consume the token. + + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the FLOW-SEQUENCE-END of FLOW-MAPPING-END token. + token := yaml_token_t{ + typ: typ, + start_mark: start_mark, + end_mark: end_mark, + } + // Append the token to the queue. + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the FLOW-ENTRY token. +func yaml_parser_fetch_flow_entry(parser *yaml_parser_t) bool { + // Reset any potential simple keys on the current flow level. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + // Simple keys are allowed after ','. + parser.simple_key_allowed = true + + // Consume the token. + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the FLOW-ENTRY token and append it to the queue. + token := yaml_token_t{ + typ: yaml_FLOW_ENTRY_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the BLOCK-ENTRY token. +func yaml_parser_fetch_block_entry(parser *yaml_parser_t) bool { + // Check if the scanner is in the block context. + if parser.flow_level == 0 { + // Check if we are allowed to start a new entry. + if !parser.simple_key_allowed { + return yaml_parser_set_scanner_error(parser, "", parser.mark, + "block sequence entries are not allowed in this context") + } + // Add the BLOCK-SEQUENCE-START token if needed. + if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_SEQUENCE_START_TOKEN, parser.mark) { + return false + } + } else { + // It is an error for the '-' indicator to occur in the flow context, + // but we let the Parser detect and report about it because the Parser + // is able to point to the context. + } + + // Reset any potential simple keys on the current flow level. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + // Simple keys are allowed after '-'. + parser.simple_key_allowed = true + + // Consume the token. + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the BLOCK-ENTRY token and append it to the queue. + token := yaml_token_t{ + typ: yaml_BLOCK_ENTRY_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the KEY token. +func yaml_parser_fetch_key(parser *yaml_parser_t) bool { + + // In the block context, additional checks are required. + if parser.flow_level == 0 { + // Check if we are allowed to start a new key (not nessesary simple). + if !parser.simple_key_allowed { + return yaml_parser_set_scanner_error(parser, "", parser.mark, + "mapping keys are not allowed in this context") + } + // Add the BLOCK-MAPPING-START token if needed. + if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_MAPPING_START_TOKEN, parser.mark) { + return false + } + } + + // Reset any potential simple keys on the current flow level. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + // Simple keys are allowed after '?' in the block context. + parser.simple_key_allowed = parser.flow_level == 0 + + // Consume the token. + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the KEY token and append it to the queue. + token := yaml_token_t{ + typ: yaml_KEY_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the VALUE token. +func yaml_parser_fetch_value(parser *yaml_parser_t) bool { + + simple_key := &parser.simple_keys[len(parser.simple_keys)-1] + + // Have we found a simple key? + if valid, ok := yaml_simple_key_is_valid(parser, simple_key); !ok { + return false + + } else if valid { + + // Create the KEY token and insert it into the queue. + token := yaml_token_t{ + typ: yaml_KEY_TOKEN, + start_mark: simple_key.mark, + end_mark: simple_key.mark, + } + yaml_insert_token(parser, simple_key.token_number-parser.tokens_parsed, &token) + + // In the block context, we may need to add the BLOCK-MAPPING-START token. + if !yaml_parser_roll_indent(parser, simple_key.mark.column, + simple_key.token_number, + yaml_BLOCK_MAPPING_START_TOKEN, simple_key.mark) { + return false + } + + // Remove the simple key. + simple_key.possible = false + delete(parser.simple_keys_by_tok, simple_key.token_number) + + // A simple key cannot follow another simple key. + parser.simple_key_allowed = false + + } else { + // The ':' indicator follows a complex key. + + // In the block context, extra checks are required. + if parser.flow_level == 0 { + + // Check if we are allowed to start a complex value. + if !parser.simple_key_allowed { + return yaml_parser_set_scanner_error(parser, "", parser.mark, + "mapping values are not allowed in this context") + } + + // Add the BLOCK-MAPPING-START token if needed. + if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_MAPPING_START_TOKEN, parser.mark) { + return false + } + } + + // Simple keys after ':' are allowed in the block context. + parser.simple_key_allowed = parser.flow_level == 0 + } + + // Consume the token. + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the VALUE token and append it to the queue. + token := yaml_token_t{ + typ: yaml_VALUE_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the ALIAS or ANCHOR token. +func yaml_parser_fetch_anchor(parser *yaml_parser_t, typ yaml_token_type_t) bool { + // An anchor or an alias could be a simple key. + if !yaml_parser_save_simple_key(parser) { + return false + } + + // A simple key cannot follow an anchor or an alias. + parser.simple_key_allowed = false + + // Create the ALIAS or ANCHOR token and append it to the queue. + var token yaml_token_t + if !yaml_parser_scan_anchor(parser, &token, typ) { + return false + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the TAG token. +func yaml_parser_fetch_tag(parser *yaml_parser_t) bool { + // A tag could be a simple key. + if !yaml_parser_save_simple_key(parser) { + return false + } + + // A simple key cannot follow a tag. + parser.simple_key_allowed = false + + // Create the TAG token and append it to the queue. + var token yaml_token_t + if !yaml_parser_scan_tag(parser, &token) { + return false + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the SCALAR(...,literal) or SCALAR(...,folded) tokens. +func yaml_parser_fetch_block_scalar(parser *yaml_parser_t, literal bool) bool { + // Remove any potential simple keys. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + // A simple key may follow a block scalar. + parser.simple_key_allowed = true + + // Create the SCALAR token and append it to the queue. + var token yaml_token_t + if !yaml_parser_scan_block_scalar(parser, &token, literal) { + return false + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the SCALAR(...,single-quoted) or SCALAR(...,double-quoted) tokens. +func yaml_parser_fetch_flow_scalar(parser *yaml_parser_t, single bool) bool { + // A plain scalar could be a simple key. + if !yaml_parser_save_simple_key(parser) { + return false + } + + // A simple key cannot follow a flow scalar. + parser.simple_key_allowed = false + + // Create the SCALAR token and append it to the queue. + var token yaml_token_t + if !yaml_parser_scan_flow_scalar(parser, &token, single) { + return false + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the SCALAR(...,plain) token. +func yaml_parser_fetch_plain_scalar(parser *yaml_parser_t) bool { + // A plain scalar could be a simple key. + if !yaml_parser_save_simple_key(parser) { + return false + } + + // A simple key cannot follow a flow scalar. + parser.simple_key_allowed = false + + // Create the SCALAR token and append it to the queue. + var token yaml_token_t + if !yaml_parser_scan_plain_scalar(parser, &token) { + return false + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Eat whitespaces and comments until the next token is found. +func yaml_parser_scan_to_next_token(parser *yaml_parser_t) bool { + + scan_mark := parser.mark + + // Until the next token is not found. + for { + // Allow the BOM mark to start a line. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if parser.mark.column == 0 && is_bom(parser.buffer, parser.buffer_pos) { + skip(parser) + } + + // Eat whitespaces. + // Tabs are allowed: + // - in the flow context + // - in the block context, but not at the beginning of the line or + // after '-', '?', or ':' (complex value). + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + for parser.buffer[parser.buffer_pos] == ' ' || ((parser.flow_level > 0 || !parser.simple_key_allowed) && parser.buffer[parser.buffer_pos] == '\t') { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Check if we just had a line comment under a sequence entry that + // looks more like a header to the following content. Similar to this: + // + // - # The comment + // - Some data + // + // If so, transform the line comment to a head comment and reposition. + if len(parser.comments) > 0 && len(parser.tokens) > 1 { + tokenA := parser.tokens[len(parser.tokens)-2] + tokenB := parser.tokens[len(parser.tokens)-1] + comment := &parser.comments[len(parser.comments)-1] + if tokenA.typ == yaml_BLOCK_SEQUENCE_START_TOKEN && tokenB.typ == yaml_BLOCK_ENTRY_TOKEN && len(comment.line) > 0 && !is_break(parser.buffer, parser.buffer_pos) { + // If it was in the prior line, reposition so it becomes a + // header of the follow up token. Otherwise, keep it in place + // so it becomes a header of the former. + comment.head = comment.line + comment.line = nil + if comment.start_mark.line == parser.mark.line-1 { + comment.token_mark = parser.mark + } + } + } + + // Eat a comment until a line break. + if parser.buffer[parser.buffer_pos] == '#' { + if !yaml_parser_scan_comments(parser, scan_mark) { + return false + } + } + + // If it is a line break, eat it. + if is_break(parser.buffer, parser.buffer_pos) { + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + skip_line(parser) + + // In the block context, a new line may start a simple key. + if parser.flow_level == 0 { + parser.simple_key_allowed = true + } + } else { + break // We have found a token. + } + } + + return true +} + +// Scan a YAML-DIRECTIVE or TAG-DIRECTIVE token. +// +// Scope: +// %YAML 1.1 # a comment \n +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// %TAG !yaml! tag:yaml.org,2002: \n +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// +func yaml_parser_scan_directive(parser *yaml_parser_t, token *yaml_token_t) bool { + // Eat '%'. + start_mark := parser.mark + skip(parser) + + // Scan the directive name. + var name []byte + if !yaml_parser_scan_directive_name(parser, start_mark, &name) { + return false + } + + // Is it a YAML directive? + if bytes.Equal(name, []byte("YAML")) { + // Scan the VERSION directive value. + var major, minor int8 + if !yaml_parser_scan_version_directive_value(parser, start_mark, &major, &minor) { + return false + } + end_mark := parser.mark + + // Create a VERSION-DIRECTIVE token. + *token = yaml_token_t{ + typ: yaml_VERSION_DIRECTIVE_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + major: major, + minor: minor, + } + + // Is it a TAG directive? + } else if bytes.Equal(name, []byte("TAG")) { + // Scan the TAG directive value. + var handle, prefix []byte + if !yaml_parser_scan_tag_directive_value(parser, start_mark, &handle, &prefix) { + return false + } + end_mark := parser.mark + + // Create a TAG-DIRECTIVE token. + *token = yaml_token_t{ + typ: yaml_TAG_DIRECTIVE_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + value: handle, + prefix: prefix, + } + + // Unknown directive. + } else { + yaml_parser_set_scanner_error(parser, "while scanning a directive", + start_mark, "found unknown directive name") + return false + } + + // Eat the rest of the line including any comments. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + for is_blank(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + if parser.buffer[parser.buffer_pos] == '#' { + // [Go] Discard this inline comment for the time being. + //if !yaml_parser_scan_line_comment(parser, start_mark) { + // return false + //} + for !is_breakz(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + } + + // Check if we are at the end of the line. + if !is_breakz(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a directive", + start_mark, "did not find expected comment or line break") + return false + } + + // Eat a line break. + if is_break(parser.buffer, parser.buffer_pos) { + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + skip_line(parser) + } + + return true +} + +// Scan the directive name. +// +// Scope: +// %YAML 1.1 # a comment \n +// ^^^^ +// %TAG !yaml! tag:yaml.org,2002: \n +// ^^^ +// +func yaml_parser_scan_directive_name(parser *yaml_parser_t, start_mark yaml_mark_t, name *[]byte) bool { + // Consume the directive name. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + var s []byte + for is_alpha(parser.buffer, parser.buffer_pos) { + s = read(parser, s) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Check if the name is empty. + if len(s) == 0 { + yaml_parser_set_scanner_error(parser, "while scanning a directive", + start_mark, "could not find expected directive name") + return false + } + + // Check for an blank character after the name. + if !is_blankz(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a directive", + start_mark, "found unexpected non-alphabetical character") + return false + } + *name = s + return true +} + +// Scan the value of VERSION-DIRECTIVE. +// +// Scope: +// %YAML 1.1 # a comment \n +// ^^^^^^ +func yaml_parser_scan_version_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, major, minor *int8) bool { + // Eat whitespaces. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + for is_blank(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Consume the major version number. + if !yaml_parser_scan_version_directive_number(parser, start_mark, major) { + return false + } + + // Eat '.'. + if parser.buffer[parser.buffer_pos] != '.' { + return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", + start_mark, "did not find expected digit or '.' character") + } + + skip(parser) + + // Consume the minor version number. + if !yaml_parser_scan_version_directive_number(parser, start_mark, minor) { + return false + } + return true +} + +const max_number_length = 2 + +// Scan the version number of VERSION-DIRECTIVE. +// +// Scope: +// %YAML 1.1 # a comment \n +// ^ +// %YAML 1.1 # a comment \n +// ^ +func yaml_parser_scan_version_directive_number(parser *yaml_parser_t, start_mark yaml_mark_t, number *int8) bool { + + // Repeat while the next character is digit. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + var value, length int8 + for is_digit(parser.buffer, parser.buffer_pos) { + // Check if the number is too long. + length++ + if length > max_number_length { + return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", + start_mark, "found extremely long version number") + } + value = value*10 + int8(as_digit(parser.buffer, parser.buffer_pos)) + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Check if the number was present. + if length == 0 { + return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", + start_mark, "did not find expected version number") + } + *number = value + return true +} + +// Scan the value of a TAG-DIRECTIVE token. +// +// Scope: +// %TAG !yaml! tag:yaml.org,2002: \n +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// +func yaml_parser_scan_tag_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, handle, prefix *[]byte) bool { + var handle_value, prefix_value []byte + + // Eat whitespaces. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + for is_blank(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Scan a handle. + if !yaml_parser_scan_tag_handle(parser, true, start_mark, &handle_value) { + return false + } + + // Expect a whitespace. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if !is_blank(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a %TAG directive", + start_mark, "did not find expected whitespace") + return false + } + + // Eat whitespaces. + for is_blank(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Scan a prefix. + if !yaml_parser_scan_tag_uri(parser, true, nil, start_mark, &prefix_value) { + return false + } + + // Expect a whitespace or line break. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if !is_blankz(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a %TAG directive", + start_mark, "did not find expected whitespace or line break") + return false + } + + *handle = handle_value + *prefix = prefix_value + return true +} + +func yaml_parser_scan_anchor(parser *yaml_parser_t, token *yaml_token_t, typ yaml_token_type_t) bool { + var s []byte + + // Eat the indicator character. + start_mark := parser.mark + skip(parser) + + // Consume the value. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + for is_alpha(parser.buffer, parser.buffer_pos) { + s = read(parser, s) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + end_mark := parser.mark + + /* + * Check if length of the anchor is greater than 0 and it is followed by + * a whitespace character or one of the indicators: + * + * '?', ':', ',', ']', '}', '%', '@', '`'. + */ + + if len(s) == 0 || + !(is_blankz(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == '?' || + parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == ',' || + parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '}' || + parser.buffer[parser.buffer_pos] == '%' || parser.buffer[parser.buffer_pos] == '@' || + parser.buffer[parser.buffer_pos] == '`') { + context := "while scanning an alias" + if typ == yaml_ANCHOR_TOKEN { + context = "while scanning an anchor" + } + yaml_parser_set_scanner_error(parser, context, start_mark, + "did not find expected alphabetic or numeric character") + return false + } + + // Create a token. + *token = yaml_token_t{ + typ: typ, + start_mark: start_mark, + end_mark: end_mark, + value: s, + } + + return true +} + +/* + * Scan a TAG token. + */ + +func yaml_parser_scan_tag(parser *yaml_parser_t, token *yaml_token_t) bool { + var handle, suffix []byte + + start_mark := parser.mark + + // Check if the tag is in the canonical form. + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + + if parser.buffer[parser.buffer_pos+1] == '<' { + // Keep the handle as '' + + // Eat '!<' + skip(parser) + skip(parser) + + // Consume the tag value. + if !yaml_parser_scan_tag_uri(parser, false, nil, start_mark, &suffix) { + return false + } + + // Check for '>' and eat it. + if parser.buffer[parser.buffer_pos] != '>' { + yaml_parser_set_scanner_error(parser, "while scanning a tag", + start_mark, "did not find the expected '>'") + return false + } + + skip(parser) + } else { + // The tag has either the '!suffix' or the '!handle!suffix' form. + + // First, try to scan a handle. + if !yaml_parser_scan_tag_handle(parser, false, start_mark, &handle) { + return false + } + + // Check if it is, indeed, handle. + if handle[0] == '!' && len(handle) > 1 && handle[len(handle)-1] == '!' { + // Scan the suffix now. + if !yaml_parser_scan_tag_uri(parser, false, nil, start_mark, &suffix) { + return false + } + } else { + // It wasn't a handle after all. Scan the rest of the tag. + if !yaml_parser_scan_tag_uri(parser, false, handle, start_mark, &suffix) { + return false + } + + // Set the handle to '!'. + handle = []byte{'!'} + + // A special case: the '!' tag. Set the handle to '' and the + // suffix to '!'. + if len(suffix) == 0 { + handle, suffix = suffix, handle + } + } + } + + // Check the character which ends the tag. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if !is_blankz(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a tag", + start_mark, "did not find expected whitespace or line break") + return false + } + + end_mark := parser.mark + + // Create a token. + *token = yaml_token_t{ + typ: yaml_TAG_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + value: handle, + suffix: suffix, + } + return true +} + +// Scan a tag handle. +func yaml_parser_scan_tag_handle(parser *yaml_parser_t, directive bool, start_mark yaml_mark_t, handle *[]byte) bool { + // Check the initial '!' character. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if parser.buffer[parser.buffer_pos] != '!' { + yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "did not find expected '!'") + return false + } + + var s []byte + + // Copy the '!' character. + s = read(parser, s) + + // Copy all subsequent alphabetical and numerical characters. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + for is_alpha(parser.buffer, parser.buffer_pos) { + s = read(parser, s) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Check if the trailing character is '!' and copy it. + if parser.buffer[parser.buffer_pos] == '!' { + s = read(parser, s) + } else { + // It's either the '!' tag or not really a tag handle. If it's a %TAG + // directive, it's an error. If it's a tag token, it must be a part of URI. + if directive && string(s) != "!" { + yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "did not find expected '!'") + return false + } + } + + *handle = s + return true +} + +// Scan a tag. +func yaml_parser_scan_tag_uri(parser *yaml_parser_t, directive bool, head []byte, start_mark yaml_mark_t, uri *[]byte) bool { + //size_t length = head ? strlen((char *)head) : 0 + var s []byte + hasTag := len(head) > 0 + + // Copy the head if needed. + // + // Note that we don't copy the leading '!' character. + if len(head) > 1 { + s = append(s, head[1:]...) + } + + // Scan the tag. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + // The set of characters that may appear in URI is as follows: + // + // '0'-'9', 'A'-'Z', 'a'-'z', '_', '-', ';', '/', '?', ':', '@', '&', + // '=', '+', '$', ',', '.', '!', '~', '*', '\'', '(', ')', '[', ']', + // '%'. + // [Go] TODO Convert this into more reasonable logic. + for is_alpha(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == ';' || + parser.buffer[parser.buffer_pos] == '/' || parser.buffer[parser.buffer_pos] == '?' || + parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == '@' || + parser.buffer[parser.buffer_pos] == '&' || parser.buffer[parser.buffer_pos] == '=' || + parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '$' || + parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == '.' || + parser.buffer[parser.buffer_pos] == '!' || parser.buffer[parser.buffer_pos] == '~' || + parser.buffer[parser.buffer_pos] == '*' || parser.buffer[parser.buffer_pos] == '\'' || + parser.buffer[parser.buffer_pos] == '(' || parser.buffer[parser.buffer_pos] == ')' || + parser.buffer[parser.buffer_pos] == '[' || parser.buffer[parser.buffer_pos] == ']' || + parser.buffer[parser.buffer_pos] == '%' { + // Check if it is a URI-escape sequence. + if parser.buffer[parser.buffer_pos] == '%' { + if !yaml_parser_scan_uri_escapes(parser, directive, start_mark, &s) { + return false + } + } else { + s = read(parser, s) + } + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + hasTag = true + } + + if !hasTag { + yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "did not find expected tag URI") + return false + } + *uri = s + return true +} + +// Decode an URI-escape sequence corresponding to a single UTF-8 character. +func yaml_parser_scan_uri_escapes(parser *yaml_parser_t, directive bool, start_mark yaml_mark_t, s *[]byte) bool { + + // Decode the required number of characters. + w := 1024 + for w > 0 { + // Check for a URI-escaped octet. + if parser.unread < 3 && !yaml_parser_update_buffer(parser, 3) { + return false + } + + if !(parser.buffer[parser.buffer_pos] == '%' && + is_hex(parser.buffer, parser.buffer_pos+1) && + is_hex(parser.buffer, parser.buffer_pos+2)) { + return yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "did not find URI escaped octet") + } + + // Get the octet. + octet := byte((as_hex(parser.buffer, parser.buffer_pos+1) << 4) + as_hex(parser.buffer, parser.buffer_pos+2)) + + // If it is the leading octet, determine the length of the UTF-8 sequence. + if w == 1024 { + w = width(octet) + if w == 0 { + return yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "found an incorrect leading UTF-8 octet") + } + } else { + // Check if the trailing octet is correct. + if octet&0xC0 != 0x80 { + return yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "found an incorrect trailing UTF-8 octet") + } + } + + // Copy the octet and move the pointers. + *s = append(*s, octet) + skip(parser) + skip(parser) + skip(parser) + w-- + } + return true +} + +// Scan a block scalar. +func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, literal bool) bool { + // Eat the indicator '|' or '>'. + start_mark := parser.mark + skip(parser) + + // Scan the additional block scalar indicators. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + // Check for a chomping indicator. + var chomping, increment int + if parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '-' { + // Set the chomping method and eat the indicator. + if parser.buffer[parser.buffer_pos] == '+' { + chomping = +1 + } else { + chomping = -1 + } + skip(parser) + + // Check for an indentation indicator. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if is_digit(parser.buffer, parser.buffer_pos) { + // Check that the indentation is greater than 0. + if parser.buffer[parser.buffer_pos] == '0' { + yaml_parser_set_scanner_error(parser, "while scanning a block scalar", + start_mark, "found an indentation indicator equal to 0") + return false + } + + // Get the indentation level and eat the indicator. + increment = as_digit(parser.buffer, parser.buffer_pos) + skip(parser) + } + + } else if is_digit(parser.buffer, parser.buffer_pos) { + // Do the same as above, but in the opposite order. + + if parser.buffer[parser.buffer_pos] == '0' { + yaml_parser_set_scanner_error(parser, "while scanning a block scalar", + start_mark, "found an indentation indicator equal to 0") + return false + } + increment = as_digit(parser.buffer, parser.buffer_pos) + skip(parser) + + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '-' { + if parser.buffer[parser.buffer_pos] == '+' { + chomping = +1 + } else { + chomping = -1 + } + skip(parser) + } + } + + // Eat whitespaces and comments to the end of the line. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + for is_blank(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + if parser.buffer[parser.buffer_pos] == '#' { + if !yaml_parser_scan_line_comment(parser, start_mark) { + return false + } + for !is_breakz(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + } + + // Check if we are at the end of the line. + if !is_breakz(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a block scalar", + start_mark, "did not find expected comment or line break") + return false + } + + // Eat a line break. + if is_break(parser.buffer, parser.buffer_pos) { + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + skip_line(parser) + } + + end_mark := parser.mark + + // Set the indentation level if it was specified. + var indent int + if increment > 0 { + if parser.indent >= 0 { + indent = parser.indent + increment + } else { + indent = increment + } + } + + // Scan the leading line breaks and determine the indentation level if needed. + var s, leading_break, trailing_breaks []byte + if !yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks, start_mark, &end_mark) { + return false + } + + // Scan the block scalar content. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + var leading_blank, trailing_blank bool + for parser.mark.column == indent && !is_z(parser.buffer, parser.buffer_pos) { + // We are at the beginning of a non-empty line. + + // Is it a trailing whitespace? + trailing_blank = is_blank(parser.buffer, parser.buffer_pos) + + // Check if we need to fold the leading line break. + if !literal && !leading_blank && !trailing_blank && len(leading_break) > 0 && leading_break[0] == '\n' { + // Do we need to join the lines by space? + if len(trailing_breaks) == 0 { + s = append(s, ' ') + } + } else { + s = append(s, leading_break...) + } + leading_break = leading_break[:0] + + // Append the remaining line breaks. + s = append(s, trailing_breaks...) + trailing_breaks = trailing_breaks[:0] + + // Is it a leading whitespace? + leading_blank = is_blank(parser.buffer, parser.buffer_pos) + + // Consume the current line. + for !is_breakz(parser.buffer, parser.buffer_pos) { + s = read(parser, s) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Consume the line break. + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + + leading_break = read_line(parser, leading_break) + + // Eat the following indentation spaces and line breaks. + if !yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks, start_mark, &end_mark) { + return false + } + } + + // Chomp the tail. + if chomping != -1 { + s = append(s, leading_break...) + } + if chomping == 1 { + s = append(s, trailing_breaks...) + } + + // Create a token. + *token = yaml_token_t{ + typ: yaml_SCALAR_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + value: s, + style: yaml_LITERAL_SCALAR_STYLE, + } + if !literal { + token.style = yaml_FOLDED_SCALAR_STYLE + } + return true +} + +// Scan indentation spaces and line breaks for a block scalar. Determine the +// indentation level if needed. +func yaml_parser_scan_block_scalar_breaks(parser *yaml_parser_t, indent *int, breaks *[]byte, start_mark yaml_mark_t, end_mark *yaml_mark_t) bool { + *end_mark = parser.mark + + // Eat the indentation spaces and line breaks. + max_indent := 0 + for { + // Eat the indentation spaces. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + for (*indent == 0 || parser.mark.column < *indent) && is_space(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + if parser.mark.column > max_indent { + max_indent = parser.mark.column + } + + // Check for a tab character messing the indentation. + if (*indent == 0 || parser.mark.column < *indent) && is_tab(parser.buffer, parser.buffer_pos) { + return yaml_parser_set_scanner_error(parser, "while scanning a block scalar", + start_mark, "found a tab character where an indentation space is expected") + } + + // Have we found a non-empty line? + if !is_break(parser.buffer, parser.buffer_pos) { + break + } + + // Consume the line break. + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + // [Go] Should really be returning breaks instead. + *breaks = read_line(parser, *breaks) + *end_mark = parser.mark + } + + // Determine the indentation level if needed. + if *indent == 0 { + *indent = max_indent + if *indent < parser.indent+1 { + *indent = parser.indent + 1 + } + if *indent < 1 { + *indent = 1 + } + } + return true +} + +// Scan a quoted scalar. +func yaml_parser_scan_flow_scalar(parser *yaml_parser_t, token *yaml_token_t, single bool) bool { + // Eat the left quote. + start_mark := parser.mark + skip(parser) + + // Consume the content of the quoted scalar. + var s, leading_break, trailing_breaks, whitespaces []byte + for { + // Check that there are no document indicators at the beginning of the line. + if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) { + return false + } + + if parser.mark.column == 0 && + ((parser.buffer[parser.buffer_pos+0] == '-' && + parser.buffer[parser.buffer_pos+1] == '-' && + parser.buffer[parser.buffer_pos+2] == '-') || + (parser.buffer[parser.buffer_pos+0] == '.' && + parser.buffer[parser.buffer_pos+1] == '.' && + parser.buffer[parser.buffer_pos+2] == '.')) && + is_blankz(parser.buffer, parser.buffer_pos+3) { + yaml_parser_set_scanner_error(parser, "while scanning a quoted scalar", + start_mark, "found unexpected document indicator") + return false + } + + // Check for EOF. + if is_z(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a quoted scalar", + start_mark, "found unexpected end of stream") + return false + } + + // Consume non-blank characters. + leading_blanks := false + for !is_blankz(parser.buffer, parser.buffer_pos) { + if single && parser.buffer[parser.buffer_pos] == '\'' && parser.buffer[parser.buffer_pos+1] == '\'' { + // Is is an escaped single quote. + s = append(s, '\'') + skip(parser) + skip(parser) + + } else if single && parser.buffer[parser.buffer_pos] == '\'' { + // It is a right single quote. + break + } else if !single && parser.buffer[parser.buffer_pos] == '"' { + // It is a right double quote. + break + + } else if !single && parser.buffer[parser.buffer_pos] == '\\' && is_break(parser.buffer, parser.buffer_pos+1) { + // It is an escaped line break. + if parser.unread < 3 && !yaml_parser_update_buffer(parser, 3) { + return false + } + skip(parser) + skip_line(parser) + leading_blanks = true + break + + } else if !single && parser.buffer[parser.buffer_pos] == '\\' { + // It is an escape sequence. + code_length := 0 + + // Check the escape character. + switch parser.buffer[parser.buffer_pos+1] { + case '0': + s = append(s, 0) + case 'a': + s = append(s, '\x07') + case 'b': + s = append(s, '\x08') + case 't', '\t': + s = append(s, '\x09') + case 'n': + s = append(s, '\x0A') + case 'v': + s = append(s, '\x0B') + case 'f': + s = append(s, '\x0C') + case 'r': + s = append(s, '\x0D') + case 'e': + s = append(s, '\x1B') + case ' ': + s = append(s, '\x20') + case '"': + s = append(s, '"') + case '\'': + s = append(s, '\'') + case '\\': + s = append(s, '\\') + case 'N': // NEL (#x85) + s = append(s, '\xC2') + s = append(s, '\x85') + case '_': // #xA0 + s = append(s, '\xC2') + s = append(s, '\xA0') + case 'L': // LS (#x2028) + s = append(s, '\xE2') + s = append(s, '\x80') + s = append(s, '\xA8') + case 'P': // PS (#x2029) + s = append(s, '\xE2') + s = append(s, '\x80') + s = append(s, '\xA9') + case 'x': + code_length = 2 + case 'u': + code_length = 4 + case 'U': + code_length = 8 + default: + yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", + start_mark, "found unknown escape character") + return false + } + + skip(parser) + skip(parser) + + // Consume an arbitrary escape code. + if code_length > 0 { + var value int + + // Scan the character value. + if parser.unread < code_length && !yaml_parser_update_buffer(parser, code_length) { + return false + } + for k := 0; k < code_length; k++ { + if !is_hex(parser.buffer, parser.buffer_pos+k) { + yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", + start_mark, "did not find expected hexdecimal number") + return false + } + value = (value << 4) + as_hex(parser.buffer, parser.buffer_pos+k) + } + + // Check the value and write the character. + if (value >= 0xD800 && value <= 0xDFFF) || value > 0x10FFFF { + yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", + start_mark, "found invalid Unicode character escape code") + return false + } + if value <= 0x7F { + s = append(s, byte(value)) + } else if value <= 0x7FF { + s = append(s, byte(0xC0+(value>>6))) + s = append(s, byte(0x80+(value&0x3F))) + } else if value <= 0xFFFF { + s = append(s, byte(0xE0+(value>>12))) + s = append(s, byte(0x80+((value>>6)&0x3F))) + s = append(s, byte(0x80+(value&0x3F))) + } else { + s = append(s, byte(0xF0+(value>>18))) + s = append(s, byte(0x80+((value>>12)&0x3F))) + s = append(s, byte(0x80+((value>>6)&0x3F))) + s = append(s, byte(0x80+(value&0x3F))) + } + + // Advance the pointer. + for k := 0; k < code_length; k++ { + skip(parser) + } + } + } else { + // It is a non-escaped non-blank character. + s = read(parser, s) + } + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + } + + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + // Check if we are at the end of the scalar. + if single { + if parser.buffer[parser.buffer_pos] == '\'' { + break + } + } else { + if parser.buffer[parser.buffer_pos] == '"' { + break + } + } + + // Consume blank characters. + for is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos) { + if is_blank(parser.buffer, parser.buffer_pos) { + // Consume a space or a tab character. + if !leading_blanks { + whitespaces = read(parser, whitespaces) + } else { + skip(parser) + } + } else { + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + + // Check if it is a first line break. + if !leading_blanks { + whitespaces = whitespaces[:0] + leading_break = read_line(parser, leading_break) + leading_blanks = true + } else { + trailing_breaks = read_line(parser, trailing_breaks) + } + } + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Join the whitespaces or fold line breaks. + if leading_blanks { + // Do we need to fold line breaks? + if len(leading_break) > 0 && leading_break[0] == '\n' { + if len(trailing_breaks) == 0 { + s = append(s, ' ') + } else { + s = append(s, trailing_breaks...) + } + } else { + s = append(s, leading_break...) + s = append(s, trailing_breaks...) + } + trailing_breaks = trailing_breaks[:0] + leading_break = leading_break[:0] + } else { + s = append(s, whitespaces...) + whitespaces = whitespaces[:0] + } + } + + // Eat the right quote. + skip(parser) + end_mark := parser.mark + + // Create a token. + *token = yaml_token_t{ + typ: yaml_SCALAR_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + value: s, + style: yaml_SINGLE_QUOTED_SCALAR_STYLE, + } + if !single { + token.style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + return true +} + +// Scan a plain scalar. +func yaml_parser_scan_plain_scalar(parser *yaml_parser_t, token *yaml_token_t) bool { + + var s, leading_break, trailing_breaks, whitespaces []byte + var leading_blanks bool + var indent = parser.indent + 1 + + start_mark := parser.mark + end_mark := parser.mark + + // Consume the content of the plain scalar. + for { + // Check for a document indicator. + if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) { + return false + } + if parser.mark.column == 0 && + ((parser.buffer[parser.buffer_pos+0] == '-' && + parser.buffer[parser.buffer_pos+1] == '-' && + parser.buffer[parser.buffer_pos+2] == '-') || + (parser.buffer[parser.buffer_pos+0] == '.' && + parser.buffer[parser.buffer_pos+1] == '.' && + parser.buffer[parser.buffer_pos+2] == '.')) && + is_blankz(parser.buffer, parser.buffer_pos+3) { + break + } + + // Check for a comment. + if parser.buffer[parser.buffer_pos] == '#' { + break + } + + // Consume non-blank characters. + for !is_blankz(parser.buffer, parser.buffer_pos) { + + // Check for indicators that may end a plain scalar. + if (parser.buffer[parser.buffer_pos] == ':' && is_blankz(parser.buffer, parser.buffer_pos+1)) || + (parser.flow_level > 0 && + (parser.buffer[parser.buffer_pos] == ',' || + parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == '[' || + parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '{' || + parser.buffer[parser.buffer_pos] == '}')) { + break + } + + // Check if we need to join whitespaces and breaks. + if leading_blanks || len(whitespaces) > 0 { + if leading_blanks { + // Do we need to fold line breaks? + if leading_break[0] == '\n' { + if len(trailing_breaks) == 0 { + s = append(s, ' ') + } else { + s = append(s, trailing_breaks...) + } + } else { + s = append(s, leading_break...) + s = append(s, trailing_breaks...) + } + trailing_breaks = trailing_breaks[:0] + leading_break = leading_break[:0] + leading_blanks = false + } else { + s = append(s, whitespaces...) + whitespaces = whitespaces[:0] + } + } + + // Copy the character. + s = read(parser, s) + + end_mark = parser.mark + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + } + + // Is it the end? + if !(is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos)) { + break + } + + // Consume blank characters. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + for is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos) { + if is_blank(parser.buffer, parser.buffer_pos) { + + // Check for tab characters that abuse indentation. + if leading_blanks && parser.mark.column < indent && is_tab(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a plain scalar", + start_mark, "found a tab character that violates indentation") + return false + } + + // Consume a space or a tab character. + if !leading_blanks { + whitespaces = read(parser, whitespaces) + } else { + skip(parser) + } + } else { + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + + // Check if it is a first line break. + if !leading_blanks { + whitespaces = whitespaces[:0] + leading_break = read_line(parser, leading_break) + leading_blanks = true + } else { + trailing_breaks = read_line(parser, trailing_breaks) + } + } + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Check indentation level. + if parser.flow_level == 0 && parser.mark.column < indent { + break + } + } + + // Create a token. + *token = yaml_token_t{ + typ: yaml_SCALAR_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + value: s, + style: yaml_PLAIN_SCALAR_STYLE, + } + + // Note that we change the 'simple_key_allowed' flag. + if leading_blanks { + parser.simple_key_allowed = true + } + return true +} + +func yaml_parser_scan_line_comment(parser *yaml_parser_t, token_mark yaml_mark_t) bool { + if parser.newlines > 0 { + return true + } + + var start_mark yaml_mark_t + var text []byte + + for peek := 0; peek < 512; peek++ { + if parser.unread < peek+1 && !yaml_parser_update_buffer(parser, peek+1) { + break + } + if is_blank(parser.buffer, parser.buffer_pos+peek) { + continue + } + if parser.buffer[parser.buffer_pos+peek] == '#' { + seen := parser.mark.index+peek + for { + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if is_breakz(parser.buffer, parser.buffer_pos) { + if parser.mark.index >= seen { + break + } + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + skip_line(parser) + } else if parser.mark.index >= seen { + if len(text) == 0 { + start_mark = parser.mark + } + text = read(parser, text) + } else { + skip(parser) + } + } + } + break + } + if len(text) > 0 { + parser.comments = append(parser.comments, yaml_comment_t{ + token_mark: token_mark, + start_mark: start_mark, + line: text, + }) + } + return true +} + +func yaml_parser_scan_comments(parser *yaml_parser_t, scan_mark yaml_mark_t) bool { + token := parser.tokens[len(parser.tokens)-1] + + if token.typ == yaml_FLOW_ENTRY_TOKEN && len(parser.tokens) > 1 { + token = parser.tokens[len(parser.tokens)-2] + } + + var token_mark = token.start_mark + var start_mark yaml_mark_t + var next_indent = parser.indent + if next_indent < 0 { + next_indent = 0 + } + + var recent_empty = false + var first_empty = parser.newlines <= 1 + + var line = parser.mark.line + var column = parser.mark.column + + var text []byte + + // The foot line is the place where a comment must start to + // still be considered as a foot of the prior content. + // If there's some content in the currently parsed line, then + // the foot is the line below it. + var foot_line = -1 + if scan_mark.line > 0 { + foot_line = parser.mark.line-parser.newlines+1 + if parser.newlines == 0 && parser.mark.column > 1 { + foot_line++ + } + } + + var peek = 0 + for ; peek < 512; peek++ { + if parser.unread < peek+1 && !yaml_parser_update_buffer(parser, peek+1) { + break + } + column++ + if is_blank(parser.buffer, parser.buffer_pos+peek) { + continue + } + c := parser.buffer[parser.buffer_pos+peek] + var close_flow = parser.flow_level > 0 && (c == ']' || c == '}') + if close_flow || is_breakz(parser.buffer, parser.buffer_pos+peek) { + // Got line break or terminator. + if close_flow || !recent_empty { + if close_flow || first_empty && (start_mark.line == foot_line && token.typ != yaml_VALUE_TOKEN || start_mark.column-1 < next_indent) { + // This is the first empty line and there were no empty lines before, + // so this initial part of the comment is a foot of the prior token + // instead of being a head for the following one. Split it up. + // Alternatively, this might also be the last comment inside a flow + // scope, so it must be a footer. + if len(text) > 0 { + if start_mark.column-1 < next_indent { + // If dedented it's unrelated to the prior token. + token_mark = start_mark + } + parser.comments = append(parser.comments, yaml_comment_t{ + scan_mark: scan_mark, + token_mark: token_mark, + start_mark: start_mark, + end_mark: yaml_mark_t{parser.mark.index + peek, line, column}, + foot: text, + }) + scan_mark = yaml_mark_t{parser.mark.index + peek, line, column} + token_mark = scan_mark + text = nil + } + } else { + if len(text) > 0 && parser.buffer[parser.buffer_pos+peek] != 0 { + text = append(text, '\n') + } + } + } + if !is_break(parser.buffer, parser.buffer_pos+peek) { + break + } + first_empty = false + recent_empty = true + column = 0 + line++ + continue + } + + if len(text) > 0 && (close_flow || column-1 < next_indent && column != start_mark.column) { + // The comment at the different indentation is a foot of the + // preceding data rather than a head of the upcoming one. + parser.comments = append(parser.comments, yaml_comment_t{ + scan_mark: scan_mark, + token_mark: token_mark, + start_mark: start_mark, + end_mark: yaml_mark_t{parser.mark.index + peek, line, column}, + foot: text, + }) + scan_mark = yaml_mark_t{parser.mark.index + peek, line, column} + token_mark = scan_mark + text = nil + } + + if parser.buffer[parser.buffer_pos+peek] != '#' { + break + } + + if len(text) == 0 { + start_mark = yaml_mark_t{parser.mark.index + peek, line, column} + } else { + text = append(text, '\n') + } + + recent_empty = false + + // Consume until after the consumed comment line. + seen := parser.mark.index+peek + for { + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if is_breakz(parser.buffer, parser.buffer_pos) { + if parser.mark.index >= seen { + break + } + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + skip_line(parser) + } else if parser.mark.index >= seen { + text = read(parser, text) + } else { + skip(parser) + } + } + + peek = 0 + column = 0 + line = parser.mark.line + next_indent = parser.indent + if next_indent < 0 { + next_indent = 0 + } + } + + if len(text) > 0 { + parser.comments = append(parser.comments, yaml_comment_t{ + scan_mark: scan_mark, + token_mark: start_mark, + start_mark: start_mark, + end_mark: yaml_mark_t{parser.mark.index + peek - 1, line, column}, + head: text, + }) + } + return true +} diff --git a/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/sorter.go b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/sorter.go new file mode 100644 index 0000000..9210ece --- /dev/null +++ b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/sorter.go @@ -0,0 +1,134 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package yaml + +import ( + "reflect" + "unicode" +) + +type keyList []reflect.Value + +func (l keyList) Len() int { return len(l) } +func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } +func (l keyList) Less(i, j int) bool { + a := l[i] + b := l[j] + ak := a.Kind() + bk := b.Kind() + for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() { + a = a.Elem() + ak = a.Kind() + } + for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() { + b = b.Elem() + bk = b.Kind() + } + af, aok := keyFloat(a) + bf, bok := keyFloat(b) + if aok && bok { + if af != bf { + return af < bf + } + if ak != bk { + return ak < bk + } + return numLess(a, b) + } + if ak != reflect.String || bk != reflect.String { + return ak < bk + } + ar, br := []rune(a.String()), []rune(b.String()) + digits := false + for i := 0; i < len(ar) && i < len(br); i++ { + if ar[i] == br[i] { + digits = unicode.IsDigit(ar[i]) + continue + } + al := unicode.IsLetter(ar[i]) + bl := unicode.IsLetter(br[i]) + if al && bl { + return ar[i] < br[i] + } + if al || bl { + if digits { + return al + } else { + return bl + } + } + var ai, bi int + var an, bn int64 + if ar[i] == '0' || br[i] == '0' { + for j := i - 1; j >= 0 && unicode.IsDigit(ar[j]); j-- { + if ar[j] != '0' { + an = 1 + bn = 1 + break + } + } + } + for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ { + an = an*10 + int64(ar[ai]-'0') + } + for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ { + bn = bn*10 + int64(br[bi]-'0') + } + if an != bn { + return an < bn + } + if ai != bi { + return ai < bi + } + return ar[i] < br[i] + } + return len(ar) < len(br) +} + +// keyFloat returns a float value for v if it is a number/bool +// and whether it is a number/bool or not. +func keyFloat(v reflect.Value) (f float64, ok bool) { + switch v.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return float64(v.Int()), true + case reflect.Float32, reflect.Float64: + return v.Float(), true + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return float64(v.Uint()), true + case reflect.Bool: + if v.Bool() { + return 1, true + } + return 0, true + } + return 0, false +} + +// numLess returns whether a < b. +// a and b must necessarily have the same kind. +func numLess(a, b reflect.Value) bool { + switch a.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return a.Int() < b.Int() + case reflect.Float32, reflect.Float64: + return a.Float() < b.Float() + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return a.Uint() < b.Uint() + case reflect.Bool: + return !a.Bool() && b.Bool() + } + panic("not a number") +} diff --git a/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/suite_test.go b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/suite_test.go new file mode 100644 index 0000000..4630b5f --- /dev/null +++ b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/suite_test.go @@ -0,0 +1,27 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package yaml_test + +import ( + . "gopkg.in/check.v1" + "testing" +) + +func Test(t *testing.T) { TestingT(t) } + +type S struct{} + +var _ = Suite(&S{}) diff --git a/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/writerc.go b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/writerc.go new file mode 100644 index 0000000..b8a116b --- /dev/null +++ b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/writerc.go @@ -0,0 +1,48 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// Copyright (c) 2006-2010 Kirill Simonov +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package yaml + +// Set the writer error and return false. +func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool { + emitter.error = yaml_WRITER_ERROR + emitter.problem = problem + return false +} + +// Flush the output buffer. +func yaml_emitter_flush(emitter *yaml_emitter_t) bool { + if emitter.write_handler == nil { + panic("write handler not set") + } + + // Check if the buffer is empty. + if emitter.buffer_pos == 0 { + return true + } + + if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil { + return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) + } + emitter.buffer_pos = 0 + return true +} diff --git a/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/yaml.go b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/yaml.go new file mode 100644 index 0000000..8cec6da --- /dev/null +++ b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/yaml.go @@ -0,0 +1,698 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package yaml implements YAML support for the Go language. +// +// Source code and other details for the project are available at GitHub: +// +// https://github.com/go-yaml/yaml +// +package yaml + +import ( + "errors" + "fmt" + "io" + "reflect" + "strings" + "sync" + "unicode/utf8" +) + +// The Unmarshaler interface may be implemented by types to customize their +// behavior when being unmarshaled from a YAML document. +type Unmarshaler interface { + UnmarshalYAML(value *Node) error +} + +type obsoleteUnmarshaler interface { + UnmarshalYAML(unmarshal func(interface{}) error) error +} + +// The Marshaler interface may be implemented by types to customize their +// behavior when being marshaled into a YAML document. The returned value +// is marshaled in place of the original value implementing Marshaler. +// +// If an error is returned by MarshalYAML, the marshaling procedure stops +// and returns with the provided error. +type Marshaler interface { + MarshalYAML() (interface{}, error) +} + +// Unmarshal decodes the first document found within the in byte slice +// and assigns decoded values into the out value. +// +// Maps and pointers (to a struct, string, int, etc) are accepted as out +// values. If an internal pointer within a struct is not initialized, +// the yaml package will initialize it if necessary for unmarshalling +// the provided data. The out parameter must not be nil. +// +// The type of the decoded values should be compatible with the respective +// values in out. If one or more values cannot be decoded due to a type +// mismatches, decoding continues partially until the end of the YAML +// content, and a *yaml.TypeError is returned with details for all +// missed values. +// +// Struct fields are only unmarshalled if they are exported (have an +// upper case first letter), and are unmarshalled using the field name +// lowercased as the default key. Custom keys may be defined via the +// "yaml" name in the field tag: the content preceding the first comma +// is used as the key, and the following comma-separated options are +// used to tweak the marshalling process (see Marshal). +// Conflicting names result in a runtime error. +// +// For example: +// +// type T struct { +// F int `yaml:"a,omitempty"` +// B int +// } +// var t T +// yaml.Unmarshal([]byte("a: 1\nb: 2"), &t) +// +// See the documentation of Marshal for the format of tags and a list of +// supported tag options. +// +func Unmarshal(in []byte, out interface{}) (err error) { + return unmarshal(in, out, false) +} + +// A Decoder reads and decodes YAML values from an input stream. +type Decoder struct { + parser *parser + knownFields bool +} + +// NewDecoder returns a new decoder that reads from r. +// +// The decoder introduces its own buffering and may read +// data from r beyond the YAML values requested. +func NewDecoder(r io.Reader) *Decoder { + return &Decoder{ + parser: newParserFromReader(r), + } +} + +// KnownFields ensures that the keys in decoded mappings to +// exist as fields in the struct being decoded into. +func (dec *Decoder) KnownFields(enable bool) { + dec.knownFields = enable +} + +// Decode reads the next YAML-encoded value from its input +// and stores it in the value pointed to by v. +// +// See the documentation for Unmarshal for details about the +// conversion of YAML into a Go value. +func (dec *Decoder) Decode(v interface{}) (err error) { + d := newDecoder() + d.knownFields = dec.knownFields + defer handleErr(&err) + node := dec.parser.parse() + if node == nil { + return io.EOF + } + out := reflect.ValueOf(v) + if out.Kind() == reflect.Ptr && !out.IsNil() { + out = out.Elem() + } + d.unmarshal(node, out) + if len(d.terrors) > 0 { + return &TypeError{d.terrors} + } + return nil +} + +// Decode decodes the node and stores its data into the value pointed to by v. +// +// See the documentation for Unmarshal for details about the +// conversion of YAML into a Go value. +func (n *Node) Decode(v interface{}) (err error) { + d := newDecoder() + defer handleErr(&err) + out := reflect.ValueOf(v) + if out.Kind() == reflect.Ptr && !out.IsNil() { + out = out.Elem() + } + d.unmarshal(n, out) + if len(d.terrors) > 0 { + return &TypeError{d.terrors} + } + return nil +} + +func unmarshal(in []byte, out interface{}, strict bool) (err error) { + defer handleErr(&err) + d := newDecoder() + p := newParser(in) + defer p.destroy() + node := p.parse() + if node != nil { + v := reflect.ValueOf(out) + if v.Kind() == reflect.Ptr && !v.IsNil() { + v = v.Elem() + } + d.unmarshal(node, v) + } + if len(d.terrors) > 0 { + return &TypeError{d.terrors} + } + return nil +} + +// Marshal serializes the value provided into a YAML document. The structure +// of the generated document will reflect the structure of the value itself. +// Maps and pointers (to struct, string, int, etc) are accepted as the in value. +// +// Struct fields are only marshalled if they are exported (have an upper case +// first letter), and are marshalled using the field name lowercased as the +// default key. Custom keys may be defined via the "yaml" name in the field +// tag: the content preceding the first comma is used as the key, and the +// following comma-separated options are used to tweak the marshalling process. +// Conflicting names result in a runtime error. +// +// The field tag format accepted is: +// +// `(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)` +// +// The following flags are currently supported: +// +// omitempty Only include the field if it's not set to the zero +// value for the type or to empty slices or maps. +// Zero valued structs will be omitted if all their public +// fields are zero, unless they implement an IsZero +// method (see the IsZeroer interface type), in which +// case the field will be excluded if IsZero returns true. +// +// flow Marshal using a flow style (useful for structs, +// sequences and maps). +// +// inline Inline the field, which must be a struct or a map, +// causing all of its fields or keys to be processed as if +// they were part of the outer struct. For maps, keys must +// not conflict with the yaml keys of other struct fields. +// +// In addition, if the key is "-", the field is ignored. +// +// For example: +// +// type T struct { +// F int `yaml:"a,omitempty"` +// B int +// } +// yaml.Marshal(&T{B: 2}) // Returns "b: 2\n" +// yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n" +// +func Marshal(in interface{}) (out []byte, err error) { + defer handleErr(&err) + e := newEncoder() + defer e.destroy() + e.marshalDoc("", reflect.ValueOf(in)) + e.finish() + out = e.out + return +} + +// An Encoder writes YAML values to an output stream. +type Encoder struct { + encoder *encoder +} + +// NewEncoder returns a new encoder that writes to w. +// The Encoder should be closed after use to flush all data +// to w. +func NewEncoder(w io.Writer) *Encoder { + return &Encoder{ + encoder: newEncoderWithWriter(w), + } +} + +// Encode writes the YAML encoding of v to the stream. +// If multiple items are encoded to the stream, the +// second and subsequent document will be preceded +// with a "---" document separator, but the first will not. +// +// See the documentation for Marshal for details about the conversion of Go +// values to YAML. +func (e *Encoder) Encode(v interface{}) (err error) { + defer handleErr(&err) + e.encoder.marshalDoc("", reflect.ValueOf(v)) + return nil +} + +// Encode encodes value v and stores its representation in n. +// +// See the documentation for Marshal for details about the +// conversion of Go values into YAML. +func (n *Node) Encode(v interface{}) (err error) { + defer handleErr(&err) + e := newEncoder() + defer e.destroy() + e.marshalDoc("", reflect.ValueOf(v)) + e.finish() + p := newParser(e.out) + p.textless = true + defer p.destroy() + doc := p.parse() + *n = *doc.Content[0] + return nil +} + +// SetIndent changes the used indentation used when encoding. +func (e *Encoder) SetIndent(spaces int) { + if spaces < 0 { + panic("yaml: cannot indent to a negative number of spaces") + } + e.encoder.indent = spaces +} + +// Close closes the encoder by writing any remaining data. +// It does not write a stream terminating string "...". +func (e *Encoder) Close() (err error) { + defer handleErr(&err) + e.encoder.finish() + return nil +} + +func handleErr(err *error) { + if v := recover(); v != nil { + if e, ok := v.(yamlError); ok { + *err = e.err + } else { + panic(v) + } + } +} + +type yamlError struct { + err error +} + +func fail(err error) { + panic(yamlError{err}) +} + +func failf(format string, args ...interface{}) { + panic(yamlError{fmt.Errorf("yaml: "+format, args...)}) +} + +// A TypeError is returned by Unmarshal when one or more fields in +// the YAML document cannot be properly decoded into the requested +// types. When this error is returned, the value is still +// unmarshaled partially. +type TypeError struct { + Errors []string +} + +func (e *TypeError) Error() string { + return fmt.Sprintf("yaml: unmarshal errors:\n %s", strings.Join(e.Errors, "\n ")) +} + +type Kind uint32 + +const ( + DocumentNode Kind = 1 << iota + SequenceNode + MappingNode + ScalarNode + AliasNode +) + +type Style uint32 + +const ( + TaggedStyle Style = 1 << iota + DoubleQuotedStyle + SingleQuotedStyle + LiteralStyle + FoldedStyle + FlowStyle +) + +// Node represents an element in the YAML document hierarchy. While documents +// are typically encoded and decoded into higher level types, such as structs +// and maps, Node is an intermediate representation that allows detailed +// control over the content being decoded or encoded. +// +// It's worth noting that although Node offers access into details such as +// line numbers, colums, and comments, the content when re-encoded will not +// have its original textual representation preserved. An effort is made to +// render the data plesantly, and to preserve comments near the data they +// describe, though. +// +// Values that make use of the Node type interact with the yaml package in the +// same way any other type would do, by encoding and decoding yaml data +// directly or indirectly into them. +// +// For example: +// +// var person struct { +// Name string +// Address yaml.Node +// } +// err := yaml.Unmarshal(data, &person) +// +// Or by itself: +// +// var person Node +// err := yaml.Unmarshal(data, &person) +// +type Node struct { + // Kind defines whether the node is a document, a mapping, a sequence, + // a scalar value, or an alias to another node. The specific data type of + // scalar nodes may be obtained via the ShortTag and LongTag methods. + Kind Kind + + // Style allows customizing the apperance of the node in the tree. + Style Style + + // Tag holds the YAML tag defining the data type for the value. + // When decoding, this field will always be set to the resolved tag, + // even when it wasn't explicitly provided in the YAML content. + // When encoding, if this field is unset the value type will be + // implied from the node properties, and if it is set, it will only + // be serialized into the representation if TaggedStyle is used or + // the implicit tag diverges from the provided one. + Tag string + + // Value holds the unescaped and unquoted represenation of the value. + Value string + + // Anchor holds the anchor name for this node, which allows aliases to point to it. + Anchor string + + // Alias holds the node that this alias points to. Only valid when Kind is AliasNode. + Alias *Node + + // Content holds contained nodes for documents, mappings, and sequences. + Content []*Node + + // HeadComment holds any comments in the lines preceding the node and + // not separated by an empty line. + HeadComment string + + // LineComment holds any comments at the end of the line where the node is in. + LineComment string + + // FootComment holds any comments following the node and before empty lines. + FootComment string + + // Line and Column hold the node position in the decoded YAML text. + // These fields are not respected when encoding the node. + Line int + Column int +} + +// IsZero returns whether the node has all of its fields unset. +func (n *Node) IsZero() bool { + return n.Kind == 0 && n.Style == 0 && n.Tag == "" && n.Value == "" && n.Anchor == "" && n.Alias == nil && n.Content == nil && + n.HeadComment == "" && n.LineComment == "" && n.FootComment == "" && n.Line == 0 && n.Column == 0 +} + + +// LongTag returns the long form of the tag that indicates the data type for +// the node. If the Tag field isn't explicitly defined, one will be computed +// based on the node properties. +func (n *Node) LongTag() string { + return longTag(n.ShortTag()) +} + +// ShortTag returns the short form of the YAML tag that indicates data type for +// the node. If the Tag field isn't explicitly defined, one will be computed +// based on the node properties. +func (n *Node) ShortTag() string { + if n.indicatedString() { + return strTag + } + if n.Tag == "" || n.Tag == "!" { + switch n.Kind { + case MappingNode: + return mapTag + case SequenceNode: + return seqTag + case AliasNode: + if n.Alias != nil { + return n.Alias.ShortTag() + } + case ScalarNode: + tag, _ := resolve("", n.Value) + return tag + case 0: + // Special case to make the zero value convenient. + if n.IsZero() { + return nullTag + } + } + return "" + } + return shortTag(n.Tag) +} + +func (n *Node) indicatedString() bool { + return n.Kind == ScalarNode && + (shortTag(n.Tag) == strTag || + (n.Tag == "" || n.Tag == "!") && n.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0) +} + +// SetString is a convenience function that sets the node to a string value +// and defines its style in a pleasant way depending on its content. +func (n *Node) SetString(s string) { + n.Kind = ScalarNode + if utf8.ValidString(s) { + n.Value = s + n.Tag = strTag + } else { + n.Value = encodeBase64(s) + n.Tag = binaryTag + } + if strings.Contains(n.Value, "\n") { + n.Style = LiteralStyle + } +} + +// -------------------------------------------------------------------------- +// Maintain a mapping of keys to structure field indexes + +// The code in this section was copied from mgo/bson. + +// structInfo holds details for the serialization of fields of +// a given struct. +type structInfo struct { + FieldsMap map[string]fieldInfo + FieldsList []fieldInfo + + // InlineMap is the number of the field in the struct that + // contains an ,inline map, or -1 if there's none. + InlineMap int + + // InlineUnmarshalers holds indexes to inlined fields that + // contain unmarshaler values. + InlineUnmarshalers [][]int +} + +type fieldInfo struct { + Key string + Num int + OmitEmpty bool + Flow bool + // Id holds the unique field identifier, so we can cheaply + // check for field duplicates without maintaining an extra map. + Id int + + // Inline holds the field index if the field is part of an inlined struct. + Inline []int +} + +var structMap = make(map[reflect.Type]*structInfo) +var fieldMapMutex sync.RWMutex +var unmarshalerType reflect.Type + +func init() { + var v Unmarshaler + unmarshalerType = reflect.ValueOf(&v).Elem().Type() +} + +func getStructInfo(st reflect.Type) (*structInfo, error) { + fieldMapMutex.RLock() + sinfo, found := structMap[st] + fieldMapMutex.RUnlock() + if found { + return sinfo, nil + } + + n := st.NumField() + fieldsMap := make(map[string]fieldInfo) + fieldsList := make([]fieldInfo, 0, n) + inlineMap := -1 + inlineUnmarshalers := [][]int(nil) + for i := 0; i != n; i++ { + field := st.Field(i) + if field.PkgPath != "" && !field.Anonymous { + continue // Private field + } + + info := fieldInfo{Num: i} + + tag := field.Tag.Get("yaml") + if tag == "" && strings.Index(string(field.Tag), ":") < 0 { + tag = string(field.Tag) + } + if tag == "-" { + continue + } + + inline := false + fields := strings.Split(tag, ",") + if len(fields) > 1 { + for _, flag := range fields[1:] { + switch flag { + case "omitempty": + info.OmitEmpty = true + case "flow": + info.Flow = true + case "inline": + inline = true + default: + return nil, errors.New(fmt.Sprintf("unsupported flag %q in tag %q of type %s", flag, tag, st)) + } + } + tag = fields[0] + } + + if inline { + switch field.Type.Kind() { + case reflect.Map: + if inlineMap >= 0 { + return nil, errors.New("multiple ,inline maps in struct " + st.String()) + } + if field.Type.Key() != reflect.TypeOf("") { + return nil, errors.New("option ,inline needs a map with string keys in struct " + st.String()) + } + inlineMap = info.Num + case reflect.Struct, reflect.Ptr: + ftype := field.Type + for ftype.Kind() == reflect.Ptr { + ftype = ftype.Elem() + } + if ftype.Kind() != reflect.Struct { + return nil, errors.New("option ,inline may only be used on a struct or map field") + } + if reflect.PtrTo(ftype).Implements(unmarshalerType) { + inlineUnmarshalers = append(inlineUnmarshalers, []int{i}) + } else { + sinfo, err := getStructInfo(ftype) + if err != nil { + return nil, err + } + for _, index := range sinfo.InlineUnmarshalers { + inlineUnmarshalers = append(inlineUnmarshalers, append([]int{i}, index...)) + } + for _, finfo := range sinfo.FieldsList { + if _, found := fieldsMap[finfo.Key]; found { + msg := "duplicated key '" + finfo.Key + "' in struct " + st.String() + return nil, errors.New(msg) + } + if finfo.Inline == nil { + finfo.Inline = []int{i, finfo.Num} + } else { + finfo.Inline = append([]int{i}, finfo.Inline...) + } + finfo.Id = len(fieldsList) + fieldsMap[finfo.Key] = finfo + fieldsList = append(fieldsList, finfo) + } + } + default: + return nil, errors.New("option ,inline may only be used on a struct or map field") + } + continue + } + + if tag != "" { + info.Key = tag + } else { + info.Key = strings.ToLower(field.Name) + } + + if _, found = fieldsMap[info.Key]; found { + msg := "duplicated key '" + info.Key + "' in struct " + st.String() + return nil, errors.New(msg) + } + + info.Id = len(fieldsList) + fieldsList = append(fieldsList, info) + fieldsMap[info.Key] = info + } + + sinfo = &structInfo{ + FieldsMap: fieldsMap, + FieldsList: fieldsList, + InlineMap: inlineMap, + InlineUnmarshalers: inlineUnmarshalers, + } + + fieldMapMutex.Lock() + structMap[st] = sinfo + fieldMapMutex.Unlock() + return sinfo, nil +} + +// IsZeroer is used to check whether an object is zero to +// determine whether it should be omitted when marshaling +// with the omitempty flag. One notable implementation +// is time.Time. +type IsZeroer interface { + IsZero() bool +} + +func isZero(v reflect.Value) bool { + kind := v.Kind() + if z, ok := v.Interface().(IsZeroer); ok { + if (kind == reflect.Ptr || kind == reflect.Interface) && v.IsNil() { + return true + } + return z.IsZero() + } + switch kind { + case reflect.String: + return len(v.String()) == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + case reflect.Slice: + return v.Len() == 0 + case reflect.Map: + return v.Len() == 0 + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Struct: + vt := v.Type() + for i := v.NumField() - 1; i >= 0; i-- { + if vt.Field(i).PkgPath != "" { + continue // Private field + } + if !isZero(v.Field(i)) { + return false + } + } + return true + } + return false +} diff --git a/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/yamlh.go b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/yamlh.go new file mode 100644 index 0000000..7c6d007 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/yamlh.go @@ -0,0 +1,807 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// Copyright (c) 2006-2010 Kirill Simonov +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package yaml + +import ( + "fmt" + "io" +) + +// The version directive data. +type yaml_version_directive_t struct { + major int8 // The major version number. + minor int8 // The minor version number. +} + +// The tag directive data. +type yaml_tag_directive_t struct { + handle []byte // The tag handle. + prefix []byte // The tag prefix. +} + +type yaml_encoding_t int + +// The stream encoding. +const ( + // Let the parser choose the encoding. + yaml_ANY_ENCODING yaml_encoding_t = iota + + yaml_UTF8_ENCODING // The default UTF-8 encoding. + yaml_UTF16LE_ENCODING // The UTF-16-LE encoding with BOM. + yaml_UTF16BE_ENCODING // The UTF-16-BE encoding with BOM. +) + +type yaml_break_t int + +// Line break types. +const ( + // Let the parser choose the break type. + yaml_ANY_BREAK yaml_break_t = iota + + yaml_CR_BREAK // Use CR for line breaks (Mac style). + yaml_LN_BREAK // Use LN for line breaks (Unix style). + yaml_CRLN_BREAK // Use CR LN for line breaks (DOS style). +) + +type yaml_error_type_t int + +// Many bad things could happen with the parser and emitter. +const ( + // No error is produced. + yaml_NO_ERROR yaml_error_type_t = iota + + yaml_MEMORY_ERROR // Cannot allocate or reallocate a block of memory. + yaml_READER_ERROR // Cannot read or decode the input stream. + yaml_SCANNER_ERROR // Cannot scan the input stream. + yaml_PARSER_ERROR // Cannot parse the input stream. + yaml_COMPOSER_ERROR // Cannot compose a YAML document. + yaml_WRITER_ERROR // Cannot write to the output stream. + yaml_EMITTER_ERROR // Cannot emit a YAML stream. +) + +// The pointer position. +type yaml_mark_t struct { + index int // The position index. + line int // The position line. + column int // The position column. +} + +// Node Styles + +type yaml_style_t int8 + +type yaml_scalar_style_t yaml_style_t + +// Scalar styles. +const ( + // Let the emitter choose the style. + yaml_ANY_SCALAR_STYLE yaml_scalar_style_t = 0 + + yaml_PLAIN_SCALAR_STYLE yaml_scalar_style_t = 1 << iota // The plain scalar style. + yaml_SINGLE_QUOTED_SCALAR_STYLE // The single-quoted scalar style. + yaml_DOUBLE_QUOTED_SCALAR_STYLE // The double-quoted scalar style. + yaml_LITERAL_SCALAR_STYLE // The literal scalar style. + yaml_FOLDED_SCALAR_STYLE // The folded scalar style. +) + +type yaml_sequence_style_t yaml_style_t + +// Sequence styles. +const ( + // Let the emitter choose the style. + yaml_ANY_SEQUENCE_STYLE yaml_sequence_style_t = iota + + yaml_BLOCK_SEQUENCE_STYLE // The block sequence style. + yaml_FLOW_SEQUENCE_STYLE // The flow sequence style. +) + +type yaml_mapping_style_t yaml_style_t + +// Mapping styles. +const ( + // Let the emitter choose the style. + yaml_ANY_MAPPING_STYLE yaml_mapping_style_t = iota + + yaml_BLOCK_MAPPING_STYLE // The block mapping style. + yaml_FLOW_MAPPING_STYLE // The flow mapping style. +) + +// Tokens + +type yaml_token_type_t int + +// Token types. +const ( + // An empty token. + yaml_NO_TOKEN yaml_token_type_t = iota + + yaml_STREAM_START_TOKEN // A STREAM-START token. + yaml_STREAM_END_TOKEN // A STREAM-END token. + + yaml_VERSION_DIRECTIVE_TOKEN // A VERSION-DIRECTIVE token. + yaml_TAG_DIRECTIVE_TOKEN // A TAG-DIRECTIVE token. + yaml_DOCUMENT_START_TOKEN // A DOCUMENT-START token. + yaml_DOCUMENT_END_TOKEN // A DOCUMENT-END token. + + yaml_BLOCK_SEQUENCE_START_TOKEN // A BLOCK-SEQUENCE-START token. + yaml_BLOCK_MAPPING_START_TOKEN // A BLOCK-SEQUENCE-END token. + yaml_BLOCK_END_TOKEN // A BLOCK-END token. + + yaml_FLOW_SEQUENCE_START_TOKEN // A FLOW-SEQUENCE-START token. + yaml_FLOW_SEQUENCE_END_TOKEN // A FLOW-SEQUENCE-END token. + yaml_FLOW_MAPPING_START_TOKEN // A FLOW-MAPPING-START token. + yaml_FLOW_MAPPING_END_TOKEN // A FLOW-MAPPING-END token. + + yaml_BLOCK_ENTRY_TOKEN // A BLOCK-ENTRY token. + yaml_FLOW_ENTRY_TOKEN // A FLOW-ENTRY token. + yaml_KEY_TOKEN // A KEY token. + yaml_VALUE_TOKEN // A VALUE token. + + yaml_ALIAS_TOKEN // An ALIAS token. + yaml_ANCHOR_TOKEN // An ANCHOR token. + yaml_TAG_TOKEN // A TAG token. + yaml_SCALAR_TOKEN // A SCALAR token. +) + +func (tt yaml_token_type_t) String() string { + switch tt { + case yaml_NO_TOKEN: + return "yaml_NO_TOKEN" + case yaml_STREAM_START_TOKEN: + return "yaml_STREAM_START_TOKEN" + case yaml_STREAM_END_TOKEN: + return "yaml_STREAM_END_TOKEN" + case yaml_VERSION_DIRECTIVE_TOKEN: + return "yaml_VERSION_DIRECTIVE_TOKEN" + case yaml_TAG_DIRECTIVE_TOKEN: + return "yaml_TAG_DIRECTIVE_TOKEN" + case yaml_DOCUMENT_START_TOKEN: + return "yaml_DOCUMENT_START_TOKEN" + case yaml_DOCUMENT_END_TOKEN: + return "yaml_DOCUMENT_END_TOKEN" + case yaml_BLOCK_SEQUENCE_START_TOKEN: + return "yaml_BLOCK_SEQUENCE_START_TOKEN" + case yaml_BLOCK_MAPPING_START_TOKEN: + return "yaml_BLOCK_MAPPING_START_TOKEN" + case yaml_BLOCK_END_TOKEN: + return "yaml_BLOCK_END_TOKEN" + case yaml_FLOW_SEQUENCE_START_TOKEN: + return "yaml_FLOW_SEQUENCE_START_TOKEN" + case yaml_FLOW_SEQUENCE_END_TOKEN: + return "yaml_FLOW_SEQUENCE_END_TOKEN" + case yaml_FLOW_MAPPING_START_TOKEN: + return "yaml_FLOW_MAPPING_START_TOKEN" + case yaml_FLOW_MAPPING_END_TOKEN: + return "yaml_FLOW_MAPPING_END_TOKEN" + case yaml_BLOCK_ENTRY_TOKEN: + return "yaml_BLOCK_ENTRY_TOKEN" + case yaml_FLOW_ENTRY_TOKEN: + return "yaml_FLOW_ENTRY_TOKEN" + case yaml_KEY_TOKEN: + return "yaml_KEY_TOKEN" + case yaml_VALUE_TOKEN: + return "yaml_VALUE_TOKEN" + case yaml_ALIAS_TOKEN: + return "yaml_ALIAS_TOKEN" + case yaml_ANCHOR_TOKEN: + return "yaml_ANCHOR_TOKEN" + case yaml_TAG_TOKEN: + return "yaml_TAG_TOKEN" + case yaml_SCALAR_TOKEN: + return "yaml_SCALAR_TOKEN" + } + return "<unknown token>" +} + +// The token structure. +type yaml_token_t struct { + // The token type. + typ yaml_token_type_t + + // The start/end of the token. + start_mark, end_mark yaml_mark_t + + // The stream encoding (for yaml_STREAM_START_TOKEN). + encoding yaml_encoding_t + + // The alias/anchor/scalar value or tag/tag directive handle + // (for yaml_ALIAS_TOKEN, yaml_ANCHOR_TOKEN, yaml_SCALAR_TOKEN, yaml_TAG_TOKEN, yaml_TAG_DIRECTIVE_TOKEN). + value []byte + + // The tag suffix (for yaml_TAG_TOKEN). + suffix []byte + + // The tag directive prefix (for yaml_TAG_DIRECTIVE_TOKEN). + prefix []byte + + // The scalar style (for yaml_SCALAR_TOKEN). + style yaml_scalar_style_t + + // The version directive major/minor (for yaml_VERSION_DIRECTIVE_TOKEN). + major, minor int8 +} + +// Events + +type yaml_event_type_t int8 + +// Event types. +const ( + // An empty event. + yaml_NO_EVENT yaml_event_type_t = iota + + yaml_STREAM_START_EVENT // A STREAM-START event. + yaml_STREAM_END_EVENT // A STREAM-END event. + yaml_DOCUMENT_START_EVENT // A DOCUMENT-START event. + yaml_DOCUMENT_END_EVENT // A DOCUMENT-END event. + yaml_ALIAS_EVENT // An ALIAS event. + yaml_SCALAR_EVENT // A SCALAR event. + yaml_SEQUENCE_START_EVENT // A SEQUENCE-START event. + yaml_SEQUENCE_END_EVENT // A SEQUENCE-END event. + yaml_MAPPING_START_EVENT // A MAPPING-START event. + yaml_MAPPING_END_EVENT // A MAPPING-END event. + yaml_TAIL_COMMENT_EVENT +) + +var eventStrings = []string{ + yaml_NO_EVENT: "none", + yaml_STREAM_START_EVENT: "stream start", + yaml_STREAM_END_EVENT: "stream end", + yaml_DOCUMENT_START_EVENT: "document start", + yaml_DOCUMENT_END_EVENT: "document end", + yaml_ALIAS_EVENT: "alias", + yaml_SCALAR_EVENT: "scalar", + yaml_SEQUENCE_START_EVENT: "sequence start", + yaml_SEQUENCE_END_EVENT: "sequence end", + yaml_MAPPING_START_EVENT: "mapping start", + yaml_MAPPING_END_EVENT: "mapping end", + yaml_TAIL_COMMENT_EVENT: "tail comment", +} + +func (e yaml_event_type_t) String() string { + if e < 0 || int(e) >= len(eventStrings) { + return fmt.Sprintf("unknown event %d", e) + } + return eventStrings[e] +} + +// The event structure. +type yaml_event_t struct { + + // The event type. + typ yaml_event_type_t + + // The start and end of the event. + start_mark, end_mark yaml_mark_t + + // The document encoding (for yaml_STREAM_START_EVENT). + encoding yaml_encoding_t + + // The version directive (for yaml_DOCUMENT_START_EVENT). + version_directive *yaml_version_directive_t + + // The list of tag directives (for yaml_DOCUMENT_START_EVENT). + tag_directives []yaml_tag_directive_t + + // The comments + head_comment []byte + line_comment []byte + foot_comment []byte + tail_comment []byte + + // The anchor (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_ALIAS_EVENT). + anchor []byte + + // The tag (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT). + tag []byte + + // The scalar value (for yaml_SCALAR_EVENT). + value []byte + + // Is the document start/end indicator implicit, or the tag optional? + // (for yaml_DOCUMENT_START_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_SCALAR_EVENT). + implicit bool + + // Is the tag optional for any non-plain style? (for yaml_SCALAR_EVENT). + quoted_implicit bool + + // The style (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT). + style yaml_style_t +} + +func (e *yaml_event_t) scalar_style() yaml_scalar_style_t { return yaml_scalar_style_t(e.style) } +func (e *yaml_event_t) sequence_style() yaml_sequence_style_t { return yaml_sequence_style_t(e.style) } +func (e *yaml_event_t) mapping_style() yaml_mapping_style_t { return yaml_mapping_style_t(e.style) } + +// Nodes + +const ( + yaml_NULL_TAG = "tag:yaml.org,2002:null" // The tag !!null with the only possible value: null. + yaml_BOOL_TAG = "tag:yaml.org,2002:bool" // The tag !!bool with the values: true and false. + yaml_STR_TAG = "tag:yaml.org,2002:str" // The tag !!str for string values. + yaml_INT_TAG = "tag:yaml.org,2002:int" // The tag !!int for integer values. + yaml_FLOAT_TAG = "tag:yaml.org,2002:float" // The tag !!float for float values. + yaml_TIMESTAMP_TAG = "tag:yaml.org,2002:timestamp" // The tag !!timestamp for date and time values. + + yaml_SEQ_TAG = "tag:yaml.org,2002:seq" // The tag !!seq is used to denote sequences. + yaml_MAP_TAG = "tag:yaml.org,2002:map" // The tag !!map is used to denote mapping. + + // Not in original libyaml. + yaml_BINARY_TAG = "tag:yaml.org,2002:binary" + yaml_MERGE_TAG = "tag:yaml.org,2002:merge" + + yaml_DEFAULT_SCALAR_TAG = yaml_STR_TAG // The default scalar tag is !!str. + yaml_DEFAULT_SEQUENCE_TAG = yaml_SEQ_TAG // The default sequence tag is !!seq. + yaml_DEFAULT_MAPPING_TAG = yaml_MAP_TAG // The default mapping tag is !!map. +) + +type yaml_node_type_t int + +// Node types. +const ( + // An empty node. + yaml_NO_NODE yaml_node_type_t = iota + + yaml_SCALAR_NODE // A scalar node. + yaml_SEQUENCE_NODE // A sequence node. + yaml_MAPPING_NODE // A mapping node. +) + +// An element of a sequence node. +type yaml_node_item_t int + +// An element of a mapping node. +type yaml_node_pair_t struct { + key int // The key of the element. + value int // The value of the element. +} + +// The node structure. +type yaml_node_t struct { + typ yaml_node_type_t // The node type. + tag []byte // The node tag. + + // The node data. + + // The scalar parameters (for yaml_SCALAR_NODE). + scalar struct { + value []byte // The scalar value. + length int // The length of the scalar value. + style yaml_scalar_style_t // The scalar style. + } + + // The sequence parameters (for YAML_SEQUENCE_NODE). + sequence struct { + items_data []yaml_node_item_t // The stack of sequence items. + style yaml_sequence_style_t // The sequence style. + } + + // The mapping parameters (for yaml_MAPPING_NODE). + mapping struct { + pairs_data []yaml_node_pair_t // The stack of mapping pairs (key, value). + pairs_start *yaml_node_pair_t // The beginning of the stack. + pairs_end *yaml_node_pair_t // The end of the stack. + pairs_top *yaml_node_pair_t // The top of the stack. + style yaml_mapping_style_t // The mapping style. + } + + start_mark yaml_mark_t // The beginning of the node. + end_mark yaml_mark_t // The end of the node. + +} + +// The document structure. +type yaml_document_t struct { + + // The document nodes. + nodes []yaml_node_t + + // The version directive. + version_directive *yaml_version_directive_t + + // The list of tag directives. + tag_directives_data []yaml_tag_directive_t + tag_directives_start int // The beginning of the tag directives list. + tag_directives_end int // The end of the tag directives list. + + start_implicit int // Is the document start indicator implicit? + end_implicit int // Is the document end indicator implicit? + + // The start/end of the document. + start_mark, end_mark yaml_mark_t +} + +// The prototype of a read handler. +// +// The read handler is called when the parser needs to read more bytes from the +// source. The handler should write not more than size bytes to the buffer. +// The number of written bytes should be set to the size_read variable. +// +// [in,out] data A pointer to an application data specified by +// yaml_parser_set_input(). +// [out] buffer The buffer to write the data from the source. +// [in] size The size of the buffer. +// [out] size_read The actual number of bytes read from the source. +// +// On success, the handler should return 1. If the handler failed, +// the returned value should be 0. On EOF, the handler should set the +// size_read to 0 and return 1. +type yaml_read_handler_t func(parser *yaml_parser_t, buffer []byte) (n int, err error) + +// This structure holds information about a potential simple key. +type yaml_simple_key_t struct { + possible bool // Is a simple key possible? + required bool // Is a simple key required? + token_number int // The number of the token. + mark yaml_mark_t // The position mark. +} + +// The states of the parser. +type yaml_parser_state_t int + +const ( + yaml_PARSE_STREAM_START_STATE yaml_parser_state_t = iota + + yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE // Expect the beginning of an implicit document. + yaml_PARSE_DOCUMENT_START_STATE // Expect DOCUMENT-START. + yaml_PARSE_DOCUMENT_CONTENT_STATE // Expect the content of a document. + yaml_PARSE_DOCUMENT_END_STATE // Expect DOCUMENT-END. + yaml_PARSE_BLOCK_NODE_STATE // Expect a block node. + yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE // Expect a block node or indentless sequence. + yaml_PARSE_FLOW_NODE_STATE // Expect a flow node. + yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a block sequence. + yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE // Expect an entry of a block sequence. + yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE // Expect an entry of an indentless sequence. + yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping. + yaml_PARSE_BLOCK_MAPPING_KEY_STATE // Expect a block mapping key. + yaml_PARSE_BLOCK_MAPPING_VALUE_STATE // Expect a block mapping value. + yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a flow sequence. + yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE // Expect an entry of a flow sequence. + yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE // Expect a key of an ordered mapping. + yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE // Expect a value of an ordered mapping. + yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE // Expect the and of an ordered mapping entry. + yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping. + yaml_PARSE_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping. + yaml_PARSE_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping. + yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE // Expect an empty value of a flow mapping. + yaml_PARSE_END_STATE // Expect nothing. +) + +func (ps yaml_parser_state_t) String() string { + switch ps { + case yaml_PARSE_STREAM_START_STATE: + return "yaml_PARSE_STREAM_START_STATE" + case yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE: + return "yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE" + case yaml_PARSE_DOCUMENT_START_STATE: + return "yaml_PARSE_DOCUMENT_START_STATE" + case yaml_PARSE_DOCUMENT_CONTENT_STATE: + return "yaml_PARSE_DOCUMENT_CONTENT_STATE" + case yaml_PARSE_DOCUMENT_END_STATE: + return "yaml_PARSE_DOCUMENT_END_STATE" + case yaml_PARSE_BLOCK_NODE_STATE: + return "yaml_PARSE_BLOCK_NODE_STATE" + case yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE: + return "yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE" + case yaml_PARSE_FLOW_NODE_STATE: + return "yaml_PARSE_FLOW_NODE_STATE" + case yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE: + return "yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE" + case yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE: + return "yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE" + case yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE: + return "yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE" + case yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE: + return "yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE" + case yaml_PARSE_BLOCK_MAPPING_KEY_STATE: + return "yaml_PARSE_BLOCK_MAPPING_KEY_STATE" + case yaml_PARSE_BLOCK_MAPPING_VALUE_STATE: + return "yaml_PARSE_BLOCK_MAPPING_VALUE_STATE" + case yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE: + return "yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE" + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE: + return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE" + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE: + return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE" + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE: + return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE" + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE: + return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE" + case yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE: + return "yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE" + case yaml_PARSE_FLOW_MAPPING_KEY_STATE: + return "yaml_PARSE_FLOW_MAPPING_KEY_STATE" + case yaml_PARSE_FLOW_MAPPING_VALUE_STATE: + return "yaml_PARSE_FLOW_MAPPING_VALUE_STATE" + case yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE: + return "yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE" + case yaml_PARSE_END_STATE: + return "yaml_PARSE_END_STATE" + } + return "<unknown parser state>" +} + +// This structure holds aliases data. +type yaml_alias_data_t struct { + anchor []byte // The anchor. + index int // The node id. + mark yaml_mark_t // The anchor mark. +} + +// The parser structure. +// +// All members are internal. Manage the structure using the +// yaml_parser_ family of functions. +type yaml_parser_t struct { + + // Error handling + + error yaml_error_type_t // Error type. + + problem string // Error description. + + // The byte about which the problem occurred. + problem_offset int + problem_value int + problem_mark yaml_mark_t + + // The error context. + context string + context_mark yaml_mark_t + + // Reader stuff + + read_handler yaml_read_handler_t // Read handler. + + input_reader io.Reader // File input data. + input []byte // String input data. + input_pos int + + eof bool // EOF flag + + buffer []byte // The working buffer. + buffer_pos int // The current position of the buffer. + + unread int // The number of unread characters in the buffer. + + newlines int // The number of line breaks since last non-break/non-blank character + + raw_buffer []byte // The raw buffer. + raw_buffer_pos int // The current position of the buffer. + + encoding yaml_encoding_t // The input encoding. + + offset int // The offset of the current position (in bytes). + mark yaml_mark_t // The mark of the current position. + + // Comments + + head_comment []byte // The current head comments + line_comment []byte // The current line comments + foot_comment []byte // The current foot comments + tail_comment []byte // Foot comment that happens at the end of a block. + stem_comment []byte // Comment in item preceding a nested structure (list inside list item, etc) + + comments []yaml_comment_t // The folded comments for all parsed tokens + comments_head int + + // Scanner stuff + + stream_start_produced bool // Have we started to scan the input stream? + stream_end_produced bool // Have we reached the end of the input stream? + + flow_level int // The number of unclosed '[' and '{' indicators. + + tokens []yaml_token_t // The tokens queue. + tokens_head int // The head of the tokens queue. + tokens_parsed int // The number of tokens fetched from the queue. + token_available bool // Does the tokens queue contain a token ready for dequeueing. + + indent int // The current indentation level. + indents []int // The indentation levels stack. + + simple_key_allowed bool // May a simple key occur at the current position? + simple_keys []yaml_simple_key_t // The stack of simple keys. + simple_keys_by_tok map[int]int // possible simple_key indexes indexed by token_number + + // Parser stuff + + state yaml_parser_state_t // The current parser state. + states []yaml_parser_state_t // The parser states stack. + marks []yaml_mark_t // The stack of marks. + tag_directives []yaml_tag_directive_t // The list of TAG directives. + + // Dumper stuff + + aliases []yaml_alias_data_t // The alias data. + + document *yaml_document_t // The currently parsed document. +} + +type yaml_comment_t struct { + + scan_mark yaml_mark_t // Position where scanning for comments started + token_mark yaml_mark_t // Position after which tokens will be associated with this comment + start_mark yaml_mark_t // Position of '#' comment mark + end_mark yaml_mark_t // Position where comment terminated + + head []byte + line []byte + foot []byte +} + +// Emitter Definitions + +// The prototype of a write handler. +// +// The write handler is called when the emitter needs to flush the accumulated +// characters to the output. The handler should write @a size bytes of the +// @a buffer to the output. +// +// @param[in,out] data A pointer to an application data specified by +// yaml_emitter_set_output(). +// @param[in] buffer The buffer with bytes to be written. +// @param[in] size The size of the buffer. +// +// @returns On success, the handler should return @c 1. If the handler failed, +// the returned value should be @c 0. +// +type yaml_write_handler_t func(emitter *yaml_emitter_t, buffer []byte) error + +type yaml_emitter_state_t int + +// The emitter states. +const ( + // Expect STREAM-START. + yaml_EMIT_STREAM_START_STATE yaml_emitter_state_t = iota + + yaml_EMIT_FIRST_DOCUMENT_START_STATE // Expect the first DOCUMENT-START or STREAM-END. + yaml_EMIT_DOCUMENT_START_STATE // Expect DOCUMENT-START or STREAM-END. + yaml_EMIT_DOCUMENT_CONTENT_STATE // Expect the content of a document. + yaml_EMIT_DOCUMENT_END_STATE // Expect DOCUMENT-END. + yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a flow sequence. + yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE // Expect the next item of a flow sequence, with the comma already written out + yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE // Expect an item of a flow sequence. + yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping. + yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE // Expect the next key of a flow mapping, with the comma already written out + yaml_EMIT_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping. + yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a flow mapping. + yaml_EMIT_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping. + yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a block sequence. + yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE // Expect an item of a block sequence. + yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping. + yaml_EMIT_BLOCK_MAPPING_KEY_STATE // Expect the key of a block mapping. + yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a block mapping. + yaml_EMIT_BLOCK_MAPPING_VALUE_STATE // Expect a value of a block mapping. + yaml_EMIT_END_STATE // Expect nothing. +) + +// The emitter structure. +// +// All members are internal. Manage the structure using the @c yaml_emitter_ +// family of functions. +type yaml_emitter_t struct { + + // Error handling + + error yaml_error_type_t // Error type. + problem string // Error description. + + // Writer stuff + + write_handler yaml_write_handler_t // Write handler. + + output_buffer *[]byte // String output data. + output_writer io.Writer // File output data. + + buffer []byte // The working buffer. + buffer_pos int // The current position of the buffer. + + raw_buffer []byte // The raw buffer. + raw_buffer_pos int // The current position of the buffer. + + encoding yaml_encoding_t // The stream encoding. + + // Emitter stuff + + canonical bool // If the output is in the canonical style? + best_indent int // The number of indentation spaces. + best_width int // The preferred width of the output lines. + unicode bool // Allow unescaped non-ASCII characters? + line_break yaml_break_t // The preferred line break. + + state yaml_emitter_state_t // The current emitter state. + states []yaml_emitter_state_t // The stack of states. + + events []yaml_event_t // The event queue. + events_head int // The head of the event queue. + + indents []int // The stack of indentation levels. + + tag_directives []yaml_tag_directive_t // The list of tag directives. + + indent int // The current indentation level. + + flow_level int // The current flow level. + + root_context bool // Is it the document root context? + sequence_context bool // Is it a sequence context? + mapping_context bool // Is it a mapping context? + simple_key_context bool // Is it a simple mapping key context? + + line int // The current line. + column int // The current column. + whitespace bool // If the last character was a whitespace? + indention bool // If the last character was an indentation character (' ', '-', '?', ':')? + open_ended bool // If an explicit document end is required? + + space_above bool // Is there's an empty line above? + foot_indent int // The indent used to write the foot comment above, or -1 if none. + + // Anchor analysis. + anchor_data struct { + anchor []byte // The anchor value. + alias bool // Is it an alias? + } + + // Tag analysis. + tag_data struct { + handle []byte // The tag handle. + suffix []byte // The tag suffix. + } + + // Scalar analysis. + scalar_data struct { + value []byte // The scalar value. + multiline bool // Does the scalar contain line breaks? + flow_plain_allowed bool // Can the scalar be expessed in the flow plain style? + block_plain_allowed bool // Can the scalar be expressed in the block plain style? + single_quoted_allowed bool // Can the scalar be expressed in the single quoted style? + block_allowed bool // Can the scalar be expressed in the literal or folded styles? + style yaml_scalar_style_t // The output style. + } + + // Comments + head_comment []byte + line_comment []byte + foot_comment []byte + tail_comment []byte + + key_line_comment []byte + + // Dumper stuff + + opened bool // If the stream was already opened? + closed bool // If the stream was already closed? + + // The information associated with the document nodes. + anchors *struct { + references int // The number of references. + anchor int // The anchor id. + serialized bool // If the node has been emitted? + } + + last_anchor_id int // The last assigned anchor id. + + document *yaml_document_t // The currently emitted document. +} diff --git a/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/yamlprivateh.go b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/yamlprivateh.go new file mode 100644 index 0000000..e88f9c5 --- /dev/null +++ b/.devenv/state/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/yamlprivateh.go @@ -0,0 +1,198 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// Copyright (c) 2006-2010 Kirill Simonov +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package yaml + +const ( + // The size of the input raw buffer. + input_raw_buffer_size = 512 + + // The size of the input buffer. + // It should be possible to decode the whole raw buffer. + input_buffer_size = input_raw_buffer_size * 3 + + // The size of the output buffer. + output_buffer_size = 128 + + // The size of the output raw buffer. + // It should be possible to encode the whole output buffer. + output_raw_buffer_size = (output_buffer_size*2 + 2) + + // The size of other stacks and queues. + initial_stack_size = 16 + initial_queue_size = 16 + initial_string_size = 16 +) + +// Check if the character at the specified position is an alphabetical +// character, a digit, '_', or '-'. +func is_alpha(b []byte, i int) bool { + return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'Z' || b[i] >= 'a' && b[i] <= 'z' || b[i] == '_' || b[i] == '-' +} + +// Check if the character at the specified position is a digit. +func is_digit(b []byte, i int) bool { + return b[i] >= '0' && b[i] <= '9' +} + +// Get the value of a digit. +func as_digit(b []byte, i int) int { + return int(b[i]) - '0' +} + +// Check if the character at the specified position is a hex-digit. +func is_hex(b []byte, i int) bool { + return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'F' || b[i] >= 'a' && b[i] <= 'f' +} + +// Get the value of a hex-digit. +func as_hex(b []byte, i int) int { + bi := b[i] + if bi >= 'A' && bi <= 'F' { + return int(bi) - 'A' + 10 + } + if bi >= 'a' && bi <= 'f' { + return int(bi) - 'a' + 10 + } + return int(bi) - '0' +} + +// Check if the character is ASCII. +func is_ascii(b []byte, i int) bool { + return b[i] <= 0x7F +} + +// Check if the character at the start of the buffer can be printed unescaped. +func is_printable(b []byte, i int) bool { + return ((b[i] == 0x0A) || // . == #x0A + (b[i] >= 0x20 && b[i] <= 0x7E) || // #x20 <= . <= #x7E + (b[i] == 0xC2 && b[i+1] >= 0xA0) || // #0xA0 <= . <= #xD7FF + (b[i] > 0xC2 && b[i] < 0xED) || + (b[i] == 0xED && b[i+1] < 0xA0) || + (b[i] == 0xEE) || + (b[i] == 0xEF && // #xE000 <= . <= #xFFFD + !(b[i+1] == 0xBB && b[i+2] == 0xBF) && // && . != #xFEFF + !(b[i+1] == 0xBF && (b[i+2] == 0xBE || b[i+2] == 0xBF)))) +} + +// Check if the character at the specified position is NUL. +func is_z(b []byte, i int) bool { + return b[i] == 0x00 +} + +// Check if the beginning of the buffer is a BOM. +func is_bom(b []byte, i int) bool { + return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF +} + +// Check if the character at the specified position is space. +func is_space(b []byte, i int) bool { + return b[i] == ' ' +} + +// Check if the character at the specified position is tab. +func is_tab(b []byte, i int) bool { + return b[i] == '\t' +} + +// Check if the character at the specified position is blank (space or tab). +func is_blank(b []byte, i int) bool { + //return is_space(b, i) || is_tab(b, i) + return b[i] == ' ' || b[i] == '\t' +} + +// Check if the character at the specified position is a line break. +func is_break(b []byte, i int) bool { + return (b[i] == '\r' || // CR (#xD) + b[i] == '\n' || // LF (#xA) + b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9) // PS (#x2029) +} + +func is_crlf(b []byte, i int) bool { + return b[i] == '\r' && b[i+1] == '\n' +} + +// Check if the character is a line break or NUL. +func is_breakz(b []byte, i int) bool { + //return is_break(b, i) || is_z(b, i) + return ( + // is_break: + b[i] == '\r' || // CR (#xD) + b[i] == '\n' || // LF (#xA) + b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) + // is_z: + b[i] == 0) +} + +// Check if the character is a line break, space, or NUL. +func is_spacez(b []byte, i int) bool { + //return is_space(b, i) || is_breakz(b, i) + return ( + // is_space: + b[i] == ' ' || + // is_breakz: + b[i] == '\r' || // CR (#xD) + b[i] == '\n' || // LF (#xA) + b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) + b[i] == 0) +} + +// Check if the character is a line break, space, tab, or NUL. +func is_blankz(b []byte, i int) bool { + //return is_blank(b, i) || is_breakz(b, i) + return ( + // is_blank: + b[i] == ' ' || b[i] == '\t' || + // is_breakz: + b[i] == '\r' || // CR (#xD) + b[i] == '\n' || // LF (#xA) + b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) + b[i] == 0) +} + +// Determine the width of the character. +func width(b byte) int { + // Don't replace these by a switch without first + // confirming that it is being inlined. + if b&0x80 == 0x00 { + return 1 + } + if b&0xE0 == 0xC0 { + return 2 + } + if b&0xF0 == 0xE0 { + return 3 + } + if b&0xF8 == 0xF0 { + return 4 + } + return 0 + +} diff --git a/.devenv/state/go/pkg/sumdb/sum.golang.org/latest b/.devenv/state/go/pkg/sumdb/sum.golang.org/latest new file mode 100644 index 0000000..eb4b0d7 --- /dev/null +++ b/.devenv/state/go/pkg/sumdb/sum.golang.org/latest @@ -0,0 +1,5 @@ +go.sum database tree +19632713 ++dFqIiCep9PFS2o8VBuGFtkBBGfUWWNR5ZdeHkgmu9k= + +— sum.golang.org Az3griAQL5WUYUZsThtGZu3lOLHLrxWK0YS6TDTBr7TJUTmogfpY1Hyrv21yIzVzvlSSsoq4A1vGmP1Se1EmBw8IcgQ= diff --git a/.direnv/devenv-profile-722f25393f8e218fc5cf98d5b0468ada972afd9e.rc b/.direnv/devenv-profile-722f25393f8e218fc5cf98d5b0468ada972afd9e.rc new file mode 100644 index 0000000..850b703 --- /dev/null +++ b/.direnv/devenv-profile-722f25393f8e218fc5cf98d5b0468ada972afd9e.rc @@ -0,0 +1,258 @@ +unset shellHook +PATH=${PATH:-} +nix_saved_PATH="$PATH" +XDG_DATA_DIRS=${XDG_DATA_DIRS:-} +nix_saved_XDG_DATA_DIRS="$XDG_DATA_DIRS" +BASH='/noshell' +HOSTTYPE='x86_64' +IFS=' +' +IN_NIX_SHELL='impure' +export IN_NIX_SHELL +LINENO='76' +MACHTYPE='x86_64-pc-linux-gnu' +NIX_BUILD_CORES='0' +export NIX_BUILD_CORES +NIX_STORE='/nix/store' +export NIX_STORE +OLDPWD='' +export OLDPWD +OPTERR='1' +OSTYPE='linux-gnu' +PATH='/path-not-set' +export PATH +PS4='+ ' +builder='/nix/store/kxkdrxvc3da2dpsgikn8s2ml97h88m46-bash-interactive-5.2-p15/bin/bash' +export builder +dontAddDisableDepTrack='1' +export dontAddDisableDepTrack +name='devenv-shell' +export name +out='/home/vs/workspaces/oss/go-libs/xflags/outputs/out' +export out +outputs='out' +shellHook=' +# Remove all the unnecessary noise that is set by the build env +unset NIX_BUILD_TOP NIX_BUILD_CORES NIX_STORE +unset TEMP TEMPDIR TMP TMPDIR +# $name variable is preserved to keep it compatible with pure shell https://github.com/sindresorhus/pure/blob/47c0c881f0e7cfdb5eaccd335f52ad17b897c060/pure.zsh#L235 +unset builder out shellHook stdenv system +# Flakes stuff +unset dontAddDisableDepTrack outputs + +# For `nix develop`. We get /noshell on Linux and /sbin/nologin on macOS. +if [[ "$SHELL" == "/noshell" || "$SHELL" == "/sbin/nologin" ]]; then + export SHELL=/nix/store/kxkdrxvc3da2dpsgikn8s2ml97h88m46-bash-interactive-5.2-p15/bin/bash +fi + +# https://github.com/numtide/devshell/issues/158 +PATH=${PATH#/path-not-set:} + +export DEVENV_PROFILE=/nix/store/ishxkjwm6m1nq2dzailw41xw5rczf6hi-devenv-profile + +# add installed packages to PATH +export PATH="$DEVENV_PROFILE/bin:$PATH" + +# prepend common compilation lookup paths +export PKG_CONFIG_PATH="$DEVENV_PROFILE/lib/pkgconfig:${PKG_CONFIG_PATH-}" +export LD_LIBRARY_PATH="$DEVENV_PROFILE/lib:${LD_LIBRARY_PATH-}" +export LIBRARY_PATH="$DEVENV_PROFILE/lib:${LIBRARY_PATH-}" +export C_INCLUDE_PATH="$DEVENV_PROFILE/include:${C_INCLUDE_PATH-}" + +# these provide shell completions / default config options +export XDG_DATA_DIRS="$DEVENV_PROFILE/share:${XDG_DATA_DIRS-}" +export XDG_CONFIG_DIRS="$DEVENV_PROFILE/etc/xdg:${XDG_CONFIG_DIRS-}" + +export DEVENV_DOTFILE='\''/home/vs/workspaces/oss/go-libs/xflags/.devenv'\'' +export DEVENV_PROFILE='\''/nix/store/ishxkjwm6m1nq2dzailw41xw5rczf6hi-devenv-profile'\'' +export DEVENV_ROOT='\''/home/vs/workspaces/oss/go-libs/xflags'\'' +export DEVENV_STATE='\''/home/vs/workspaces/oss/go-libs/xflags/.devenv/state'\'' +export GIT_EXTERNAL_DIFF='\''difft'\'' +export GOPATH='\''/home/vs/workspaces/oss/go-libs/xflags/.devenv/state/go'\'' +export GOROOT='\''/nix/store/m3mi5km1zdmaqdb33byirlixigzh3f4l-go-1.20.7/share/go/'\'' + + +export PATH=$GOPATH/bin:$PATH + + + +cat <<'\''EOF'\'' > Taskfile.yml + +# THIS FILE IS AUTOGENERATED BY THE DEVENVSHELL +# DO NOT EDIT THIS FILE MANUALLY +# INSTEAD EDIT THE DEVENVSHELL CONFIGURATION FILE devenv.nix +# AND OPEN A SHELL WITH THE COMMAND devenv shell +# +# Information about the task runner can be found here: +# https://taskfile.dev + +version: '\''3'\'' + +tasks: + default: + cmds: + - task --list + silent: true + + test: + desc: Execute unit tests in Go. + cmds: + - echo "Execute unit tests in Go." + - go test -cover -v ./... + - go test -bench . + - go test -race . + + test-fuzz: + desc: Conduct fuzzing tests.# + cmds: + - echo "Conduct fuzzing tests." + - go test -v -fuzztime=30s -fuzz=Fuzz ./... + + add-licenses: + desc: Attach license headers to Go files. + cmds: + - echo "Attach license headers to Go files." + - go install github.com/google/addlicense@latest + - addlicense -c "schukai GmbH" -s -l "AGPL-3.0" ./*.go + silent: true + + check-licenses: + desc: Check license headers of Go files. + silent: true + cmds: + - go-licenses save "$(get-go-default-packages)" --ignore "gitlab.schukai.com" --force --save_path ${DEVENV_ROOT}/licenses/ + + check: + desc: Confirm repository status. + cmds: + - git diff-index --quiet HEAD || (echo "There are uncommitted changes after running make. Please commit or stash them before running make."; exit 1) + silent: true + + commit: + desc: Commit changes to the repository. + aliases: + - c + - ci + - git-commit + cmds: + - do-git-commit +EOF + +cat <<'\''EOF'\'' > .gitlab-ci.yml + +# THIS FILE IS AUTOGENERATED BY THE DEVENVSHELL +# DO NOT EDIT THIS FILE MANUALLY +# INSTEAD EDIT THE DEVENVSHELL CONFIGURATION FILE devenv.nix +# AND OPEN A SHELL WITH THE COMMAND devenv shell +# + +image: docker-registry.schukai.com:443/nixos-ci-devenv:latest + +services: + - docker:dind + +variables: + # The repo name as used in + # https://github.com/nix-community/NUR/blob/master/repos.json + NIXOS_VERSION: "23.05" + NIXPKGS_ALLOW_UNFREE: "1" + NIXPKGS_ALLOW_INSECURE: "1" + DOCKER_DRIVER: overlay2 + GIT_DEPTH: 10 + +stages: + - test + - deploy + +before_script: + - nix shell nixpkgs#coreutils-full -c mkdir -p /certs/client/ + - nix shell nixpkgs#coreutils-full -c ln -fs /etc/ssl/certs/ca-bundle.crt /certs/client/ca.pem + - echo > .env-gitlab-ci + - variables=("HOME=$HOME" "CI_COMMIT_REF_NAME=$CI_COMMIT_REF_NAME" "CI_REPOSITORY_URL=$CI_REPOSITORY_URL" "GITLAB_TOKEN=$GITLAB_TOKEN" "CI_JOB_TOKEN=$CI_JOB_TOKEN" "GITLAB_USER_EMAIL=$GITLAB_USER_EMAIL" "GITLAB_USER_NAME=\"$GITLAB_USER_NAME\"" "CI_REGISTRY_USER=$CI_REGISTRY_USER" "CI_PROJECT_ID=$CI_PROJECT_ID" "CI_PROJECT_DIR=$CI_PROJECT_DIR" "CI_API_V4_URL=$CI_API_V4_URL" "CI_PROJECT_NAME=$CI_PROJECT_NAME" "CI_COMMIT_SHORT_SHA=$CI_COMMIT_SHORT_SHA"); for var in "${variables[@]}"; do echo "$var" >> .env-gitlab-ci; done + - cat .env-gitlab-ci + +after_script: + - if [ -f .env-gitlab-ci ]; then rm .env-gitlab-ci; fi + +test: + stage: test + tags: + - nixos + script: + - devenv shell test-lib + + cache: + - key: nixos + paths: + - /nix/store + + artifacts: + paths: + - dist + +deploy: + stage: deploy + tags: + - nixos + script: + - devenv shell -c deploy-lib + + when: on_success + + cache: + - key: nixos + paths: + - /nix/store + + + artifacts: + paths: + - dist +EOF + + + + +export PS1="\[\e[0;34m\](devenv)\[\e[0m\] ${PS1-}" + +# set path to locales on non-NixOS Linux hosts +if [ -z "${LOCALE_ARCHIVE-}" ]; then + export LOCALE_ARCHIVE=/nix/store/mrbcpynv50xm2ad1gm2r15jvlg22nxnm-glibc-locales-2.37-8/lib/locale/locale-archive +fi + + +# note what environments are active, but make sure we don'\''t repeat them +if [[ ! "${DIRENV_ACTIVE-}" =~ (^|:)"$PWD"(:|$) ]]; then + export DIRENV_ACTIVE="$PWD:${DIRENV_ACTIVE-}" +fi + +# devenv helper +if [ ! type -p direnv &>/dev/null && -f .envrc ]; then + echo "You have .envrc but direnv command is not installed." + echo "Please install direnv: https://direnv.net/docs/installation.html" +fi + +mkdir -p .devenv +rm -f .devenv/profile +ln -s /nix/store/ishxkjwm6m1nq2dzailw41xw5rczf6hi-devenv-profile .devenv/profile + +' +export shellHook +stdenv='/nix/store/psfv6k7b2qa49zzdgr221qbi4nq8i3km-naked-stdenv' +export stdenv +system='x86_64-linux' +export system +runHook () +{ + + eval "$shellHook"; + unset runHook +} +PATH="$PATH:$nix_saved_PATH" +XDG_DATA_DIRS="$XDG_DATA_DIRS:$nix_saved_XDG_DATA_DIRS" +export NIX_BUILD_TOP="$(mktemp -d -t nix-shell.XXXXXX)" +export TMP="$NIX_BUILD_TOP" +export TMPDIR="$NIX_BUILD_TOP" +export TEMP="$NIX_BUILD_TOP" +export TEMPDIR="$NIX_BUILD_TOP" +eval "$shellHook" diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7b2885e..4339280 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,9 +1,15 @@ + +# THIS FILE IS AUTOGENERATED BY THE DEVENVSHELL +# DO NOT EDIT THIS FILE MANUALLY +# INSTEAD EDIT THE DEVENVSHELL CONFIGURATION FILE devenv.nix +# AND OPEN A SHELL WITH THE COMMAND devenv shell +# + image: docker-registry.schukai.com:443/nixos-ci-devenv:latest services: - docker:dind - variables: # The repo name as used in # https://github.com/nix-community/NUR/blob/master/repos.json @@ -13,7 +19,6 @@ variables: DOCKER_DRIVER: overlay2 GIT_DEPTH: 10 - stages: - test - deploy diff --git a/Taskfile.yml b/Taskfile.yml index 7febe83..7095ba5 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -1,3 +1,10 @@ + +# THIS FILE IS AUTOGENERATED BY THE DEVENVSHELL +# DO NOT EDIT THIS FILE MANUALLY +# INSTEAD EDIT THE DEVENVSHELL CONFIGURATION FILE devenv.nix +# AND OPEN A SHELL WITH THE COMMAND devenv shell +# +# Information about the task runner can be found here: # https://taskfile.dev version: '3' @@ -5,13 +12,16 @@ version: '3' tasks: default: cmds: - - task --list-all + - task --list silent: true + test: desc: Execute unit tests in Go. - cmds: + cmds: - echo "Execute unit tests in Go." - go test -cover -v ./... + - go test -bench . + - go test -race . test-fuzz: desc: Conduct fuzzing tests.# @@ -23,8 +33,15 @@ tasks: desc: Attach license headers to Go files. cmds: - echo "Attach license headers to Go files." + - go install github.com/google/addlicense@latest - addlicense -c "schukai GmbH" -s -l "AGPL-3.0" ./*.go silent: true + + check-licenses: + desc: Check license headers of Go files. + silent: true + cmds: + - go-licenses save "$(get-go-default-packages)" --ignore "gitlab.schukai.com" --force --save_path ${DEVENV_ROOT}/licenses/ check: desc: Confirm repository status. @@ -32,17 +49,11 @@ tasks: - git diff-index --quiet HEAD || (echo "There are uncommitted changes after running make. Please commit or stash them before running make."; exit 1) silent: true - build: - desc: Compile the application, + commit: + desc: Commit changes to the repository. aliases: - - b - vars: - DEVENV_ROOT: - sh: echo "$DEVENV_ROOT" - + - c + - ci + - git-commit cmds: - - devenv shell build-app - sources: - - source/**/*.go - - source/**/*.mod - - dist/** + - do-git-commit diff --git a/devenv.nix b/devenv.nix index 2568f0c..ee781c6 100644 --- a/devenv.nix +++ b/devenv.nix @@ -1,149 +1,638 @@ -{ pkgs, inputs, phps, lib, config, modulesPath,... }: +{ pkgs, inputs, phps, lib, config, modulesPath, ... }: { - # https://devenv.sh/packages/ - packages = [ + packages = with pkgs; [ inputs.version.defaultPackage."${builtins.currentSystem}" - pkgs.git - pkgs.gcc12 - pkgs.go-task - pkgs.blackbox - pkgs.blackbox-terminal - pkgs.jq - pkgs.delve - pkgs.gdlv - pkgs.wget - pkgs.glab - pkgs.unixtools.xxd - pkgs.libffi - pkgs.zlib - pkgs.procps - pkgs.php81Extensions.xdebug - pkgs.ranger - pkgs.meld - pkgs.gnused - pkgs.coreutils-full - pkgs.gnugrep - pkgs.gnumake - pkgs.util-linux - pkgs.httpie - pkgs.netcat - pkgs.memcached - pkgs.fd + appimage-run + blackbox + blackbox-terminal + coreutils-full + dbeaver + delve + dialog + drill + exa + fd + fd + gcc12 + gdlv + git + glab + gnugrep + gnumake + gnused + go-licenses + go-task + gum + httpie + hurl + jq + libffi + logrotate + meld + memcached + netcat + nixfmt + procps + ranger + unixtools.xxd + unzip + util-linux + wget + zlib ]; - # https://devenv.sh/languages/ # languages.nix.enable = true; - languages = { - go = { enable = true; }; - }; - - difftastic.enable = true; + languages = { go = { enable = true; }; }; + + difftastic.enable = true; + + scripts.get-go-default-packages.exec = '' + #!${pkgs.bash}/bin/bash + echo $(awk -F ' ' '/^module / { print $2 }' go.mod) + ''; - # This script is executed when the app is built # You can use it to build the app - scripts.test-lib.exec = '' -#!${pkgs.bash}/bin/bash -#set -euo pipefail -set -x - -PATH="''${PATH}":${pkgs.coreutils}/bin -PATH="''${PATH}":${pkgs.findutils}/bin -PATH="''${PATH}":${pkgs.jq}/bin/ -PATH="''${PATH}":${pkgs.rsync}/bin/ -PATH="''${PATH}":${pkgs.bash}/bin/ -PATH="''${PATH}":${pkgs.curl}/bin/ -PATH="''${PATH}":${pkgs.moreutils}/bin/ -PATH="''${PATH}":${pkgs.gnutar}/bin -PATH="''${PATH}":${pkgs.gzip}/bin/ -PATH="''${PATH}":${pkgs.procps}/bin/ -PATH="''${PATH}":${pkgs.exa}/bin/ -PATH="''${PATH}":${pkgs.git}/bin/ -PATH="''${PATH}":${pkgs.gnugrep}/bin/ -PATH="''${PATH}":${inputs.version.defaultPackage."${builtins.currentSystem}"}/bin/ - -export -f PATH - -task test - -''; + scripts.test-lib.exec = '' + #!${pkgs.bash}/bin/bash + #set -euo pipefail + set -x + + PATH="''${PATH}":${pkgs.coreutils}/bin + PATH="''${PATH}":${pkgs.findutils}/bin + PATH="''${PATH}":${pkgs.jq}/bin/ + PATH="''${PATH}":${pkgs.rsync}/bin/ + PATH="''${PATH}":${pkgs.bash}/bin/ + PATH="''${PATH}":${pkgs.curl}/bin/ + PATH="''${PATH}":${pkgs.moreutils}/bin/ + PATH="''${PATH}":${pkgs.gnutar}/bin + PATH="''${PATH}":${pkgs.gzip}/bin/ + PATH="''${PATH}":${pkgs.procps}/bin/ + PATH="''${PATH}":${pkgs.exa}/bin/ + PATH="''${PATH}":${pkgs.git}/bin/ + PATH="''${PATH}":${pkgs.gnugrep}/bin/ + PATH="''${PATH}":${ + inputs.version.defaultPackage."${builtins.currentSystem}" + }/bin/ + + export PATH + + task test + + ''; # This scritp is used to deploy the app to the gitlab registry # It is used by the gitlab-ci.yml file # The environment variables are set in the gitlab project settings - scripts.deploy-lib.exec = '' -#!${pkgs.bash}/bin/bash - -PATH="''${PATH}":${pkgs.coreutils}/bin -PATH="''${PATH}":${pkgs.jq}/bin/ -PATH="''${PATH}":${pkgs.curl}/bin/ -PATH="''${PATH}":${pkgs.moreutils}/bin/ -PATH="''${PATH}":${pkgs.gnutar}/bin -PATH="''${PATH}":${pkgs.gzip}/bin/ -PATH="''${PATH}":${pkgs.exa}/bin/ -PATH="''${PATH}":${pkgs.git}/bin/ -PATH="''${PATH}":${inputs.version.defaultPackage."${builtins.currentSystem}"}/bin/ - -export PATH - -if [[ -f .env-gitlab-ci ]]; then - source .env-gitlab-ci - rm .env-gitlab-ci -fi - -set -x -## if $HOME not set, set it to current directory -if [[ -z "''${HOME}" ]]; then - HOME=$(pwd) -fi - -export HOME - -git config user.email "''${GITLAB_USER_EMAIL}" -git config user.name "''${GITLAB_USER_NAME:-"Gitlab CI"}" -git config pull.rebase true -git config http.sslVerify "false" -git remote set-url origin https://pad:''${GITLAB_TOKEN}@''${CI_REPOSITORY_URL#*@} - -git fetch --all --tags --unshallow -git reset --hard origin/master -git checkout $CI_COMMIT_REF_NAME -git pull origin $CI_COMMIT_REF_NAME - -if [ ! -z "''${CI_PROJECT_DIR}" ]; then - echo "CI_PROJECT_DIR is set, using it as project root." - project_root=$(realpath "''${CI_PROJECT_DIR}")/ -elif [ ! -z "''${DEVENV_ROOT}" ]; then - echo "DEVENV_ROOT is set, using it as project root." - project_root=$(realpath "''${DEVENV_ROOT}")/ -else - echo "Error: DEVENV_ROOT or CI_PROJECT_DIR environment variables are not set." - exit 1 -fi - -if [ ! -d "''${project_root}" ]; then - echo "Error: Project root directory does not seem to be valid." - echo "Check the DEVENV_ROOT or CI_PROJECT_DIR environment variables." - exit 1 -fi - -if [ -z "'CI_JOB_TOKEN" ]; then - echo "Error: CI_JOB_TOKEN variable is not set." - exit 1 -fi - -git --no-pager log --decorate=short --pretty=oneline -gitVersion=v$(version predict) -git tag -a $gitVersion -m"chore: bump version" -git --no-pager log --decorate=short --pretty=oneline -git push -o ci.skip origin ''${CI_COMMIT_REF_NAME} --tags - -echo "done" - -''; + scripts.deploy-lib.exec = '' + #!${pkgs.bash}/bin/bash + + PATH="''${PATH}":${pkgs.coreutils}/bin + PATH="''${PATH}":${pkgs.jq}/bin/ + PATH="''${PATH}":${pkgs.curl}/bin/ + PATH="''${PATH}":${pkgs.moreutils}/bin/ + PATH="''${PATH}":${pkgs.gnutar}/bin + PATH="''${PATH}":${pkgs.gzip}/bin/ + PATH="''${PATH}":${pkgs.exa}/bin/ + PATH="''${PATH}":${pkgs.git}/bin/ + PATH="''${PATH}":${ + inputs.version.defaultPackage."${builtins.currentSystem}" + }/bin/ + + export PATH + + if [[ -f .env-gitlab-ci ]]; then + source .env-gitlab-ci + rm .env-gitlab-ci + fi + + set -x + ## if $HOME not set, set it to current directory + if [[ -z "''${HOME}" ]]; then + HOME=$(pwd) + fi + + export HOME + + git config user.email "''${GITLAB_USER_EMAIL}" + git config user.name "''${GITLAB_USER_NAME:-"Gitlab CI"}" + git config pull.rebase true + git config http.sslVerify "false" + git remote set-url origin https://pad:''${GITLAB_TOKEN}@''${CI_REPOSITORY_URL#*@} + + git fetch --all --tags --unshallow + git reset --hard origin/master + git checkout $CI_COMMIT_REF_NAME + git pull origin $CI_COMMIT_REF_NAME + + if [ ! -z "''${CI_PROJECT_DIR}" ]; then + echo "CI_PROJECT_DIR is set, using it as project root." + project_root=$(realpath "''${CI_PROJECT_DIR}")/ + elif [ ! -z "''${DEVENV_ROOT}" ]; then + echo "DEVENV_ROOT is set, using it as project root." + project_root=$(realpath "''${DEVENV_ROOT}")/ + else + echo "Error: DEVENV_ROOT or CI_PROJECT_DIR environment variables are not set." + exit 1 + fi + + if [ ! -d "''${project_root}" ]; then + echo "Error: Project root directory does not seem to be valid." + echo "Check the DEVENV_ROOT or CI_PROJECT_DIR environment variables." + exit 1 + fi + + if [ -z "'CI_JOB_TOKEN" ]; then + echo "Error: CI_JOB_TOKEN variable is not set." + exit 1 + fi + + git --no-pager log --decorate=short --pretty=oneline + gitVersion=v$(version predict) + git tag -a $gitVersion -m"chore: bump version" + git --no-pager log --decorate=short --pretty=oneline + git push -o ci.skip origin ''${CI_COMMIT_REF_NAME} --tags + + echo "done" + + ''; + + enterShell = '' + + cat <<'EOF' > Taskfile.yml + + # THIS FILE IS AUTOGENERATED BY THE DEVENVSHELL + # DO NOT EDIT THIS FILE MANUALLY + # INSTEAD EDIT THE DEVENVSHELL CONFIGURATION FILE devenv.nix + # AND OPEN A SHELL WITH THE COMMAND devenv shell + # + # Information about the task runner can be found here: + # https://taskfile.dev + + version: '3' + + tasks: + default: + cmds: + - task --list + silent: true + + test: + desc: Execute unit tests in Go. + cmds: + - echo "Execute unit tests in Go." + - go test -cover -v ./... + - go test -bench . + - go test -race . + + test-fuzz: + desc: Conduct fuzzing tests.# + cmds: + - echo "Conduct fuzzing tests." + - go test -v -fuzztime=30s -fuzz=Fuzz ./... + + add-licenses: + desc: Attach license headers to Go files. + cmds: + - echo "Attach license headers to Go files." + - go install github.com/google/addlicense@latest + - addlicense -c "schukai GmbH" -s -l "AGPL-3.0" ./*.go + silent: true + + check-licenses: + desc: Check license headers of Go files. + silent: true + cmds: + - go-licenses save "$(get-go-default-packages)" --ignore "gitlab.schukai.com" --force --save_path ''${DEVENV_ROOT}/licenses/ + + check: + desc: Confirm repository status. + cmds: + - git diff-index --quiet HEAD || (echo "There are uncommitted changes after running make. Please commit or stash them before running make."; exit 1) + silent: true + + commit: + desc: Commit changes to the repository. + aliases: + - c + - ci + - git-commit + cmds: + - do-git-commit + EOF + + cat <<'EOF' > .gitlab-ci.yml + + # THIS FILE IS AUTOGENERATED BY THE DEVENVSHELL + # DO NOT EDIT THIS FILE MANUALLY + # INSTEAD EDIT THE DEVENVSHELL CONFIGURATION FILE devenv.nix + # AND OPEN A SHELL WITH THE COMMAND devenv shell + # + + image: docker-registry.schukai.com:443/nixos-ci-devenv:latest + + services: + - docker:dind + + variables: + # The repo name as used in + # https://github.com/nix-community/NUR/blob/master/repos.json + NIXOS_VERSION: "23.05" + NIXPKGS_ALLOW_UNFREE: "1" + NIXPKGS_ALLOW_INSECURE: "1" + DOCKER_DRIVER: overlay2 + GIT_DEPTH: 10 + + stages: + - test + - deploy + + before_script: + - nix shell nixpkgs#coreutils-full -c mkdir -p /certs/client/ + - nix shell nixpkgs#coreutils-full -c ln -fs /etc/ssl/certs/ca-bundle.crt /certs/client/ca.pem + - echo > .env-gitlab-ci + - variables=("HOME=$HOME" "CI_COMMIT_REF_NAME=$CI_COMMIT_REF_NAME" "CI_REPOSITORY_URL=$CI_REPOSITORY_URL" "GITLAB_TOKEN=$GITLAB_TOKEN" "CI_JOB_TOKEN=$CI_JOB_TOKEN" "GITLAB_USER_EMAIL=$GITLAB_USER_EMAIL" "GITLAB_USER_NAME=\"$GITLAB_USER_NAME\"" "CI_REGISTRY_USER=$CI_REGISTRY_USER" "CI_PROJECT_ID=$CI_PROJECT_ID" "CI_PROJECT_DIR=$CI_PROJECT_DIR" "CI_API_V4_URL=$CI_API_V4_URL" "CI_PROJECT_NAME=$CI_PROJECT_NAME" "CI_COMMIT_SHORT_SHA=$CI_COMMIT_SHORT_SHA"); for var in "''${variables[@]}"; do echo "$var" >> .env-gitlab-ci; done + - cat .env-gitlab-ci + + after_script: + - if [ -f .env-gitlab-ci ]; then rm .env-gitlab-ci; fi + + test: + stage: test + tags: + - nixos + script: + - devenv shell test-lib + + cache: + - key: nixos + paths: + - /nix/store + + artifacts: + paths: + - dist + + deploy: + stage: deploy + tags: + - nixos + script: + - devenv shell -c deploy-lib + + when: on_success + + cache: + - key: nixos + paths: + - /nix/store + + + artifacts: + paths: + - dist + EOF + + + + ''; + + scripts.do-git-commit.exec = '' + #!/usr/bin/env bash + + # Define colors if the terminal supports it + if [ -t 1 ]; then + RED='\033[0;31m' + GREEN='\033[0;32m' + RESET='\033[0m' + BOLD='\033[1m' + else + RED="" + GREEN="" + RESET="" + fi + + step=1 + + reset + clear + + # create random log file + LOGFILE="$(mktemp)" + if [ $? -ne 0 ]; then + echo -e "''${RED}✖ Could not create temporary log file. Exiting.''${RESET}" + exit 1 + fi + + log_and_display() { + echo -e "''${GREEN}==> $step. $1''${RESET}" | tee -a $LOGFILE + step=$((step + 1)) + } + + log_error_and_display() { + echo -e "''${RED}==> $step. $1''${RESET}" | tee -a $LOGFILE + } + + printLogfileAndExit() { + exit_code=$1 + echo -e "\n\n========================================\n\n\n" + + echo -e "\n\n''${BOLD}Git and GitLab Automation Script''${RESET}\n\nI have performed the following actions:\n\n" + cat "$LOGFILE" + + # Optional: Remove log file + rm -f "$LOGFILE" + + if [ $exit_code -eq 0 ]; then + echo -e "\n''${GREEN}✔''${RESET} All actions were successful" | tee -a $LOGFILE + elif [ $exit_code -eq -1 ]; then + echo -e "\n''${RED}✖''${RESET} The script was manually cancelled" | tee -a $LOGFILE + exit_code=0 + else + echo -e "\n''${RED}✖''${RESET} Some actions failed" | tee -a $LOGFILE + fi + + exit $exit_code + } + + print_headline() { + local title=$1 + local underline=$(printf '─%.0s' $(seq 1 ''${#title})) + echo -e "\n\n''${BOLD}''${title}\n''${underline}''${RESET}\n" + } + + do_cancel() { + echo -e "''${RED}==> ✖ Cancelled.''${RESET}" | tee -a $LOGFILE + printLogfileAndExit -1 + } + + # Function for unified logging and display + log_action() { + if [ $? -eq 0 ]; then + echo -e " ''${GREEN}✔''${RESET} $1: Successful" | tee -a $LOGFILE + else + echo -e " ''${RED}✖''${RESET} $1: Failed" | tee -a $LOGFILE + printLogfileAndExit 1 + fi + } + + print_what_to_do() { + echo -e "\n\nWhat do you want to do?\n" + } + + git_status=$(git status --porcelain) + if [[ -z "$git_status" ]]; then + log_error_and_display "No changes to commit. Exiting." + printLogfileAndExit 0 + fi + + print_headline "Choose commit type" + selection=$(gum choose "feat: (new feature for the user, not a new feature for build script)" "fix: (bug fix for the user, not a fix to a build script)" "chore: (updating grunt tasks etc.; no production code change)" "docs: (changes to the documentation)" "style: (formatting, missing semi colons, etc; no production code change)" "refactor: (refactoring production code, eg. renaming a variable)" "test: (adding missing tests, refactoring tests; no production code change)" "Cancel") + + commit_type=$(echo "$selection" | awk -F':' '{print $1}') + + if [[ "$commit_type" == "Cancel" ]]; then + do_cancel + fi + + log_and_display "You chose the commit type: $commit_type" + + # NEXT STEP ISSUE HANDLING ############################################################################################################ + #log_and_display "Issue handling" + + gitlabIssues=() + while IFS= read -r line; do + if [[ $line =~ ^# ]]; then + id=$(echo "$line" | awk '{print substr($1, 2)}') + title=$(echo "$line" | awk -F'about' '{print $1}' | awk '{$1=$2=""; print substr($0, 3)}') + gitlabIssues+=("$id > $title") + fi + done < <(gum spin --spinner dot --show-output --title "Ask gitlab ..." -- glab issue list --output-format=details) + + ## if issues are available, ask if user wants to use an existing issue or create a new one + createOption="Create new issue" + existingOption="Use existing issue" + cancelOption="Cancel" + + print_headline "Choose issue handling" + if [ ''${#gitlabIssues[@]} -eq 0 ]; then + log_and_display "There are no issues available." + + print_what_to_do + choice=$(gum choose "$createOption" "$cancelOption") + + else + log_and_display "There are ''${#gitlabIssues[@]} issues available." + print_what_to_do + choice=$(gum choose "$createOption" "$existingOption" "$cancelOption") + fi + + if [[ "$choice" == "$cancelOption" ]]; then + do_cancel + fi + + ## array of issue ids + work_on_issue_ids=() + + issue_text="" + + if [[ "$choice" == "$createOption" ]]; then + print_headline "Create new issue" + issue_text=$(gum input --placeholder "Enter issue title") + echo -e "Enter issue description. ''${RED}End with Ctrl+D''${RESET}\n" + issue_description=$(gum write --placeholder "Enter issue description. End with Ctrl+D") + + if [[ -z "$issue_text" ]]; then + log_error_and_display "Issue title is empty. Exiting." + printLogfileAndExit 1 + fi + + log_and_display "You entered the issue title: $issue_text" + log_and_display "You entered the issue description: $issue_description" + echo -e "\n" + + gum confirm "Do you want to create this issue?" + # gum confirm exits with status 0 if confirmed and status 1 if cancelled. + if [ $? -eq 1 ]; then + do_cancel + fi + + issue_output=$(glab issue create -t"$issue_text" --no-editor --description "$issue_description") + issue_id=$(echo "$issue_output" | grep -oP '(?<=/issues/)\d+') + + work_on_issue_ids+=("$issue_id") + + log_action "glab issue with id $issue_id created" + + else + + print_headline "Use existing issue" + echo -e "Select issue with arrow keys and press tab or space to select. Press enter to confirm.\n" + issue_ids=$(gum choose --no-limit "''${gitlabIssues[@]}") + + # assign issue_ids to work_on_issue_ids. iterate over lines and take integer from beginning of line + while IFS= read -r line; do + work_on_issue_ids+=($(echo "$line" | grep -oP '^\d+')) + done <<<"$issue_ids" + + fi + + if [ ''${#work_on_issue_ids[@]} -eq 0 ]; then + log_and_display "No issue selected. Exiting." + printLogfileAndExit 0 + fi + + # NEXT STEP COMMIT MESSAGE ############################################################################################################ + # print work_on_issue_ids + work_on_issue_ids_string="" + for i in "''${work_on_issue_ids[@]}"; do + work_on_issue_ids_string+="#$i " + done + + log_and_display "You chose to work on the following issues: ''${work_on_issue_ids_string}" + + + print_headline "Check for changes to commit" + + # ' ' = unmodified + # M = modified + # T = file type changed (regular file, symbolic link or submodule) + # A = added + # D = deleted + # R = renamed + # C = copied (if config option status.renames is set to "copies") + # U = updated but unmerged + # https://man.freebsd.org/cgi/man.cgi?query=git-status&sektion=1&manpath=freebsd-release-ports + + count_all_changes=$(echo "$git_status" | wc -l) + count_staged_changes=$(echo "$git_status" | grep -c '^M') + count_new_staged_files=$(echo "$git_status" | grep -c '^A') + count_staged_changes=$((count_staged_changes + count_new_staged_files)) + + + + git_options_all="All $count_all_changes changes" + git_options_staged="Only the $count_staged_changes staged changes" + git_options_select_files="Select files" + git_options_cancel="Cancel" + + git_options_array=() + if [[ $count_all_changes -gt 0 ]]; then + git_options_array+=("$git_options_all") + fi + + if [[ $count_staged_changes -gt 0 ]]; then + git_options_array+=("$git_options_staged") + fi + + git_options_array+=( "$git_options_select_files" ) + git_options_array+=( "$git_options_cancel" ) + + + selection=$(gum choose "''${git_options_array[@]}") + if [[ "$selection" == "$git_options_cancel" ]]; then + do_cancel + fi + + if [[ "$selection" == "$git_options_all" ]]; then + git add -A + echo "1" + elif [[ "$selection" == "$git_options_select_files" ]]; then + + files=() + while IFS= read -r line; do + files+=("$line") + done <<<"$git_status" + + selected_files=$(gum choose --no-limit "''${files[@]}") + + # no files selected + if [[ -z "$selected_files" ]]; then + log_and_display "No files selected. Exiting." + printLogfileAndExit 0 + fi + + # add selected files + while IFS= read -r line; do + ## git proclimne could have letter, ? or ! at the beginning of the line + file=$(echo "$line" | awk '{print $2}') + if [[ -z "$file" || ! -f "$file" ]]; then + log_and_display "No file found in line: $line" + continue + fi + + git add "$file" + done <<<"$selected_files" + + fi + + ## count staged changes again and print + count_staged_changes=$(echo "$git_status" | grep -c '^M') + count_new_staged_files=$(echo "$git_status" | grep -c '^A') + count_staged_changes=$((count_staged_changes + count_new_staged_files)) + + log_and_display "You have $count_staged_changes staged changes to commit." + + # NEXT STEP COMMIT MESSAGE ############################################################################################################ + + print_headline "Enter commit message" + commit_message=$(gum input --placeholder "Enter commit message" --value "$commit_type: $issue_text $work_on_issue_ids_string") + + if [[ -z "$commit_message" ]]; then + log_error_and_display "Commit message is empty. Exiting." + printLogfileAndExit 1 + fi + + log_and_display "You entered the commit message: $commit_message" + + gum confirm "Do you want to commit with this message?" + if [ $? -eq 1 ]; then + do_cancel + fi + + # NEXT STEP COMMIT #################################################################################################################### + print_headline "Committing changes" + + if ! git commit -m "$commit_message" ; then + log_error_and_display "Commit failed. Exiting." + printLogfileAndExit 1 + fi + + log_and_display "Commit successful." + + # NEXT STEP PUSH ###################################################################################################################### + + print_headline "Pushing changes" + + if ! git push ; then + log_error_and_display "Push failed. Exiting." + printLogfileAndExit 1 + fi + + log_and_display "Push successful." + + # Close issue ###################################################################################################################### + + print_headline "Closing issues" + + for issue_id in "''${work_on_issue_ids[@]}"; do + + gum confirm "Do you want to close issue #$issue_id?" + if [ $? -eq 1 ]; then + continue + fi + + if ! glab issue close "$issue_id" ; then + log_error_and_display "Closing issue $issue_id failed. Exiting." + else + log_and_display "Closing issue $issue_id successful." + fi + done + printLogfileAndExit 0 + ''; } diff --git a/go.mod b/go.mod index 0f74947..b725f36 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( github.com/stretchr/testify v1.8.2 - gitlab.schukai.com/oss/libraries/go/utilities/pathfinder v0.5.2 + gitlab.schukai.com/oss/libraries/go/utilities/pathfinder v0.8.1 ) require ( diff --git a/go.sum b/go.sum index 7a203a3..83ee2b1 100644 --- a/go.sum +++ b/go.sum @@ -24,6 +24,8 @@ gitlab.schukai.com/oss/libraries/go/utilities/pathfinder v0.3.1 h1:oyElaqEiyr2Xg gitlab.schukai.com/oss/libraries/go/utilities/pathfinder v0.3.1/go.mod h1:UvdD4NAf3gLKYafabJD7e9ZCOetzM9JZ9y4GkZukPVU= gitlab.schukai.com/oss/libraries/go/utilities/pathfinder v0.5.2 h1:R+dL2NJCM+AQNPK4DPDmfvx1eomi1Xb1dl0XKEFj7Ek= gitlab.schukai.com/oss/libraries/go/utilities/pathfinder v0.5.2/go.mod h1:UvdD4NAf3gLKYafabJD7e9ZCOetzM9JZ9y4GkZukPVU= +gitlab.schukai.com/oss/libraries/go/utilities/pathfinder v0.8.1 h1:A3KvLvu4rV3OstgEn6xHulhQaXawVvzFzbafYHWHUfs= +gitlab.schukai.com/oss/libraries/go/utilities/pathfinder v0.8.1/go.mod h1:MqCBFv7DXKoBE2rZDc51LGvl2QI7Kz0D+XkQ0izj+ws= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -- GitLab