mirror of
https://gitee.com/johng/gf
synced 2026-07-04 21:03:13 +08:00
This pull request standardizes the use of the Go 1.18+ `any` type alias
instead of `interface{}` throughout the codebase. The change improves
code readability and aligns with modern Go best practices. The update
touches many files, including core data structures, code generation
templates, logging utilities, and test data, ensuring consistency across
all usages.
**Type alias migration to `any`:**
* Replaced all instances of `interface{}` with `any` in core data
structures such as `garray` and in generated model structs (e.g.,
`TableUser`, `User1`, `User2`) to modernize type usage.
[[1]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L31-R31)
[[2]](diffhunk://#diff-6c19859cb32c7516ea95ddc8f8235460818eb2f24d2204308e0d9e1b19e7d90fL15-R19)
[[3]](diffhunk://#diff-a15ba2f5e830b4833c47b902515a4f9e5a4f83a3707698f3229b307ec3776b41L15-R18)
[[4]](diffhunk://#diff-52e0837e84d49221d1b810d88fdf78221f36cffcd664fb42f8aba49a79b974dcL15-R19)
[[5]](diffhunk://#diff-11c3457d1a23a4ca6ecd00d6b856289774936b6a708384cf03aff164044e7546L15-R19)
[[6]](diffhunk://#diff-2cff9cf8e6a0cc34087326d8c8149c3bbaf74c76fdbdf5a73daed13cc04249e1L15-R19)
* Updated function signatures, method parameters, and return types from
`interface{}` to `any` in various parts of the codebase, including code
generation, service logic, and logging utilities (e.g., `mlog`).
[[1]](diffhunk://#diff-175edfeea54490b8fe4e18ffcbea5835efaf8f0b8acf623359073987cae7eb76L48-R55)
[[2]](diffhunk://#diff-2b1953fb78cf3593d8c2c7d911e95b65fd0b847c30ed0b4d167d16fe6d781235L54-R74)
[[3]](diffhunk://#diff-e001b7a4b63603b9b14f00de78a4d570bb76c5f57d856a24643f071032e12356L66-R73)
[[4]](diffhunk://#diff-5582954e8a9983988dc8854ad82067fb2ac6269b988e07357ad8db1dfec5f1a0L39-R41)
[[5]](diffhunk://#diff-c5d51d56f487779a2b6207c7ad26c7a20bbadcc846ce094fe60ab4cabff58c51L107-R107)
[[6]](diffhunk://#diff-f96e6a9fdb416eb1804ceaba1fe0ac637bff22c43837f8bb849c2366ce72d4a1L116-R121)
[[7]](diffhunk://#diff-f94c83a1b08ae060d9346f4a6031fc4a7b9a0b894e02d9afaa09018b6598eac0L112-R112)
[[8]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L36-R36)
[[9]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L74-R74)
[[10]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L96-R96)
**Generated code and templates:**
* Adjusted generated files and code generation templates to output `any`
instead of `interface{}` for relevant struct fields and function
signatures, ensuring that new code generation aligns with the updated
convention.
[[1]](diffhunk://#diff-6c19859cb32c7516ea95ddc8f8235460818eb2f24d2204308e0d9e1b19e7d90fL15-R19)
[[2]](diffhunk://#diff-a15ba2f5e830b4833c47b902515a4f9e5a4f83a3707698f3229b307ec3776b41L15-R18)
[[3]](diffhunk://#diff-52e0837e84d49221d1b810d88fdf78221f36cffcd664fb42f8aba49a79b974dcL15-R19)
[[4]](diffhunk://#diff-11c3457d1a23a4ca6ecd00d6b856289774936b6a708384cf03aff164044e7546L15-R19)
[[5]](diffhunk://#diff-2cff9cf8e6a0cc34087326d8c8149c3bbaf74c76fdbdf5a73daed13cc04249e1L15-R19)
[[6]](diffhunk://#diff-175edfeea54490b8fe4e18ffcbea5835efaf8f0b8acf623359073987cae7eb76L48-R55)
[[7]](diffhunk://#diff-e001b7a4b63603b9b14f00de78a4d570bb76c5f57d856a24643f071032e12356L66-R73)
[[8]](diffhunk://#diff-5582954e8a9983988dc8854ad82067fb2ac6269b988e07357ad8db1dfec5f1a0L39-R41)
**Container and utility updates:**
* Refactored the `garray` container implementation and related
constructors/methods to use `[]any` instead of `[]interface{}`, along
with corresponding function signatures.
[[1]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L31-R31)
[[2]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L52-R52)
[[3]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L62-R62)
[[4]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L73-R86)
[[5]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L96-R97)
[[6]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L107-R114)
[[7]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L124-R124)
[[8]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L135-R143)
[[9]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L167-R167)
These changes collectively modernize the codebase and prepare it for
future Go developments by using the idiomatic `any` type.
438 lines
11 KiB
Go
438 lines
11 KiB
Go
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
|
//
|
|
// This Source Code Form is subject to the terms of the MIT License.
|
|
// If a copy of the MIT was not distributed with this file,
|
|
// You can obtain one at https://github.com/gogf/gf.
|
|
|
|
package ghttp_test
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gogf/gf/v2/container/garray"
|
|
"github.com/gogf/gf/v2/encoding/gbase64"
|
|
"github.com/gogf/gf/v2/frame/g"
|
|
"github.com/gogf/gf/v2/net/ghttp"
|
|
"github.com/gogf/gf/v2/test/gtest"
|
|
"github.com/gogf/gf/v2/util/guid"
|
|
)
|
|
|
|
func Test_Request_IsFileRequest(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
s := g.Server(guid.S())
|
|
s.Group("/", func(group *ghttp.RouterGroup) {
|
|
group.ALL("/", func(r *ghttp.Request) {
|
|
r.Response.Write(r.IsFileRequest())
|
|
})
|
|
})
|
|
s.SetDumpRouterMap(false)
|
|
s.Start()
|
|
defer s.Shutdown()
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
c := g.Client()
|
|
c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
|
|
|
t.Assert(c.GetContent(ctx, "/"), false)
|
|
})
|
|
}
|
|
|
|
func Test_Request_IsAjaxRequest(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
s := g.Server(guid.S())
|
|
s.Group("/", func(group *ghttp.RouterGroup) {
|
|
group.ALL("/", func(r *ghttp.Request) {
|
|
r.Response.Write(r.IsAjaxRequest())
|
|
})
|
|
})
|
|
s.SetDumpRouterMap(false)
|
|
s.Start()
|
|
defer s.Shutdown()
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
c := g.Client()
|
|
c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
|
|
|
t.Assert(c.GetContent(ctx, "/"), false)
|
|
})
|
|
}
|
|
|
|
func Test_Request_GetClientIp(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
s := g.Server(guid.S())
|
|
s.Group("/", func(group *ghttp.RouterGroup) {
|
|
group.ALL("/", func(r *ghttp.Request) {
|
|
r.Response.Write(r.GetClientIp())
|
|
})
|
|
})
|
|
s.SetDumpRouterMap(false)
|
|
s.Start()
|
|
defer s.Shutdown()
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
c := g.Client()
|
|
c.SetHeader("X-Forwarded-For", "192.168.0.1")
|
|
c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
|
|
|
t.Assert(c.GetContent(ctx, "/"), "192.168.0.1")
|
|
})
|
|
}
|
|
|
|
func Test_Request_GetUrl(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
s := g.Server(guid.S())
|
|
s.Group("/", func(group *ghttp.RouterGroup) {
|
|
group.ALL("/", func(r *ghttp.Request) {
|
|
r.Response.Write(r.GetUrl())
|
|
})
|
|
})
|
|
s.SetDumpRouterMap(false)
|
|
s.Start()
|
|
defer s.Shutdown()
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
c := g.Client()
|
|
c.SetHeader("X-Forwarded-Proto", "https")
|
|
c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
|
|
|
t.Assert(c.GetContent(ctx, "/"), fmt.Sprintf("https://127.0.0.1:%d/", s.GetListenedPort()))
|
|
})
|
|
}
|
|
|
|
func Test_Request_GetReferer(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
s := g.Server(guid.S())
|
|
s.Group("/", func(group *ghttp.RouterGroup) {
|
|
group.ALL("/", func(r *ghttp.Request) {
|
|
r.Response.Write(r.GetReferer())
|
|
})
|
|
})
|
|
s.SetDumpRouterMap(false)
|
|
s.Start()
|
|
defer s.Shutdown()
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
c := g.Client()
|
|
c.SetHeader("Referer", "Referer")
|
|
c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
|
|
|
t.Assert(c.GetContent(ctx, "/"), "Referer")
|
|
})
|
|
}
|
|
|
|
func Test_Request_GetServeHandler(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
s := g.Server(guid.S())
|
|
s.Group("/", func(group *ghttp.RouterGroup) {
|
|
group.ALL("/", func(r *ghttp.Request) {
|
|
r.Response.Write(r.GetServeHandler() != nil)
|
|
})
|
|
})
|
|
s.SetDumpRouterMap(false)
|
|
s.Start()
|
|
defer s.Shutdown()
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
c := g.Client()
|
|
c.SetHeader("Referer", "Referer")
|
|
c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
|
|
|
t.Assert(c.GetContent(ctx, "/"), true)
|
|
})
|
|
}
|
|
|
|
func Test_Request_BasicAuth(t *testing.T) {
|
|
const (
|
|
user = "root"
|
|
pass = "123456"
|
|
wrongPass = "12345"
|
|
)
|
|
|
|
s := g.Server(guid.S())
|
|
s.Group("/", func(group *ghttp.RouterGroup) {
|
|
group.ALL("/auth1", func(r *ghttp.Request) {
|
|
r.BasicAuth(user, pass, "tips")
|
|
})
|
|
group.ALL("/auth2", func(r *ghttp.Request) {
|
|
r.BasicAuth(user, pass)
|
|
})
|
|
})
|
|
s.SetDumpRouterMap(false)
|
|
s.Start()
|
|
defer s.Shutdown()
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
gtest.C(t, func(t *gtest.T) {
|
|
c := g.Client()
|
|
c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
|
|
|
rsp, err := c.Get(ctx, "/auth1")
|
|
t.AssertNil(err)
|
|
t.Assert(rsp.Header.Get("WWW-Authenticate"), "Basic realm=\"tips\"")
|
|
t.Assert(rsp.StatusCode, http.StatusUnauthorized)
|
|
|
|
rsp, err = c.SetHeader("Authorization", user+pass).Get(ctx, "/auth1")
|
|
t.AssertNil(err)
|
|
t.Assert(rsp.StatusCode, http.StatusForbidden)
|
|
|
|
rsp, err = c.SetHeader("Authorization", "Test "+user+pass).Get(ctx, "/auth1")
|
|
t.AssertNil(err)
|
|
t.Assert(rsp.StatusCode, http.StatusForbidden)
|
|
|
|
rsp, err = c.SetHeader("Authorization", "Basic "+user+pass).Get(ctx, "/auth1")
|
|
t.AssertNil(err)
|
|
t.Assert(rsp.StatusCode, http.StatusForbidden)
|
|
|
|
rsp, err = c.SetHeader("Authorization", "Basic "+gbase64.EncodeString(user+pass)).Get(ctx, "/auth1")
|
|
t.AssertNil(err)
|
|
t.Assert(rsp.StatusCode, http.StatusForbidden)
|
|
|
|
rsp, err = c.SetHeader("Authorization", "Basic "+gbase64.EncodeString(user+":"+wrongPass)).Get(ctx, "/auth1")
|
|
t.AssertNil(err)
|
|
t.Assert(rsp.StatusCode, http.StatusUnauthorized)
|
|
|
|
rsp, err = c.BasicAuth(user, pass).Get(ctx, "/auth1")
|
|
t.AssertNil(err)
|
|
t.Assert(rsp.StatusCode, http.StatusOK)
|
|
|
|
rsp, err = c.Get(ctx, "/auth2")
|
|
t.AssertNil(err)
|
|
t.Assert(rsp.Header.Get("WWW-Authenticate"), "Basic realm=\"Need Login\"")
|
|
t.Assert(rsp.StatusCode, http.StatusUnauthorized)
|
|
})
|
|
}
|
|
|
|
func Test_Request_SetCtx(t *testing.T) {
|
|
type ctxKey string
|
|
const testkey ctxKey = "test"
|
|
s := g.Server(guid.S())
|
|
s.Group("/", func(group *ghttp.RouterGroup) {
|
|
group.Middleware(func(r *ghttp.Request) {
|
|
ctx := context.WithValue(r.Context(), testkey, 1)
|
|
r.SetCtx(ctx)
|
|
r.Middleware.Next()
|
|
})
|
|
group.ALL("/", func(r *ghttp.Request) {
|
|
r.Response.Write(r.Context().Value(testkey))
|
|
})
|
|
})
|
|
s.SetDumpRouterMap(false)
|
|
s.Start()
|
|
defer s.Shutdown()
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
gtest.C(t, func(t *gtest.T) {
|
|
c := g.Client()
|
|
c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
|
|
|
t.Assert(c.GetContent(ctx, "/"), "1")
|
|
})
|
|
}
|
|
|
|
func Test_Request_GetCtx(t *testing.T) {
|
|
type ctxKey string
|
|
const testkey ctxKey = "test"
|
|
s := g.Server(guid.S())
|
|
s.Group("/", func(group *ghttp.RouterGroup) {
|
|
group.Middleware(func(r *ghttp.Request) {
|
|
ctx := context.WithValue(r.GetCtx(), testkey, 1)
|
|
r.SetCtx(ctx)
|
|
r.Middleware.Next()
|
|
})
|
|
group.ALL("/", func(r *ghttp.Request) {
|
|
r.Response.Write(r.Context().Value(testkey))
|
|
})
|
|
})
|
|
s.SetDumpRouterMap(false)
|
|
s.Start()
|
|
defer s.Shutdown()
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
gtest.C(t, func(t *gtest.T) {
|
|
c := g.Client()
|
|
c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
|
|
|
t.Assert(c.GetContent(ctx, "/"), "1")
|
|
})
|
|
}
|
|
|
|
func Test_Request_GetCtxVar(t *testing.T) {
|
|
s := g.Server(guid.S())
|
|
s.Group("/", func(group *ghttp.RouterGroup) {
|
|
group.Middleware(func(r *ghttp.Request) {
|
|
r.Middleware.Next()
|
|
})
|
|
group.GET("/", func(r *ghttp.Request) {
|
|
r.Response.Write(r.GetCtxVar("key", "val"))
|
|
})
|
|
})
|
|
s.SetDumpRouterMap(false)
|
|
s.Start()
|
|
defer s.Shutdown()
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
gtest.C(t, func(t *gtest.T) {
|
|
client := g.Client()
|
|
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
|
|
|
t.Assert(client.GetContent(ctx, "/"), "val")
|
|
})
|
|
}
|
|
|
|
func Test_Request_Form(t *testing.T) {
|
|
type User struct {
|
|
Id int
|
|
Name string
|
|
}
|
|
s := g.Server(guid.S())
|
|
s.Group("/", func(group *ghttp.RouterGroup) {
|
|
group.ALL("/", func(r *ghttp.Request) {
|
|
r.SetForm("key", "val")
|
|
r.Response.Write(r.GetForm("key"))
|
|
})
|
|
group.ALL("/useDef", func(r *ghttp.Request) {
|
|
r.Response.Write(r.GetForm("key", "defVal"))
|
|
})
|
|
group.ALL("/GetFormMap", func(r *ghttp.Request) {
|
|
r.Response.Write(r.GetFormMap(map[string]any{"key": "val"}))
|
|
})
|
|
group.ALL("/GetFormMap1", func(r *ghttp.Request) {
|
|
r.Response.Write(r.GetFormMap(map[string]any{"array": "val"}))
|
|
})
|
|
group.ALL("/GetFormMapStrVar", func(r *ghttp.Request) {
|
|
if r.Get("a") != nil {
|
|
r.Response.Write(r.GetFormMapStrVar()["a"])
|
|
}
|
|
})
|
|
group.ALL("/GetFormStruct", func(r *ghttp.Request) {
|
|
var user User
|
|
if err := r.GetFormStruct(&user); err != nil {
|
|
r.Response.Write(err.Error())
|
|
} else {
|
|
r.Response.Write(user.Name)
|
|
}
|
|
})
|
|
})
|
|
s.SetDumpRouterMap(false)
|
|
s.Start()
|
|
defer s.Shutdown()
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
gtest.C(t, func(t *gtest.T) {
|
|
client := g.Client()
|
|
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
|
|
|
t.Assert(client.GetContent(ctx, "/"), "val")
|
|
t.Assert(client.GetContent(ctx, "/useDef"), "defVal")
|
|
t.Assert(client.PostContent(ctx, "/GetFormMap"), "{\"key\":\"val\"}")
|
|
t.Assert(client.PostContent(ctx, "/GetFormMap", "array[]=1&array[]=2"), "{\"key\":\"val\"}")
|
|
t.Assert(client.PostContent(ctx, "/GetFormMap1", "array[]=1&array[]=2"), "{\"array\":[\"1\",\"2\"]}")
|
|
t.Assert(client.GetContent(ctx, "/GetFormMapStrVar", "a=1&b=2"), nil)
|
|
t.Assert(client.PostContent(ctx, "/GetFormMapStrVar", "a=1&b=2"), `1`)
|
|
t.Assert(client.PostContent(ctx, "/GetFormStruct", g.Map{
|
|
"id": 1,
|
|
"name": "john",
|
|
}), "john")
|
|
})
|
|
}
|
|
|
|
func Test_Request_NeverDoneCtx_Done(t *testing.T) {
|
|
var array = garray.New(true)
|
|
s := g.Server(guid.S())
|
|
s.BindHandler("/done", func(r *ghttp.Request) {
|
|
var (
|
|
ctx = r.Context()
|
|
ticker = time.NewTimer(time.Millisecond * 1500)
|
|
)
|
|
defer ticker.Stop()
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
array.Append(1)
|
|
return
|
|
case <-ticker.C:
|
|
array.Append(1)
|
|
return
|
|
}
|
|
}
|
|
})
|
|
s.SetDumpRouterMap(false)
|
|
s.Start()
|
|
defer s.Shutdown()
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
c := g.Client()
|
|
c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
|
gtest.C(t, func(t *gtest.T) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
|
|
go func() {
|
|
result := c.GetContent(ctx, "/done")
|
|
fmt.Println(result)
|
|
}()
|
|
time.Sleep(time.Millisecond * 100)
|
|
|
|
t.Assert(array.Len(), 0)
|
|
cancel()
|
|
|
|
time.Sleep(time.Millisecond * 500)
|
|
t.Assert(array.Len(), 1)
|
|
})
|
|
}
|
|
|
|
func Test_Request_NeverDoneCtx_NeverDone(t *testing.T) {
|
|
var array = garray.New(true)
|
|
s := g.Server(guid.S())
|
|
s.Use(ghttp.MiddlewareNeverDoneCtx)
|
|
s.BindHandler("/never-done", func(r *ghttp.Request) {
|
|
var (
|
|
ctx = r.Context()
|
|
ticker = time.NewTimer(time.Millisecond * 1500)
|
|
)
|
|
defer ticker.Stop()
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
array.Append(1)
|
|
return
|
|
case <-ticker.C:
|
|
array.Append(1)
|
|
return
|
|
}
|
|
}
|
|
})
|
|
s.SetDumpRouterMap(false)
|
|
s.Start()
|
|
defer s.Shutdown()
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
c := g.Client()
|
|
c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
|
gtest.C(t, func(t *gtest.T) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
|
|
go func() {
|
|
result := c.GetContent(ctx, "/never-done")
|
|
fmt.Println(result)
|
|
}()
|
|
time.Sleep(time.Millisecond * 100)
|
|
|
|
t.Assert(array.Len(), 0)
|
|
cancel()
|
|
|
|
time.Sleep(time.Millisecond * 1500)
|
|
t.Assert(array.Len(), 1)
|
|
})
|
|
}
|