github.com/thiagoyeds/go-cloud@v0.26.0/pubsub/drivertest/drivertest.go (about)

     1  // Copyright 2018 The Go Cloud Development Kit Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     https://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package drivertest provides a conformance test for implementations of
    16  // driver.
    17  package drivertest // import "gocloud.dev/pubsub/drivertest"
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"errors"
    23  	"sort"
    24  	"strconv"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/google/go-cmp/cmp"
    29  	"github.com/google/go-cmp/cmp/cmpopts"
    30  	"gocloud.dev/gcerrors"
    31  	"gocloud.dev/internal/escape"
    32  	"gocloud.dev/internal/retry"
    33  	"gocloud.dev/pubsub"
    34  	"gocloud.dev/pubsub/batcher"
    35  	"gocloud.dev/pubsub/driver"
    36  	"golang.org/x/sync/errgroup"
    37  )
    38  
    39  // Harness descibes the functionality test harnesses must provide to run
    40  // conformance tests.
    41  type Harness interface {
    42  	// CreateTopic creates a new topic and returns a driver.Topic
    43  	// for testing. The topic may have to be removed manually if the test is
    44  	// abruptly terminated or the network connection fails.
    45  	CreateTopic(ctx context.Context, testName string) (dt driver.Topic, cleanup func(), err error)
    46  
    47  	// MakeNonexistentTopic makes a driver.Topic referencing a topic that
    48  	// does not exist.
    49  	MakeNonexistentTopic(ctx context.Context) (driver.Topic, error)
    50  
    51  	// CreateSubscription creates a new subscription, subscribed
    52  	// to the given topic, and returns a driver.Subscription for testing. The
    53  	// subscription may have to be cleaned up manually if the test is abruptly
    54  	// terminated or the network connection fails.
    55  	CreateSubscription(ctx context.Context, t driver.Topic, testName string) (ds driver.Subscription, cleanup func(), err error)
    56  
    57  	// MakeNonexistentSubscription makes a driver.Subscription referencing a
    58  	// subscription that does not exist.
    59  	MakeNonexistentSubscription(ctx context.Context) (ds driver.Subscription, cleanup func(), err error)
    60  
    61  	// Close closes resources used by the harness, but does not call Close
    62  	// on the Topics and Subscriptions generated by the Harness.
    63  	Close()
    64  
    65  	// MaxBatchSizes returns the maximum size of SendBatch/Send(Na|A)cks, or 0
    66  	// if there's no max.
    67  	MaxBatchSizes() (int, int)
    68  
    69  	// SupportsMultipleSubscriptions reports whether the driver supports
    70  	// multiple subscriptions for the same topic.
    71  	SupportsMultipleSubscriptions() bool
    72  }
    73  
    74  // HarnessMaker describes functions that construct a harness for running tests.
    75  // It is called exactly once per test; Harness.Close() will be called when the test is complete.
    76  type HarnessMaker func(ctx context.Context, t *testing.T) (Harness, error)
    77  
    78  // AsTest represents a test of As functionality.
    79  // The conformance test:
    80  // 1. Calls TopicCheck.
    81  // 2. Calls SubscriptionCheck.
    82  // 3. Sends a message, setting Message.BeforeSend to BeforeSend
    83  //    and Message.AfterSend to AfterSend.
    84  // 4. Receives the message and calls MessageCheck.
    85  // 5. Calls TopicErrorCheck.
    86  // 6. Calls SubscriptionErrorCheck.
    87  type AsTest interface {
    88  	// Name should return a descriptive name for the test.
    89  	Name() string
    90  	// TopicCheck will be called to allow verifcation of Topic.As.
    91  	TopicCheck(t *pubsub.Topic) error
    92  	// SubscriptionCheck will be called to allow verification of Subscription.As.
    93  	SubscriptionCheck(s *pubsub.Subscription) error
    94  	// TopicErrorCheck will be called to allow verification of Topic.ErrorAs.
    95  	// The error will be the one returned from SendBatch when called with
    96  	// a non-existent topic.
    97  	TopicErrorCheck(t *pubsub.Topic, err error) error
    98  	// SubscriptionErrorCheck will be called to allow verification of
    99  	// Subscription.ErrorAs.
   100  	// The error will be the one returned from ReceiveBatch when called with
   101  	// a non-existent subscription.
   102  	SubscriptionErrorCheck(s *pubsub.Subscription, err error) error
   103  	// MessageCheck will be called to allow verification of Message.As.
   104  	MessageCheck(m *pubsub.Message) error
   105  	// BeforeSend will be used as Message.BeforeSend as part of sending a test
   106  	// message.
   107  	BeforeSend(as func(interface{}) bool) error
   108  	// AfterSend will be used as Message.AfterSend as part of sending a test
   109  	// message.
   110  	AfterSend(as func(interface{}) bool) error
   111  }
   112  
   113  // Many tests set the maximum batch size to 1 to make record/replay stable.
   114  var batchSizeOne = &batcher.Options{MaxBatchSize: 1, MaxHandlers: 1}
   115  
   116  type verifyAsFailsOnNil struct{}
   117  
   118  func (verifyAsFailsOnNil) Name() string {
   119  	return "verify As returns false when passed nil"
   120  }
   121  
   122  func (verifyAsFailsOnNil) TopicCheck(t *pubsub.Topic) error {
   123  	if t.As(nil) {
   124  		return errors.New("want Topic.As to return false when passed nil")
   125  	}
   126  	return nil
   127  }
   128  
   129  func (verifyAsFailsOnNil) SubscriptionCheck(s *pubsub.Subscription) error {
   130  	if s.As(nil) {
   131  		return errors.New("want Subscription.As to return false when passed nil")
   132  	}
   133  	return nil
   134  }
   135  
   136  func (verifyAsFailsOnNil) TopicErrorCheck(t *pubsub.Topic, err error) (ret error) {
   137  	defer func() {
   138  		if recover() == nil {
   139  			ret = errors.New("want Topic.ErrorAs to panic when passed nil")
   140  		}
   141  	}()
   142  	t.ErrorAs(err, nil)
   143  	return nil
   144  }
   145  
   146  func (verifyAsFailsOnNil) SubscriptionErrorCheck(s *pubsub.Subscription, err error) (ret error) {
   147  	defer func() {
   148  		if recover() == nil {
   149  			ret = errors.New("want Subscription.ErrorAs to panic when passed nil")
   150  		}
   151  	}()
   152  	s.ErrorAs(err, nil)
   153  	return nil
   154  }
   155  
   156  func (verifyAsFailsOnNil) MessageCheck(m *pubsub.Message) error {
   157  	if m.As(nil) {
   158  		return errors.New("want Message.As to return false when passed nil")
   159  	}
   160  	return nil
   161  }
   162  
   163  func (verifyAsFailsOnNil) BeforeSend(as func(interface{}) bool) error {
   164  	if as(nil) {
   165  		return errors.New("want Message.BeforeSend's As function to return false when passed nil")
   166  	}
   167  	return nil
   168  }
   169  
   170  func (verifyAsFailsOnNil) AfterSend(as func(interface{}) bool) error {
   171  	if as(nil) {
   172  		return errors.New("want Message.AfterSend's As function to return false when passed nil")
   173  	}
   174  	return nil
   175  }
   176  
   177  // RunConformanceTests runs conformance tests for driver implementations of pubsub.
   178  func RunConformanceTests(t *testing.T, newHarness HarnessMaker, asTests []AsTest) {
   179  	tests := map[string]func(t *testing.T, newHarness HarnessMaker){
   180  		"TestSendReceive":                          testSendReceive,
   181  		"TestSendReceiveTwo":                       testSendReceiveTwo,
   182  		"TestSendReceiveJSON":                      testSendReceiveJSON,
   183  		"TestNack":                                 testNack,
   184  		"TestBatching":                             testBatching,
   185  		"TestDoubleAck":                            testDoubleAck,
   186  		"TestErrorOnSendToClosedTopic":             testErrorOnSendToClosedTopic,
   187  		"TestErrorOnReceiveFromClosedSubscription": testErrorOnReceiveFromClosedSubscription,
   188  		"TestCancelSendReceive":                    testCancelSendReceive,
   189  		"TestNonExistentTopicSucceedsOnOpenButFailsOnSend":           testNonExistentTopicSucceedsOnOpenButFailsOnSend,
   190  		"TestNonExistentSubscriptionSucceedsOnOpenButFailsOnReceive": testNonExistentSubscriptionSucceedsOnOpenButFailsOnReceive,
   191  		"TestMetadata":           testMetadata,
   192  		"TestNonUTF8MessageBody": testNonUTF8MessageBody,
   193  	}
   194  	for name, test := range tests {
   195  		t.Run(name, func(t *testing.T) { test(t, newHarness) })
   196  	}
   197  
   198  	asTests = append(asTests, verifyAsFailsOnNil{})
   199  	t.Run("TestAs", func(t *testing.T) {
   200  		for _, st := range asTests {
   201  			if st.Name() == "" {
   202  				t.Fatalf("AsTest.Name is required")
   203  			}
   204  			t.Run(st.Name(), func(t *testing.T) { testAs(t, newHarness, st) })
   205  		}
   206  	})
   207  }
   208  
   209  // RunBenchmarks runs benchmarks for driver implementations of pubsub.
   210  func RunBenchmarks(b *testing.B, topic *pubsub.Topic, sub *pubsub.Subscription) {
   211  	b.Run("BenchmarkReceive", func(b *testing.B) {
   212  		benchmark(b, topic, sub, false)
   213  	})
   214  	b.Run("BenchmarkSend", func(b *testing.B) {
   215  		benchmark(b, topic, sub, true)
   216  	})
   217  }
   218  
   219  func testNonExistentTopicSucceedsOnOpenButFailsOnSend(t *testing.T, newHarness HarnessMaker) {
   220  	// Set up.
   221  	ctx := context.Background()
   222  	h, err := newHarness(ctx, t)
   223  	if err != nil {
   224  		t.Fatal(err)
   225  	}
   226  	defer h.Close()
   227  
   228  	dt, err := h.MakeNonexistentTopic(ctx)
   229  	if err != nil {
   230  		// Failure shouldn't happen for non-existent topics until messages are sent
   231  		// to them.
   232  		t.Fatalf("creating a local topic that doesn't exist on the server: %v", err)
   233  	}
   234  	topic := pubsub.NewTopic(dt, nil)
   235  	defer func() {
   236  		if err := topic.Shutdown(ctx); err != nil {
   237  			t.Error(err)
   238  		}
   239  	}()
   240  
   241  	m := &pubsub.Message{}
   242  	err = topic.Send(ctx, m)
   243  	if err == nil || gcerrors.Code(err) != gcerrors.NotFound {
   244  		t.Errorf("got error %v for send to non-existent topic, want code=NotFound", err)
   245  	}
   246  }
   247  
   248  func testNonExistentSubscriptionSucceedsOnOpenButFailsOnReceive(t *testing.T, newHarness HarnessMaker) {
   249  	// Set up.
   250  	ctx := context.Background()
   251  	h, err := newHarness(ctx, t)
   252  	if err != nil {
   253  		t.Fatal(err)
   254  	}
   255  	defer h.Close()
   256  
   257  	ds, cleanup, err := h.MakeNonexistentSubscription(ctx)
   258  	if err != nil {
   259  		t.Fatalf("failed to make non-existent subscription: %v", err)
   260  	}
   261  	defer cleanup()
   262  	sub := pubsub.NewSubscription(ds, batchSizeOne, batchSizeOne)
   263  	defer func() {
   264  		if err := sub.Shutdown(ctx); err != nil {
   265  			t.Error(err)
   266  		}
   267  	}()
   268  
   269  	// The test will hang here if the message isn't available, so use a shorter timeout.
   270  	ctx2, cancel := context.WithTimeout(ctx, 30*time.Second)
   271  	defer cancel()
   272  	_, err = sub.Receive(ctx2)
   273  	if err == nil || ctx2.Err() != nil || gcerrors.Code(err) != gcerrors.NotFound {
   274  		t.Errorf("got error %v for receive from non-existent subscription, want code=NotFound", err)
   275  	}
   276  }
   277  
   278  func testSendReceive(t *testing.T, newHarness HarnessMaker) {
   279  	// Set up.
   280  	ctx := context.Background()
   281  	h, err := newHarness(ctx, t)
   282  	if err != nil {
   283  		t.Fatal(err)
   284  	}
   285  	defer h.Close()
   286  	topic, sub, cleanup, err := makePair(ctx, t, h)
   287  	if err != nil {
   288  		t.Fatal(err)
   289  	}
   290  	defer cleanup()
   291  
   292  	want := publishN(ctx, t, topic, 3)
   293  	got := receiveN(ctx, t, sub, len(want))
   294  
   295  	// Verify LoggableID is set.
   296  	for _, msg := range got {
   297  		if msg.LoggableID == "" {
   298  			t.Errorf("msg.LoggableID was empty, should be set")
   299  		}
   300  	}
   301  
   302  	// Check that the received messages match the sent ones.
   303  	if diff := diffMessageSets(got, want); diff != "" {
   304  		t.Error(diff)
   305  	}
   306  }
   307  
   308  // Receive from two subscriptions to the same topic.
   309  // Verify both get all the messages.
   310  func testSendReceiveTwo(t *testing.T, newHarness HarnessMaker) {
   311  	// Set up.
   312  	ctx := context.Background()
   313  	h, err := newHarness(ctx, t)
   314  	if err != nil {
   315  		t.Fatal(err)
   316  	}
   317  	defer h.Close()
   318  	if !h.SupportsMultipleSubscriptions() {
   319  		t.Skip("multiple subscriptions to a topic not supported")
   320  	}
   321  
   322  	dt, cleanup, err := h.CreateTopic(ctx, t.Name())
   323  	if err != nil {
   324  		t.Fatal(err)
   325  	}
   326  	defer cleanup()
   327  	topic := pubsub.NewTopic(dt, batchSizeOne)
   328  	defer func() {
   329  		if err := topic.Shutdown(ctx); err != nil {
   330  			t.Error(err)
   331  		}
   332  	}()
   333  
   334  	var ss []*pubsub.Subscription
   335  	for i := 0; i < 2; i++ {
   336  		ds, cleanup, err := h.CreateSubscription(ctx, dt, t.Name())
   337  		if err != nil {
   338  			t.Fatal(err)
   339  		}
   340  		defer cleanup()
   341  		s := pubsub.NewSubscription(ds, batchSizeOne, batchSizeOne)
   342  		defer func() {
   343  			if err := s.Shutdown(ctx); err != nil {
   344  				t.Error(err)
   345  			}
   346  		}()
   347  		ss = append(ss, s)
   348  	}
   349  	want := publishN(ctx, t, topic, 3)
   350  	for i, s := range ss {
   351  		got := receiveN(ctx, t, s, len(want))
   352  		if diff := diffMessageSets(got, want); diff != "" {
   353  			t.Errorf("sub #%d: %s", i, diff)
   354  		}
   355  	}
   356  }
   357  
   358  func testSendReceiveJSON(t *testing.T, newHarness HarnessMaker) {
   359  	const json = `{"Foo": "Bar"}`
   360  	// Set up.
   361  	ctx := context.Background()
   362  	h, err := newHarness(ctx, t)
   363  	if err != nil {
   364  		t.Fatal(err)
   365  	}
   366  	defer h.Close()
   367  	topic, sub, cleanup, err := makePair(ctx, t, h)
   368  	if err != nil {
   369  		t.Fatal(err)
   370  	}
   371  	defer cleanup()
   372  
   373  	sendM := &pubsub.Message{Body: []byte(json)}
   374  	if err := topic.Send(ctx, sendM); err != nil {
   375  		t.Fatal(err)
   376  	}
   377  	ctx2, cancel := context.WithTimeout(ctx, 30*time.Second)
   378  	defer cancel()
   379  	receiveM, err := sub.Receive(ctx2)
   380  	if err != nil {
   381  		t.Fatal(err)
   382  	}
   383  	receiveM.Ack()
   384  	if diff := diffMessageSets([]*pubsub.Message{receiveM}, []*pubsub.Message{sendM}); diff != "" {
   385  		t.Error(diff)
   386  	}
   387  }
   388  
   389  func testNack(t *testing.T, newHarness HarnessMaker) {
   390  	const nMessages = 2
   391  
   392  	// Set up.
   393  	ctx := context.Background()
   394  	h, err := newHarness(ctx, t)
   395  	if err != nil {
   396  		t.Fatal(err)
   397  	}
   398  	defer h.Close()
   399  	dt, topicCleanup, err := h.CreateTopic(ctx, t.Name())
   400  	if err != nil {
   401  		t.Fatal(err)
   402  	}
   403  	defer topicCleanup()
   404  	ds, subCleanup, err := h.CreateSubscription(ctx, dt, t.Name())
   405  	if err != nil {
   406  		t.Fatal(err)
   407  	}
   408  	defer subCleanup()
   409  	if !ds.CanNack() {
   410  		t.Skip("Nack not supported")
   411  	}
   412  	topic := pubsub.NewTopic(dt, batchSizeOne)
   413  	defer func() {
   414  		if err := topic.Shutdown(ctx); err != nil {
   415  			t.Error(err)
   416  		}
   417  	}()
   418  	sub := pubsub.NewSubscription(ds, batchSizeOne, batchSizeOne)
   419  	defer func() {
   420  		if err := sub.Shutdown(ctx); err != nil {
   421  			t.Error(err)
   422  		}
   423  	}()
   424  
   425  	want := publishN(ctx, t, topic, nMessages)
   426  
   427  	// Get the messages, but nack them.
   428  	// Make sure to nack after receiving all of them; otherwise, we could
   429  	// receive one of the messages twice instead of receiving all nMessages.
   430  	// The test will hang here if the messages aren't redelivered, so use a shorter timeout.
   431  	ctx2, cancel := context.WithTimeout(ctx, 30*time.Second)
   432  	defer cancel()
   433  	var got []*pubsub.Message
   434  	for i := 0; i < nMessages; i++ {
   435  		m, err := sub.Receive(ctx2)
   436  		if err != nil {
   437  			t.Fatal(err)
   438  		}
   439  		got = append(got, m)
   440  	}
   441  	for _, m := range got {
   442  		m.Nack()
   443  	}
   444  	// Check that the received messages match the sent ones.
   445  	if diff := diffMessageSets(got, want); diff != "" {
   446  		t.Error(diff)
   447  	}
   448  	// The test will hang here if the messages aren't redelivered, so use a shorter timeout.
   449  	ctx2, cancel = context.WithTimeout(ctx, 30*time.Second)
   450  	defer cancel()
   451  
   452  	got = nil
   453  	for i := 0; i < nMessages; i++ {
   454  		m, err := sub.Receive(ctx2)
   455  		if err != nil {
   456  			t.Fatal(err)
   457  		}
   458  		got = append(got, m)
   459  		m.Ack()
   460  	}
   461  	if diff := diffMessageSets(got, want); diff != "" {
   462  		t.Error(diff)
   463  	}
   464  }
   465  
   466  func testBatching(t *testing.T, newHarness HarnessMaker) {
   467  	const nMessages = 12 // must be divisible by 2
   468  	const batchSize = nMessages / 2
   469  
   470  	// Set up.
   471  	ctx := context.Background()
   472  	h, err := newHarness(ctx, t)
   473  	if err != nil {
   474  		t.Fatal(err)
   475  	}
   476  	defer h.Close()
   477  	maxSendBatch, maxAckBatch := h.MaxBatchSizes()
   478  
   479  	dt, topicCleanup, err := h.CreateTopic(ctx, t.Name())
   480  	if err != nil {
   481  		t.Fatal(err)
   482  	}
   483  	defer topicCleanup()
   484  	ds, subCleanup, err := h.CreateSubscription(ctx, dt, t.Name())
   485  	if err != nil {
   486  		t.Fatal(err)
   487  	}
   488  	defer subCleanup()
   489  
   490  	sendBatchOpts := &batcher.Options{MinBatchSize: batchSize, MaxBatchSize: batchSize}
   491  	// If the driver doesn't support batchSize batches, fall back to size 1.
   492  	if maxSendBatch != 0 && batchSize > maxSendBatch {
   493  		sendBatchOpts = batchSizeOne
   494  	}
   495  	topic := pubsub.NewTopic(dt, sendBatchOpts)
   496  	defer func() {
   497  		if err := topic.Shutdown(ctx); err != nil {
   498  			t.Error(err)
   499  		}
   500  	}()
   501  	ackBatchOpts := &batcher.Options{MinBatchSize: batchSize, MaxBatchSize: batchSize}
   502  	// If the driver doesn't support batchSize batches, fall back to size 1.
   503  	if maxAckBatch != 0 && batchSize > maxAckBatch {
   504  		ackBatchOpts = batchSizeOne
   505  	}
   506  	sub := pubsub.NewSubscription(ds, batchSizeOne, ackBatchOpts)
   507  	defer func() {
   508  		if err := sub.Shutdown(ctx); err != nil {
   509  			t.Error(err)
   510  		}
   511  	}()
   512  
   513  	// Publish nMessages. We have to do them asynchronously because topic.Send
   514  	// blocks until the message is sent, and these messages won't be sent until
   515  	// all batchSize are queued.
   516  	// Note: this test uses the same Body for each message, because the order
   517  	// that they appear in the SendBatch is not stable.
   518  	gr, grctx := errgroup.WithContext(ctx)
   519  	var want []*pubsub.Message
   520  	for i := 0; i < nMessages; i++ {
   521  		m := &pubsub.Message{Body: []byte("hello world")}
   522  		want = append(want, m)
   523  		gr.Go(func() error { return topic.Send(grctx, m) })
   524  	}
   525  	if err := gr.Wait(); err != nil {
   526  		t.Fatal(err)
   527  	}
   528  
   529  	// Get the messages.
   530  	// The test will hang here if the messages aren't delivered, so use a shorter timeout.
   531  	ctx2, cancel := context.WithTimeout(ctx, 30*time.Second)
   532  	defer cancel()
   533  	var got []*pubsub.Message
   534  	for i := 0; i < nMessages; i++ {
   535  		m, err := sub.Receive(ctx2)
   536  		if err != nil {
   537  			t.Fatal(err)
   538  		}
   539  		got = append(got, m)
   540  		m.Ack()
   541  	}
   542  	if diff := diffMessageSets(got, want); diff != "" {
   543  		t.Error(diff)
   544  	}
   545  }
   546  
   547  func testDoubleAck(t *testing.T, newHarness HarnessMaker) {
   548  	// Set up.
   549  	ctx := context.Background()
   550  	h, err := newHarness(ctx, t)
   551  	if err != nil {
   552  		t.Fatal(err)
   553  	}
   554  	defer h.Close()
   555  	dt, topicCleanup, err := h.CreateTopic(ctx, t.Name())
   556  	if err != nil {
   557  		t.Fatal(err)
   558  	}
   559  	defer topicCleanup()
   560  	ds, subCleanup, err := h.CreateSubscription(ctx, dt, t.Name())
   561  	if err != nil {
   562  		t.Fatal(err)
   563  	}
   564  	defer subCleanup()
   565  
   566  	// Publish 3 messages.
   567  	for i := 0; i < 3; i++ {
   568  		err := dt.SendBatch(ctx, []*driver.Message{{Body: []byte(strconv.Itoa(i))}})
   569  		if err != nil {
   570  			t.Fatal(err)
   571  		}
   572  	}
   573  
   574  	// Retrieve the messages.
   575  	// The test will hang here if the messages aren't delivered, so use a shorter timeout.
   576  	ctx2, cancel := context.WithTimeout(ctx, 30*time.Second)
   577  	defer cancel()
   578  	var dms []*driver.Message
   579  	for len(dms) != 3 {
   580  		curdms, err := ds.ReceiveBatch(ctx2, 3)
   581  		if err != nil {
   582  			t.Fatal(err)
   583  		}
   584  		if err := ctx2.Err(); err != nil {
   585  			t.Fatalf("never received expected messages: %v", err)
   586  		}
   587  		dms = append(dms, curdms...)
   588  	}
   589  
   590  	// Ack the first two messages.
   591  	err = ds.SendAcks(ctx, []driver.AckID{dms[0].AckID, dms[1].AckID})
   592  	if err != nil {
   593  		t.Fatal(err)
   594  	}
   595  
   596  	// Ack them again; this should succeed even though we've acked them before.
   597  	// If services return an error for this, drivers should drop them.
   598  	err = ds.SendAcks(ctx, []driver.AckID{dms[0].AckID, dms[1].AckID})
   599  	if err != nil {
   600  		t.Fatal(err)
   601  	}
   602  
   603  	if !ds.CanNack() {
   604  		return
   605  	}
   606  
   607  	// Nack all 3 messages. This should also succeed, and the nack of the third
   608  	// message should take effect, so we should be able to fetch it again.
   609  	// Note that the other messages *may* also be re-sent, because we're nacking
   610  	// them here (even though we acked them earlier); it depends on service
   611  	// semantics and time-sensitivity.
   612  	err = ds.SendNacks(ctx, []driver.AckID{dms[0].AckID, dms[1].AckID, dms[2].AckID})
   613  	if err != nil {
   614  		t.Fatal(err)
   615  	}
   616  
   617  	// The test will hang here if the message isn't redelivered, so use a shorter timeout.
   618  	ctx2, cancel = context.WithTimeout(ctx, 30*time.Second)
   619  	defer cancel()
   620  
   621  	// We're looking to re-receive dms[2].
   622  Loop:
   623  	for {
   624  		curdms, err := ds.ReceiveBatch(ctx2, 1)
   625  		if err != nil {
   626  			t.Fatal(err)
   627  		}
   628  		for _, curdm := range curdms {
   629  			if bytes.Equal(curdm.Body, dms[2].Body) {
   630  				// Found it!
   631  				break Loop
   632  			}
   633  		}
   634  	}
   635  }
   636  
   637  // Publish n different messages to the topic. Return the messages.
   638  func publishN(ctx context.Context, t *testing.T, topic *pubsub.Topic, n int) []*pubsub.Message {
   639  	var ms []*pubsub.Message
   640  	for i := 0; i < n; i++ {
   641  		m := &pubsub.Message{
   642  			Body:     []byte(strconv.Itoa(i)),
   643  			Metadata: map[string]string{"a": strconv.Itoa(i)},
   644  		}
   645  		if err := topic.Send(ctx, m); err != nil {
   646  			t.Fatal(err)
   647  		}
   648  		ms = append(ms, m)
   649  	}
   650  	return ms
   651  }
   652  
   653  // Receive and ack n messages from sub.
   654  func receiveN(ctx context.Context, t *testing.T, sub *pubsub.Subscription, n int) []*pubsub.Message {
   655  	// The test will hang here if the message(s) aren't available, so use a shorter timeout.
   656  	ctx2, cancel := context.WithTimeout(ctx, 30*time.Second)
   657  	defer cancel()
   658  	var ms []*pubsub.Message
   659  	for i := 0; i < n; i++ {
   660  		m, err := sub.Receive(ctx2)
   661  		if err != nil {
   662  			t.Fatal(err)
   663  		}
   664  		ms = append(ms, m)
   665  		m.Ack()
   666  	}
   667  	return ms
   668  }
   669  
   670  // Find the differences between two sets of messages.
   671  func diffMessageSets(got, want []*pubsub.Message) string {
   672  	for _, m := range got {
   673  		m.LoggableID = ""
   674  	}
   675  	less := func(x, y *pubsub.Message) bool { return bytes.Compare(x.Body, y.Body) < 0 }
   676  	return cmp.Diff(got, want, cmpopts.SortSlices(less), cmpopts.IgnoreUnexported(pubsub.Message{}))
   677  }
   678  
   679  func testErrorOnSendToClosedTopic(t *testing.T, newHarness HarnessMaker) {
   680  	// Set up.
   681  	ctx := context.Background()
   682  	h, err := newHarness(ctx, t)
   683  	if err != nil {
   684  		t.Fatal(err)
   685  	}
   686  	defer h.Close()
   687  
   688  	dt, cleanup, err := h.CreateTopic(ctx, t.Name())
   689  	if err != nil {
   690  		t.Fatal(err)
   691  	}
   692  	defer cleanup()
   693  
   694  	topic := pubsub.NewTopic(dt, batchSizeOne)
   695  	if err := topic.Shutdown(ctx); err != nil {
   696  		t.Error(err)
   697  	}
   698  
   699  	// Check that sending to the closed topic fails.
   700  	m := &pubsub.Message{}
   701  	if err := topic.Send(ctx, m); err == nil {
   702  		t.Error("topic.Send returned nil, want error")
   703  	}
   704  	if err := topic.Shutdown(ctx); err == nil {
   705  		t.Error("wanted error on double Shutdown")
   706  	}
   707  }
   708  
   709  func testErrorOnReceiveFromClosedSubscription(t *testing.T, newHarness HarnessMaker) {
   710  	ctx := context.Background()
   711  	h, err := newHarness(ctx, t)
   712  	if err != nil {
   713  		t.Fatal(err)
   714  	}
   715  	defer h.Close()
   716  
   717  	dt, cleanup, err := h.CreateTopic(ctx, t.Name())
   718  	if err != nil {
   719  		t.Fatal(err)
   720  	}
   721  	defer cleanup()
   722  
   723  	ds, cleanup, err := h.CreateSubscription(ctx, dt, t.Name())
   724  	if err != nil {
   725  		t.Fatal(err)
   726  	}
   727  	defer cleanup()
   728  
   729  	sub := pubsub.NewSubscription(ds, batchSizeOne, batchSizeOne)
   730  	if err := sub.Shutdown(ctx); err != nil {
   731  		t.Error(err)
   732  	}
   733  	if _, err = sub.Receive(ctx); err == nil {
   734  		t.Error("sub.Receive returned nil, want error")
   735  	}
   736  	if err := sub.Shutdown(ctx); err == nil {
   737  		t.Error("wanted error on double Shutdown")
   738  	}
   739  }
   740  
   741  func testCancelSendReceive(t *testing.T, newHarness HarnessMaker) {
   742  	ctx := context.Background()
   743  	h, err := newHarness(ctx, t)
   744  	if err != nil {
   745  		t.Fatal(err)
   746  	}
   747  	defer h.Close()
   748  	topic, sub, cleanup, err := makePair(ctx, t, h)
   749  	if err != nil {
   750  		t.Fatal(err)
   751  	}
   752  	defer cleanup()
   753  
   754  	ctx, cancel := context.WithCancel(ctx)
   755  	cancel()
   756  
   757  	m := &pubsub.Message{}
   758  	if err := topic.Send(ctx, m); !isCanceled(err) {
   759  		t.Errorf("topic.Send returned %v (%T), want context.Canceled", err, err)
   760  	}
   761  	if _, err := sub.Receive(ctx); !isCanceled(err) {
   762  		t.Errorf("sub.Receive returned %v (%T), want context.Canceled", err, err)
   763  	}
   764  
   765  	// It would be nice to add a test that cancels an in-flight blocking Receive.
   766  	// However, because pubsub.Subscription.Receive repeatedly calls
   767  	// driver.ReceiveBatch if it returns 0 messages, it's difficult to write
   768  	// such a test without it being flaky for drivers with record/replay
   769  	// (the number of times driver.ReceiveBatch is called is timing-dependent).
   770  }
   771  
   772  func testMetadata(t *testing.T, newHarness HarnessMaker) {
   773  	// Set up.
   774  	ctx := context.Background()
   775  	h, err := newHarness(ctx, t)
   776  	if err != nil {
   777  		t.Fatal(err)
   778  	}
   779  	defer h.Close()
   780  
   781  	// Some services limit the number of metadata per message.
   782  	// Sort the escape.WeirdStrings values for record/replay consistency,
   783  	// then break the weird strings up into groups of at most maxMetadataKeys.
   784  	const maxMetadataKeys = 10
   785  	var weirdStrings []string
   786  	for _, v := range escape.WeirdStrings {
   787  		weirdStrings = append(weirdStrings, v)
   788  	}
   789  	sort.Slice(weirdStrings, func(i, j int) bool { return weirdStrings[i] < weirdStrings[j] })
   790  
   791  	weirdMetaDataGroups := []map[string]string{{}}
   792  	i := 0
   793  	for _, k := range weirdStrings {
   794  		weirdMetaDataGroups[i][k] = k
   795  		if len(weirdMetaDataGroups[i]) == maxMetadataKeys {
   796  			weirdMetaDataGroups = append(weirdMetaDataGroups, map[string]string{})
   797  			i++
   798  		}
   799  	}
   800  
   801  	topic, sub, cleanup, err := makePair(ctx, t, h)
   802  	if err != nil {
   803  		t.Fatal(err)
   804  	}
   805  	defer cleanup()
   806  
   807  	for _, wm := range weirdMetaDataGroups {
   808  		m := &pubsub.Message{
   809  			Body:     []byte("hello world"),
   810  			Metadata: wm,
   811  		}
   812  		if err := topic.Send(ctx, m); err != nil {
   813  			t.Fatal(err)
   814  		}
   815  
   816  		// The test will hang here if the messages aren't delivered, so use a shorter timeout.
   817  		ctx2, cancel := context.WithTimeout(ctx, 30*time.Second)
   818  		defer cancel()
   819  		m, err = sub.Receive(ctx2)
   820  		if err != nil {
   821  			t.Fatal(err)
   822  		}
   823  		m.Ack()
   824  
   825  		if diff := cmp.Diff(m.Metadata, wm); diff != "" {
   826  			t.Fatalf("got\n%v\nwant\n%v\ndiff\n%s", m.Metadata, wm, diff)
   827  		}
   828  	}
   829  
   830  	// Verify that non-UTF8 strings in metadata key or value fail.
   831  	m := &pubsub.Message{
   832  		Body:     []byte("hello world"),
   833  		Metadata: map[string]string{escape.NonUTF8String: "bar"},
   834  	}
   835  	if err := topic.Send(ctx, m); err == nil {
   836  		t.Error("got nil error, expected error for using non-UTF8 string as metadata key")
   837  	}
   838  	m.Metadata = map[string]string{"foo": escape.NonUTF8String}
   839  	if err := topic.Send(ctx, m); err == nil {
   840  		t.Error("got nil error, expected error for using non-UTF8 string as metadata value")
   841  	}
   842  }
   843  
   844  func testNonUTF8MessageBody(t *testing.T, newHarness HarnessMaker) {
   845  	// Set up.
   846  	ctx := context.Background()
   847  	h, err := newHarness(ctx, t)
   848  	if err != nil {
   849  		t.Fatal(err)
   850  	}
   851  	defer h.Close()
   852  
   853  	topic, sub, cleanup, err := makePair(ctx, t, h)
   854  	if err != nil {
   855  		t.Fatal(err)
   856  	}
   857  	defer cleanup()
   858  
   859  	// Sort the WeirdStrings map for record/replay consistency.
   860  	var weirdStrings [][]string // [0] = key, [1] = value
   861  	for k, v := range escape.WeirdStrings {
   862  		weirdStrings = append(weirdStrings, []string{k, v})
   863  	}
   864  	sort.Slice(weirdStrings, func(i, j int) bool { return weirdStrings[i][0] < weirdStrings[j][0] })
   865  
   866  	// Construct a message body with the weird strings and some non-UTF-8 bytes.
   867  	var body []byte
   868  	for _, v := range weirdStrings {
   869  		body = append(body, []byte(v[1])...)
   870  	}
   871  	body = append(body, []byte(escape.NonUTF8String)...)
   872  	m := &pubsub.Message{Body: body}
   873  
   874  	if err := topic.Send(ctx, m); err != nil {
   875  		t.Fatal(err)
   876  	}
   877  	// The test will hang here if the messages aren't delivered, so use a shorter timeout.
   878  	ctx2, cancel := context.WithTimeout(ctx, 30*time.Second)
   879  	defer cancel()
   880  	m, err = sub.Receive(ctx2)
   881  	if err != nil {
   882  		t.Fatal(err)
   883  	}
   884  	m.Ack()
   885  
   886  	if diff := cmp.Diff(m.Body, body); diff != "" {
   887  		t.Fatalf("got\n%v\nwant\n%v\ndiff\n%s", m.Body, body, diff)
   888  	}
   889  }
   890  
   891  func isCanceled(err error) bool {
   892  	if err == context.Canceled {
   893  		return true
   894  	}
   895  	if cerr, ok := err.(*retry.ContextError); ok {
   896  		return cerr.CtxErr == context.Canceled
   897  	}
   898  	return gcerrors.Code(err) == gcerrors.Canceled
   899  }
   900  
   901  func makePair(ctx context.Context, t *testing.T, h Harness) (*pubsub.Topic, *pubsub.Subscription, func(), error) {
   902  	dt, topicCleanup, err := h.CreateTopic(ctx, t.Name())
   903  	if err != nil {
   904  		return nil, nil, nil, err
   905  	}
   906  	ds, subCleanup, err := h.CreateSubscription(ctx, dt, t.Name())
   907  	if err != nil {
   908  		topicCleanup()
   909  		return nil, nil, nil, err
   910  	}
   911  	topic := pubsub.NewTopic(dt, batchSizeOne)
   912  	sub := pubsub.NewSubscription(ds, batchSizeOne, batchSizeOne)
   913  	cleanup := func() {
   914  		if err := topic.Shutdown(ctx); err != nil {
   915  			t.Error(err)
   916  		}
   917  		if err := sub.Shutdown(ctx); err != nil {
   918  			t.Error(err)
   919  		}
   920  		subCleanup()
   921  		topicCleanup()
   922  	}
   923  	return topic, sub, cleanup, nil
   924  }
   925  
   926  // testAs tests the various As functions, using AsTest.
   927  func testAs(t *testing.T, newHarness HarnessMaker, st AsTest) {
   928  	ctx := context.Background()
   929  	h, err := newHarness(ctx, t)
   930  	if err != nil {
   931  		t.Fatal(err)
   932  	}
   933  	defer h.Close()
   934  	topic, sub, cleanup, err := makePair(ctx, t, h)
   935  	if err != nil {
   936  		t.Fatal(err)
   937  	}
   938  	defer cleanup()
   939  
   940  	if err := st.TopicCheck(topic); err != nil {
   941  		t.Error(err)
   942  	}
   943  
   944  	if err := st.SubscriptionCheck(sub); err != nil {
   945  		t.Error(err)
   946  	}
   947  
   948  	msg := &pubsub.Message{
   949  		Body:       []byte("x"),
   950  		BeforeSend: st.BeforeSend,
   951  		AfterSend:  st.AfterSend,
   952  	}
   953  	if err := topic.Send(ctx, msg); err != nil {
   954  		t.Fatal(err)
   955  	}
   956  	// The test will hang here if the messages aren't delivered, so use a shorter timeout.
   957  	ctx2, cancel := context.WithTimeout(ctx, 30*time.Second)
   958  	defer cancel()
   959  	m, err := sub.Receive(ctx2)
   960  	if err != nil {
   961  		t.Fatal(err)
   962  	}
   963  	if err := st.MessageCheck(m); err != nil {
   964  		t.Error(err)
   965  	}
   966  	m.Ack()
   967  
   968  	// Make a nonexistent topic and try to to send on it, to get an error we can
   969  	// use to call TopicErrorCheck.
   970  	dt, err := h.MakeNonexistentTopic(ctx)
   971  	if err != nil {
   972  		t.Fatal(err)
   973  	}
   974  	nonexistentTopic := pubsub.NewTopic(dt, batchSizeOne)
   975  	defer func() {
   976  		if err := nonexistentTopic.Shutdown(ctx); err != nil {
   977  			t.Error(err)
   978  		}
   979  	}()
   980  	topicErr := nonexistentTopic.Send(ctx, &pubsub.Message{})
   981  	if topicErr == nil || gcerrors.Code(topicErr) != gcerrors.NotFound {
   982  		t.Errorf("got error %v sending to nonexistent topic, want Code=NotFound", topicErr)
   983  	} else if err := st.TopicErrorCheck(topic, topicErr); err != nil {
   984  		t.Error(err)
   985  	}
   986  
   987  	// Make a nonexistent subscription and try to receive from it, to get an error
   988  	// we can use to call SubscriptionErrorCheck.
   989  	ds, cleanup, err := h.MakeNonexistentSubscription(ctx)
   990  	if err != nil {
   991  		t.Fatal(err)
   992  	}
   993  	defer cleanup()
   994  	nonExistentSub := pubsub.NewSubscription(ds, batchSizeOne, batchSizeOne)
   995  	defer func() {
   996  		if err := nonExistentSub.Shutdown(ctx); err != nil {
   997  			t.Error(err)
   998  		}
   999  	}()
  1000  
  1001  	// The test will hang here if Receive doesn't fail quickly, so set a shorter timeout.
  1002  	ctx2, cancel = context.WithTimeout(ctx, 30*time.Second)
  1003  	defer cancel()
  1004  	_, subErr := nonExistentSub.Receive(ctx2)
  1005  	if subErr == nil || ctx2.Err() != nil || gcerrors.Code(subErr) != gcerrors.NotFound {
  1006  		t.Errorf("got error %v receiving from nonexistent subscription, want Code=NotFound", subErr)
  1007  	} else if err := st.SubscriptionErrorCheck(nonExistentSub, subErr); err != nil {
  1008  		t.Error(err)
  1009  	}
  1010  }
  1011  
  1012  // Publishes a large number of messages to topic concurrently, and then times
  1013  // how long it takes to send (if timeSend is true) or receive (if timeSend
  1014  // is false) them all.
  1015  func benchmark(b *testing.B, topic *pubsub.Topic, sub *pubsub.Subscription, timeSend bool) {
  1016  	attrs := map[string]string{"label": "value"}
  1017  	body := []byte("hello, world")
  1018  	const (
  1019  		nMessages          = 10000
  1020  		concurrencySend    = 100
  1021  		concurrencyReceive = 100
  1022  	)
  1023  	if nMessages%concurrencySend != 0 || nMessages%concurrencyReceive != 0 {
  1024  		b.Fatal("nMessages must be divisible by # of sending/receiving goroutines")
  1025  	}
  1026  	b.ResetTimer()
  1027  	for i := 0; i < b.N; i++ {
  1028  		if !timeSend {
  1029  			b.StopTimer()
  1030  		}
  1031  		if err := publishNConcurrently(topic, nMessages, concurrencySend, attrs, body); err != nil {
  1032  			b.Fatalf("publishing: %v", err)
  1033  		}
  1034  		b.Logf("published %d messages", nMessages)
  1035  		if timeSend {
  1036  			b.StopTimer()
  1037  		} else {
  1038  			b.StartTimer()
  1039  		}
  1040  		if err := receiveNConcurrently(sub, nMessages, concurrencyReceive); err != nil {
  1041  			b.Fatalf("receiving: %v", err)
  1042  		}
  1043  		b.SetBytes(nMessages * 1e6)
  1044  		b.Log("MB/s is actually number of messages received per second")
  1045  		if timeSend {
  1046  			b.StartTimer()
  1047  		}
  1048  	}
  1049  }
  1050  
  1051  func publishNConcurrently(topic *pubsub.Topic, nMessages, nGoroutines int, attrs map[string]string, body []byte) error {
  1052  	return runConcurrently(nMessages, nGoroutines, func(ctx context.Context) error {
  1053  		return topic.Send(ctx, &pubsub.Message{Metadata: attrs, Body: body})
  1054  	})
  1055  }
  1056  
  1057  func receiveNConcurrently(sub *pubsub.Subscription, nMessages, nGoroutines int) error {
  1058  	return runConcurrently(nMessages, nGoroutines, func(ctx context.Context) error {
  1059  		m, err := sub.Receive(ctx)
  1060  		if err != nil {
  1061  			return err
  1062  		}
  1063  		m.Ack()
  1064  		return nil
  1065  	})
  1066  }
  1067  
  1068  // Call function f n times concurrently, using g goroutines. g must divide n.
  1069  // Wait until all calls complete. If any fail, cancel the remaining ones.
  1070  func runConcurrently(n, g int, f func(context.Context) error) error {
  1071  	gr, ctx := errgroup.WithContext(context.Background())
  1072  	ng := n / g
  1073  	for i := 0; i < g; i++ {
  1074  		gr.Go(func() error {
  1075  			for j := 0; j < ng; j++ {
  1076  				if err := f(ctx); err != nil {
  1077  					return err
  1078  				}
  1079  			}
  1080  			return nil
  1081  		})
  1082  	}
  1083  	return gr.Wait()
  1084  }