diff --git a/owner/fail_test.go b/owner/fail_test.go index 1ef50572b0878..4ce291af10cbb 100644 --- a/owner/fail_test.go +++ b/owner/fail_test.go @@ -10,7 +10,6 @@ // distributed under the License is distributed on an "AS IS" BASIS, // See the License for the specific language governing permissions and // limitations under the License. -// +build !windows package owner @@ -20,69 +19,50 @@ import ( "math" "net" "os" + "runtime" "sync" "testing" "time" - . "github.com/pingcap/check" "github.com/pingcap/failpoint" "github.com/pingcap/parser/terror" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/testleak" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "go.etcd.io/etcd/clientv3" "google.golang.org/grpc" ) -// Ignore this test on the windows platform, because calling unix socket with address in -// host:port format fails on windows. -func TestT(t *testing.T) { - CustomVerboseFlag = true - logLevel := os.Getenv("log_level") - err := logutil.InitLogger(logutil.NewLogConfig(logLevel, "", "", logutil.EmptyFileLogConfig, false)) - if err != nil { - t.Fatal(err) - } - TestingT(t) -} - -var _ = Suite(&testSuite{}) - -type testSuite struct { -} - -func (s *testSuite) SetUpSuite(c *C) { -} - -func (s *testSuite) TearDownSuite(c *C) { -} - var ( - dialTimeout = 5 * time.Second + dialTimeout = 3 * time.Second retryCnt = math.MaxInt32 ) -func (s *testSuite) TestFailNewSession(c *C) { - os.Remove("new_session:0") +func TestFailNewSession(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("integration.NewClusterV3 will create file contains a colon which is not allowed on Windows") + } + + _ = os.Remove("new_session:0") ln, err := net.Listen("unix", "new_session:0") - c.Assert(err, IsNil) + require.NoError(t, err) + addr := ln.Addr() endpoints := []string{fmt.Sprintf("%s://%s", addr.Network(), addr.String())} - c.Assert(err, IsNil) + require.NoError(t, err) + srv := grpc.NewServer(grpc.ConnectionTimeout(time.Minute)) var stop sync.WaitGroup stop.Add(1) + go func() { - if err = srv.Serve(ln); err != nil { - c.Errorf("can't serve gRPC requests %v", err) - } - stop.Done() + defer stop.Done() + err = srv.Serve(ln) + assert.NoError(t, err) }() - leakFunc := testleak.AfterTest(c) defer func() { srv.Stop() stop.Wait() - leakFunc() }() func() { @@ -90,14 +70,14 @@ func (s *testSuite) TestFailNewSession(c *C) { Endpoints: endpoints, DialTimeout: dialTimeout, }) - c.Assert(err, IsNil) + require.NoError(t, err) defer func() { if cli != nil { - cli.Close() + _ = cli.Close() } - c.Assert(failpoint.Disable("github.com/pingcap/tidb/owner/closeClient"), IsNil) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/owner/closeClient")) }() - c.Assert(failpoint.Enable("github.com/pingcap/tidb/owner/closeClient", `return(true)`), IsNil) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/owner/closeClient", `return(true)`)) // TODO: It takes more than 2s here in etcd client, the CI takes 5s to run this test. // The config is hard coded, not way to control it outside. @@ -105,9 +85,9 @@ func (s *testSuite) TestFailNewSession(c *C) { // https://github.com/etcd-io/etcd/blob/ae9734e/clientv3/concurrency/session.go#L38 // https://github.com/etcd-io/etcd/blob/ae9734ed278b7a1a7dfc82e800471ebbf9fce56f/clientv3/client.go#L253 // https://github.com/etcd-io/etcd/blob/ae9734ed278b7a1a7dfc82e800471ebbf9fce56f/clientv3/retry_interceptor.go#L63 - _, err = NewSession(context.Background(), "fail_new_serssion", cli, retryCnt, ManagerSessionTTL) + _, err = NewSession(context.Background(), "fail_new_session", cli, retryCnt, ManagerSessionTTL) isContextDone := terror.ErrorEqual(grpc.ErrClientConnClosing, err) || terror.ErrorEqual(context.Canceled, err) - c.Assert(isContextDone, IsTrue, Commentf("err %v", err)) + require.Truef(t, isContextDone, "err %v", err) }() func() { @@ -115,19 +95,19 @@ func (s *testSuite) TestFailNewSession(c *C) { Endpoints: endpoints, DialTimeout: dialTimeout, }) - c.Assert(err, IsNil) + require.NoError(t, err) defer func() { if cli != nil { - cli.Close() + _ = cli.Close() } - c.Assert(failpoint.Disable("github.com/pingcap/tidb/owner/closeGrpc"), IsNil) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/owner/closeGrpc")) }() - c.Assert(failpoint.Enable("github.com/pingcap/tidb/owner/closeGrpc", `return(true)`), IsNil) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/owner/closeGrpc", `return(true)`)) // TODO: It takes more than 2s here in etcd client, the CI takes 5s to run this test. // The config is hard coded, not way to control it outside. - _, err = NewSession(context.Background(), "fail_new_serssion", cli, retryCnt, ManagerSessionTTL) + _, err = NewSession(context.Background(), "fail_new_session", cli, retryCnt, ManagerSessionTTL) isContextDone := terror.ErrorEqual(grpc.ErrClientConnClosing, err) || terror.ErrorEqual(context.Canceled, err) - c.Assert(isContextDone, IsTrue, Commentf("err %v", err)) + require.Truef(t, isContextDone, "err %v", err) }() } diff --git a/owner/main_test.go b/owner/main_test.go new file mode 100644 index 0000000000000..ab9498a86833c --- /dev/null +++ b/owner/main_test.go @@ -0,0 +1,29 @@ +//Copyright 2021 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + +package owner + +import ( + "testing" + + "github.com/pingcap/tidb/util/testbridge" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testbridge.WorkaroundGoCheckFlags() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("go.etcd.io/etcd/pkg/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/owner/manager_test.go b/owner/manager_test.go index ef8a2283e5f23..9f923437ea73f 100644 --- a/owner/manager_test.go +++ b/owner/manager_test.go @@ -20,7 +20,6 @@ import ( "testing" "time" - . "github.com/pingcap/check" "github.com/pingcap/errors" "github.com/pingcap/parser/terror" . "github.com/pingcap/tidb/ddl" @@ -28,6 +27,7 @@ import ( "github.com/pingcap/tidb/owner" "github.com/pingcap/tidb/store/mockstore" "github.com/pingcap/tidb/util/logutil" + "github.com/stretchr/testify/require" "go.etcd.io/etcd/clientv3" "go.etcd.io/etcd/clientv3/concurrency" "go.etcd.io/etcd/integration" @@ -36,112 +36,87 @@ import ( const testLease = 5 * time.Millisecond -func checkOwner(d DDL, fbVal bool) (isOwner bool) { - manager := d.OwnerManager() - // The longest to wait for 30 seconds to - // make sure that campaigning owners is completed. - for i := 0; i < 6000; i++ { - time.Sleep(5 * time.Millisecond) - isOwner = manager.IsOwner() - if isOwner == fbVal { - break - } - } - return -} - func TestSingle(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("integration.NewClusterV3 will create file contains a colon which is not allowed on Windows") } + store, err := mockstore.NewMockStore() - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) defer func() { err := store.Close() - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) }() - clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1}) - defer clus.Terminate(t) - cli := clus.RandClient() + cluster := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1}) + defer cluster.Terminate(t) + + client := cluster.RandClient() ctx := goctx.Background() ic := infoschema.NewCache(2) ic.Insert(infoschema.MockInfoSchemaWithSchemaVer(nil, 0), 0) d := NewDDL( ctx, - WithEtcdClient(cli), + WithEtcdClient(client), WithStore(store), WithLease(testLease), WithInfoCache(ic), ) err = d.Start(nil) - if err != nil { - t.Fatalf("DDL start failed %v", err) - } + require.NoError(t, err) defer func() { _ = d.Stop() }() isOwner := checkOwner(d, true) - if !isOwner { - t.Fatalf("expect true, got isOwner:%v", isOwner) - } + require.True(t, isOwner) // test for newSession failed ctx, cancel := goctx.WithCancel(ctx) - manager := owner.NewOwnerManager(ctx, cli, "ddl", "ddl_id", DDLOwnerKey) + manager := owner.NewOwnerManager(ctx, client, "ddl", "ddl_id", DDLOwnerKey) cancel() + err = manager.CampaignOwner() - if !terror.ErrorEqual(err, goctx.Canceled) && - !terror.ErrorEqual(err, goctx.DeadlineExceeded) { - t.Fatalf("campaigned result don't match, err %v", err) - } + comment := fmt.Sprintf("campaigned result don't match, err %v", err) + require.True(t, terror.ErrorEqual(err, goctx.Canceled) || terror.ErrorEqual(err, goctx.DeadlineExceeded), comment) + isOwner = checkOwner(d, true) - if !isOwner { - t.Fatalf("expect true, got isOwner:%v", isOwner) - } + require.True(t, isOwner) + // The test is used to exit campaign loop. d.OwnerManager().Cancel() isOwner = checkOwner(d, false) - if isOwner { - t.Fatalf("expect false, got isOwner:%v", isOwner) - } + require.False(t, isOwner) + time.Sleep(200 * time.Millisecond) + + // err is ok to be not nil since we canceled the manager. ownerID, _ := manager.GetOwnerID(goctx.Background()) - // The error is ok to be not nil since we canceled the manager. - if ownerID != "" { - t.Fatalf("owner %s is not empty", ownerID) - } + require.Equal(t, "", ownerID) } func TestCluster(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("integration.NewClusterV3 will create file contains a colon which is not allowed on Windows") } - tmpTTL := 3 - orignalTTL := owner.ManagerSessionTTL - owner.ManagerSessionTTL = tmpTTL + + originalTTL := owner.ManagerSessionTTL + owner.ManagerSessionTTL = 3 defer func() { - owner.ManagerSessionTTL = orignalTTL + owner.ManagerSessionTTL = originalTTL }() + store, err := mockstore.NewMockStore() - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) defer func() { err := store.Close() - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) }() - clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 4}) - defer clus.Terminate(t) - cli := clus.Client(0) + cluster := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 4}) + defer cluster.Terminate(t) + + cli := cluster.Client(0) ic := infoschema.NewCache(2) ic.Insert(infoschema.MockInfoSchemaWithSchemaVer(nil, 0), 0) d := NewDDL( @@ -151,15 +126,14 @@ func TestCluster(t *testing.T) { WithLease(testLease), WithInfoCache(ic), ) + err = d.Start(nil) - if err != nil { - t.Fatalf("DDL start failed %v", err) - } + require.NoError(t, err) + isOwner := checkOwner(d, true) - if !isOwner { - t.Fatalf("expect true, got isOwner:%v", isOwner) - } - cli1 := clus.Client(1) + require.True(t, isOwner) + + cli1 := cluster.Client(1) ic2 := infoschema.NewCache(2) ic2.Insert(infoschema.MockInfoSchemaWithSchemaVer(nil, 0), 0) d1 := NewDDL( @@ -170,30 +144,24 @@ func TestCluster(t *testing.T) { WithInfoCache(ic2), ) err = d1.Start(nil) - if err != nil { - t.Fatalf("DDL start failed %v", err) - } + require.NoError(t, err) + isOwner = checkOwner(d1, false) - if isOwner { - t.Fatalf("expect false, got isOwner:%v", isOwner) - } + require.False(t, isOwner) // Delete the leader key, the d1 become the owner. - cliRW := clus.Client(2) + cliRW := cluster.Client(2) err = deleteLeader(cliRW, DDLOwnerKey) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) + isOwner = checkOwner(d, false) - if isOwner { - t.Fatalf("expect false, got isOwner:%v", isOwner) - } + require.False(t, isOwner) + err = d.Stop() - if err != nil { - t.Fatal(err, IsNil) - } + require.NoError(t, err) + // d3 (not owner) stop - cli3 := clus.Client(3) + cli3 := cluster.Client(3) ic3 := infoschema.NewCache(2) ic3.Insert(infoschema.MockInfoSchemaWithSchemaVer(nil, 0), 0) d3 := NewDDL( @@ -204,40 +172,44 @@ func TestCluster(t *testing.T) { WithInfoCache(ic3), ) err = d3.Start(nil) - if err != nil { - t.Fatalf("DDL start failed %v", err) - } + require.NoError(t, err) defer func() { err = d3.Stop() - if err != nil { - t.Fatal(err, IsNil) - } + require.NoError(t, err) }() + isOwner = checkOwner(d3, false) - if isOwner { - t.Fatalf("expect false, got isOwner:%v", isOwner) - } + require.False(t, isOwner) + err = d3.Stop() - if err != nil { - t.Fatal(err, IsNil) - } + require.NoError(t, err) // Cancel the owner context, there is no owner. err = d1.Stop() - if err != nil { - t.Fatal(err, IsNil) - } + require.NoError(t, err) + session, err := concurrency.NewSession(cliRW) - if err != nil { - t.Fatalf("new session failed %v", err) - } - elec := concurrency.NewElection(session, DDLOwnerKey) + require.NoError(t, err) + + election := concurrency.NewElection(session, DDLOwnerKey) logPrefix := fmt.Sprintf("[ddl] %s ownerManager %s", DDLOwnerKey, "useless id") logCtx := logutil.WithKeyValue(context.Background(), "owner info", logPrefix) - _, err = owner.GetOwnerInfo(goctx.Background(), logCtx, elec, "useless id") - if !terror.ErrorEqual(err, concurrency.ErrElectionNoLeader) { - t.Fatalf("get owner info result don't match, err %v", err) + _, err = owner.GetOwnerInfo(goctx.Background(), logCtx, election, "useless id") + require.Truef(t, terror.ErrorEqual(err, concurrency.ErrElectionNoLeader), "get owner info result don't match, err %v", err) +} + +func checkOwner(d DDL, fbVal bool) (isOwner bool) { + manager := d.OwnerManager() + // The longest to wait for 30 seconds to + // make sure that campaigning owners is completed. + for i := 0; i < 6000; i++ { + time.Sleep(5 * time.Millisecond) + isOwner = manager.IsOwner() + if isOwner == fbVal { + break + } } + return } func deleteLeader(cli *clientv3.Client, prefixKey string) error { @@ -245,9 +217,11 @@ func deleteLeader(cli *clientv3.Client, prefixKey string) error { if err != nil { return errors.Trace(err) } - defer session.Close() - elec := concurrency.NewElection(session, prefixKey) - resp, err := elec.Leader(goctx.Background()) + defer func() { + _ = session.Close() + }() + election := concurrency.NewElection(session, prefixKey) + resp, err := election.Leader(goctx.Background()) if err != nil { return errors.Trace(err) }