pax_global_header00006660000000000000000000000064152071207140014511gustar00rootroot0000000000000052 comment=a569c199036a5f23e578089edb0233b619662007 golang-github-ysmood-got-0.40.0/000077500000000000000000000000001520712071400164205ustar00rootroot00000000000000golang-github-ysmood-got-0.40.0/.github/000077500000000000000000000000001520712071400177605ustar00rootroot00000000000000golang-github-ysmood-got-0.40.0/.github/FUNDING.yml000066400000000000000000000000211520712071400215660ustar00rootroot00000000000000github: [ysmood] golang-github-ysmood-got-0.40.0/.github/workflows/000077500000000000000000000000001520712071400220155ustar00rootroot00000000000000golang-github-ysmood-got-0.40.0/.github/workflows/example.yml000066400000000000000000000010311520712071400241660ustar00rootroot00000000000000name: Example on: [push] env: GODEBUG: tracebackancestors=1000 jobs: test: runs-on: ubuntu-latest defaults: run: working-directory: lib/example steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: 1.22 - name: lint run: go run github.com/ysmood/golangci-lint@latest -v 1.59.1 - name: test run: | go test -race -coverprofile=coverage.out ./... go run github.com/ysmood/got/cmd/check-cov@latest golang-github-ysmood-got-0.40.0/.github/workflows/test.yml000066400000000000000000000013231520712071400235160ustar00rootroot00000000000000name: Test on: [push, pull_request] env: GODEBUG: tracebackancestors=1000 jobs: test: runs-on: ${{ matrix.os }} strategy: matrix: os: [macos-latest, windows-latest, ubuntu-latest] steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: 1.22 - name: lint if: matrix.os == 'ubuntu-latest' run: go run github.com/ysmood/golangci-lint@latest -v 1.59.1 - name: test env: TERM: xterm-256color run: go test -coverprofile="coverage.out" . ./lib/diff ./lib/lcs ./lib/mock ./lib/utils - name: coverage if: matrix.os == 'ubuntu-latest' run: go run ./cmd/check-cov golang-github-ysmood-got-0.40.0/.gitignore000066400000000000000000000000271520712071400204070ustar00rootroot00000000000000*.out *.test tmp/ .got/golang-github-ysmood-got-0.40.0/.golangci.yml000066400000000000000000000002511520712071400210020ustar00rootroot00000000000000linters: enable: - gofmt - revive - gocyclo - misspell linters-settings: gocyclo: min-complexity: 15 issues: exclude-dirs-use-default: false golang-github-ysmood-got-0.40.0/LICENSE000066400000000000000000000020511520712071400174230ustar00rootroot00000000000000The MIT License Copyright 2020 Yad Smood 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.golang-github-ysmood-got-0.40.0/README.md000066400000000000000000000017371520712071400177070ustar00rootroot00000000000000# Overview An enjoyable golang test framework. ## Features - Pretty output using [gop](https://github.com/ysmood/gop) and [diff](lib/diff) - Fluent API design that takes the full advantage of IDE - Handy assertion helpers - Handy utils for testing - Value snapshot assertion - Customizable assertion error output ## Guides Read the [example project](lib/example) to get started. Got uses itself as the test framework, so the source code itself is the best doc. Install the [vscode extension](https://marketplace.visualstudio.com/items?itemName=ysmood.got-vscode-extension) for snippets like: `gp`, `gt`, and `gsetup`. To ensure test coverage of your project, you can run the command below: ```shell go test -race -coverprofile=coverage.out ./... go run github.com/ysmood/got/cmd/check-cov@latest ``` By default the [check-cov](cmd/check-cov) requires 100% coverage, run it with the `-h` flag to see the help doc. ## API reference [Link](https://pkg.go.dev/github.com/ysmood/got) golang-github-ysmood-got-0.40.0/assertions.go000066400000000000000000000175421520712071400211520ustar00rootroot00000000000000package got import ( "errors" "fmt" "math" "reflect" "regexp" "runtime" "strings" "sync/atomic" "github.com/ysmood/got/lib/utils" ) // Assertions helpers type Assertions struct { Testable ErrorHandler AssertionError must bool desc []string } // Desc returns a clone with the format description. The description will be printed before the error message. func (as Assertions) Desc(format string, args ...interface{}) Assertions { n := as n.desc = append(n.desc, fmt.Sprintf(format, args...)) return n } // Must returns a clone with the FailNow enabled. It will exit the current goroutine if the assertion fails. func (as Assertions) Must() Assertions { n := as n.must = true return n } // Eq asserts that x equals y when converted to the same type, such as compare float 1.0 and integer 1 . // For strict value and type comparison use Assertions.Equal . // For how comparison works, see [utils.SmartCompare] . func (as Assertions) Eq(x, y interface{}) { as.Helper() if utils.SmartCompare(x, y) == 0 { return } as.err(AssertionEq, x, y) } // Neq asserts that x not equals y even when converted to the same type. // For how comparison works, see [utils.SmartCompare] . func (as Assertions) Neq(x, y interface{}) { as.Helper() if utils.SmartCompare(x, y) != 0 { return } _, xNil := utils.IsNil(x) _, yNil := utils.IsNil(y) if !xNil && !yNil && reflect.TypeOf(x).Kind() == reflect.TypeOf(y).Kind() { as.err(AssertionNeqSame, x, y) return } as.err(AssertionNeq, x, y) } // Equal asserts that x equals y. // For loose type comparison use Assertions.Eq, such as compare float 1.0 and integer 1 . func (as Assertions) Equal(x, y interface{}) { as.Helper() if utils.Compare(x, y) == 0 { return } as.err(AssertionEq, x, y) } // Gt asserts that x is greater than y. // For how comparison works, see [utils.SmartCompare] . func (as Assertions) Gt(x, y interface{}) { as.Helper() if utils.SmartCompare(x, y) > 0 { return } as.err(AssertionGt, x, y) } // Gte asserts that x is greater than or equal to y. // For how comparison works, see [utils.SmartCompare] . func (as Assertions) Gte(x, y interface{}) { as.Helper() if utils.SmartCompare(x, y) >= 0 { return } as.err(AssertionGte, x, y) } // Lt asserts that x is less than y. // For how comparison works, see [utils.SmartCompare] . func (as Assertions) Lt(x, y interface{}) { as.Helper() if utils.SmartCompare(x, y) < 0 { return } as.err(AssertionLt, x, y) } // Lte asserts that x is less than or equal to b. // For how comparison works, see [utils.SmartCompare] . func (as Assertions) Lte(x, y interface{}) { as.Helper() if utils.SmartCompare(x, y) <= 0 { return } as.err(AssertionLte, x, y) } // InDelta asserts that x and y are within the delta of each other. // For how comparison works, see [utils.SmartCompare] . func (as Assertions) InDelta(x, y interface{}, delta float64) { as.Helper() if math.Abs(utils.SmartCompare(x, y)) <= delta { return } as.err(AssertionInDelta, x, y, delta) } // True asserts that x is true. func (as Assertions) True(x bool) { as.Helper() if x { return } as.err(AssertionTrue) } // False asserts that x is false. func (as Assertions) False(x bool) { as.Helper() if !x { return } as.err(AssertionFalse) } // Nil asserts that the last item in args is nilable and nil func (as Assertions) Nil(args ...interface{}) { as.Helper() if len(args) == 0 { as.err(AssertionNoArgs) return } last := args[len(args)-1] if _, yes := utils.IsNil(last); yes { return } as.err(AssertionNil, last, args) } // NotNil asserts that the last item in args is nilable and not nil func (as Assertions) NotNil(args ...interface{}) { as.Helper() if len(args) == 0 { as.err(AssertionNoArgs) return } last := args[len(args)-1] if last == nil { as.err(AssertionNotNil, last, args) return } nilable, yes := utils.IsNil(last) if !nilable { as.err(AssertionNotNilable, last, args) return } if yes { as.err(AssertionNotNilableNil, last, args) } } // Zero asserts x is zero value for its type. func (as Assertions) Zero(x interface{}) { as.Helper() if reflect.DeepEqual(x, reflect.Zero(reflect.TypeOf(x)).Interface()) { return } as.err(AssertionZero, x) } // NotZero asserts that x is not zero value for its type. func (as Assertions) NotZero(x interface{}) { as.Helper() if reflect.DeepEqual(x, reflect.Zero(reflect.TypeOf(x)).Interface()) { as.err(AssertionNotZero, x) } } // Regex asserts that str matches the regex pattern func (as Assertions) Regex(pattern, str string) { as.Helper() if regexp.MustCompile(pattern).MatchString(str) { return } as.err(AssertionRegex, pattern, str) } // Has asserts that container has item. // The container can be a string, []byte, slice, array, or map. // For how comparison works, see [utils.SmartCompare] . func (as Assertions) Has(container, item interface{}) { as.Helper() if c, ok := container.(string); ok && hasStr(c, item) { return } else if c, ok := container.([]byte); ok && hasStr(string(c), item) { return } cv := reflect.Indirect(reflect.ValueOf(container)) switch cv.Kind() { case reflect.Slice, reflect.Array: for i := 0; i < cv.Len(); i++ { if utils.SmartCompare(cv.Index(i).Interface(), item) == 0 { return } } case reflect.Map: keys := cv.MapKeys() for _, k := range keys { if utils.SmartCompare(cv.MapIndex(k).Interface(), item) == 0 { return } } } as.err(AssertionHas, container, item) } // Len asserts that the length of list equals l func (as Assertions) Len(list interface{}, l int) { as.Helper() actual := reflect.ValueOf(list).Len() if actual == l { return } as.err(AssertionLen, actual, l, list) } // Err asserts that the last item in args is error func (as Assertions) Err(args ...interface{}) { as.Helper() if len(args) == 0 { as.err(AssertionNoArgs) return } last := args[len(args)-1] if err, _ := last.(error); err != nil { return } as.err(AssertionErr, last, args) } // E is a shortcut for Must().Nil(args...) func (as Assertions) E(args ...interface{}) { as.Helper() as.Must().Nil(args...) } // Panic executes fn and asserts that fn panics func (as Assertions) Panic(fn func()) (val interface{}) { as.Helper() defer func() { as.Helper() val = recover() if val == nil { as.err(AssertionPanic, fn) } }() fn() return } // Is asserts that x is kind of y, it uses reflect.Kind to compare. // If x and y are both error type, it will use errors.Is to compare. func (as Assertions) Is(x, y interface{}) { as.Helper() if x == nil && y == nil { return } if ae, ok := x.(error); ok { if be, ok := y.(error); ok { if errors.Is(ae, be) { return } as.err(AssertionIsInChain, x, y) return } } at := reflect.TypeOf(x) bt := reflect.TypeOf(y) if x != nil && y != nil && at.Kind() == bt.Kind() { return } as.err(AssertionIsKind, x, y) } // Count asserts that the returned function will be called n times func (as Assertions) Count(n int) func() { as.Helper() count := int64(0) as.Cleanup(func() { c := int(atomic.LoadInt64(&count)) if c != n { as.Helper() as.err(AssertionCount, n, c) } }) return func() { atomic.AddInt64(&count, 1) } } func (as Assertions) err(t AssertionErrType, details ...interface{}) { as.Helper() if len(as.desc) > 0 { for _, d := range as.desc { as.Logf("%s", d) } } // TODO: we should take advantage of the Helper function _, f, l, _ := runtime.Caller(2) c := &AssertionCtx{ Type: t, Details: details, File: f, Line: l, } as.Logf("%s", as.ErrorHandler.Report(c)) if as.must { as.FailNow() return } as.Fail() } func hasStr(c string, item interface{}) bool { if it, ok := item.(string); ok { if strings.Contains(c, it) { return true } } else if it, ok := item.([]byte); ok { if strings.Contains(c, string(it)) { return true } } else if it, ok := item.(rune); ok { if strings.ContainsRune(c, it) { return true } } return false } golang-github-ysmood-got-0.40.0/assertions_error.go000066400000000000000000000150221520712071400223520ustar00rootroot00000000000000package got import ( "context" "strings" "time" "github.com/ysmood/gop" "github.com/ysmood/got/lib/diff" ) // AssertionErrType enum type AssertionErrType int const ( // AssertionEq type AssertionEq AssertionErrType = iota // AssertionNeqSame type AssertionNeqSame // AssertionNeq type AssertionNeq // AssertionGt type AssertionGt // AssertionGte type AssertionGte // AssertionLt type AssertionLt // AssertionLte type AssertionLte // AssertionInDelta type AssertionInDelta // AssertionTrue type AssertionTrue // AssertionFalse type AssertionFalse // AssertionNil type AssertionNil // AssertionNoArgs type AssertionNoArgs // AssertionNotNil type AssertionNotNil // AssertionNotNilable type AssertionNotNilable // AssertionNotNilableNil type AssertionNotNilableNil // AssertionZero type AssertionZero // AssertionNotZero type AssertionNotZero // AssertionRegex type AssertionRegex // AssertionHas type AssertionHas // AssertionLen type AssertionLen // AssertionErr type AssertionErr // AssertionPanic type AssertionPanic // AssertionIsInChain type AssertionIsInChain // AssertionIsKind type AssertionIsKind // AssertionCount type AssertionCount // AssertionSnapshot type AssertionSnapshot ) // AssertionCtx holds the context of an assertion type AssertionCtx struct { Type AssertionErrType Details []interface{} File string Line int } // AssertionError handler type AssertionError interface { Report(*AssertionCtx) string } var _ AssertionError = AssertionErrorReport(nil) // AssertionErrorReport is used to convert a func to AssertionError type AssertionErrorReport func(*AssertionCtx) string // Report interface func (ae AssertionErrorReport) Report(ac *AssertionCtx) string { return ae(ac) } type defaultAssertionError struct { fns map[AssertionErrType]func(details ...interface{}) string } // NewDefaultAssertionError handler func NewDefaultAssertionError(theme gop.Theme, diffTheme diff.Theme) AssertionError { f := func(v interface{}) string { return gop.Format(gop.Tokenize(v), theme) } k := func(s string) string { return " " + gop.Stylize("⦗"+s+"⦘", theme(gop.Error)) + " " } fns := map[AssertionErrType]func(details ...interface{}) string{ AssertionEq: func(details ...interface{}) string { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() x := f(details[0]) y := f(details[1]) if diffTheme == nil { return j(x, k("not =="), y) } if hasNewline(x, y) { df := diff.Format(diff.Tokenize(ctx, gop.StripANSI(x), gop.StripANSI(y)), diffTheme) return j(x, k("not =="), y, df) } dx, dy := diff.TokenizeLine(ctx, gop.StripANSI(x), gop.StripANSI(y)) return diff.Format(dx, diffTheme) + k("not ==") + diff.Format(dy, diffTheme) }, AssertionNeqSame: func(details ...interface{}) string { x := f(details[0]) y := f(details[1]) return j(x, k("=="), y) }, AssertionNeq: func(details ...interface{}) string { x := f(details[0]) y := f(details[1]) return j(x, k("=="), y, k("when converted to the same type")) }, AssertionGt: func(details ...interface{}) string { x := f(details[0]) y := f(details[1]) return j(x, k("not >"), y) }, AssertionGte: func(details ...interface{}) string { x := f(details[0]) y := f(details[1]) return j(x, k("not ≥"), y) }, AssertionLt: func(details ...interface{}) string { x := f(details[0]) y := f(details[1]) return j(x, k("not <"), y) }, AssertionLte: func(details ...interface{}) string { x := f(details[0]) y := f(details[1]) return j(x, k("not ≤"), y) }, AssertionInDelta: func(details ...interface{}) string { x := f(details[0]) y := f(details[1]) delta := f(details[2]) return j(k("delta between"), x, k("and"), y, k("not ≤"), delta) }, AssertionTrue: func(_ ...interface{}) string { return k("should be") + f(true) }, AssertionFalse: func(_ ...interface{}) string { return k("should be") + f(false) }, AssertionNil: func(details ...interface{}) string { last := f(details[0]) return j(k("last argument"), last, k("should be"), f(nil)) }, AssertionNoArgs: func(_ ...interface{}) string { return k("no arguments received") }, AssertionNotNil: func(_ ...interface{}) string { return k("last argument shouldn't be") + f(nil) }, AssertionNotNilable: func(details ...interface{}) string { last := f(details[0]) return j(k("last argument"), last, k("is not nilable")) }, AssertionNotNilableNil: func(details ...interface{}) string { last := f(details[0]) return j(k("last argument"), last, k("shouldn't be"), f(nil)) }, AssertionZero: func(details ...interface{}) string { x := f(details[0]) return j(x, k("should be zero value for its type")) }, AssertionNotZero: func(details ...interface{}) string { x := f(details[0]) return j(x, k("shouldn't be zero value for its type")) }, AssertionRegex: func(details ...interface{}) string { pattern := f(details[0]) str := f(details[1]) return j(pattern, k("should match"), str) }, AssertionHas: func(details ...interface{}) string { container := f(details[0]) str := f(details[1]) return j(container, k("should has"), str) }, AssertionLen: func(details ...interface{}) string { actual := f(details[0]) l := f(details[1]) return k("expect len") + actual + k("to be") + l }, AssertionErr: func(details ...interface{}) string { last := f(details[0]) return j(k("last value"), last, k("should be ")) }, AssertionPanic: func(_ ...interface{}) string { return k("should panic") }, AssertionIsInChain: func(details ...interface{}) string { x := f(details[0]) y := f(details[1]) return j(x, k("should in chain of"), y) }, AssertionIsKind: func(details ...interface{}) string { x := f(details[0]) y := f(details[1]) return j(x, k("should be kind of"), y) }, AssertionCount: func(details ...interface{}) string { n := f(details[0]) count := f(details[1]) return k("should count") + n + k("times, but got") + count }, } return &defaultAssertionError{fns: fns} } // Report interface func (ae *defaultAssertionError) Report(ac *AssertionCtx) string { return ae.fns[ac.Type](ac.Details...) } func j(args ...string) string { if hasNewline(args...) { for i := 0; i < len(args); i++ { args[i] = strings.Trim(args[i], " ") } return "\n" + strings.Join(args, "\n\n") } return strings.Join(args, "") } func hasNewline(args ...string) bool { for _, arg := range args { if strings.Contains(arg, "\n") { return true } } return false } golang-github-ysmood-got-0.40.0/assertions_test.go000066400000000000000000000131171520712071400222030ustar00rootroot00000000000000package got_test import ( "errors" "fmt" "os" "sync" "testing" "time" "github.com/ysmood/gop" "github.com/ysmood/got" ) func TestAssertion(t *testing.T) { as := setup(t) as.Eq(1, 1) as.Eq(1.0, 1) as.Eq([]int{1, 3}, []int{1, 3}) as.Eq(map[int]int{1: 2, 3: 4}, map[int]int{3: 4, 1: 2}) as.Eq(nil, nil) as.Eq(map[int]int(nil), nil) fn := func() {} as.Eq(map[int]interface{}{1: fn, 2: nil}, map[int]interface{}{2: nil, 1: fn}) as.Eq((*int)(nil), nil) as.Neq(1.1, 1) as.Neq([]int{1, 2}, []int{2, 1}) as.Neq("true", true) as.Neq(errors.New("a"), errors.New("b")) as.Equal(1, 1) arr := []int{1, 2} as.Equal(arr, arr) as.Equal(fn, fn) as.Lt(time.Millisecond, time.Second) as.Lte(1, 1) as.Gt(2, 1.5) as.Gte(2, 2.0) now := time.Now() as.Eq(now, now) as.Lt(now, now.Add(time.Second)) as.Gt(now.Add(time.Second), now) as.InDelta(1.1, 1.2, 0.2) as.True(true) as.False(false) as.Nil(nil) as.Nil((*int)(nil)) as.Nil(os.Stat("go.mod")) as.NotNil([]int{}) as.Zero("") as.Zero(0) as.Zero(time.Time{}) as.NotZero(1) as.NotZero("ok") as.NotZero(time.Now()) as.Regex(`\d\d`, "10") as.Has(`test`, 'e') as.Has(`test`, "es") as.Has(`test`, []byte("es")) as.Has([]byte(`test`), "es") as.Has([]byte(`test`), []byte("es")) as.Has([]int{1, 2, 3}, 2) as.Has([3]int{1, 2, 3}, 2) as.Has(map[int]int{1: 4, 2: 5, 3: 6}, 5) as.Len([]int{1, 2}, 2) as.Err(1, 2, errors.New("err")) as.Panic(func() { panic(1) }) as.Is(1, 2) err := errors.New("err") as.Is(err, err) as.Is(fmt.Errorf("%w", err), err) as.Is(nil, nil) as.Must().Eq(1, 1) count := as.Count(2) count() count() } func TestAssertionErr(t *testing.T) { m := &mock{t: t} as := got.New(m) as.Assertions.ErrorHandler = got.NewDefaultAssertionError(gop.ThemeNone, nil) type data struct { A int S string } as.Desc("not %s", "equal").Eq(1, 2.0) m.check("not equal\n1 ⦗not ==⦘ 2.0") as.Desc("test").Desc("not %s", "equal").Eq(1, 2.0) m.check("test\nnot equal\n1 ⦗not ==⦘ 2.0") as.Eq(data{1, "a"}, data{1, "b"}) m.check(` got_test.data{ A: 1, S: "a", } ⦗not ==⦘ got_test.data{ A: 1, S: "b", }`) as.Eq(true, "a&") m.check(`true ⦗not ==⦘ "a&"`) as.Eq(nil, "ok") m.check(`nil ⦗not ==⦘ "ok"`) as.Eq(1, nil) m.check(`1 ⦗not ==⦘ nil`) as.Equal(1, 1.0) m.check("1 ⦗not ==⦘ 1.0") as.Equal([]int{1}, []int{2}) m.check(` []int{ 1, } ⦗not ==⦘ []int{ 2, }`) as.Neq(1, 1) m.check("1 ⦗==⦘ 1") as.Neq(1.0, 1) m.check("1.0 ⦗==⦘ 1 ⦗when converted to the same type⦘ ") as.Lt(1, 1) m.check("1 ⦗not <⦘ 1") as.Lte(2, 1) m.check("2 ⦗not ≤⦘ 1") as.Gt(1, 1) m.check("1 ⦗not >⦘ 1") as.Gte(1, 2) m.check("1 ⦗not ≥⦘ 2") as.InDelta(10, 20, 3) m.check(" ⦗delta between⦘ 10 ⦗and⦘ 20 ⦗not ≤⦘ 3.0") as.True(false) m.check(" ⦗should be⦘ true") as.False(true) m.check(" ⦗should be⦘ false") as.Nil(1) m.check(" ⦗last argument⦘ 1 ⦗should be⦘ nil") as.Nil() m.check(" ⦗no arguments received⦘ ") as.NotNil(nil) m.check(" ⦗last argument shouldn't be⦘ nil") as.NotNil((*int)(nil)) m.check(" ⦗last argument⦘ (*int)(nil) ⦗shouldn't be⦘ nil") as.NotNil() m.check(" ⦗no arguments received⦘ ") as.NotNil(1) m.check(" ⦗last argument⦘ 1 ⦗is not nilable⦘ ") as.Zero(1) m.check("1 ⦗should be zero value for its type⦘ ") as.NotZero(0) m.check("0 ⦗shouldn't be zero value for its type⦘ ") as.Regex(`\d\d`, "aaa") m.check(`"\\d\\d" ⦗should match⦘ "aaa"`) as.Has(`test`, "x") m.check(`"test" ⦗should has⦘ "x"`) as.Len([]int{1, 2}, 3) m.check(" ⦗expect len⦘ 2 ⦗to be⦘ 3") as.Err(nil) m.check(" ⦗last value⦘ nil ⦗should be ⦘ ") as.Panic(func() {}) m.check(" ⦗should panic⦘ ") as.Err() m.check(" ⦗no arguments received⦘ ") as.Err(1) m.check(" ⦗last value⦘ 1 ⦗should be ⦘ ") func() { defer func() { _ = recover() }() as.E(1, errors.New("E")) }() m.check(` ⦗last argument⦘ &errors.errorString{ s: "E", } ⦗should be⦘ nil`) as.Is(1, 2.2) m.check("1 ⦗should be kind of⦘ 2.2") as.Is(errors.New("a"), errors.New("b")) m.check(` &errors.errorString{ s: "a", } ⦗should in chain of⦘ &errors.errorString{ s: "b", }`) as.Is(nil, errors.New("a")) m.check(` nil ⦗should be kind of⦘ &errors.errorString{ s: "a", }`) as.Is(errors.New("a"), nil) m.check(` &errors.errorString{ s: "a", } ⦗should be kind of⦘ nil`) { count := as.Count(2) count() m.cleanup() m.check(` ⦗should count⦘ 2 ⦗times, but got⦘ 1`) count = as.Count(1) wg := sync.WaitGroup{} wg.Add(2) go func() { count() wg.Done() }() go func() { count() wg.Done() }() wg.Wait() m.cleanup() m.check(` ⦗should count⦘ 1 ⦗times, but got⦘ 2`) } } func TestAssertionColor(t *testing.T) { m := &mock{t: t} g := got.New(m) g.Eq([]int{1, 2}, []int{1, 3}) m.checkWithStyle(true, ` <36>[]int<39>{ <32>1<39>, <32>2<39>, } <31><4>⦗not ==⦘<24><39> <36>[]int<39>{ <32>1<39>, <32>3<39>, } <45><30>@@ diff chunk @@<39><49> 2 2 1, <31>3 -<39> <31>2<39>, <32> 3 +<39> <32>3<39>, 4 4 } `) g.Eq("abc", "axc") m.checkWithStyle(true, `"a<31>b<39>c" <31><4>⦗not ==⦘<24><39> "a<32>x<39>c"`) g.Eq(3, "a") m.checkWithStyle(true, `<31>3<39> <31><4>⦗not ==⦘<24><39> <32>"a"<39>`) } func TestCustomAssertionError(t *testing.T) { m := &mock{t: t} g := got.New(m) g.ErrorHandler = got.AssertionErrorReport(func(c *got.AssertionCtx) string { if c.Type == got.AssertionEq { return "custom eq" } return "" }) g.Eq(1, 2) m.check("custom eq") } golang-github-ysmood-got-0.40.0/cli_utils.go000066400000000000000000000024751520712071400207460ustar00rootroot00000000000000package got import ( "fmt" "os" "os/exec" "regexp" "strconv" "strings" ) // EnsureCoverage via report file generated from, for example: // // go test -coverprofile=coverage.out // // Return error if any file's coverage is less than min, min is a percentage value. func EnsureCoverage(path string, min float64) error { tmp, _ := os.CreateTemp("", "") report := tmp.Name() defer func() { _ = os.Remove(report) }() _ = tmp.Close() _, err := exec.Command("go", "tool", "cover", "-html", path, "-o", report).CombinedOutput() if err != nil { return err } list := parseReport(report) rejected := []string{} for _, c := range list { if c.coverage < min { rejected = append(rejected, fmt.Sprintf(" %s (%0.1f%%)", c.path, c.coverage)) } } if len(rejected) > 0 { return fmt.Errorf( "Test coverage for these files should be greater than %.2f%%:\n%s", min, strings.Join(rejected, "\n"), ) } return nil } type cov struct { path string coverage float64 } var regCov = regexp.MustCompile(``) func parseReport(path string) []cov { out, _ := os.ReadFile(path) ms := regCov.FindAllStringSubmatch(string(out), -1) list := []cov{} for _, m := range ms { c, _ := strconv.ParseFloat(m[2], 32) list = append(list, cov{m[1], c}) } return list } golang-github-ysmood-got-0.40.0/cli_utils_test.go000066400000000000000000000004451520712071400220000ustar00rootroot00000000000000package got_test import ( "testing" "github.com/ysmood/got" ) func TestEnsureCoverage(t *testing.T) { g := setup(t) g.Nil(got.EnsureCoverage("fixtures/coverage/cov.txt", 100)) g.Err(got.EnsureCoverage("fixtures/coverage/cov.txt", 120)) g.Err(got.EnsureCoverage("not-exists", 100)) } golang-github-ysmood-got-0.40.0/cmd/000077500000000000000000000000001520712071400171635ustar00rootroot00000000000000golang-github-ysmood-got-0.40.0/cmd/check-cov/000077500000000000000000000000001520712071400210255ustar00rootroot00000000000000golang-github-ysmood-got-0.40.0/cmd/check-cov/main.go000066400000000000000000000006321520712071400223010ustar00rootroot00000000000000// Package main ... package main import ( "flag" "fmt" "os" "github.com/ysmood/got" ) var covFile = flag.String("cov-file", "coverage.out", "the path of the coverage report") var min = flag.Float64("min", 100, "min coverage rate or exit code with 1") func main() { if !flag.Parsed() { flag.Parse() } err := got.EnsureCoverage(*covFile, *min) if err != nil { fmt.Println(err) os.Exit(1) } } golang-github-ysmood-got-0.40.0/each.go000066400000000000000000000067451520712071400176630ustar00rootroot00000000000000package got import ( "reflect" "runtime/debug" ) // Only run tests with it type Only struct{} // Skip the current test type Skip struct{} // Each runs each exported method Fn on type Ctx as a subtest of t. // The iteratee can be a struct Ctx or: // // iteratee(t Testable) (ctx Ctx) // // Each Fn will be called like: // // ctx.Fn() // // If iteratee is Ctx, its G field will be set to New(t) for each test. // Any Fn that has the same name with the embedded one will be ignored. func Each(t Testable, iteratee interface{}) (count int) { t.Helper() itVal := normalizeIteratee(t, iteratee) ctxType := itVal.Type().Out(0) methods := filterMethods(ctxType) runVal := reflect.ValueOf(t).MethodByName("Run") cbType := runVal.Type().In(1) for _, m := range methods { // because the callback is in another goroutine, we create closures for each loop method := m runVal.Call([]reflect.Value{ reflect.ValueOf(method.Name), reflect.MakeFunc(cbType, func(args []reflect.Value) []reflect.Value { t := args[0].Interface().(Testable) doSkip(t, method) count++ res := itVal.Call(args) return callMethod(t, method, res[0]) }), }) } return } func normalizeIteratee(t Testable, iteratee interface{}) reflect.Value { t.Helper() if iteratee == nil { t.Logf("iteratee shouldn't be nil") t.FailNow() } itVal := reflect.ValueOf(iteratee) itType := itVal.Type() fail := true switch itType.Kind() { case reflect.Func: if itType.NumIn() != 1 || itType.NumOut() != 1 { break } try(func() { _ = reflect.New(itType.In(0).Elem()).Interface().(Testable) fail = false }) case reflect.Struct: fnType := reflect.FuncOf([]reflect.Type{reflect.TypeOf(t)}, []reflect.Type{itType}, false) structVal := itVal itVal = reflect.MakeFunc(fnType, func(args []reflect.Value) []reflect.Value { sub := args[0].Interface().(Testable) as := reflect.ValueOf(New(sub)) c := reflect.New(itType).Elem() c.Set(structVal) try(func() { c.FieldByName("G").Set(as) }) return []reflect.Value{c} }) fail = false } if fail { t.Logf("iteratee <%v> should be a struct or ", itType) t.FailNow() } return itVal } func callMethod(t Testable, method reflect.Method, receiver reflect.Value) []reflect.Value { args := make([]reflect.Value, method.Type.NumIn()) args[0] = receiver for i := 1; i < len(args); i++ { args[i] = reflect.New(method.Type.In(i)).Elem() } defer func() { if err := recover(); err != nil { t.Logf("[panic] %v\n%s", err, debug.Stack()) t.Fail() } }() method.Func.Call(args) return []reflect.Value{} } func filterMethods(typ reflect.Type) []reflect.Method { embedded := map[string]struct{}{} for i := 0; i < typ.NumField(); i++ { field := typ.Field(i) if field.Anonymous { for j := 0; j < field.Type.NumMethod(); j++ { embedded[field.Type.Method(j).Name] = struct{}{} } } } methods := []reflect.Method{} onlyList := []reflect.Method{} for i := 0; i < typ.NumMethod(); i++ { method := typ.Method(i) if _, has := embedded[method.Name]; has { continue } if method.Type.NumIn() > 1 && method.Type.In(1) == reflect.TypeOf(Only{}) { onlyList = append(onlyList, method) } methods = append(methods, method) } if len(onlyList) > 0 { return onlyList } return methods } func doSkip(t Testable, method reflect.Method) { if method.Type.NumIn() > 1 && method.Type.In(1) == reflect.TypeOf(Skip{}) { t.SkipNow() } } func try(fn func()) { defer func() { _ = recover() }() fn() } golang-github-ysmood-got-0.40.0/each_test.go000066400000000000000000000031161520712071400207070ustar00rootroot00000000000000package got_test import ( "testing" "github.com/ysmood/got" ) func TestEach(t *testing.T) { count := got.Each(t, StructVal{val: 1}) got.New(t).Eq(count, 2) } type StructVal struct { got.G val int } func (c StructVal) Normal() { c.Eq(c.val, 1) } func (c StructVal) ExtraInOut(int) int { c.Eq(c.val, 1) return 0 } func (c StructVal) TestSkip(got.Skip) { } func TestEachEmbedded(t *testing.T) { got.Each(t, Container{}) } type Container struct { Embedded } func (c Container) A() { c.Fail() } func (c Container) B() {} type Embedded struct { *testing.T } func (c Embedded) A() {} func (c Embedded) C() { c.Fail() } func TestEachWithOnly(t *testing.T) { got.Each(t, Only{}) } type Only struct { *testing.T } func (c Only) A(got.Only) {} func (c Only) B() { panic("") } func TestEachErr(t *testing.T) { as := got.New(t) m := &mock{t: t} as.Panic(func() { got.Each(m, nil) }) m.check("iteratee shouldn't be nil") as.Panic(func() { got.Each(m, 1) }) m.check("iteratee should be a struct or ") it := func() Err { return Err{} } as.Panic(func() { got.Each(m, it) }) m.check("iteratee should be a struct or ") } type Err struct { } func (s Err) A(int) {} func TestPanicAsFailure(t *testing.T) { as := got.New(t) m := &mock{t: t} it := func(_ *mock) PanicAsFailure { return PanicAsFailure{} } as.Eq(got.Each(m, it), 2) as.True(m.failed) as.Has(m.msg, "[panic] err") } type PanicAsFailure struct { } func (p PanicAsFailure) A() { panic("err") } func (p PanicAsFailure) B() { } golang-github-ysmood-got-0.40.0/fixtures/000077500000000000000000000000001520712071400202715ustar00rootroot00000000000000golang-github-ysmood-got-0.40.0/fixtures/coverage/000077500000000000000000000000001520712071400220645ustar00rootroot00000000000000golang-github-ysmood-got-0.40.0/fixtures/coverage/cov.txt000066400000000000000000000001111520712071400234050ustar00rootroot00000000000000mode: atomic github.com/ysmood/got/fixtures/coverage/foo.go:4.16,6.2 1 1 golang-github-ysmood-got-0.40.0/fixtures/coverage/foo.go000066400000000000000000000001221520712071400231710ustar00rootroot00000000000000// Package coverage ... package coverage // Foo ... func Foo() int { return 1 } golang-github-ysmood-got-0.40.0/fixtures/coverage/foo_test.go000066400000000000000000000001121520712071400242270ustar00rootroot00000000000000package coverage import "testing" func TestFoo(_ *testing.T) { Foo() } golang-github-ysmood-got-0.40.0/go.mod000066400000000000000000000001141520712071400175220ustar00rootroot00000000000000module github.com/ysmood/got go 1.21 require github.com/ysmood/gop v0.2.0 golang-github-ysmood-got-0.40.0/go.sum000066400000000000000000000002411520712071400175500ustar00rootroot00000000000000github.com/ysmood/gop v0.2.0 h1:+tFrG0TWPxT6p9ZaZs+VY+opCvHU8/3Fk6BaNv6kqKg= github.com/ysmood/gop v0.2.0/go.mod h1:rr5z2z27oGEbyB787hpEcx4ab8cCiPnKxn0SUHt6xzk= golang-github-ysmood-got-0.40.0/go.work000066400000000000000000000000641520712071400177310ustar00rootroot00000000000000go 1.22 use ( . ./lib/benchmark ./lib/example ) golang-github-ysmood-got-0.40.0/go.work.sum000066400000000000000000000006241520712071400205360ustar00rootroot00000000000000github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= golang-github-ysmood-got-0.40.0/got.go000066400000000000000000000050311520712071400175370ustar00rootroot00000000000000// Package got is an enjoyable golang test framework. package got import ( "flag" "os" "reflect" "regexp" "strings" "sync" "github.com/ysmood/gop" "github.com/ysmood/got/lib/diff" ) // Testable interface. Usually, you use *testing.T as it. type Testable interface { Name() string // same as testing.common.Name Skipped() bool // same as testing.common.Skipped Failed() bool // same as testing.common.Failed Cleanup(func()) // same as testing.common.Cleanup FailNow() // same as testing.common.FailNow Fail() // same as testing.common.Fail Helper() // same as testing.common.Helper Logf(format string, args ...interface{}) // same as testing.common.Logf SkipNow() // same as testing.common.Skip } // G is the helper context, it provides some handy helpers for testing type G struct { Testable Assertions Utils snapshots *sync.Map } // Setup returns a helper to init G instance func Setup(init func(g G)) func(t Testable) G { return func(t Testable) G { g := New(t) if init != nil { init(g) } return g } } // T is the shortcut for New func T(t Testable) G { return New(t) } // New G instance func New(t Testable) G { eh := NewDefaultAssertionError(gop.ThemeDefault, diff.ThemeDefault) g := G{ t, Assertions{Testable: t, ErrorHandler: eh}, Utils{t}, &sync.Map{}, } g.loadSnapshots() return g } // DefaultFlags will set the "go test" flag if not yet presented. // It must be executed in the init() function. // Such as the timeout: // // DefaultFlags("timeout=10s") func DefaultFlags(flags ...string) { // remove default timeout from "go test" filtered := []string{} for _, arg := range os.Args { if arg != "-test.timeout=10m0s" { filtered = append(filtered, arg) } } os.Args = filtered list := map[string]struct{}{} reg := regexp.MustCompile(`^-test\.(\w+)`) for _, arg := range os.Args { ms := reg.FindStringSubmatch(arg) if ms != nil { list[ms[1]] = struct{}{} } } for _, flag := range flags { if _, has := list[strings.Split(flag, "=")[0]]; !has { os.Args = append(os.Args, "-test."+flag) } } } // Parallel config of "go test -parallel" func Parallel() (n int) { flag.Parse() flag.Visit(func(f *flag.Flag) { if f.Name == "test.parallel" { v := reflect.ValueOf(f.Value).Elem().Convert(reflect.TypeOf(n)) n = v.Interface().(int) } }) return } golang-github-ysmood-got-0.40.0/got_test.go000066400000000000000000000001451520712071400205770ustar00rootroot00000000000000package got_test import ( "testing" ) func TestSetup(t *testing.T) { g := setup(t) g.Eq(1, 1) } golang-github-ysmood-got-0.40.0/lib/000077500000000000000000000000001520712071400171665ustar00rootroot00000000000000golang-github-ysmood-got-0.40.0/lib/benchmark/000077500000000000000000000000001520712071400211205ustar00rootroot00000000000000golang-github-ysmood-got-0.40.0/lib/benchmark/.golangci.yml000066400000000000000000000003051520712071400235020ustar00rootroot00000000000000 run: skip-dirs-use-default: false linters: enable: - gofmt - revive - gocyclo - misspell - bodyclose gocyclo: min-complexity: 15 issues: exclude-use-default: false golang-github-ysmood-got-0.40.0/lib/benchmark/bench_test.go000066400000000000000000000016231520712071400235670ustar00rootroot00000000000000package benchmark import ( "context" "crypto/rand" "strings" "testing" "github.com/sergi/go-diff/diffmatchpatch" "github.com/ysmood/got/lib/benchmark/myers" "github.com/ysmood/got/lib/lcs" ) var x = randStr(100) var y = randStr(100) func BenchmarkRandomYad(b *testing.B) { c := context.Background() xs := lcs.NewChars(x) ys := lcs.NewChars(y) b.StartTimer() for i := 0; i < b.N; i++ { xs.YadLCS(c, ys) } } func BenchmarkRandomGoogle(b *testing.B) { dmp := diffmatchpatch.New() xs, ys := []rune(x), []rune(y) b.StartTimer() for i := 0; i < b.N; i++ { dmp.DiffMainRunes(xs, ys, false) } } func BenchmarkRandomMyers(b *testing.B) { xs, ys := split(x), split(y) for i := 0; i < b.N; i++ { _ = myers.Diff(xs, ys) } } func randStr(n int) string { b := make([]byte, n) _, _ = rand.Read(b) return string(b) } func split(text string) []string { return strings.Split(text, "") } golang-github-ysmood-got-0.40.0/lib/benchmark/go.mod000066400000000000000000000001351520712071400222250ustar00rootroot00000000000000module github.com/ysmood/got/lib/benchmark go 1.18 require github.com/sergi/go-diff v1.2.0 golang-github-ysmood-got-0.40.0/lib/benchmark/go.sum000066400000000000000000000030221520712071400222500ustar00rootroot00000000000000github.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/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 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/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= golang-github-ysmood-got-0.40.0/lib/benchmark/myers/000077500000000000000000000000001520712071400222575ustar00rootroot00000000000000golang-github-ysmood-got-0.40.0/lib/benchmark/myers/diff.go000066400000000000000000000100361520712071400235160ustar00rootroot00000000000000// Copyright 2019 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package myers implements the Myers diff algorithm. package myers // Sources: // https://blog.jcoglan.com/2017/02/17/the-myers-diff-algorithm-part-3/ // https://www.codeproject.com/Articles/42279/%2FArticles%2F42279%2FInvestigating-Myers-diff-algorithm-Part-1-of-2 // OpKind ... type OpKind int const ( // Delete ... Delete OpKind = iota // Insert ... Insert ) // Diff ... func Diff(before, after []string) []*Operation { return operations(before, after) } // Operation ... type Operation struct { Kind OpKind Content []string // content from b I1, I2 int // indices of the line in a J1 int // indices of the line in b, J2 implied by len(Content) } // operations returns the list of operations to convert a into b, consolidating // operations for multiple lines and not including equal lines. func operations(a, b []string) []*Operation { if len(a) == 0 && len(b) == 0 { return nil } trace, offset := shortestEditSequence(a, b) snakes := backtrack(trace, len(a), len(b), offset) M, N := len(a), len(b) var i int solution := make([]*Operation, len(a)+len(b)) add := func(op *Operation, i2, j2 int) { if op == nil { return } op.I2 = i2 if op.Kind == Insert { op.Content = b[op.J1:j2] } solution[i] = op i++ } x, y := 0, 0 for _, snake := range snakes { if len(snake) < 2 { continue } var op *Operation // delete (horizontal) for snake[0]-snake[1] > x-y { if op == nil { op = &Operation{ Kind: Delete, I1: x, J1: y, } } x++ if x == M { break } } add(op, x, y) op = nil // insert (vertical) for snake[0]-snake[1] < x-y { if op == nil { op = &Operation{ Kind: Insert, I1: x, J1: y, } } y++ } add(op, x, y) op = nil // equal (diagonal) for x < snake[0] { x++ y++ } if x >= M && y >= N { break } } return solution[:i] } // backtrack uses the trace for the edit sequence computation and returns the // "snakes" that make up the solution. A "snake" is a single deletion or // insertion followed by zero or diagonals. func backtrack(trace [][]int, x, y, offset int) [][]int { snakes := make([][]int, len(trace)) d := len(trace) - 1 for ; x > 0 && y > 0 && d > 0; d-- { V := trace[d] if len(V) == 0 { continue } snakes[d] = []int{x, y} k := x - y var prev int if k == -d || (k != d && V[k-1+offset] < V[k+1+offset]) { prev = k + 1 } else { prev = k - 1 } x = V[prev+offset] y = x - prev } if x < 0 || y < 0 { return snakes } snakes[d] = []int{x, y} return snakes } // shortestEditSequence returns the shortest edit sequence that converts a into b. func shortestEditSequence(a, b []string) ([][]int, int) { M, N := len(a), len(b) V := make([]int, 2*(N+M)+1) offset := N + M trace := make([][]int, N+M+1) // Iterate through the maximum possible length of the SES (N+M). for d := 0; d <= N+M; d++ { copyV := make([]int, len(V)) // k lines are represented by the equation y = x - k. We move in // increments of 2 because end points for even d are on even k lines. for k := -d; k <= d; k += 2 { // At each point, we either go down or to the right. We go down if // k == -d, and we go to the right if k == d. We also prioritize // the maximum x value, because we prefer deletions to insertions. var x int if k == -d || (k != d && V[k-1+offset] < V[k+1+offset]) { x = V[k+1+offset] // down } else { x = V[k-1+offset] + 1 // right } y := x - k // Diagonal moves while we have equal contents. for x < M && y < N && a[x] == b[y] { x++ y++ } V[k+offset] = x // Return if we've exceeded the maximum values. if x == M && y == N { // Makes sure to save the state of the array before returning. copy(copyV, V) trace[d] = copyV return trace, offset } } // Save the state of the array. copy(copyV, V) trace[d] = copyV } return nil, 0 } golang-github-ysmood-got-0.40.0/lib/benchmark/readme.md000066400000000000000000000014471520712071400227050ustar00rootroot00000000000000# Overview ```txt $ go test -benchmem -bench . goos: darwin goarch: amd64 pkg: github.com/ysmood/got/lib/benchmark cpu: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz BenchmarkRandomYad-8 52597 20401 ns/op 14931 B/op 213 allocs/op BenchmarkRandomGoogle-8 18609 63802 ns/op 44848 B/op 906 allocs/op BenchmarkRandomMyers-8 15781 75848 ns/op 411412 B/op 360 allocs/op PASS ok github.com/ysmood/got/lib/benchmark 3.886s ``` YadLCS is faster and uses less memory than [Google Myer's algorithm](https://github.com/sergi/go-diff/blob/849d7ebc9716f43ec1295e9bc00e5c8cffef3d9f/diffmatchpatch/diff.go#L5-L7) when the item histogram is large, it's common in line based diff in large text context. golang-github-ysmood-got-0.40.0/lib/diff/000077500000000000000000000000001520712071400200765ustar00rootroot00000000000000golang-github-ysmood-got-0.40.0/lib/diff/README.md000066400000000000000000000001741520712071400213570ustar00rootroot00000000000000# Overview A simple lib to diff two string with pretty output. It also provide some low-level API to customize the output. golang-github-ysmood-got-0.40.0/lib/diff/ast.go000066400000000000000000000011511520712071400212120ustar00rootroot00000000000000// Package diff ... package diff // TokenLine of tokens type TokenLine struct { Type Type Tokens []*Token } // ParseTokenLines of tokens func ParseTokenLines(ts []*Token) []*TokenLine { list := []*TokenLine{} var l *TokenLine for _, t := range ts { switch t.Type { case SameSymbol, AddSymbol, DelSymbol: l = &TokenLine{} list = append(list, l) l.Type = t.Type } l.Tokens = append(l.Tokens, t) } return list } // SpreadTokenLines to tokens func SpreadTokenLines(lines []*TokenLine) []*Token { out := []*Token{} for _, l := range lines { out = append(out, l.Tokens...) } return out } golang-github-ysmood-got-0.40.0/lib/diff/format.go000066400000000000000000000054751520712071400217300ustar00rootroot00000000000000package diff import ( "context" "time" "github.com/ysmood/gop" ) // Theme for diff type Theme func(t Type) []gop.Style // ThemeDefault colors for Sprint var ThemeDefault = func(t Type) []gop.Style { switch t { case AddSymbol: return []gop.Style{gop.Green} case DelSymbol: return []gop.Style{gop.Red} case AddWords: return []gop.Style{gop.Green} case DelWords: return []gop.Style{gop.Red} case ChunkStart: return []gop.Style{gop.Black, gop.BgMagenta} } return []gop.Style{gop.None} } // ThemeNone colors for Sprint var ThemeNone = func(_ Type) []gop.Style { return []gop.Style{gop.None} } // Diff x and y into a human readable string. func Diff(x, y string) string { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() return Format(Tokenize(ctx, x, y), ThemeDefault) } // Tokenize x and y into diff tokens with diff words and narrow chunks. func Tokenize(ctx context.Context, x, y string) []*Token { ts := TokenizeText(ctx, x, y) lines := ParseTokenLines(ts) lines = Narrow(1, lines) Words(ctx, lines) return SpreadTokenLines(lines) } // Format tokens into a human readable string func Format(ts []*Token, theme Theme) string { out := "" for _, t := range ts { s := t.Literal out += gop.Stylize(s, theme(t.Type)) } return out } // Narrow the context around each diff section to n lines. func Narrow(n int, lines []*TokenLine) []*TokenLine { if n < 0 { n = 0 } keep := map[int]bool{} for i, l := range lines { switch l.Type { case AddSymbol, DelSymbol: for j := max(i-n, 0); j <= i+n && j < len(lines); j++ { keep[j] = true } } } out := []*TokenLine{} for i, l := range lines { if !keep[i] { continue } if _, has := keep[i-1]; !has { ts := []*Token{{ChunkStart, "@@ diff chunk @@"}, {Newline, "\n"}} out = append(out, &TokenLine{ChunkStart, ts}) } out = append(out, l) if _, has := keep[i+1]; !has { ts := []*Token{{ChunkEnd, ""}, {Newline, "\n"}} out = append(out, &TokenLine{ChunkEnd, ts}) } } return out } // Words diff func Words(ctx context.Context, lines []*TokenLine) { delLines := []*TokenLine{} addLines := []*TokenLine{} df := func() { if len(delLines) == 0 || len(delLines) != len(addLines) { return } for i := 0; i < len(delLines); i++ { d := delLines[i] a := addLines[i] dts, ats := TokenizeLine(ctx, d.Tokens[2].Literal, a.Tokens[2].Literal) d.Tokens = append(d.Tokens[0:2], append(dts, d.Tokens[3:]...)...) a.Tokens = append(a.Tokens[0:2], append(ats, a.Tokens[3:]...)...) } delLines = []*TokenLine{} addLines = []*TokenLine{} } for _, l := range lines { switch l.Type { case DelSymbol: delLines = append(delLines, l) case AddSymbol: addLines = append(addLines, l) default: df() } } df() } func max(x, y int) int { if x < y { return y } return x } golang-github-ysmood-got-0.40.0/lib/diff/format_test.go000066400000000000000000000070251520712071400227600ustar00rootroot00000000000000package diff_test import ( "context" "strings" "testing" "github.com/ysmood/gop" "github.com/ysmood/got" "github.com/ysmood/got/lib/diff" "github.com/ysmood/got/lib/lcs" ) var setup = got.Setup(func(g got.G) { g.ErrorHandler = got.NewDefaultAssertionError(nil, nil) }) func split(s string) []string { return strings.Split(s, "") } func TestDiff(t *testing.T) { g := setup(t) out := gop.StripANSI(diff.Diff("abc", "axc")) g.Eq(out, `@@ diff chunk @@ 1 - abc 1 + axc `) } func TestFormat(t *testing.T) { g := setup(t) ts := diff.TokenizeText( g.Context(), strings.ReplaceAll("a b c d f g h h j q z", " ", "\n"), strings.ReplaceAll("a b c d e f g i j k r x y z", " ", "\n"), ) df := diff.Format(ts, diff.ThemeNone) g.Eq(df, ""+ "01 01 a\n"+ "02 02 b\n"+ "03 03 c\n"+ "04 04 d\n"+ " 05 + e\n"+ "05 06 f\n"+ "06 07 g\n"+ "07 - h\n"+ "08 - h\n"+ " 08 + i\n"+ "09 09 j\n"+ "10 - q\n"+ " 10 + k\n"+ " 11 + r\n"+ " 12 + x\n"+ " 13 + y\n"+ "11 14 z\n"+ "") } func TestDisconnectedChunks(t *testing.T) { g := setup(t) ts := diff.TokenizeText( g.Context(), strings.ReplaceAll("a b c d f g h i j k l m n", " ", "\n"), strings.ReplaceAll("x b c d f g h i x k l m n", " ", "\n"), ) lines := diff.ParseTokenLines(ts) lines = diff.Narrow(1, lines) ts = diff.SpreadTokenLines(lines) df := diff.Format(ts, diff.ThemeNone) g.Eq(df, ""+ "@@ diff chunk @@\n"+ "01 - a\n"+ " 01 + x\n"+ "02 02 b\n"+ "\n"+ "@@ diff chunk @@\n"+ "08 08 i\n"+ "09 - j\n"+ " 09 + x\n"+ "10 10 k\n"+ "\n"+ "") } func TestChunks0(t *testing.T) { g := setup(t) ts := diff.TokenizeText( g.Context(), strings.ReplaceAll("a b c", " ", "\n"), strings.ReplaceAll("a x c", " ", "\n"), ) lines := diff.ParseTokenLines(ts) lines = diff.Narrow(-1, lines) ts = diff.SpreadTokenLines(lines) df := diff.Format(ts, diff.ThemeNone) g.Eq(df, ""+ "@@ diff chunk @@\n"+ "2 - b\n"+ " 2 + x\n"+ "\n"+ "") } func TestNoDifference(t *testing.T) { g := setup(t) ts := diff.TokenizeText(g.Context(), "a", "b") df := diff.Format(ts, diff.ThemeNone) g.Eq(df, ""+ "1 - a\n"+ " 1 + b\n"+ "") } func TestTwoLines(t *testing.T) { g := setup(t) format := func(ts []*diff.Token) string { out := "" for _, t := range ts { txt := strings.TrimSpace(strings.ReplaceAll(t.Literal, "", " ")) switch t.Type { case diff.DelWords: out += "-" + txt case diff.AddWords: out += "+" + txt default: out += "=" + txt } } return out } check := func(x, y, ex, ey string) { t.Helper() tx, ty := diff.TokenizeLine(g.Context(), strings.ReplaceAll(x, " ", ""), strings.ReplaceAll(y, " ", "")) dx, dy := format(tx), format(ty) if dx != ex || dy != ey { t.Error("\n", dx, "\n", dy, "\n!=\n", ex, "\n", ey) } } check( " a b c d f g h i j k l m n", " x x b c d f g h i x k l m n", "-a=b c d f g h i-j=k l m n", "+x x=b c d f g h i+x=k l m n", ) check( " 4 9 0 4 5 0 8 8 5 3", " 4 9 0 5 4 3 7 5 2", "=4 9 0 4 5-0 8 8 5 3", "=4 9 0+5=4+3 7=5+2", ) check( " 4 9 0 4 5 0 8", " 4 9 0 5 4 3 7", "=4 9 0 4-5 0 8", "=4 9 0+5=4+3 7", ) } func TestColor(t *testing.T) { g := setup(t) out := diff.Diff("abc", "axc") g.Eq(gop.VisualizeANSI(out), `<45><30>@@ diff chunk @@<39><49> <31>1 -<39> a<31>b<39>c <32> 1 +<39> a<32>x<39>c `) } func TestCustomSplit(t *testing.T) { g := setup(t) ctx := context.WithValue(g.Context(), lcs.SplitKey, split) g.Eq(diff.TokenizeLine(ctx, "abc", "abc")) } golang-github-ysmood-got-0.40.0/lib/diff/token.go000066400000000000000000000063371520712071400215560ustar00rootroot00000000000000package diff import ( "context" "fmt" "strings" "github.com/ysmood/got/lib/lcs" ) // Type of token type Type int const ( // Newline type Newline Type = iota // Space type Space // ChunkStart type ChunkStart // ChunkEnd type ChunkEnd // SameSymbol type SameSymbol // SameLine type SameLine // AddSymbol type AddSymbol // AddLine type AddLine // DelSymbol typ DelSymbol // DelLine type DelLine // SameWords type SameWords // AddWords type AddWords // DelWords type DelWords // EmptyLine type EmptyLine ) // Token presents a symbol in diff layout type Token struct { Type Type Literal string } // TokenizeText text block a and b into diff tokens. func TokenizeText(ctx context.Context, x, y string) []*Token { xls := lcs.NewLines(x) // x lines yls := lcs.NewLines(y) // y lines // TODO: We should use index to check equality, remove the usage of xs.Sub s := xls.Sub(xls.YadLCS(ctx, yls)) ts := []*Token{} xNum, yNum, sNum := numFormat(xls, yls) for i, j, k := 0, 0, 0; i < len(xls) || j < len(yls); { if i < len(xls) && (k == len(s) || neq(xls[i], s[k])) { ts = append(ts, &Token{DelSymbol, fmt.Sprintf(xNum, i+1) + "-"}, &Token{Space, " "}, &Token{DelLine, xls[i].String()}, &Token{Newline, "\n"}) i++ } else if j < len(yls) && (k == len(s) || neq(yls[j], s[k])) { ts = append(ts, &Token{AddSymbol, fmt.Sprintf(yNum, j+1) + "+"}, &Token{Space, " "}, &Token{AddLine, yls[j].String()}, &Token{Newline, "\n"}) j++ } else { ts = append(ts, &Token{SameSymbol, fmt.Sprintf(sNum, i+1, j+1) + " "}, &Token{Space, " "}, &Token{SameLine, s[k].String() + "\n"}) i, j, k = i+1, j+1, k+1 } } return ts } // TokenizeLine two different lines func TokenizeLine(ctx context.Context, x, y string) ([]*Token, []*Token) { split := lcs.Split val := ctx.Value(lcs.SplitKey) if val != nil { split = val.(func(string) []string) } xs := lcs.NewWords(split(x)) ys := lcs.NewWords(split(y)) // TODO: We should use index to check equality, remove the usage of xs.Sub s := xs.Sub(xs.YadLCS(ctx, ys)) xTokens := []*Token{} yTokens := []*Token{} merge := func(ts []*Token) []*Token { last := len(ts) - 1 if last > 0 && ts[last].Type == ts[last-1].Type { ts[last-1].Literal += ts[last].Literal ts = ts[:last] } return ts } for i, j, k := 0, 0, 0; i < len(xs) || j < len(ys); { if i < len(xs) && (k == len(s) || neq(xs[i], s[k])) { xTokens = append(xTokens, &Token{DelWords, xs[i].String()}) i++ } else if j < len(ys) && (k == len(s) || neq(ys[j], s[k])) { yTokens = append(yTokens, &Token{AddWords, ys[j].String()}) j++ } else { xTokens = append(xTokens, &Token{SameWords, s[k].String()}) yTokens = append(yTokens, &Token{SameWords, s[k].String()}) i, j, k = i+1, j+1, k+1 } xTokens = merge(xTokens) yTokens = merge(yTokens) } return xTokens, yTokens } func numFormat(x, y lcs.Sequence) (string, string, string) { xl := len(fmt.Sprintf("%d", len(x))) yl := len(fmt.Sprintf("%d", len(y))) return fmt.Sprintf("%%0%dd "+strings.Repeat(" ", yl+1), xl), fmt.Sprintf(strings.Repeat(" ", xl)+" %%0%dd ", yl), fmt.Sprintf("%%0%dd %%0%dd ", xl, yl) } func neq(x, y lcs.Comparable) bool { return x.String() != y.String() } golang-github-ysmood-got-0.40.0/lib/example/000077500000000000000000000000001520712071400206215ustar00rootroot00000000000000golang-github-ysmood-got-0.40.0/lib/example/.golangci.yml000066400000000000000000000003051520712071400232030ustar00rootroot00000000000000 run: skip-dirs-use-default: false linters: enable: - gofmt - revive - gocyclo - misspell - bodyclose gocyclo: min-complexity: 15 issues: exclude-use-default: false golang-github-ysmood-got-0.40.0/lib/example/01_simple_assertion_test.go000066400000000000000000000005111520712071400260640ustar00rootroot00000000000000package example_test import ( "testing" "github.com/ysmood/got" "github.com/ysmood/got/lib/example" ) // Use got as an light weight assertion lib in standard Go test function. func TestAssertion(t *testing.T) { // Run "go doc got.Assertions" to list available assertion methods. got.T(t).Eq(example.Sum("1", "1"), "2") } golang-github-ysmood-got-0.40.0/lib/example/02_advanced_test.go000066400000000000000000000023031520712071400242530ustar00rootroot00000000000000package example_test import ( "testing" "time" "github.com/ysmood/got" "github.com/ysmood/got/lib/example" ) func TestChainMethods(t *testing.T) { g := setup(t) g.Desc("1 must equal 1").Must().Eq(example.Sum("1", "2"), "3") } func TestUtils(t *testing.T) { g := setup(t) // Run "go doc got.Utils" to list available helpers s := g.Serve() s.Mux.HandleFunc("/", example.ServeSum) val := g.Req("", s.URL("?a=1&b=2")).Bytes().String() g.Eq(val, "3") } func TestTableDriven(t *testing.T) { testCases := []struct{ desc, a, b, expected string }{{ "first", "1", "2", "3", }, { "second", "2", "3", "5", }} for _, c := range testCases { t.Run(c.desc, func(t *testing.T) { g := setup(t) g.Eq(example.Sum(c.a, c.b), c.expected) }) } } func TestSnapshot(t *testing.T) { g := setup(t) g.Snapshot("snapshot the map value", map[int]string{1: "1", 2: "2"}) } func TestWaitGroup(t *testing.T) { g := got.T(t) check := func() { time.Sleep(time.Millisecond * 30) g.Eq(1, 1) } // This check won't be executed because the test will end before the goroutine starts. go check() // This check will be executed because the test will wait for the goroutine to finish. g.Go(check) } golang-github-ysmood-got-0.40.0/lib/example/03_setup_test.go000066400000000000000000000016441520712071400236560ustar00rootroot00000000000000package example_test import ( "testing" "time" "github.com/ysmood/got" "github.com/ysmood/gotrace" ) func init() { // Set default timeout for the entire "go test" got.DefaultFlags("timeout=10s") } // G is your custom test context. type G struct { got.G // You can add your own fields, usually data you want init before each test. time string } // setup is a helper function to setup your test context G. var setup = func(t *testing.T) G { g := got.T(t) // The function passed to it will be surely executed after the test g.Cleanup(func() {}) // Concurrently run each test g.Parallel() // Make sure there's no goroutine leak for each test gotrace.CheckLeak(g, 0) // Timeout for each test g.PanicAfter(time.Second) return G{g, time.Now().Format(time.DateTime)} } func TestSetup(t *testing.T) { g := setup(t) // Here we use the custom field we have defined in G g.Gt(g.time, "2023-01-02 15:04:05") } golang-github-ysmood-got-0.40.0/lib/example/04_suite_test.go000066400000000000000000000015051520712071400236440ustar00rootroot00000000000000package example_test import ( "testing" "time" "github.com/ysmood/got" "github.com/ysmood/got/lib/example" ) func TestSuite(t *testing.T) { // Execute each exported methods of SumSuite. // Each exported methods on SumSuite is a test case. got.Each(t, SumSuite{}) } type SumSuite struct { got.G } func (g SumSuite) Sum() { g.Eq(example.Sum("1", "1"), "2") } func TestSumAdvancedSuite(t *testing.T) { // The got.Each can also accept a function to init the g for each test case. got.Each(t, func(t *testing.T) SumAdvancedSuite { g := got.New(t) // Concurrently run each test g.Parallel() // Timeout for each test g.PanicAfter(time.Second) return SumAdvancedSuite{g, "1", "2"} }) } type SumAdvancedSuite struct { got.G a, b string } func (g SumAdvancedSuite) Sum() { g.Eq(example.Sum(g.a, g.b), "3") } golang-github-ysmood-got-0.40.0/lib/example/05_mocking_test.go000066400000000000000000000037251520712071400241510ustar00rootroot00000000000000package example_test import ( "math/rand" "net/http" "net/url" "testing" "github.com/ysmood/got/lib/example" "github.com/ysmood/got/lib/mock" ) // Mocking the http.ResponseWriter interface type mockResponseWriter struct { mock.Mock } func (m *mockResponseWriter) Write(b []byte) (int, error) { // Proxy the input and output of mockResponseWriter.Write method return mock.Proxy(m, m.Write)(b) } func (m *mockResponseWriter) Header() http.Header { return mock.Proxy(m, m.Header)() } func (m *mockResponseWriter) WriteHeader(code int) { mock.Proxy(m, m.WriteHeader)(code) } func TestMocking(t *testing.T) { g := setup(t) m := &mockResponseWriter{} // Stub the mockResponseWriter.Write method with ours mock.Stub(m, m.Write, func(b []byte) (int, error) { // Here want to ensure the input is "3" g.Eq(string(b), "3") return 0, nil }) u, _ := url.Parse("?a=1&b=2") example.ServeSum(m, &http.Request{URL: u}) // When the input is "3" let the mockResponseWriter.Write return (1, nil) mock.On(m, m.Write).When([]byte("3")).Return(1, nil) example.ServeSum(m, &http.Request{URL: u}) // We can use the Calls helper to get all the input and output history of a method. // Check the lib/example/.got/snapshots/TestMocking/calls.gop file for the details. g.Snapshot("calls", m.Calls(m.Write)) g.Desc("the Write should be called twice").Len(m.Calls(m.Write), 2) } // mock the rand.Source type mockSource struct { mock.Mock } func (m *mockSource) Int63() int64 { return mock.Proxy(m, m.Int63)() } func (m *mockSource) Seed(seed int64) { mock.Proxy(m, m.Seed)(seed) } func TestFallback(t *testing.T) { g := setup(t) m := &mockSource{} // Sometimes if there are a lot of method, and you want to stub only one of them, // you can use the Fallback method to fallback all non-stubbed methods to the struct you have passed to it. m.Fallback(rand.NewSource(1)) // Here the Rand method will always return the same value. g.Eq(example.Rand(m), "5577006791947779410") } golang-github-ysmood-got-0.40.0/lib/example/06_customize_assertion_output_test.go000066400000000000000000000011201520712071400302370ustar00rootroot00000000000000package example_test import ( "fmt" "testing" "github.com/ysmood/gop" "github.com/ysmood/got" "github.com/ysmood/got/lib/diff" ) // An example to only override the default error output of got.Assertions.Eq func TestCustomizeAssertionOutput(t *testing.T) { g := got.New(t) dh := got.NewDefaultAssertionError(gop.ThemeDefault, diff.ThemeDefault) h := got.AssertionErrorReport(func(c *got.AssertionCtx) string { if c.Type == got.AssertionEq { return fmt.Sprintf("%v != %v", c.Details[0], c.Details[1]) } return dh.Report(c) }) g.Assertions.ErrorHandler = h g.Eq(1, 1) } golang-github-ysmood-got-0.40.0/lib/example/README.md000066400000000000000000000003161520712071400221000ustar00rootroot00000000000000# Overview This is an example project to demonstrate how to use `got` for testing. Run `go test` for testing. For CI setup example check the [Example Github Action](../../.github/workflows/example.yml). golang-github-ysmood-got-0.40.0/lib/example/example.go000066400000000000000000000010661520712071400226060ustar00rootroot00000000000000// Package example ... package example import ( "fmt" "math/rand" "net/http" "strconv" ) // Sum a and b as number func Sum(a, b string) string { an, _ := strconv.ParseInt(a, 10, 32) bn, _ := strconv.ParseInt(b, 10, 32) return fmt.Sprintf("%d", an+bn) } // ServeSum http handler function func ServeSum(w http.ResponseWriter, r *http.Request) { s := Sum(r.URL.Query().Get("a"), r.URL.Query().Get("b")) _, _ = w.Write([]byte(s)) } // Rand generate a random int as string func Rand(src rand.Source) string { return fmt.Sprintf("%d", rand.New(src).Int()) } golang-github-ysmood-got-0.40.0/lib/example/go.mod000066400000000000000000000001771520712071400217340ustar00rootroot00000000000000module github.com/ysmood/got/lib/example go 1.18 require ( github.com/ysmood/gop v0.2.0 github.com/ysmood/gotrace v0.6.0 ) golang-github-ysmood-got-0.40.0/lib/example/go.sum000066400000000000000000000003661520712071400217610ustar00rootroot00000000000000github.com/ysmood/gop v0.2.0 h1:+tFrG0TWPxT6p9ZaZs+VY+opCvHU8/3Fk6BaNv6kqKg= github.com/ysmood/gotrace v0.6.0 h1:SyI1d4jclswLhg7SWTL6os3L1WOKeNn/ZtzVQF8QmdY= github.com/ysmood/gotrace v0.6.0/go.mod h1:TzhIG7nHDry5//eYZDYcTzuJLYQIkykJzCRIo4/dzQM= golang-github-ysmood-got-0.40.0/lib/got-vscode-extension/000077500000000000000000000000001520712071400232525ustar00rootroot00000000000000golang-github-ysmood-got-0.40.0/lib/got-vscode-extension/.eslintrc.json000066400000000000000000000010041520712071400260410ustar00rootroot00000000000000{ "root": true, "parser": "@typescript-eslint/parser", "parserOptions": { "ecmaVersion": 6, "sourceType": "module" }, "plugins": [ "@typescript-eslint" ], "rules": { "@typescript-eslint/naming-convention": "warn", "@typescript-eslint/semi": "warn", "curly": "warn", "eqeqeq": "warn", "no-throw-literal": "warn", "semi": "off" }, "ignorePatterns": [ "out", "dist", "**/*.d.ts" ] } golang-github-ysmood-got-0.40.0/lib/got-vscode-extension/.gitignore000066400000000000000000000000531520712071400252400ustar00rootroot00000000000000out dist node_modules .vscode-test/ *.vsix golang-github-ysmood-got-0.40.0/lib/got-vscode-extension/.vscode/000077500000000000000000000000001520712071400246135ustar00rootroot00000000000000golang-github-ysmood-got-0.40.0/lib/got-vscode-extension/.vscode/extensions.json000066400000000000000000000002511520712071400277030ustar00rootroot00000000000000{ // See http://go.microsoft.com/fwlink/?LinkId=827846 // for the documentation about the extensions.json format "recommendations": [ "dbaeumer.vscode-eslint" ] } golang-github-ysmood-got-0.40.0/lib/got-vscode-extension/.vscode/launch.json000066400000000000000000000016401520712071400267610ustar00rootroot00000000000000// A launch configuration that compiles the extension and then opens it inside a new window // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 { "version": "0.2.0", "configurations": [ { "name": "Run Extension", "type": "extensionHost", "request": "launch", "args": [ "--extensionDevelopmentPath=${workspaceFolder}" ], "outFiles": [ "${workspaceFolder}/out/**/*.js" ], "preLaunchTask": "${defaultBuildTask}" }, { "name": "Extension Tests", "type": "extensionHost", "request": "launch", "args": [ "--extensionDevelopmentPath=${workspaceFolder}", "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" ], "outFiles": [ "${workspaceFolder}/out/test/**/*.js" ], "preLaunchTask": "${defaultBuildTask}" } ] } golang-github-ysmood-got-0.40.0/lib/got-vscode-extension/.vscode/settings.json000066400000000000000000000006741520712071400273550ustar00rootroot00000000000000// Place your settings in this file to overwrite default and user settings. { "files.exclude": { "out": false // set this to true to hide the "out" folder with the compiled JS files }, "search.exclude": { "out": true // set this to false to include "out" folder in search results }, // Turn off tsc task auto detection since we have the necessary tasks as npm scripts "typescript.tsc.autoDetect": "off" }golang-github-ysmood-got-0.40.0/lib/got-vscode-extension/.vscode/tasks.json000066400000000000000000000005561520712071400266410ustar00rootroot00000000000000// See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format { "version": "2.0.0", "tasks": [ { "type": "npm", "script": "watch", "problemMatcher": "$tsc-watch", "isBackground": true, "presentation": { "reveal": "never" }, "group": { "kind": "build", "isDefault": true } } ] } golang-github-ysmood-got-0.40.0/lib/got-vscode-extension/.vscodeignore000066400000000000000000000002221520712071400257360ustar00rootroot00000000000000.vscode/** .vscode-test/** out/test/** src/** .gitignore .yarnrc vsc-extension-quickstart.md **/tsconfig.json **/.eslintrc.json **/*.map **/*.ts golang-github-ysmood-got-0.40.0/lib/got-vscode-extension/LICENSE000066400000000000000000000020511520712071400242550ustar00rootroot00000000000000The MIT License Copyright 2020 Yad Smood 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.golang-github-ysmood-got-0.40.0/lib/got-vscode-extension/README.md000066400000000000000000000001031520712071400245230ustar00rootroot00000000000000# got-vscode-extension README ## Features - Some useful snippets golang-github-ysmood-got-0.40.0/lib/got-vscode-extension/package-lock.json000066400000000000000000002266101520712071400264750ustar00rootroot00000000000000{ "name": "got-vscode-extension", "version": "0.0.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "got-vscode-extension", "version": "0.0.1", "devDependencies": { "@types/node": "^16.0.0", "@types/vscode": "^1.65.0", "@typescript-eslint/eslint-plugin": "^4.16.0", "@typescript-eslint/parser": "^4.16.0", "@vscode/test-electron": "^2.1.3", "eslint": "^7.21.0", "typescript": "^4.6.2" }, "engines": { "vscode": "^1.65.2" } }, "node_modules/@babel/code-frame": { "version": "7.12.11", "dev": true, "license": "MIT", "dependencies": { "@babel/highlight": "^7.10.4" } }, "node_modules/@babel/helper-validator-identifier": { "version": "7.14.0", "dev": true, "license": "MIT" }, "node_modules/@babel/highlight": { "version": "7.14.0", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.14.0", "chalk": "^2.0.0", "js-tokens": "^4.0.0" } }, "node_modules/@babel/highlight/node_modules/ansi-styles": { "version": "3.2.1", "dev": true, "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, "engines": { "node": ">=4" } }, "node_modules/@babel/highlight/node_modules/chalk": { "version": "2.4.2", "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" }, "engines": { "node": ">=4" } }, "node_modules/@babel/highlight/node_modules/color-convert": { "version": "1.9.3", "dev": true, "license": "MIT", "dependencies": { "color-name": "1.1.3" } }, "node_modules/@babel/highlight/node_modules/color-name": { "version": "1.1.3", "dev": true, "license": "MIT" }, "node_modules/@babel/highlight/node_modules/escape-string-regexp": { "version": "1.0.5", "dev": true, "license": "MIT", "engines": { "node": ">=0.8.0" } }, "node_modules/@babel/highlight/node_modules/has-flag": { "version": "3.0.0", "dev": true, "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/@babel/highlight/node_modules/supports-color": { "version": "5.5.0", "dev": true, "license": "MIT", "dependencies": { "has-flag": "^3.0.0" }, "engines": { "node": ">=4" } }, "node_modules/@eslint/eslintrc": { "version": "0.4.0", "dev": true, "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.1.1", "espree": "^7.3.0", "globals": "^12.1.0", "ignore": "^4.0.6", "import-fresh": "^3.2.1", "js-yaml": "^3.13.1", "minimatch": "^3.0.4", "strip-json-comments": "^3.1.1" }, "engines": { "node": "^10.12.0 || >=12.0.0" } }, "node_modules/@eslint/eslintrc/node_modules/globals": { "version": "12.4.0", "dev": true, "license": "MIT", "dependencies": { "type-fest": "^0.8.1" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@eslint/eslintrc/node_modules/type-fest": { "version": "0.8.1", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=8" } }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.4", "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.4", "run-parallel": "^1.1.9" }, "engines": { "node": ">= 8" } }, "node_modules/@nodelib/fs.stat": { "version": "2.0.4", "dev": true, "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/@nodelib/fs.walk": { "version": "1.2.6", "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.4", "fastq": "^1.6.0" }, "engines": { "node": ">= 8" } }, "node_modules/@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmmirror.com/@tootallnate/once/-/once-1.1.2.tgz", "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", "dev": true, "engines": { "node": ">= 6" } }, "node_modules/@types/json-schema": { "version": "7.0.7", "dev": true, "license": "MIT" }, "node_modules/@types/node": { "version": "16.11.26", "resolved": "https://registry.npmmirror.com/@types/node/-/node-16.11.26.tgz", "integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ==", "dev": true }, "node_modules/@types/vscode": { "version": "1.65.0", "resolved": "https://registry.npmmirror.com/@types/vscode/-/vscode-1.65.0.tgz", "integrity": "sha512-wQhExnh2nEzpjDMSKhUvnNmz3ucpd3E+R7wJkOhBNK3No6fG3VUdmVmMOKD0A8NDZDDDiQcLNxe3oGmX5SjJ5w==", "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "4.22.1", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/experimental-utils": "4.22.1", "@typescript-eslint/scope-manager": "4.22.1", "debug": "^4.1.1", "functional-red-black-tree": "^1.0.1", "lodash": "^4.17.15", "regexpp": "^3.0.0", "semver": "^7.3.2", "tsutils": "^3.17.1" }, "engines": { "node": "^10.12.0 || >=12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "@typescript-eslint/parser": "^4.0.0", "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" }, "peerDependenciesMeta": { "typescript": { "optional": true } } }, "node_modules/@typescript-eslint/experimental-utils": { "version": "4.22.1", "dev": true, "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.3", "@typescript-eslint/scope-manager": "4.22.1", "@typescript-eslint/types": "4.22.1", "@typescript-eslint/typescript-estree": "4.22.1", "eslint-scope": "^5.0.0", "eslint-utils": "^2.0.0" }, "engines": { "node": "^10.12.0 || >=12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "eslint": "*" } }, "node_modules/@typescript-eslint/parser": { "version": "4.22.1", "dev": true, "license": "BSD-2-Clause", "dependencies": { "@typescript-eslint/scope-manager": "4.22.1", "@typescript-eslint/types": "4.22.1", "@typescript-eslint/typescript-estree": "4.22.1", "debug": "^4.1.1" }, "engines": { "node": "^10.12.0 || >=12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" }, "peerDependenciesMeta": { "typescript": { "optional": true } } }, "node_modules/@typescript-eslint/scope-manager": { "version": "4.22.1", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/types": "4.22.1", "@typescript-eslint/visitor-keys": "4.22.1" }, "engines": { "node": "^8.10.0 || ^10.13.0 || >=11.10.1" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, "node_modules/@typescript-eslint/types": { "version": "4.22.1", "dev": true, "license": "MIT", "engines": { "node": "^8.10.0 || ^10.13.0 || >=11.10.1" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, "node_modules/@typescript-eslint/typescript-estree": { "version": "4.22.1", "dev": true, "license": "BSD-2-Clause", "dependencies": { "@typescript-eslint/types": "4.22.1", "@typescript-eslint/visitor-keys": "4.22.1", "debug": "^4.1.1", "globby": "^11.0.1", "is-glob": "^4.0.1", "semver": "^7.3.2", "tsutils": "^3.17.1" }, "engines": { "node": "^10.12.0 || >=12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependenciesMeta": { "typescript": { "optional": true } } }, "node_modules/@typescript-eslint/visitor-keys": { "version": "4.22.1", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/types": "4.22.1", "eslint-visitor-keys": "^2.0.0" }, "engines": { "node": "^8.10.0 || ^10.13.0 || >=11.10.1" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, "node_modules/@vscode/test-electron": { "version": "2.1.3", "resolved": "https://registry.npmmirror.com/@vscode/test-electron/-/test-electron-2.1.3.tgz", "integrity": "sha512-ps/yJ/9ToUZtR1dHfWi1mDXtep1VoyyrmGKC3UnIbScToRQvbUjyy1VMqnMEW3EpMmC3g7+pyThIPtPyCLHyow==", "dev": true, "dependencies": { "http-proxy-agent": "^4.0.1", "https-proxy-agent": "^5.0.0", "rimraf": "^3.0.2", "unzipper": "^0.10.11" }, "engines": { "node": ">=8.9.3" } }, "node_modules/acorn": { "version": "7.4.1", "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" }, "engines": { "node": ">=0.4.0" } }, "node_modules/acorn-jsx": { "version": "5.3.1", "dev": true, "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dev": true, "dependencies": { "debug": "4" }, "engines": { "node": ">= 6.0.0" } }, "node_modules/ajv": { "version": "6.12.6", "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" }, "funding": { "type": "github", "url": "https://github.com/sponsors/epoberezkin" } }, "node_modules/ansi-colors": { "version": "4.1.1", "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/ansi-regex": { "version": "5.0.0", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/ansi-styles": { "version": "4.3.0", "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/argparse": { "version": "1.0.10", "dev": true, "license": "MIT", "dependencies": { "sprintf-js": "~1.0.2" } }, "node_modules/array-union": { "version": "2.1.0", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/astral-regex": { "version": "2.0.0", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/balanced-match": { "version": "1.0.2", "dev": true, "license": "MIT" }, "node_modules/big-integer": { "version": "1.6.51", "resolved": "https://registry.npmmirror.com/big-integer/-/big-integer-1.6.51.tgz", "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", "dev": true, "engines": { "node": ">=0.6" } }, "node_modules/binary": { "version": "0.3.0", "resolved": "https://registry.npmmirror.com/binary/-/binary-0.3.0.tgz", "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", "dev": true, "dependencies": { "buffers": "~0.1.1", "chainsaw": "~0.1.0" }, "engines": { "node": "*" } }, "node_modules/bluebird": { "version": "3.4.7", "resolved": "https://registry.npmmirror.com/bluebird/-/bluebird-3.4.7.tgz", "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", "dev": true }, "node_modules/brace-expansion": { "version": "1.1.11", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "node_modules/braces": { "version": "3.0.2", "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.0.1" }, "engines": { "node": ">=8" } }, "node_modules/buffer-indexof-polyfill": { "version": "1.0.2", "resolved": "https://registry.npmmirror.com/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", "dev": true, "engines": { "node": ">=0.10" } }, "node_modules/buffers": { "version": "0.1.1", "resolved": "https://registry.npmmirror.com/buffers/-/buffers-0.1.1.tgz", "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", "dev": true, "engines": { "node": ">=0.2.0" } }, "node_modules/callsites": { "version": "3.1.0", "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/chainsaw": { "version": "0.1.0", "resolved": "https://registry.npmmirror.com/chainsaw/-/chainsaw-0.1.0.tgz", "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", "dev": true, "dependencies": { "traverse": ">=0.3.0 <0.4" }, "engines": { "node": "*" } }, "node_modules/chalk": { "version": "4.1.1", "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/color-convert": { "version": "2.0.1", "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, "engines": { "node": ">=7.0.0" } }, "node_modules/color-name": { "version": "1.1.4", "dev": true, "license": "MIT" }, "node_modules/concat-map": { "version": "0.0.1", "dev": true, "license": "MIT" }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, "node_modules/cross-spawn": { "version": "7.0.3", "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" }, "engines": { "node": ">= 8" } }, "node_modules/debug": { "version": "4.3.1", "dev": true, "license": "MIT", "dependencies": { "ms": "2.1.2" }, "engines": { "node": ">=6.0" }, "peerDependenciesMeta": { "supports-color": { "optional": true } } }, "node_modules/deep-is": { "version": "0.1.3", "dev": true, "license": "MIT" }, "node_modules/dir-glob": { "version": "3.0.1", "dev": true, "license": "MIT", "dependencies": { "path-type": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/doctrine": { "version": "3.0.0", "dev": true, "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, "engines": { "node": ">=6.0.0" } }, "node_modules/duplexer2": { "version": "0.1.4", "resolved": "https://registry.npmmirror.com/duplexer2/-/duplexer2-0.1.4.tgz", "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", "dev": true, "dependencies": { "readable-stream": "^2.0.2" } }, "node_modules/emoji-regex": { "version": "8.0.0", "dev": true, "license": "MIT" }, "node_modules/enquirer": { "version": "2.3.6", "dev": true, "license": "MIT", "dependencies": { "ansi-colors": "^4.1.1" }, "engines": { "node": ">=8.6" } }, "node_modules/eslint": { "version": "7.25.0", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "7.12.11", "@eslint/eslintrc": "^0.4.0", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.0.1", "doctrine": "^3.0.0", "enquirer": "^2.3.5", "eslint-scope": "^5.1.1", "eslint-utils": "^2.1.0", "eslint-visitor-keys": "^2.0.0", "espree": "^7.3.1", "esquery": "^1.4.0", "esutils": "^2.0.2", "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", "glob-parent": "^5.0.0", "globals": "^13.6.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash": "^4.17.21", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", "progress": "^2.0.0", "regexpp": "^3.1.0", "semver": "^7.2.1", "strip-ansi": "^6.0.0", "strip-json-comments": "^3.1.0", "table": "^6.0.4", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { "node": "^10.12.0 || >=12.0.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-scope": { "version": "5.1.1", "dev": true, "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" }, "engines": { "node": ">=8.0.0" } }, "node_modules/eslint-utils": { "version": "2.1.0", "dev": true, "license": "MIT", "dependencies": { "eslint-visitor-keys": "^1.1.0" }, "engines": { "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/mysticatea" } }, "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { "version": "1.3.0", "dev": true, "license": "Apache-2.0", "engines": { "node": ">=4" } }, "node_modules/eslint-visitor-keys": { "version": "2.1.0", "dev": true, "license": "Apache-2.0", "engines": { "node": ">=10" } }, "node_modules/espree": { "version": "7.3.1", "dev": true, "license": "BSD-2-Clause", "dependencies": { "acorn": "^7.4.0", "acorn-jsx": "^5.3.1", "eslint-visitor-keys": "^1.3.0" }, "engines": { "node": "^10.12.0 || >=12.0.0" } }, "node_modules/espree/node_modules/eslint-visitor-keys": { "version": "1.3.0", "dev": true, "license": "Apache-2.0", "engines": { "node": ">=4" } }, "node_modules/esprima": { "version": "4.0.1", "dev": true, "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" }, "engines": { "node": ">=4" } }, "node_modules/esquery": { "version": "1.4.0", "dev": true, "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" }, "engines": { "node": ">=0.10" } }, "node_modules/esquery/node_modules/estraverse": { "version": "5.2.0", "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } }, "node_modules/esrecurse": { "version": "4.3.0", "dev": true, "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, "engines": { "node": ">=4.0" } }, "node_modules/esrecurse/node_modules/estraverse": { "version": "5.2.0", "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } }, "node_modules/estraverse": { "version": "4.3.0", "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } }, "node_modules/esutils": { "version": "2.0.3", "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/fast-deep-equal": { "version": "3.1.3", "dev": true, "license": "MIT" }, "node_modules/fast-glob": { "version": "3.2.5", "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.0", "merge2": "^1.3.0", "micromatch": "^4.0.2", "picomatch": "^2.2.1" }, "engines": { "node": ">=8" } }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "dev": true, "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", "dev": true, "license": "MIT" }, "node_modules/fastq": { "version": "1.11.0", "dev": true, "license": "ISC", "dependencies": { "reusify": "^1.0.4" } }, "node_modules/file-entry-cache": { "version": "6.0.1", "dev": true, "license": "MIT", "dependencies": { "flat-cache": "^3.0.4" }, "engines": { "node": "^10.12.0 || >=12.0.0" } }, "node_modules/fill-range": { "version": "7.0.1", "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/flat-cache": { "version": "3.0.4", "dev": true, "license": "MIT", "dependencies": { "flatted": "^3.1.0", "rimraf": "^3.0.2" }, "engines": { "node": "^10.12.0 || >=12.0.0" } }, "node_modules/flatted": { "version": "3.1.1", "dev": true, "license": "ISC" }, "node_modules/fs.realpath": { "version": "1.0.0", "dev": true, "license": "ISC" }, "node_modules/fstream": { "version": "1.0.12", "resolved": "https://registry.npmmirror.com/fstream/-/fstream-1.0.12.tgz", "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", "dev": true, "dependencies": { "graceful-fs": "^4.1.2", "inherits": "~2.0.0", "mkdirp": ">=0.5 0", "rimraf": "2" }, "engines": { "node": ">=0.6" } }, "node_modules/fstream/node_modules/rimraf": { "version": "2.7.1", "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-2.7.1.tgz", "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "dev": true, "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "node_modules/functional-red-black-tree": { "version": "1.0.1", "dev": true, "license": "MIT" }, "node_modules/glob": { "version": "7.1.6", "dev": true, "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, "engines": { "node": "*" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/glob-parent": { "version": "5.1.2", "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, "engines": { "node": ">= 6" } }, "node_modules/globals": { "version": "13.8.0", "dev": true, "license": "MIT", "dependencies": { "type-fest": "^0.20.2" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/globby": { "version": "11.0.3", "dev": true, "license": "MIT", "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.1.1", "ignore": "^5.1.4", "merge2": "^1.3.0", "slash": "^3.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/globby/node_modules/ignore": { "version": "5.1.8", "dev": true, "license": "MIT", "engines": { "node": ">= 4" } }, "node_modules/graceful-fs": { "version": "4.2.9", "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.9.tgz", "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", "dev": true }, "node_modules/has-flag": { "version": "4.0.0", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/http-proxy-agent": { "version": "4.0.1", "resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", "dev": true, "dependencies": { "@tootallnate/once": "1", "agent-base": "6", "debug": "4" }, "engines": { "node": ">= 6" } }, "node_modules/https-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", "dev": true, "dependencies": { "agent-base": "6", "debug": "4" }, "engines": { "node": ">= 6" } }, "node_modules/ignore": { "version": "4.0.6", "dev": true, "license": "MIT", "engines": { "node": ">= 4" } }, "node_modules/import-fresh": { "version": "3.3.0", "dev": true, "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" }, "engines": { "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/imurmurhash": { "version": "0.1.4", "dev": true, "license": "MIT", "engines": { "node": ">=0.8.19" } }, "node_modules/inflight": { "version": "1.0.6", "dev": true, "license": "ISC", "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "node_modules/inherits": { "version": "2.0.4", "dev": true, "license": "ISC" }, "node_modules/is-extglob": { "version": "2.1.1", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/is-glob": { "version": "4.0.1", "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, "engines": { "node": ">=0.10.0" } }, "node_modules/is-number": { "version": "7.0.0", "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" } }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "dev": true }, "node_modules/isexe": { "version": "2.0.0", "dev": true, "license": "ISC" }, "node_modules/js-tokens": { "version": "4.0.0", "dev": true, "license": "MIT" }, "node_modules/js-yaml": { "version": "3.14.1", "dev": true, "license": "MIT", "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "node_modules/json-schema-traverse": { "version": "0.4.1", "dev": true, "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "dev": true, "license": "MIT" }, "node_modules/levn": { "version": "0.4.1", "dev": true, "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/listenercount": { "version": "1.0.1", "resolved": "https://registry.npmmirror.com/listenercount/-/listenercount-1.0.1.tgz", "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", "dev": true }, "node_modules/lodash": { "version": "4.17.21", "dev": true, "license": "MIT" }, "node_modules/lodash.clonedeep": { "version": "4.5.0", "dev": true, "license": "MIT" }, "node_modules/lodash.flatten": { "version": "4.4.0", "dev": true, "license": "MIT" }, "node_modules/lodash.truncate": { "version": "4.4.2", "dev": true, "license": "MIT" }, "node_modules/lru-cache": { "version": "6.0.0", "dev": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, "engines": { "node": ">=10" } }, "node_modules/merge2": { "version": "1.4.1", "dev": true, "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/micromatch": { "version": "4.0.4", "dev": true, "license": "MIT", "dependencies": { "braces": "^3.0.1", "picomatch": "^2.2.3" }, "engines": { "node": ">=8.6" } }, "node_modules/minimatch": { "version": "3.0.4", "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, "engines": { "node": "*" } }, "node_modules/minimist": { "version": "1.2.6", "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.6.tgz", "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-0.5.6.tgz", "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "dependencies": { "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" } }, "node_modules/ms": { "version": "2.1.2", "dev": true, "license": "MIT" }, "node_modules/natural-compare": { "version": "1.4.0", "dev": true, "license": "MIT" }, "node_modules/once": { "version": "1.4.0", "dev": true, "license": "ISC", "dependencies": { "wrappy": "1" } }, "node_modules/optionator": { "version": "0.9.1", "dev": true, "license": "MIT", "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.3" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/parent-module": { "version": "1.0.1", "dev": true, "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, "engines": { "node": ">=6" } }, "node_modules/path-is-absolute": { "version": "1.0.1", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/path-key": { "version": "3.1.1", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/path-type": { "version": "4.0.0", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/picomatch": { "version": "2.2.3", "dev": true, "license": "MIT", "engines": { "node": ">=8.6" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/prelude-ls": { "version": "1.2.1", "dev": true, "license": "MIT", "engines": { "node": ">= 0.8.0" } }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, "node_modules/progress": { "version": "2.0.3", "dev": true, "license": "MIT", "engines": { "node": ">=0.4.0" } }, "node_modules/punycode": { "version": "2.1.1", "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/queue-microtask": { "version": "1.2.3", "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/feross" }, { "type": "patreon", "url": "https://www.patreon.com/feross" }, { "type": "consulting", "url": "https://feross.org/support" } ], "license": "MIT" }, "node_modules/readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "node_modules/regexpp": { "version": "3.1.0", "dev": true, "license": "MIT", "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/mysticatea" } }, "node_modules/require-from-string": { "version": "2.0.2", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/resolve-from": { "version": "4.0.0", "dev": true, "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/reusify": { "version": "1.0.4", "dev": true, "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" } }, "node_modules/rimraf": { "version": "3.0.2", "dev": true, "license": "ISC", "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/run-parallel": { "version": "1.2.0", "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/feross" }, { "type": "patreon", "url": "https://www.patreon.com/feross" }, { "type": "consulting", "url": "https://feross.org/support" } ], "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, "node_modules/semver": { "version": "7.3.5", "dev": true, "license": "ISC", "dependencies": { "lru-cache": "^6.0.0" }, "bin": { "semver": "bin/semver.js" }, "engines": { "node": ">=10" } }, "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmmirror.com/setimmediate/-/setimmediate-1.0.5.tgz", "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", "dev": true }, "node_modules/shebang-command": { "version": "2.0.0", "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, "engines": { "node": ">=8" } }, "node_modules/shebang-regex": { "version": "3.0.0", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/slash": { "version": "3.0.0", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/slice-ansi": { "version": "4.0.0", "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", "is-fullwidth-code-point": "^3.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, "node_modules/sprintf-js": { "version": "1.0.3", "dev": true, "license": "BSD-3-Clause" }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "dependencies": { "safe-buffer": "~5.1.0" } }, "node_modules/string-width": { "version": "4.2.2", "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.0" }, "engines": { "node": ">=8" } }, "node_modules/strip-ansi": { "version": "6.0.0", "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.0" }, "engines": { "node": ">=8" } }, "node_modules/strip-json-comments": { "version": "3.1.1", "dev": true, "license": "MIT", "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/supports-color": { "version": "7.2.0", "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/table": { "version": "6.6.0", "dev": true, "license": "BSD-3-Clause", "dependencies": { "ajv": "^8.0.1", "lodash.clonedeep": "^4.5.0", "lodash.flatten": "^4.4.0", "lodash.truncate": "^4.4.2", "slice-ansi": "^4.0.0", "string-width": "^4.2.0", "strip-ansi": "^6.0.0" }, "engines": { "node": ">=10.0.0" } }, "node_modules/table/node_modules/ajv": { "version": "8.2.0", "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", "uri-js": "^4.2.2" }, "funding": { "type": "github", "url": "https://github.com/sponsors/epoberezkin" } }, "node_modules/table/node_modules/json-schema-traverse": { "version": "1.0.0", "dev": true, "license": "MIT" }, "node_modules/text-table": { "version": "0.2.0", "dev": true, "license": "MIT" }, "node_modules/to-regex-range": { "version": "5.0.1", "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, "engines": { "node": ">=8.0" } }, "node_modules/traverse": { "version": "0.3.9", "resolved": "https://registry.npmmirror.com/traverse/-/traverse-0.3.9.tgz", "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", "dev": true, "engines": { "node": "*" } }, "node_modules/tslib": { "version": "1.14.1", "dev": true, "license": "0BSD" }, "node_modules/tsutils": { "version": "3.21.0", "dev": true, "license": "MIT", "dependencies": { "tslib": "^1.8.1" }, "engines": { "node": ">= 6" }, "peerDependencies": { "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, "node_modules/type-check": { "version": "0.4.0", "dev": true, "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/type-fest": { "version": "0.20.2", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/typescript": { "version": "4.6.2", "resolved": "https://registry.npmmirror.com/typescript/-/typescript-4.6.2.tgz", "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { "node": ">=4.2.0" } }, "node_modules/unzipper": { "version": "0.10.11", "resolved": "https://registry.npmmirror.com/unzipper/-/unzipper-0.10.11.tgz", "integrity": "sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw==", "dev": true, "dependencies": { "big-integer": "^1.6.17", "binary": "~0.3.0", "bluebird": "~3.4.1", "buffer-indexof-polyfill": "~1.0.0", "duplexer2": "~0.1.4", "fstream": "^1.0.12", "graceful-fs": "^4.2.2", "listenercount": "~1.0.1", "readable-stream": "~2.3.6", "setimmediate": "~1.0.4" } }, "node_modules/uri-js": { "version": "4.4.1", "dev": true, "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, "node_modules/v8-compile-cache": { "version": "2.3.0", "dev": true, "license": "MIT" }, "node_modules/which": { "version": "2.0.2", "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" }, "engines": { "node": ">= 8" } }, "node_modules/word-wrap": { "version": "1.2.3", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/wrappy": { "version": "1.0.2", "dev": true, "license": "ISC" }, "node_modules/yallist": { "version": "4.0.0", "dev": true, "license": "ISC" } }, "dependencies": { "@babel/code-frame": { "version": "7.12.11", "dev": true, "requires": { "@babel/highlight": "^7.10.4" } }, "@babel/helper-validator-identifier": { "version": "7.14.0", "dev": true }, "@babel/highlight": { "version": "7.14.0", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.14.0", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, "dependencies": { "ansi-styles": { "version": "3.2.1", "dev": true, "requires": { "color-convert": "^1.9.0" } }, "chalk": { "version": "2.4.2", "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "color-convert": { "version": "1.9.3", "dev": true, "requires": { "color-name": "1.1.3" } }, "color-name": { "version": "1.1.3", "dev": true }, "escape-string-regexp": { "version": "1.0.5", "dev": true }, "has-flag": { "version": "3.0.0", "dev": true }, "supports-color": { "version": "5.5.0", "dev": true, "requires": { "has-flag": "^3.0.0" } } } }, "@eslint/eslintrc": { "version": "0.4.0", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.1.1", "espree": "^7.3.0", "globals": "^12.1.0", "ignore": "^4.0.6", "import-fresh": "^3.2.1", "js-yaml": "^3.13.1", "minimatch": "^3.0.4", "strip-json-comments": "^3.1.1" }, "dependencies": { "globals": { "version": "12.4.0", "dev": true, "requires": { "type-fest": "^0.8.1" } }, "type-fest": { "version": "0.8.1", "dev": true } } }, "@nodelib/fs.scandir": { "version": "2.1.4", "dev": true, "requires": { "@nodelib/fs.stat": "2.0.4", "run-parallel": "^1.1.9" } }, "@nodelib/fs.stat": { "version": "2.0.4", "dev": true }, "@nodelib/fs.walk": { "version": "1.2.6", "dev": true, "requires": { "@nodelib/fs.scandir": "2.1.4", "fastq": "^1.6.0" } }, "@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmmirror.com/@tootallnate/once/-/once-1.1.2.tgz", "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", "dev": true }, "@types/json-schema": { "version": "7.0.7", "dev": true }, "@types/node": { "version": "16.11.26", "resolved": "https://registry.npmmirror.com/@types/node/-/node-16.11.26.tgz", "integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ==", "dev": true }, "@types/vscode": { "version": "1.65.0", "resolved": "https://registry.npmmirror.com/@types/vscode/-/vscode-1.65.0.tgz", "integrity": "sha512-wQhExnh2nEzpjDMSKhUvnNmz3ucpd3E+R7wJkOhBNK3No6fG3VUdmVmMOKD0A8NDZDDDiQcLNxe3oGmX5SjJ5w==", "dev": true }, "@typescript-eslint/eslint-plugin": { "version": "4.22.1", "dev": true, "requires": { "@typescript-eslint/experimental-utils": "4.22.1", "@typescript-eslint/scope-manager": "4.22.1", "debug": "^4.1.1", "functional-red-black-tree": "^1.0.1", "lodash": "^4.17.15", "regexpp": "^3.0.0", "semver": "^7.3.2", "tsutils": "^3.17.1" } }, "@typescript-eslint/experimental-utils": { "version": "4.22.1", "dev": true, "requires": { "@types/json-schema": "^7.0.3", "@typescript-eslint/scope-manager": "4.22.1", "@typescript-eslint/types": "4.22.1", "@typescript-eslint/typescript-estree": "4.22.1", "eslint-scope": "^5.0.0", "eslint-utils": "^2.0.0" } }, "@typescript-eslint/parser": { "version": "4.22.1", "dev": true, "requires": { "@typescript-eslint/scope-manager": "4.22.1", "@typescript-eslint/types": "4.22.1", "@typescript-eslint/typescript-estree": "4.22.1", "debug": "^4.1.1" } }, "@typescript-eslint/scope-manager": { "version": "4.22.1", "dev": true, "requires": { "@typescript-eslint/types": "4.22.1", "@typescript-eslint/visitor-keys": "4.22.1" } }, "@typescript-eslint/types": { "version": "4.22.1", "dev": true }, "@typescript-eslint/typescript-estree": { "version": "4.22.1", "dev": true, "requires": { "@typescript-eslint/types": "4.22.1", "@typescript-eslint/visitor-keys": "4.22.1", "debug": "^4.1.1", "globby": "^11.0.1", "is-glob": "^4.0.1", "semver": "^7.3.2", "tsutils": "^3.17.1" } }, "@typescript-eslint/visitor-keys": { "version": "4.22.1", "dev": true, "requires": { "@typescript-eslint/types": "4.22.1", "eslint-visitor-keys": "^2.0.0" } }, "@vscode/test-electron": { "version": "2.1.3", "resolved": "https://registry.npmmirror.com/@vscode/test-electron/-/test-electron-2.1.3.tgz", "integrity": "sha512-ps/yJ/9ToUZtR1dHfWi1mDXtep1VoyyrmGKC3UnIbScToRQvbUjyy1VMqnMEW3EpMmC3g7+pyThIPtPyCLHyow==", "dev": true, "requires": { "http-proxy-agent": "^4.0.1", "https-proxy-agent": "^5.0.0", "rimraf": "^3.0.2", "unzipper": "^0.10.11" } }, "acorn": { "version": "7.4.1", "dev": true }, "acorn-jsx": { "version": "5.3.1", "dev": true, "requires": {} }, "agent-base": { "version": "6.0.2", "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dev": true, "requires": { "debug": "4" } }, "ajv": { "version": "6.12.6", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "ansi-colors": { "version": "4.1.1", "dev": true }, "ansi-regex": { "version": "5.0.0", "dev": true }, "ansi-styles": { "version": "4.3.0", "dev": true, "requires": { "color-convert": "^2.0.1" } }, "argparse": { "version": "1.0.10", "dev": true, "requires": { "sprintf-js": "~1.0.2" } }, "array-union": { "version": "2.1.0", "dev": true }, "astral-regex": { "version": "2.0.0", "dev": true }, "balanced-match": { "version": "1.0.2", "dev": true }, "big-integer": { "version": "1.6.51", "resolved": "https://registry.npmmirror.com/big-integer/-/big-integer-1.6.51.tgz", "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", "dev": true }, "binary": { "version": "0.3.0", "resolved": "https://registry.npmmirror.com/binary/-/binary-0.3.0.tgz", "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", "dev": true, "requires": { "buffers": "~0.1.1", "chainsaw": "~0.1.0" } }, "bluebird": { "version": "3.4.7", "resolved": "https://registry.npmmirror.com/bluebird/-/bluebird-3.4.7.tgz", "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", "dev": true }, "brace-expansion": { "version": "1.1.11", "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "braces": { "version": "3.0.2", "dev": true, "requires": { "fill-range": "^7.0.1" } }, "buffer-indexof-polyfill": { "version": "1.0.2", "resolved": "https://registry.npmmirror.com/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", "dev": true }, "buffers": { "version": "0.1.1", "resolved": "https://registry.npmmirror.com/buffers/-/buffers-0.1.1.tgz", "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", "dev": true }, "callsites": { "version": "3.1.0", "dev": true }, "chainsaw": { "version": "0.1.0", "resolved": "https://registry.npmmirror.com/chainsaw/-/chainsaw-0.1.0.tgz", "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", "dev": true, "requires": { "traverse": ">=0.3.0 <0.4" } }, "chalk": { "version": "4.1.1", "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "color-convert": { "version": "2.0.1", "dev": true, "requires": { "color-name": "~1.1.4" } }, "color-name": { "version": "1.1.4", "dev": true }, "concat-map": { "version": "0.0.1", "dev": true }, "core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, "cross-spawn": { "version": "7.0.3", "dev": true, "requires": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "debug": { "version": "4.3.1", "dev": true, "requires": { "ms": "2.1.2" } }, "deep-is": { "version": "0.1.3", "dev": true }, "dir-glob": { "version": "3.0.1", "dev": true, "requires": { "path-type": "^4.0.0" } }, "doctrine": { "version": "3.0.0", "dev": true, "requires": { "esutils": "^2.0.2" } }, "duplexer2": { "version": "0.1.4", "resolved": "https://registry.npmmirror.com/duplexer2/-/duplexer2-0.1.4.tgz", "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", "dev": true, "requires": { "readable-stream": "^2.0.2" } }, "emoji-regex": { "version": "8.0.0", "dev": true }, "enquirer": { "version": "2.3.6", "dev": true, "requires": { "ansi-colors": "^4.1.1" } }, "eslint": { "version": "7.25.0", "dev": true, "requires": { "@babel/code-frame": "7.12.11", "@eslint/eslintrc": "^0.4.0", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.0.1", "doctrine": "^3.0.0", "enquirer": "^2.3.5", "eslint-scope": "^5.1.1", "eslint-utils": "^2.1.0", "eslint-visitor-keys": "^2.0.0", "espree": "^7.3.1", "esquery": "^1.4.0", "esutils": "^2.0.2", "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", "glob-parent": "^5.0.0", "globals": "^13.6.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash": "^4.17.21", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", "progress": "^2.0.0", "regexpp": "^3.1.0", "semver": "^7.2.1", "strip-ansi": "^6.0.0", "strip-json-comments": "^3.1.0", "table": "^6.0.4", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" } }, "eslint-scope": { "version": "5.1.1", "dev": true, "requires": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" } }, "eslint-utils": { "version": "2.1.0", "dev": true, "requires": { "eslint-visitor-keys": "^1.1.0" }, "dependencies": { "eslint-visitor-keys": { "version": "1.3.0", "dev": true } } }, "eslint-visitor-keys": { "version": "2.1.0", "dev": true }, "espree": { "version": "7.3.1", "dev": true, "requires": { "acorn": "^7.4.0", "acorn-jsx": "^5.3.1", "eslint-visitor-keys": "^1.3.0" }, "dependencies": { "eslint-visitor-keys": { "version": "1.3.0", "dev": true } } }, "esprima": { "version": "4.0.1", "dev": true }, "esquery": { "version": "1.4.0", "dev": true, "requires": { "estraverse": "^5.1.0" }, "dependencies": { "estraverse": { "version": "5.2.0", "dev": true } } }, "esrecurse": { "version": "4.3.0", "dev": true, "requires": { "estraverse": "^5.2.0" }, "dependencies": { "estraverse": { "version": "5.2.0", "dev": true } } }, "estraverse": { "version": "4.3.0", "dev": true }, "esutils": { "version": "2.0.3", "dev": true }, "fast-deep-equal": { "version": "3.1.3", "dev": true }, "fast-glob": { "version": "3.2.5", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.0", "merge2": "^1.3.0", "micromatch": "^4.0.2", "picomatch": "^2.2.1" } }, "fast-json-stable-stringify": { "version": "2.1.0", "dev": true }, "fast-levenshtein": { "version": "2.0.6", "dev": true }, "fastq": { "version": "1.11.0", "dev": true, "requires": { "reusify": "^1.0.4" } }, "file-entry-cache": { "version": "6.0.1", "dev": true, "requires": { "flat-cache": "^3.0.4" } }, "fill-range": { "version": "7.0.1", "dev": true, "requires": { "to-regex-range": "^5.0.1" } }, "flat-cache": { "version": "3.0.4", "dev": true, "requires": { "flatted": "^3.1.0", "rimraf": "^3.0.2" } }, "flatted": { "version": "3.1.1", "dev": true }, "fs.realpath": { "version": "1.0.0", "dev": true }, "fstream": { "version": "1.0.12", "resolved": "https://registry.npmmirror.com/fstream/-/fstream-1.0.12.tgz", "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", "dev": true, "requires": { "graceful-fs": "^4.1.2", "inherits": "~2.0.0", "mkdirp": ">=0.5 0", "rimraf": "2" }, "dependencies": { "rimraf": { "version": "2.7.1", "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-2.7.1.tgz", "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "dev": true, "requires": { "glob": "^7.1.3" } } } }, "functional-red-black-tree": { "version": "1.0.1", "dev": true }, "glob": { "version": "7.1.6", "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "glob-parent": { "version": "5.1.2", "dev": true, "requires": { "is-glob": "^4.0.1" } }, "globals": { "version": "13.8.0", "dev": true, "requires": { "type-fest": "^0.20.2" } }, "globby": { "version": "11.0.3", "dev": true, "requires": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.1.1", "ignore": "^5.1.4", "merge2": "^1.3.0", "slash": "^3.0.0" }, "dependencies": { "ignore": { "version": "5.1.8", "dev": true } } }, "graceful-fs": { "version": "4.2.9", "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.9.tgz", "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", "dev": true }, "has-flag": { "version": "4.0.0", "dev": true }, "http-proxy-agent": { "version": "4.0.1", "resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", "dev": true, "requires": { "@tootallnate/once": "1", "agent-base": "6", "debug": "4" } }, "https-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", "dev": true, "requires": { "agent-base": "6", "debug": "4" } }, "ignore": { "version": "4.0.6", "dev": true }, "import-fresh": { "version": "3.3.0", "dev": true, "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "imurmurhash": { "version": "0.1.4", "dev": true }, "inflight": { "version": "1.0.6", "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" } }, "inherits": { "version": "2.0.4", "dev": true }, "is-extglob": { "version": "2.1.1", "dev": true }, "is-fullwidth-code-point": { "version": "3.0.0", "dev": true }, "is-glob": { "version": "4.0.1", "dev": true, "requires": { "is-extglob": "^2.1.1" } }, "is-number": { "version": "7.0.0", "dev": true }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "dev": true }, "isexe": { "version": "2.0.0", "dev": true }, "js-tokens": { "version": "4.0.0", "dev": true }, "js-yaml": { "version": "3.14.1", "dev": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" } }, "json-schema-traverse": { "version": "0.4.1", "dev": true }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", "dev": true }, "levn": { "version": "0.4.1", "dev": true, "requires": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "listenercount": { "version": "1.0.1", "resolved": "https://registry.npmmirror.com/listenercount/-/listenercount-1.0.1.tgz", "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", "dev": true }, "lodash": { "version": "4.17.21", "dev": true }, "lodash.clonedeep": { "version": "4.5.0", "dev": true }, "lodash.flatten": { "version": "4.4.0", "dev": true }, "lodash.truncate": { "version": "4.4.2", "dev": true }, "lru-cache": { "version": "6.0.0", "dev": true, "requires": { "yallist": "^4.0.0" } }, "merge2": { "version": "1.4.1", "dev": true }, "micromatch": { "version": "4.0.4", "dev": true, "requires": { "braces": "^3.0.1", "picomatch": "^2.2.3" } }, "minimatch": { "version": "3.0.4", "dev": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "1.2.6", "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.6.tgz", "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, "mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-0.5.6.tgz", "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "requires": { "minimist": "^1.2.6" } }, "ms": { "version": "2.1.2", "dev": true }, "natural-compare": { "version": "1.4.0", "dev": true }, "once": { "version": "1.4.0", "dev": true, "requires": { "wrappy": "1" } }, "optionator": { "version": "0.9.1", "dev": true, "requires": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.3" } }, "parent-module": { "version": "1.0.1", "dev": true, "requires": { "callsites": "^3.0.0" } }, "path-is-absolute": { "version": "1.0.1", "dev": true }, "path-key": { "version": "3.1.1", "dev": true }, "path-type": { "version": "4.0.0", "dev": true }, "picomatch": { "version": "2.2.3", "dev": true }, "prelude-ls": { "version": "1.2.1", "dev": true }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, "progress": { "version": "2.0.3", "dev": true }, "punycode": { "version": "2.1.1", "dev": true }, "queue-microtask": { "version": "1.2.3", "dev": true }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "regexpp": { "version": "3.1.0", "dev": true }, "require-from-string": { "version": "2.0.2", "dev": true }, "resolve-from": { "version": "4.0.0", "dev": true }, "reusify": { "version": "1.0.4", "dev": true }, "rimraf": { "version": "3.0.2", "dev": true, "requires": { "glob": "^7.1.3" } }, "run-parallel": { "version": "1.2.0", "dev": true, "requires": { "queue-microtask": "^1.2.2" } }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, "semver": { "version": "7.3.5", "dev": true, "requires": { "lru-cache": "^6.0.0" } }, "setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmmirror.com/setimmediate/-/setimmediate-1.0.5.tgz", "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", "dev": true }, "shebang-command": { "version": "2.0.0", "dev": true, "requires": { "shebang-regex": "^3.0.0" } }, "shebang-regex": { "version": "3.0.0", "dev": true }, "slash": { "version": "3.0.0", "dev": true }, "slice-ansi": { "version": "4.0.0", "dev": true, "requires": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", "is-fullwidth-code-point": "^3.0.0" } }, "sprintf-js": { "version": "1.0.3", "dev": true }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" } }, "string-width": { "version": "4.2.2", "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.0" } }, "strip-ansi": { "version": "6.0.0", "dev": true, "requires": { "ansi-regex": "^5.0.0" } }, "strip-json-comments": { "version": "3.1.1", "dev": true }, "supports-color": { "version": "7.2.0", "dev": true, "requires": { "has-flag": "^4.0.0" } }, "table": { "version": "6.6.0", "dev": true, "requires": { "ajv": "^8.0.1", "lodash.clonedeep": "^4.5.0", "lodash.flatten": "^4.4.0", "lodash.truncate": "^4.4.2", "slice-ansi": "^4.0.0", "string-width": "^4.2.0", "strip-ansi": "^6.0.0" }, "dependencies": { "ajv": { "version": "8.2.0", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", "uri-js": "^4.2.2" } }, "json-schema-traverse": { "version": "1.0.0", "dev": true } } }, "text-table": { "version": "0.2.0", "dev": true }, "to-regex-range": { "version": "5.0.1", "dev": true, "requires": { "is-number": "^7.0.0" } }, "traverse": { "version": "0.3.9", "resolved": "https://registry.npmmirror.com/traverse/-/traverse-0.3.9.tgz", "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", "dev": true }, "tslib": { "version": "1.14.1", "dev": true }, "tsutils": { "version": "3.21.0", "dev": true, "requires": { "tslib": "^1.8.1" } }, "type-check": { "version": "0.4.0", "dev": true, "requires": { "prelude-ls": "^1.2.1" } }, "type-fest": { "version": "0.20.2", "dev": true }, "typescript": { "version": "4.6.2", "resolved": "https://registry.npmmirror.com/typescript/-/typescript-4.6.2.tgz", "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==", "dev": true }, "unzipper": { "version": "0.10.11", "resolved": "https://registry.npmmirror.com/unzipper/-/unzipper-0.10.11.tgz", "integrity": "sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw==", "dev": true, "requires": { "big-integer": "^1.6.17", "binary": "~0.3.0", "bluebird": "~3.4.1", "buffer-indexof-polyfill": "~1.0.0", "duplexer2": "~0.1.4", "fstream": "^1.0.12", "graceful-fs": "^4.2.2", "listenercount": "~1.0.1", "readable-stream": "~2.3.6", "setimmediate": "~1.0.4" } }, "uri-js": { "version": "4.4.1", "dev": true, "requires": { "punycode": "^2.1.0" } }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, "v8-compile-cache": { "version": "2.3.0", "dev": true }, "which": { "version": "2.0.2", "dev": true, "requires": { "isexe": "^2.0.0" } }, "word-wrap": { "version": "1.2.3", "dev": true }, "wrappy": { "version": "1.0.2", "dev": true }, "yallist": { "version": "4.0.0", "dev": true } } } golang-github-ysmood-got-0.40.0/lib/got-vscode-extension/package.json000066400000000000000000000022201520712071400255340ustar00rootroot00000000000000{ "name": "got-vscode-extension", "displayName": "got-vscode-extension", "description": "got vscode extension", "version": "0.1.4", "publisher": "ysmood", "repository": { "type": "git", "url": "https://github.com/ysmood/got.git" }, "engines": { "vscode": "^1.65.2" }, "categories": [ "Snippets" ], "activationEvents": [ "onLanguage:go" ], "main": "./out/extension.js", "contributes": { "snippets": [ { "language": "go", "path": "./snippets.json" } ], "commands": [ { "command": "got-vscode-extension.testCurrent", "title": "got: test current focused case" } ] }, "scripts": { "vscode:prepublish": "npm run compile", "compile": "tsc -p ./", "lint": "eslint . --ext .ts,.tsx", "watch": "tsc -watch -p ./", "test": "node ./out/test/runTest.js" }, "devDependencies": { "@types/node": "^16.0.0", "@types/vscode": "^1.65.0", "@vscode/test-electron": "^2.1.3", "@typescript-eslint/eslint-plugin": "^4.16.0", "@typescript-eslint/parser": "^4.16.0", "eslint": "^7.21.0", "typescript": "^4.6.2" } }golang-github-ysmood-got-0.40.0/lib/got-vscode-extension/snippets.json000066400000000000000000000025031520712071400260120ustar00rootroot00000000000000{ "gop print": { "prefix": "gp", "body": [ "gop.P($0)" ] }, "got setup": { "prefix": "gsetup", "body": [ "package ${0:example}_test", "", "import (", "\t\"time\"", "", "\t\"github.com/ysmood/got\"", "\t\"github.com/ysmood/gotrace\"", ")", "", "func init() {", "\t// Set default timeout for the entire \"go test\"", "\tgot.DefaultFlags(\"timeout=10s\")", "}", "", "var setup = got.Setup(func(g got.G) {", "\t// The function passed to it will be surely executed after the test", "\tg.Cleanup(func() {})", "", "\t// Concurrently run each test", "\tg.Parallel()", "", "\t// Make sure there's no goroutine leak for each test", "\tgotrace.CheckLeak(g, 0)", "", "\t// Timeout for each test", "\tg.PanicAfter(time.Second)", "})", "" ] }, "got test function": { "prefix": "gt", "body": [ "", "func Test$1(t *testing.T) {", "\tg := got.T(t)", "", "\t${0:g.Eq(1, 1)}", "}", "" ] }, "got test function with setup": { "prefix": "gts", "body": [ "", "func Test$1(t *testing.T) {", "\tg := setup(t)", "", "\t${0:g.Eq(1, 1)}", "}", "" ] } } golang-github-ysmood-got-0.40.0/lib/got-vscode-extension/snippets/000077500000000000000000000000001520712071400251175ustar00rootroot00000000000000golang-github-ysmood-got-0.40.0/lib/got-vscode-extension/snippets/main.go000066400000000000000000000023551520712071400263770ustar00rootroot00000000000000// Package main ... package main import ( "bytes" "encoding/json" "log" "os" "strings" ) type snippet struct { Prefix string `json:"prefix,omitempty"` Body []string `json:"body,omitempty"` Description string `json:"description,omitempty"` } type snippets map[string]snippet func main() { b, err := os.ReadFile("lib/example/03_setup_test.go") if err != nil { log.Fatal(err) } setup := strings.Replace(string(b), "example_test", "${0:example}_test", -1) s := snippets{ "gop print": { Prefix: "gp", Body: []string{"gop.P($0)"}, }, "got test function": { Prefix: "gt", Body: strings.Split(` func Test$1(t *testing.T) { g := got.T(t) ${0:g.Eq(1, 1)} } `, "\n"), }, "got test function with setup": { Prefix: "gts", Body: strings.Split(` func Test$1(t *testing.T) { g := setup(t) ${0:g.Eq(1, 1)} } `, "\n"), }, "got setup": { Prefix: "gsetup", Body: strings.Split(string(setup), "\n"), }, } buf := bytes.NewBuffer(nil) enc := json.NewEncoder(buf) enc.SetEscapeHTML(false) enc.SetIndent("", " ") err = enc.Encode(s) if err != nil { log.Fatal(err) } err = os.WriteFile("lib/got-vscode-extension/snippets.json", buf.Bytes(), 0764) if err != nil { log.Fatal(err) } } golang-github-ysmood-got-0.40.0/lib/got-vscode-extension/src/000077500000000000000000000000001520712071400240415ustar00rootroot00000000000000golang-github-ysmood-got-0.40.0/lib/got-vscode-extension/src/extension.ts000066400000000000000000000005561520712071400264330ustar00rootroot00000000000000import * as vscode from 'vscode'; export function activate(context: vscode.ExtensionContext) { let disposable = vscode.commands.registerCommand('got-vscode-extension.testCurrent', () => { vscode.window.showInformationMessage('todo'); }); context.subscriptions.push(disposable); console.log('got-vscode-extension loaded'); } export function deactivate() {} golang-github-ysmood-got-0.40.0/lib/got-vscode-extension/src/test/000077500000000000000000000000001520712071400250205ustar00rootroot00000000000000golang-github-ysmood-got-0.40.0/lib/got-vscode-extension/src/test/runTest.ts000066400000000000000000000013421520712071400270340ustar00rootroot00000000000000import * as path from 'path'; import { runTests } from '@vscode/test-electron'; async function main() { try { // The folder containing the Extension Manifest package.json // Passed to `--extensionDevelopmentPath` const extensionDevelopmentPath = path.resolve(__dirname, '../../'); // The path to the extension test runner script // Passed to --extensionTestsPath const extensionTestsPath = path.resolve(__dirname, './suite/index'); // Download VS Code, unzip it and run the integration test await runTests({ extensionDevelopmentPath, extensionTestsPath, launchArgs: ['../../'] }); } catch (err) { console.error(err); console.error('Failed to run tests'); process.exit(1); } } main();golang-github-ysmood-got-0.40.0/lib/got-vscode-extension/src/test/suite/000077500000000000000000000000001520712071400261515ustar00rootroot00000000000000golang-github-ysmood-got-0.40.0/lib/got-vscode-extension/src/test/suite/index.ts000066400000000000000000000001101520712071400276200ustar00rootroot00000000000000 export function run(): Promise { return new Promise(() => {}); }golang-github-ysmood-got-0.40.0/lib/got-vscode-extension/tsconfig.json000066400000000000000000000011101520712071400257520ustar00rootroot00000000000000{ "compilerOptions": { "module": "commonjs", "target": "es6", "outDir": "out", "lib": [ "es6" ], "sourceMap": true, "rootDir": "src", "strict": true /* enable all strict type-checking options */ /* Additional Checks */ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ // "noUnusedParameters": true, /* Report errors on unused parameters. */ }, "exclude": [ "node_modules", ".vscode-test" ] } golang-github-ysmood-got-0.40.0/lib/got-vscode-extension/vsc-extension-quickstart.md000066400000000000000000000050701520712071400305730ustar00rootroot00000000000000# Welcome to your VS Code Extension ## What's in the folder * This folder contains all of the files necessary for your extension. * `package.json` - this is the manifest file in which you declare your extension and command. * The sample plugin registers a command and defines its title and command name. With this information VS Code can show the command in the command palette. It doesn’t yet need to load the plugin. * `src/extension.ts` - this is the main file where you will provide the implementation of your command. * The file exports one function, `activate`, which is called the very first time your extension is activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`. * We pass the function containing the implementation of the command as the second parameter to `registerCommand`. ## Get up and running straight away * Press `F5` to open a new window with your extension loaded. * Run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`. * Set breakpoints in your code inside `src/extension.ts` to debug your extension. * Find output from your extension in the debug console. ## Make changes * You can relaunch the extension from the debug toolbar after changing code in `src/extension.ts`. * You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes. ## Explore the API * You can open the full set of our API when you open the file `node_modules/@types/vscode/index.d.ts`. ## Run tests * Open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Extension Tests`. * Press `F5` to run the tests in a new window with your extension loaded. * See the output of the test result in the debug console. * Make changes to `src/test/suite/extension.test.ts` or create new test files inside the `test/suite` folder. * The provided test runner will only consider files matching the name pattern `**.test.ts`. * You can create folders inside the `test` folder to structure your tests any way you want. ## Go further * Reduce the extension size and improve the startup time by [bundling your extension](https://code.visualstudio.com/api/working-with-extensions/bundling-extension). * [Publish your extension](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) on the VSCode extension marketplace. * Automate builds by setting up [Continuous Integration](https://code.visualstudio.com/api/working-with-extensions/continuous-integration). golang-github-ysmood-got-0.40.0/lib/lcs/000077500000000000000000000000001520712071400177475ustar00rootroot00000000000000golang-github-ysmood-got-0.40.0/lib/lcs/lcs.go000066400000000000000000000053411520712071400210620ustar00rootroot00000000000000// Package lcs ... package lcs import ( "context" ) // Indices is the index list of items in xs that forms the LCS between xs and ys. type Indices []int // YadLCS returns the x index of each Comparable that are in the YadLCS between x and y. // The complexity is O(M * log(L)), M is the number of char matches between x and y, L is the length of LCS. // The worst memory complexity is O(M), but usually it's much less. // // The advantage of this algorithm is it's easy to understand and implement. It converts the LCS // problem into problems that are familiar to us, such as LIS, binary-search, object-recycle, etc., which give us // more room to do the optimization for each streamline. func (xs Sequence) YadLCS(ctx context.Context, ys Sequence) Indices { o := xs.Occurrence(ys) r := result{list: make([]*node, 0, min(len(xs), len(ys)))} rest := len(ys) for _, xi := range o { if ctx.Err() != nil { break } from := len(r.list) for _, i := range xi { from = r.add(from, i, rest) } rest-- } return r.lcs() } type node struct { x int p *node c int // pointer count for node recycle } func (n *node) link(x int, m *node) { if m != nil { m.c++ } n.p = m n.x = x } type result struct { list []*node // reuse node to reduce memory allocation recycle []*node } func (r *result) new(x int, n *node) *node { var m *node // reuse node if possible l := len(r.recycle) if l > 0 { m = r.recycle[l-1] r.recycle = r.recycle[:l-1] } else { m = &node{} } m.link(x, n) return m } func (r *result) replace(i, x int, n *node) { // recycle nodes if m := r.list[i]; m.c == 0 { for p := m.p; p != nil && p != n; p = p.p { p.c-- if p.c == 0 { r.recycle = append(r.recycle, p) } else { break } } m.link(x, n) return } r.list[i] = r.new(x, n) } func (r *result) add(from, x, rest int) int { l := len(r.list) next, n := r.find(from, x) if n != nil { if l-next < rest { // only when we have enough rest xs if next == l { r.list = append(r.list, r.new(x, n)) } else if x < r.list[next].x { r.replace(next, x, n) } return next } } if l == 0 { r.list = append(r.list, r.new(x, n)) return 1 } if l-1 < rest && x < r.list[0].x { r.replace(0, x, nil) } return 0 } // binary search to find the largest r.list[i].x that is smaller than x func (r *result) find(from, x int) (int, *node) { var found *node for i, j := 0, from; i < j; { h := (i + j) >> 1 n := r.list[h] if n.x < x { from = h found = n i = h + 1 } else { j = h } } return from + 1, found } func (r *result) lcs() Indices { l := len(r.list) idx := make(Indices, l) if l == 0 { return idx } for p := r.list[l-1]; p != nil; p = p.p { l-- idx[l] = p.x } return idx } golang-github-ysmood-got-0.40.0/lib/lcs/lcs_test.go000066400000000000000000000044161520712071400221230ustar00rootroot00000000000000package lcs_test import ( "bytes" "context" "crypto/rand" "runtime/debug" "testing" "time" "github.com/ysmood/got" "github.com/ysmood/got/lib/lcs" ) var setup = got.Setup(func(g got.G) { g.ErrorHandler = got.NewDefaultAssertionError(nil, nil) }) func TestLCS(t *testing.T) { g := got.T(t) check := func(i int, x, y string) { t.Helper() e := func(msg ...interface{}) { t.Helper() t.Log(i, x, y) t.Error(msg...) t.FailNow() } defer func() { err := recover() if err != nil { debug.PrintStack() e(err) } }() xs, ys := lcs.NewChars(x), lcs.NewChars(y) s := xs.Sub(xs.YadLCS(context.Background(), ys)) out := s.String() expected := lcs.StandardLCS(xs, ys).String() if !s.IsSubsequenceOf(xs) { e(s.String(), "is not subsequence of", x) } if !s.IsSubsequenceOf(ys) { e(s.String(), "is not subsequence of", y) } if len(out) != len(expected) { e("length of", out, "doesn't equal length of", expected) } } randStr := func() string { const c = 8 b := make([]byte, c) for i := 0; i < c; i++ { b[i] = byte('a' + g.RandInt(0, c)) } return string(b) } check(0, "", "") check(0, "", "a") for i := 1; i < 1000; i++ { check(i, randStr(), randStr()) } } func TestLCSLongContentSmallChange(t *testing.T) { eq := func(x, y, expected string) { t.Helper() ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() xs, ys := lcs.NewChars(x), lcs.NewChars(y) lcs := xs.YadLCS(ctx, ys) out := xs.Sub(lcs).String() if out != expected { t.Error(out, "!=", expected) } } x := bytes.Repeat([]byte("x"), 100000) y := bytes.Repeat([]byte("y"), 100000) eq(string(x), string(y), "") x[len(x)/2] = byte('a') y[len(y)/2] = byte('a') eq(string(x), string(y), "a") x[len(x)/2] = byte('y') y[len(y)/2] = byte('x') eq(string(x), string(y), "xy") } func TestContext(t *testing.T) { g := got.T(t) c := g.Context() c.Cancel() l := lcs.NewChars("abc").YadLCS(c, lcs.NewChars("abc")) g.Len(l, 0) } func TestLongRandom(_ *testing.T) { size := 10000 x := randStr(size) y := randStr(size) c := context.Background() xs := lcs.NewChars(x) ys := lcs.NewChars(y) xs.YadLCS(c, ys) } func randStr(n int) string { b := make([]byte, n) _, _ = rand.Read(b) return string(b) } golang-github-ysmood-got-0.40.0/lib/lcs/sequence.go000066400000000000000000000047651520712071400221220ustar00rootroot00000000000000package lcs import ( "bufio" "bytes" "regexp" ) // Sequence list type Sequence []Comparable // Sub from p, it will automatically decompress the compressed p. func (xs Sequence) Sub(idx Indices) Sequence { s := make(Sequence, len(idx)) for i, ix := range idx { s[i] = xs[ix] } return s } // IsSubsequenceOf returns true if x is a subsequence of y func (xs Sequence) IsSubsequenceOf(ys Sequence) bool { for i, j := 0, 0; i < len(xs); i++ { for { if j >= len(ys) { return false } if eq(xs[i], ys[j]) { j++ break } j++ } } return true } // Histogram of each Comparable func (xs Sequence) Histogram() map[string][]int { h := map[string][]int{} for i := len(xs) - 1; i >= 0; i-- { s := xs[i].String() h[s] = append(h[s], i) } return h } // Occurrence returns the position of each element of y in x. func (xs Sequence) Occurrence(y Sequence) [][]int { o := make([][]int, len(y)) h := xs.Histogram() for i, c := range y { if indexes, has := h[c.String()]; has { o[i] = indexes } } return o } // Comparable interface type Comparable interface { // String for comparison, such as the hash String() string } // Element of a line, a word, or a character type Element string // String returns the full content func (e Element) String() string { return string(e) } // NewChars from string func NewChars(s string) Sequence { cs := Sequence{} for _, r := range s { cs = append(cs, Element(r)) } return cs } // NewWords from string list func NewWords(words []string) Sequence { cs := make(Sequence, len(words)) for i, word := range words { cs[i] = Element(word) } return cs } // NewLines from string. It will split the s via newlines. func NewLines(s string) Sequence { sc := bufio.NewScanner(bytes.NewBufferString(s)) cs := Sequence{} for i := 0; sc.Scan(); i++ { cs = append(cs, Element(sc.Text())) } if len(s) > 0 && s[len(s)-1] == '\n' { cs = append(cs, Element("")) } return cs } // RegWord to match a word var regWord = regexp.MustCompile(`(?s)` + // enable . to match newline `[[:alpha:]]{1,12}` + // match alphabets, length limit is 12 `|[[:digit:]]{1,3}` + // match digits, length limit is 3 `|.` + // match others as single-char words ``) // RegRune to match a rune var regRune = regexp.MustCompile(`(?s).`) // SplitKey for context var SplitKey = struct{}{} // Split a line into words func Split(s string) []string { var reg *regexp.Regexp if len(s) <= 100 { reg = regRune } else { reg = regWord } return reg.FindAllString(s, -1) } golang-github-ysmood-got-0.40.0/lib/lcs/sequence_test.go000066400000000000000000000026741520712071400231560ustar00rootroot00000000000000package lcs_test import ( "strings" "testing" "github.com/ysmood/got" "github.com/ysmood/got/lib/lcs" ) func TestSplit(t *testing.T) { g := got.T(t) g.Len(lcs.Split(""), 0) check := func(in string, expect ...string) { g.Helper() out := lcs.Split(strings.Repeat("\t", 100) + in)[100:] g.Eq(out, expect) } check("find a place to eat 热干面", "find", " ", "a", " ", "place", " ", "to", " ", "eat", " ", "热", "干", "面") check(" as.Equal(arr, arr) test", " ", "as", ".", "Equal", "(", "arr", ",", " ", "arr", ")", " ", "test") check("English-Words紧接着中文", "English", "-", "Words", "紧", "接", "着", "中", "文") check("123456test12345", "123", "456", "test", "123", "45") check("WordVeryVeryVeryVeryVeryVeryVerylong", "WordVeryVery", "VeryVeryVery", "VeryVerylong") } func TestIsSubsequenceOf(t *testing.T) { g := got.T(t) y := lcs.NewChars("abc") g.True(lcs.NewChars("ab").IsSubsequenceOf(y)) g.True(lcs.NewChars("ac").IsSubsequenceOf(y)) g.True(lcs.NewChars("bc").IsSubsequenceOf(y)) g.False(lcs.NewChars("cb").IsSubsequenceOf(y)) g.False(lcs.NewChars("ba").IsSubsequenceOf(y)) g.False(lcs.NewChars("ca").IsSubsequenceOf(y)) } func TestNew(t *testing.T) { g := setup(t) g.Len(lcs.NewLines("a"), 1) g.Len(lcs.NewLines("a\n"), 2) g.Len(lcs.NewLines("a\n\n"), 3) g.Len(lcs.NewLines("\na"), 2) g.Eq(lcs.NewLines("\nabc\nabc").String(), "\nabc\nabc") g.Len(lcs.NewWords([]string{"a", "b"}), 2) } golang-github-ysmood-got-0.40.0/lib/lcs/utils.go000066400000000000000000000022361520712071400214410ustar00rootroot00000000000000package lcs import ( "strings" ) func min(x, y int) int { if x < y { return x } return y } func eq(x, y Comparable) bool { return x.String() == y.String() } // String interface func (xs Sequence) String() string { if len(xs) == 0 { return "" } l := 0 for _, el := range xs { l += len(el.String()) } if l == len(xs) { out := "" for _, c := range xs { out += c.String() } return out } out := []string{} for _, c := range xs { out = append(out, c.String()) } return strings.Join(out, "\n") } // StandardLCS implementation for testing purpose only, because it's very inefficient. // https://en.wikipedia.org/wiki/Longest_common_subsequence_problem#LCS_function_defined. func StandardLCS(xs, ys Sequence) Sequence { last := func(s Sequence) Comparable { return s[len(s)-1] } noLast := func(s Sequence) Sequence { return s[:len(s)-1] } if len(xs)*len(ys) == 0 { return Sequence{} } else if last(xs).String() == last(ys).String() { return append(StandardLCS(noLast(xs), noLast(ys)), last(xs)) } left, right := StandardLCS(xs, noLast(ys)), StandardLCS(noLast(xs), ys) if len(left) > len(right) { return left } return right } golang-github-ysmood-got-0.40.0/lib/mock/000077500000000000000000000000001520712071400201175ustar00rootroot00000000000000golang-github-ysmood-got-0.40.0/lib/mock/mock.go000066400000000000000000000041101520712071400213730ustar00rootroot00000000000000// Package mock provides a simple way to stub struct methods. package mock import ( "reflect" "regexp" "runtime" "strings" "sync" ) // Fallbackable interface type Fallbackable interface { Fallback(any) } // Mock helper for interface stubbing type Mock struct { lock sync.Mutex fallback reflect.Value stubs map[string]interface{} calls map[string][]Call } // Fallback the methods that are not stubbed to fb. func (m *Mock) Fallback(fb interface{}) { m.lock.Lock() defer m.lock.Unlock() m.fallback = reflect.ValueOf(fb) } // Stop the stub func (m *Mock) Stop(method any) { panicIfNotFunc(method) m.lock.Lock() defer m.lock.Unlock() delete(m.stubs, fnName(method)) } // Proxy the input and output of method on mock for later stub. func Proxy[M any](mock Fallbackable, method M) M { panicIfNotFunc(method) m := toMock(mock) m.lock.Lock() defer m.lock.Unlock() name := fnName(method) if fn, has := m.stubs[name]; has { return fn.(M) } if !m.fallback.IsValid() { panic("you should specify the mock.Mock.Fallback") } methodVal := m.fallback.MethodByName(name) if !methodVal.IsValid() { panic(m.fallback.Type().String() + " doesn't have method: " + name) } return m.spy(name, m.fallback.MethodByName(name).Interface()).(M) } func toMock(mock Fallbackable) *Mock { if m, ok := mock.(*Mock); ok { return m } return reflect.Indirect(reflect.ValueOf(mock)).FieldByName("Mock").Addr().Interface().(*Mock) } func fnName(fn interface{}) string { fv := reflect.ValueOf(fn) fi := runtime.FuncForPC(fv.Pointer()) name := regexp.MustCompile(`^.+\.`).ReplaceAllString(fi.Name(), "") // remove the "-fm" suffix for struct methods name = strings.TrimSuffix(name, "-fm") return name } func panicIfNotFunc(fn any) { if reflect.TypeOf(fn).Kind() != reflect.Func { panic("the input should be a function") } } func toReturnValues(t reflect.Type, res []reflect.Value) []reflect.Value { out := []reflect.Value{} for i := 0; i < t.NumOut(); i++ { v := reflect.New(t.Out(i)).Elem() if res[i].IsValid() { v.Set(res[i]) } out = append(out, v) } return out } golang-github-ysmood-got-0.40.0/lib/mock/mock_test.go000066400000000000000000000036351520712071400224450ustar00rootroot00000000000000package mock_test import ( "bytes" "testing" "github.com/ysmood/got" "github.com/ysmood/got/lib/mock" ) type mockBuffer struct { mock.Mock } func (t *mockBuffer) Write(p []byte) (n int, err error) { return mock.Proxy(t, t.Write)(p) } func (t *mockBuffer) Len() int { return mock.Proxy(t, t.Len)() } func (t *mockBuffer) NonExists() int { return mock.Proxy(t, t.NonExists)() } func TestMock(t *testing.T) { g := got.T(t) b := bytes.NewBuffer(nil) m := &mockBuffer{} m.Fallback(b) mock.Stub(m, m.Write, func(p []byte) (int, error) { return b.Write(append(p, []byte(" ")...)) }) n, err := m.Write([]byte("test")) g.Nil(err) g.Eq(n, 6) g.Eq(m.Len(), 6) val := g.Panic(func() { m := mockBuffer{} m.Len() }) g.Eq(val, "you should specify the mock.Mock.Fallback") val = g.Panic(func() { m := mockBuffer{} m.Fallback(b) m.NonExists() }) g.Eq(val, `*bytes.Buffer doesn't have method: NonExists`) g.Eq(g.Panic(func() { m.Stop("") }), "the input should be a function") } func TestMockUtils(t *testing.T) { g := got.T(t) b := bytes.NewBuffer(nil) m := &mockBuffer{} m.Fallback(b) { when := mock.On(m, m.Write).When([]byte{}) when.Return(2, nil).Times(2) n, err := m.Write([]byte{}) g.Nil(err) g.Eq(n, 2) n, err = m.Write([]byte{}) g.Nil(err) g.Eq(n, 2) n, err = m.Write([]byte{}) g.Nil(err) g.Eq(n, 0) g.Eq(when.Count(), 2) g.Len(m.Calls(m.Write), 3) g.Snapshot("calls", m.Calls(m.Write)) } { mock.On(m, m.Write).When(mock.Any).Return(2, nil) n, err := m.Write([]byte{}) g.Nil(err) g.Eq(n, 2) } { mock.On(m, m.Write).When([]byte{}).Return(2, nil).Once() n, err := m.Write([]byte{}) g.Nil(err) g.Eq(n, 2) n, err = m.Write([]byte{}) g.Nil(err) g.Eq(n, 0) } { mock.On(m, m.Write).When(true).Return(2, nil) v := g.Panic(func() { _, _ = m.Write(nil) }) g.Eq(v, "No mock.StubOn.When matches: []interface {}{[]uint8(nil)}") } } golang-github-ysmood-got-0.40.0/lib/mock/spy.go000066400000000000000000000016311520712071400212620ustar00rootroot00000000000000package mock import "reflect" // Call record the input and output of a method call type Call struct { Input []any Return []any } // Calls returns all the calls of method func (m *Mock) Calls(method any) []Call { panicIfNotFunc(method) m.lock.Lock() defer m.lock.Unlock() return m.calls[fnName(method)] } // Record all the input and output of a method func (m *Mock) spy(name string, fn any) any { v := reflect.ValueOf(fn) t := v.Type() if m.calls == nil { m.calls = map[string][]Call{} } return reflect.MakeFunc(t, func(args []reflect.Value) []reflect.Value { ret := v.Call(args) m.lock.Lock() m.calls[name] = append(m.calls[name], Call{ valToInterface(args), valToInterface(ret), }) m.lock.Unlock() return ret }).Interface() } func valToInterface(list []reflect.Value) []any { ret := make([]any, len(list)) for i, v := range list { ret[i] = v.Interface() } return ret } golang-github-ysmood-got-0.40.0/lib/mock/stub.go000066400000000000000000000047731520712071400214360ustar00rootroot00000000000000package mock import ( "fmt" "reflect" "sync" "github.com/ysmood/got/lib/utils" ) // Stub the method with stub func Stub[M any](mock Fallbackable, method M, stub M) { panicIfNotFunc(method) m := toMock(mock) m.lock.Lock() defer m.lock.Unlock() if m.stubs == nil { m.stubs = map[string]interface{}{} } name := fnName(method) m.stubs[name] = m.spy(name, stub) } // StubOn utils type StubOn struct { when []*StubWhen } // StubWhen utils type StubWhen struct { lock *sync.Mutex on *StubOn in []interface{} ret *StubReturn count int // how many times this stub has been matched } // StubReturn utils type StubReturn struct { on *StubOn out []reflect.Value times *StubTimes } // StubTimes utils type StubTimes struct { count int } // On helper to stub methods to conditionally return values. func On[M any](mock Fallbackable, method M) *StubOn { panicIfNotFunc(method) m := toMock(mock) s := &StubOn{ when: []*StubWhen{}, } eq := func(in, arg []interface{}) bool { for i := 0; i < len(in); i++ { if in[i] != Any && utils.Compare(in[i], arg[i]) != 0 { return false } } return true } t := reflect.TypeOf(method) fn := reflect.MakeFunc(t, func(args []reflect.Value) []reflect.Value { argsIt := utils.ToInterfaces(args) for _, when := range s.when { if eq(when.in, argsIt) { when.lock.Lock() when.ret.times.count-- if when.ret.times.count == 0 { m.Stop(method) } when.count++ when.lock.Unlock() return toReturnValues(t, when.ret.out) } } panic(fmt.Sprintf("No mock.StubOn.When matches: %#v", argsIt)) }) Stub(m, method, fn.Interface().(M)) return s } // Any input var Any = struct{}{} // When input args of stubbed method matches in func (s *StubOn) When(in ...interface{}) *StubWhen { w := &StubWhen{lock: &sync.Mutex{}, on: s, in: in} s.when = append(s.when, w) return w } // Return the out as the return values of stubbed method func (s *StubWhen) Return(out ...interface{}) *StubReturn { r := &StubReturn{on: s.on, out: utils.ToValues(out)} r.Times(0) s.ret = r return r } // Count returns how many times this condition has been matched func (s *StubWhen) Count() int { s.lock.Lock() defer s.lock.Unlock() return s.count } // Times specifies how how many stubs before stop, if n <= 0 it will never stop. func (s *StubReturn) Times(n int) *StubOn { t := &StubTimes{count: n} s.times = t return s.on } // Once specifies stubs only once before stop func (s *StubReturn) Once() *StubOn { return s.Times(1) } golang-github-ysmood-got-0.40.0/lib/utils/000077500000000000000000000000001520712071400203265ustar00rootroot00000000000000golang-github-ysmood-got-0.40.0/lib/utils/utils.go000066400000000000000000000041611520712071400220170ustar00rootroot00000000000000// Package utils ... package utils import ( "reflect" "strings" "time" "github.com/ysmood/gop" ) var float64Type = reflect.TypeOf(0.0) // SmartCompare returns the float value of x minus y. // If x and y are numerical types, the result will be the subtraction between them, such as x is int(1), y is float64(1.2), // the result will be -0.2 . time.Time is also a numerical value. // If x or y are not numerical types, both of them will be converted to string format of its value type, the result will be // the strings.Compare result between them, such as x is int(1), y is "a", the result will be 1 . func SmartCompare(x, y interface{}) float64 { _, xNil := IsNil(x) _, yNil := IsNil(y) if xNil && yNil { return 0 } if reflect.DeepEqual(x, y) { return 0 } if x != nil && y != nil { xVal := reflect.Indirect(reflect.ValueOf(x)) yVal := reflect.Indirect(reflect.ValueOf(y)) if xVal.Type().ConvertibleTo(float64Type) && yVal.Type().ConvertibleTo(float64Type) { return xVal.Convert(float64Type).Float() - yVal.Convert(float64Type).Float() } if xt, ok := xVal.Interface().(time.Time); ok { if yt, ok := yVal.Interface().(time.Time); ok { return float64(xt.Sub(yt)) } } } return Compare(x, y) } // Compare returns the float value of x minus y func Compare(x, y interface{}) float64 { return float64(strings.Compare(gop.Plain(x), gop.Plain(y))) } // ToInterfaces convertor func ToInterfaces(vs []reflect.Value) []interface{} { out := []interface{}{} for _, v := range vs { out = append(out, v.Interface()) } return out } // ToValues convertor func ToValues(vs []interface{}) []reflect.Value { out := []reflect.Value{} for _, v := range vs { out = append(out, reflect.ValueOf(v)) } return out } // IsNil returns true, true if the value is nilable and is nil. func IsNil(x interface{}) (bool, bool) { if x == nil { return true, true } val := reflect.ValueOf(x) k := val.Kind() nilable := k == reflect.Chan || k == reflect.Func || k == reflect.Interface || k == reflect.Map || k == reflect.Ptr || k == reflect.Slice if nilable { return true, val.IsNil() } return false, false } golang-github-ysmood-got-0.40.0/lib/utils/utils_test.go000066400000000000000000000022121520712071400230510ustar00rootroot00000000000000package utils_test import ( "fmt" "testing" "time" "github.com/ysmood/got/lib/utils" ) func TestSmartCompare(t *testing.T) { now := time.Now() circular := map[int]interface{}{} circular[0] = circular fn := func() {} fn2 := func() {} ch := make(chan int, 1) ch2 := make(chan int, 1) testCases := []struct { x interface{} y interface{} s interface{} }{ {1, 1, 0.0}, {1, 3.0, -2.0}, {1, "a", 1.0}, {"b", "a", 1.0}, {1, nil, -1.0}, {fn, fn, 0.0}, {fn, fn2, -1.0}, {ch, ch, 0.0}, {ch, ch2, -1.0}, {now.Add(time.Second), now, float64(time.Second)}, {circular, circular, 0.0}, {circular, 0, 1.0}, {map[int]interface{}{1: 1.0}, map[int]interface{}{1: 1}, 1.0}, {[]byte(nil), nil, 0.0}, } for i, c := range testCases { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { s := utils.SmartCompare(c.x, c.y) if s != c.s { t.Fail() t.Log("expect s to be", c.s, "but got", s) } }) } } func TestCompare(t *testing.T) { if utils.Compare(1, 1.0) == 0 { t.Fail() } } func TestOthers(t *testing.T) { vs := utils.ToValues([]interface{}{1}) if utils.ToInterfaces(vs)[0] != 1 { t.Error("fail") } } golang-github-ysmood-got-0.40.0/setup_test.go000066400000000000000000000034351520712071400211530ustar00rootroot00000000000000package got_test import ( "fmt" "sync" "testing" "github.com/ysmood/gop" "github.com/ysmood/got" ) var setup = got.Setup(func(g got.G) { g.Parallel() }) var _ got.Testable = &mock{} type mock struct { sync.Mutex t *testing.T failed bool skipped bool msg string cleanupList []func() recover bool name string } func (m *mock) Name() string { if m.name == "" { return "mock" } return m.name } func (m *mock) Skipped() bool { return m.skipped } func (m *mock) Failed() bool { return m.failed } func (m *mock) Helper() {} func (m *mock) Cleanup(f func()) { m.cleanupList = append([]func(){f}, m.cleanupList...) } func (m *mock) SkipNow() {} func (m *mock) Fail() { m.failed = true } func (m *mock) FailNow() { m.Lock() defer m.Unlock() m.failed = true if !m.recover { panic("fail now") } m.recover = false } func (m *mock) Logf(format string, args ...interface{}) { m.Lock() defer m.Unlock() if m.msg != "" { m.msg += "\n" } m.msg += fmt.Sprintf(format, args...) } func (m *mock) Run(_ string, fn func(*mock)) { fn(m) } func (m *mock) cleanup() { for _, f := range m.cleanupList { f() } m.cleanupList = nil } func (m *mock) check(expected string) { m.t.Helper() m.checkWithStyle(false, expected) } func (m *mock) reset() { m.Lock() defer m.Unlock() m.failed = false m.msg = "" } func (m *mock) checkWithStyle(visualizeStyle bool, expected string) { m.Lock() defer m.Unlock() m.t.Helper() if !m.failed { m.t.Error("should fail") } msg := "" if visualizeStyle { msg = gop.VisualizeANSI(m.msg) } else { msg = gop.StripANSI(m.msg) } if msg != expected { m.t.Errorf("\n\n[[[msg]]]\n\n%s\n\n[[[doesn't equal]]]\n\n%s\n\n", msg, expected) } m.failed = false m.msg = "" } golang-github-ysmood-got-0.40.0/snapshots.go000066400000000000000000000045561520712071400210030ustar00rootroot00000000000000package got import ( "os" "path/filepath" "regexp" "strings" "github.com/ysmood/got/lib/utils" ) const snapshotJSONExt = ".json" type snapshot struct { value any used bool } func (g G) snapshotsDir() string { return filepath.Join(".got", "snapshots", escapeFileName(g.Name())) } func (g G) loadSnapshots() { paths, err := filepath.Glob(filepath.Join(g.snapshotsDir(), "*"+snapshotJSONExt)) g.E(err) for _, path := range paths { g.snapshots.Store(path, snapshot{g.JSON(g.Read(path)), false}) } g.Cleanup(func() { if g.Failed() { return } g.snapshots.Range(func(path, data interface{}) bool { s := data.(snapshot) if !s.used { g.E(os.Remove(path.(string))) } return true }) }) } // Snapshot asserts that x equals the snapshot with the specified name, name should be unique under the same test case. // It will create a new snapshot file if the name is not found. // The snapshot file will be saved to ".got/snapshots/{TEST_NAME}". // To update the snapshot, just change the name of the snapshot or remove the corresponding snapshot file. // It will auto-remove the unused snapshot files after the test. // The snapshot files should be version controlled. // The format of the snapshot file is json. func (g G) Snapshot(name string, x interface{}) { g.Helper() path := filepath.Join(g.snapshotsDir(), escapeFileName(name)+snapshotJSONExt) if data, ok := g.snapshots.Load(path); ok { s := data.(snapshot) xVal := g.JSON(g.ToJSON(x).Bytes()) if utils.SmartCompare(xVal, s.value) == 0 { g.snapshots.Store(path, snapshot{x, true}) } else { g.Assertions.err(AssertionEq, xVal, s.value) } return } g.snapshots.Store(path, snapshot{x, true}) g.Cleanup(func() { g.E(os.MkdirAll(g.snapshotsDir(), 0755)) g.E(os.WriteFile(path, g.ToJSON(x).Bytes(), 0644)) }) } func escapeFileName(fileName string) string { // Define the invalid characters for both Windows and Unix invalidChars := `< > : " / \ | ? *` // Replace the invalid characters with an underscore regex := "[" + regexp.QuoteMeta(invalidChars) + "]" escapedFileName := regexp.MustCompile(regex).ReplaceAllString(fileName, "_") // Remove any leading or trailing spaces or dots escapedFileName = strings.Trim(escapedFileName, " .") // Remove consecutive dots escapedFileName = regexp.MustCompile(`\.{2,}`).ReplaceAllString(escapedFileName, ".") return escapedFileName } golang-github-ysmood-got-0.40.0/snapshots_test.go000066400000000000000000000027641520712071400220410ustar00rootroot00000000000000package got_test import ( "os" "path/filepath" "testing" "github.com/ysmood/gop" "github.com/ysmood/got" ) func TestSnapshots(t *testing.T) { g := got.T(t) type C struct { Val int } g.Snapshot("a", "ok") g.Snapshot("b", 1) g.Snapshot("b", 1) g.Snapshot("c", C{10}) g.Run("sub", func(g got.G) { g.Snapshot("d", "ok") }) m := &mock{t: t, name: t.Name()} gm := got.New(m) gm.Snapshot("a", "ok") gm.Snapshot("a", "no") m.check(`"no" ⦗not ==⦘ "ok"`) gm.Snapshot("a", map[int]int{1: 2}) g.Has(m.msg, "diff chunk") m.reset() gm.ErrorHandler = got.NewDefaultAssertionError(gop.ThemeNone, nil) gm.Snapshot("a", "no") m.checkWithStyle(true, `"no" ⦗not ==⦘ "ok"`) } func TestSnapshotsCreate(t *testing.T) { path := filepath.FromSlash(".got/snapshots/TestSnapshotsCreate/a.json") err := os.RemoveAll(path) if err != nil { panic(err) } g := got.T(t) g.Cleanup(func() { g.True(g.PathExists(path)) }) g.Snapshot("a", "ok") } func TestSnapshotsNotUsed(t *testing.T) { path := filepath.FromSlash(".got/snapshots/TestSnapshotsNotUsed/a.json") g := got.T(t) g.WriteFile(path, []byte(`1`)) m := &mock{t: t, name: t.Name()} got.New(m) m.cleanup() g.False(g.PathExists(path)) } func TestSnapshotsNotUsedWhenFailure(t *testing.T) { path := filepath.FromSlash(".got/snapshots/TestSnapshotsNotUsedWhenFailure/a.json") g := got.T(t) g.WriteFile(path, []byte(`1`)) m := &mock{t: t, name: t.Name()} gm := got.New(m) gm.Fail() m.cleanup() g.True(g.PathExists(path)) } golang-github-ysmood-got-0.40.0/utils.go000066400000000000000000000173061520712071400201160ustar00rootroot00000000000000package got import ( "bytes" "context" "crypto/rand" "encoding/hex" "encoding/json" "fmt" "io" "io/fs" "math/big" "os" "path/filepath" "reflect" "sync" "text/template" "time" ) // Context helper type Context struct { context.Context Cancel func() } // Utils for commonly used methods type Utils struct { Testable } // Fatal is the same as [testing.common.Fatal] func (ut Utils) Fatal(args ...interface{}) { ut.Helper() ut.Log(args...) ut.FailNow() } // Fatalf is the same as [testing.common.Fatalf] func (ut Utils) Fatalf(format string, args ...interface{}) { ut.Helper() ut.Logf(format, args...) ut.FailNow() } // Log is the same as [testing.common.Log] func (ut Utils) Log(args ...interface{}) { ut.Helper() ut.Logf("%s", fmt.Sprintln(args...)) } // Error is the same as [testing.common.Error] func (ut Utils) Error(args ...interface{}) { ut.Helper() ut.Log(args...) ut.Fail() } // Errorf is the same as [testing.common.Errorf] func (ut Utils) Errorf(format string, args ...interface{}) { ut.Helper() ut.Logf(format, args...) ut.Fail() } // Skipf is the same as [testing.common.Skipf] func (ut Utils) Skipf(format string, args ...interface{}) { ut.Helper() ut.Logf(format, args...) ut.SkipNow() } // Skip is the same as [testing.common.Skip] func (ut Utils) Skip(args ...interface{}) { ut.Helper() ut.Log(args...) ut.SkipNow() } // Go runs f in a goroutine and wait for it to finish before the test ends. func (ut Utils) Go(f func()) { wait := make(chan struct{}) ut.Cleanup(func() { <-wait }) go func() { f() wait <- struct{}{} }() } // Run f as a sub-test func (ut Utils) Run(name string, f func(t G)) bool { runVal := reflect.ValueOf(ut.Testable).MethodByName("Run") return runVal.Call([]reflect.Value{ reflect.ValueOf(name), reflect.MakeFunc(runVal.Type().In(1), func(args []reflect.Value) []reflect.Value { f(New(args[0].Interface().(Testable))) return nil }), })[0].Interface().(bool) } // Parallel is the same as [testing.T.Parallel] func (ut Utils) Parallel() Utils { reflect.ValueOf(ut.Testable).MethodByName("Parallel").Call(nil) return ut } // DoAfter d duration if the test is still running func (ut Utils) DoAfter(d time.Duration, do func()) (cancel func()) { ctx := ut.Context() go func() { ut.Helper() tmr := time.NewTimer(d) defer tmr.Stop() select { case <-ctx.Done(): case <-tmr.C: do() } }() return ctx.Cancel } // PanicAfter d duration if the test is still running func (ut Utils) PanicAfter(d time.Duration) (cancel func()) { return ut.DoAfter(d, func() { ut.Helper() panicWithTrace(fmt.Sprintf("%s timeout after %v", ut.Name(), d)) }) } // Context that will be canceled after the test func (ut Utils) Context() Context { ctx, cancel := context.WithCancel(context.Background()) ut.Cleanup(cancel) return Context{ctx, cancel} } // Timeout context that will be canceled after the test func (ut Utils) Timeout(d time.Duration) Context { ctx, cancel := context.WithTimeout(context.Background(), d) ut.Cleanup(cancel) return Context{ctx, cancel} } // RandStr generates a random string with the specified length func (ut Utils) RandStr(l int) string { ut.Helper() b := ut.RandBytes((l + 1) / 2) return hex.EncodeToString(b)[:l] } // RandInt generates a random integer within [min, max) func (ut Utils) RandInt(min, max int) int { ut.Helper() n, err := rand.Int(rand.Reader, big.NewInt(int64(max-min))) ut.err(err) return int(n.Int64()) + min } // RandBytes generates a random byte array with the specified length func (ut Utils) RandBytes(l int) []byte { ut.Helper() b := make([]byte, l) _, err := rand.Read(b) ut.err(err) return b } // Render template. It will use [Utils.Read] to read the value as the template string. func (ut Utils) Render(value interface{}, data interface{}) *bytes.Buffer { ut.Helper() out := bytes.NewBuffer(nil) t := template.New("") t, err := t.Parse(ut.Read(value).String()) ut.err(err) ut.err(t.Execute(out, data)) return out } // WriteFile at path with content, it uses [Utils.Open] to open the file. func (ut Utils) WriteFile(path string, content interface{}) { f := ut.Open(true, path) defer func() { ut.err(f.Close()) }() ut.Write(content)(f) } // PathExists checks if path exists func (ut Utils) PathExists(path string) bool { _, err := os.Stat(path) return err == nil } // Chdir is like [os.Chdir] but will restore the dir after test. func (ut Utils) Chdir(dir string) { ut.Helper() cwd, err := os.Getwd() ut.err(err) ut.err(os.Chdir(dir)) ut.Cleanup(func() { _ = os.Chdir(cwd) }) } // Setenv is like [os.Setenv] but will restore the env after test. func (ut Utils) Setenv(key, value string) { ut.Helper() old := os.Getenv(key) ut.err(os.Setenv(key, value)) ut.Cleanup(func() { _ = os.Setenv(key, old) }) } // MkdirAll is like [os.MkdirAll] but will remove the dir after test and fail the test if error. // The default perm is 0755. func (ut Utils) MkdirAll(perm fs.FileMode, path string) { if perm == 0 { perm = 0755 } dir := filepath.Dir(path) if !ut.PathExists(dir) { ut.MkdirAll(perm, dir) } if ut.PathExists(path) { return } ut.err(os.Mkdir(path, perm)) ut.Cleanup(func() { _ = os.RemoveAll(path) }) } // Open a file. Override it if create is true. Directories will be auto-created. // If the directory and file doesn't exist, it will be removed after the test. func (ut Utils) Open(create bool, path string) (f *os.File) { ut.Helper() var err error if create { ut.MkdirAll(0, filepath.Dir(path)) f, err = os.Create(path) if err == nil { ut.Cleanup(func() { _ = os.Remove(path) }) } } else { f, err = os.Open(path) } ut.err(err) return f } // Read all from value. If the value is string and it's a file path, // the file content will be read, or the string will be returned. // If the value is [io.Reader], the reader will be read. If the value is []byte, the value will be returned. // Others will be converted to string and returned. func (ut Utils) Read(value interface{}) *bytes.Buffer { ut.Helper() var r io.Reader switch v := value.(type) { case string: if !ut.PathExists(v) { return bytes.NewBufferString(v) } f := ut.Open(false, v) defer func() { ut.err(f.Close()) }() r = f case io.Reader: r = v case []byte: return bytes.NewBuffer(v) default: return bytes.NewBufferString(fmt.Sprintf("%v", v)) } b := bytes.NewBuffer(nil) _, err := io.Copy(b, r) ut.err(err) return b } // JSON from string, []byte, or io.Reader func (ut Utils) JSON(src interface{}) (v interface{}) { ut.Helper() var b []byte switch obj := src.(type) { case []byte: b = obj case string: b = []byte(obj) case io.Reader: var err error b, err = io.ReadAll(obj) ut.err(err) } ut.err(json.Unmarshal(b, &v)) return } // ToJSON convert obj to JSON bytes func (ut Utils) ToJSON(obj interface{}) *bytes.Buffer { ut.Helper() b, err := json.MarshalIndent(obj, "", " ") ut.err(err) return bytes.NewBuffer(b) } // ToJSONString convert obj to JSON string func (ut Utils) ToJSONString(obj interface{}) string { ut.Helper() return ut.ToJSON(obj).String() } // Write obj to the writer. Encode obj to []byte and cache it for writer. // If obj is not []byte, string, or [io.Reader], it will be encoded as JSON. func (ut Utils) Write(obj interface{}) (writer func(io.Writer)) { lock := sync.Mutex{} var cache []byte return func(w io.Writer) { lock.Lock() defer lock.Unlock() ut.Helper() if cache != nil { _, err := w.Write(cache) ut.err(err) return } buf := bytes.NewBuffer(nil) w = io.MultiWriter(buf, w) var err error switch v := obj.(type) { case []byte: _, err = w.Write(v) case string: _, err = w.Write([]byte(v)) case io.Reader: _, err = io.Copy(w, v) default: err = json.NewEncoder(w).Encode(v) } ut.err(err) cache = buf.Bytes() } } golang-github-ysmood-got-0.40.0/utils_private_test.go000066400000000000000000000006141520712071400227010ustar00rootroot00000000000000package got import ( "testing" "time" ) func TestPanicAfter(t *testing.T) { ut := New(t) ut.Panic(func() { panicWithTrace(1) }) wait := make(chan struct{}) old := panicWithTrace panicWithTrace = func(v interface{}) { ut.Eq(v, "TestPanicAfter timeout after 1ns") close(wait) } defer func() { panicWithTrace = old }() ut.PanicAfter(1) time.Sleep(time.Millisecond) <-wait } golang-github-ysmood-got-0.40.0/utils_req.go000066400000000000000000000055201520712071400207600ustar00rootroot00000000000000package got import ( "bytes" "context" "encoding/json" "io" "mime" "net/http" "path/filepath" ) // ReqMIME option type, it should be like ".json", "test.json", "a/b/c.jpg", etc type ReqMIME string // Req is a helper method to send http request. It will handle errors automatically, so you don't need to check errors. // The method is the http method, default value is "GET". // If an option is [http.Header], it will be used as the request header. // If an option is [ReqMIME], it will be used to set the Content-Type header. // If an option is [context.Context], it will be used as the request context. // Other option type will be treat as request body, it will be encoded by [Utils.Write]. // Some request examples: // // Req("GET", "http://example.com") // Req("GET", "http://example.com", context.TODO()) // Req("POST", "http://example.com", map[string]any{"a": 1}) // Req("POST", "http://example.com", http.Header{"Host": "example.com"}, ReqMIME(".json"), map[string]any{"a": 1}) func (ut Utils) Req(method, url string, options ...interface{}) *ResHelper { ut.Helper() header := http.Header{} var host string var contentType string var body io.Reader ctx := context.Background() for _, item := range options { switch val := item.(type) { case http.Header: host = val.Get("Host") val.Del("Host") header = val case ReqMIME: contentType = mime.TypeByExtension(filepath.Ext(string(val))) case context.Context: ctx = val default: buf := bytes.NewBuffer(nil) ut.Write(val)(buf) body = buf } } req, err := http.NewRequestWithContext(ctx, method, url, body) if err != nil { return &ResHelper{ut, nil, err} } if header != nil { req.Header = header } req.Host = host req.Header.Set("Content-Type", contentType) res, err := http.DefaultClient.Do(req) return &ResHelper{ut, res, err} } // ResHelper of the request type ResHelper struct { ut Utils *http.Response err error } // Bytes parses body as [*bytes.Buffer] and returns the result func (res *ResHelper) Bytes() *bytes.Buffer { res.ut.Helper() res.ut.err(res.err) return res.ut.Read(res.Body) } // String parses body as string and returns the result func (res *ResHelper) String() string { res.ut.Helper() return res.Bytes().String() } // JSON parses body as json and returns the result func (res *ResHelper) JSON() (v interface{}) { res.ut.Helper() res.ut.err(res.err) return res.ut.JSON(res.Body) } // Unmarshal body to v as json, it's like [json.Unmarshal]. func (res *ResHelper) Unmarshal(v interface{}) { res.ut.Helper() res.ut.err(json.Unmarshal(res.Bytes().Bytes(), v)) } // Err of request protocol func (res *ResHelper) Err() error { return res.err } func (ut Utils) err(err error) { ut.Helper() if err != nil { ut.Fatal(err) } } // there no way to stop a blocking test from outside var panicWithTrace = func(v interface{}) { panic(v) } golang-github-ysmood-got-0.40.0/utils_serve.go000066400000000000000000000041151520712071400213140ustar00rootroot00000000000000package got import ( "mime" "net" "net/http" "net/url" "os" "path/filepath" "strings" ) // Serve http on a random port. The server will be auto-closed after the test. func (ut Utils) Serve() *Router { ut.Helper() return ut.ServeWith("tcp4", "127.0.0.1:0") } // ServeWith specified network and address func (ut Utils) ServeWith(network, address string) *Router { ut.Helper() mux := http.NewServeMux() srv := &http.Server{Handler: mux} l, err := net.Listen(network, address) ut.err(err) ut.Cleanup(func() { _ = srv.Close() }) go func() { _ = srv.Serve(l) }() u, err := url.Parse("http://" + l.Addr().String()) ut.err(err) return &Router{ut, u, srv, mux} } // Router of a http server type Router struct { ut Utils HostURL *url.URL Server *http.Server Mux *http.ServeMux } // URL will prefix the path with the server's host func (rt *Router) URL(path ...string) string { p := strings.Join(path, "") if !strings.HasPrefix(p, "/") { p = "/" + p } return rt.HostURL.String() + p } // Route on the pattern. Check the doc of [http.ServeMux] for the syntax of pattern. // It will use [Utils.HandleHTTP] to handle each request. func (rt *Router) Route(pattern, file string, value ...interface{}) *Router { h := rt.ut.HandleHTTP(file, value...) rt.Mux.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) { h(w, r) }) return rt } // HandleHTTP handles a request. If file exists serve the file content. The file will be used to set the Content-Type header. // If the file doesn't exist, the value will be encoded by [Utils.Write] and used as the response body. func (ut Utils) HandleHTTP(file string, value ...interface{}) func(http.ResponseWriter, *http.Request) { var obj interface{} if len(value) > 1 { obj = value } else if len(value) == 1 { obj = value[0] } write := ut.Write(obj) return func(w http.ResponseWriter, r *http.Request) { if _, err := os.Stat(file); err == nil { http.ServeFile(w, r, file) return } if obj == nil { return } w.Header().Add("Content-Type", mime.TypeByExtension(filepath.Ext(file))) write(w) } } golang-github-ysmood-got-0.40.0/utils_test.go000066400000000000000000000102571520712071400211530ustar00rootroot00000000000000package got_test import ( "bytes" "fmt" "io" "net/http" "os" "sync" "testing" "time" "github.com/ysmood/got" ) func init() { got.DefaultFlags("parallel=3") } func TestHelper(t *testing.T) { ut := got.T(t) ctx := ut.Context() ctx.Cancel() <-ctx.Done() <-ut.Timeout(0).Done() ut.Len(ut.RandStr(10), 10) ut.Lt(ut.RandInt(0, 1), 1) ut.Gt(ut.RandInt(-2, -1), -3) f := ut.Open(true, "tmp/test.txt") ut.Nil(os.Stat("tmp/test.txt")) ut.Write(1)(f) ut.Nil(f.Close()) f = ut.Open(false, "tmp/test.txt") ut.Eq(ut.JSON(f), 1) ut.Setenv(ut.RandStr(8), ut.RandStr(8)) p := fmt.Sprintf("tmp/%s/b/c", ut.RandStr(8)) ut.MkdirAll(0, p) ut.MkdirAll(0, "tmp") ut.PathExists(p) s := ut.RandStr(16) ut.WriteFile("tmp/test.txt", s) ut.Eq(ut.Read("tmp/test.txt").String(), s) ut.Eq(ut.Read(123).String(), "123") ut.Eq(ut.Read([]byte("ok")).String(), "ok") ut.Eq(ut.Render("{{.}}", 10).String(), "10") ut.Eq(ut.JSON([]byte("1")), 1) ut.Eq(ut.JSON("true"), true) ut.Eq(ut.ToJSONString(10), "10") buf := bytes.NewBuffer(nil) ut.Write([]byte("ok"))(buf) ut.Eq(buf.String(), "ok") ut.Run("subtest", func(t got.G) { t.Eq(1, 1) }) ut.Eq(got.Parallel(), 3) { s := ut.Serve() s.Route("/", ".txt") s.Route("/file", "go.mod") s.Route("/a", ".html", "ok") s.Route("/b", ".json", "ok", 1) f, err := os.Open("go.mod") ut.E(err) s.Route("/c", ".html", f) s.Mux.HandleFunc("/d", func(_ http.ResponseWriter, r *http.Request) { ut.Eq(ut.Read(r.Body).String(), "1\n") }) s.Mux.HandleFunc("/f", func(_ http.ResponseWriter, r *http.Request) { ut.Has(r.Header.Get("Content-Type"), "application/json") ut.Eq(r.Header.Get("Test-Header"), "ok") ut.Eq(r.Host, "test.com") }) s.Mux.HandleFunc("/timeout", func(_ http.ResponseWriter, r *http.Request) { <-r.Context().Done() }) ut.Eq(ut.Req("", s.URL()).String(), "") ut.Has(ut.Req("", s.URL("/file")).String(), "ysmood/got") ut.Eq(ut.Req("", s.URL("/a")).String(), "ok") ut.Eq(ut.Req("", s.URL("a")).String(), "ok") ut.Has(ut.Req("", s.URL("/c")).String(), "ysmood/got") ut.Req(http.MethodPost, s.URL("/d"), 1) ut.Req(http.MethodPost, s.URL("/f"), http.Header{"Test-Header": {"ok"}, "Host": {"test.com"}}, got.ReqMIME(".json"), 1) ut.Has(ut.Req("", s.URL("/timeout"), ut.Timeout(100*time.Millisecond)).Err().Error(), "context deadline exceeded") ut.Has(ut.Req("", string(rune(0x7f))).Err().Error(), `invalid control character in URL`) res := ut.Req("", s.URL("/b")) ut.Eq(res.JSON(), []interface{}{"ok", float64(1)}) ut.Has(res.Header.Get("Content-Type"), "application/json") res = ut.Req("", s.URL("/b")) var v []interface{} res.Unmarshal(&v) ut.Eq(v, []interface{}{"ok", 1.0}) } ut.DoAfter(time.Hour, func() {}) m := &mock{t: t} mut := got.New(m) m.msg = "" mut.Log("a", 1) ut.Eq(m.msg, "a 1\n") m.msg = "" ut.Panic(func() { buf := bytes.NewBufferString("a") mut.JSON(buf) }) ut.Eq(m.msg, "invalid character 'a' looking for beginning of value\n") m.msg = "" ut.Panic(func() { mut.Fatal("test skip") }) ut.Eq(m.msg, "test skip\n") m.msg = "" ut.Panic(func() { mut.Fatalf("test skip") }) ut.Eq(m.msg, "test skip") m.msg = "" mut.Error("test skip") ut.Eq(m.msg, "test skip\n") m.msg = "" mut.Errorf("test skip") ut.Eq(m.msg, "test skip") m.msg = "" mut.Skip("test skip") ut.Eq(m.msg, "test skip\n") m.msg = "" mut.Skipf("test skip") ut.Eq(m.msg, "test skip") } func TestServe(t *testing.T) { ut := setup(t) key := ut.RandStr(8) s := ut.Serve().Route("/", "", key) count := 30 wg := sync.WaitGroup{} wg.Add(count) request := func() { req, err := http.NewRequest(http.MethodGet, s.URL(), nil) ut.E(err) res, err := http.DefaultClient.Do(req) ut.E(err) b, err := io.ReadAll(res.Body) ut.E(err) ut.Eq(string(b), key) wg.Done() } for i := 0; i < count; i++ { go request() } wg.Wait() } func TestPathExists(t *testing.T) { g := got.T(t) g.False(g.PathExists("not-exists")) g.False(g.PathExists("*!")) g.True(g.PathExists("lib")) } func TestChdir(t *testing.T) { g := got.T(t) g.Chdir("lib") g.PathExists("diff") } func TestGo(t *testing.T) { g := got.T(t) c := g.Count(1) g.Go(func() { time.Sleep(time.Millisecond * 30) c() }) }