github.com/aavshr/aws-sdk-go@v1.41.3/aws/request/request_pagination_test.go (about)

     1  package request_test
     2  
     3  import (
     4  	"reflect"
     5  	"testing"
     6  
     7  	"github.com/aavshr/aws-sdk-go/aws"
     8  	"github.com/aavshr/aws-sdk-go/aws/request"
     9  	"github.com/aavshr/aws-sdk-go/awstesting"
    10  	"github.com/aavshr/aws-sdk-go/awstesting/unit"
    11  	"github.com/aavshr/aws-sdk-go/service/dynamodb"
    12  	"github.com/aavshr/aws-sdk-go/service/route53"
    13  	"github.com/aavshr/aws-sdk-go/service/s3"
    14  )
    15  
    16  // Use DynamoDB methods for simplicity
    17  func TestPaginationQueryPage(t *testing.T) {
    18  	db := dynamodb.New(unit.Session)
    19  	tokens, pages, numPages, gotToEnd := []map[string]*dynamodb.AttributeValue{}, []map[string]*dynamodb.AttributeValue{}, 0, false
    20  
    21  	reqNum := 0
    22  	resps := []*dynamodb.QueryOutput{
    23  		{
    24  			LastEvaluatedKey: map[string]*dynamodb.AttributeValue{"key": {S: aws.String("key1")}},
    25  			Count:            aws.Int64(1),
    26  			Items: []map[string]*dynamodb.AttributeValue{
    27  				{
    28  					"key": {S: aws.String("key1")},
    29  				},
    30  			},
    31  		},
    32  		{
    33  			LastEvaluatedKey: map[string]*dynamodb.AttributeValue{"key": {S: aws.String("key2")}},
    34  			Count:            aws.Int64(1),
    35  			Items: []map[string]*dynamodb.AttributeValue{
    36  				{
    37  					"key": {S: aws.String("key2")},
    38  				},
    39  			},
    40  		},
    41  		{
    42  			LastEvaluatedKey: map[string]*dynamodb.AttributeValue{},
    43  			Count:            aws.Int64(1),
    44  			Items: []map[string]*dynamodb.AttributeValue{
    45  				{
    46  					"key": {S: aws.String("key3")},
    47  				},
    48  			},
    49  		},
    50  	}
    51  
    52  	db.Handlers.Send.Clear() // mock sending
    53  	db.Handlers.Unmarshal.Clear()
    54  	db.Handlers.UnmarshalMeta.Clear()
    55  	db.Handlers.ValidateResponse.Clear()
    56  	db.Handlers.Build.PushBack(func(r *request.Request) {
    57  		in := r.Params.(*dynamodb.QueryInput)
    58  		if in == nil {
    59  			tokens = append(tokens, nil)
    60  		} else if len(in.ExclusiveStartKey) != 0 {
    61  			tokens = append(tokens, in.ExclusiveStartKey)
    62  		}
    63  	})
    64  	db.Handlers.Unmarshal.PushBack(func(r *request.Request) {
    65  		r.Data = resps[reqNum]
    66  		reqNum++
    67  	})
    68  
    69  	params := &dynamodb.QueryInput{
    70  		Limit:     aws.Int64(2),
    71  		TableName: aws.String("tablename"),
    72  	}
    73  	err := db.QueryPages(params, func(p *dynamodb.QueryOutput, last bool) bool {
    74  		numPages++
    75  		pages = append(pages, p.Items...)
    76  		if last {
    77  			if gotToEnd {
    78  				t.Errorf("last=true happened twice")
    79  			}
    80  			gotToEnd = true
    81  		}
    82  		return true
    83  	})
    84  	if err != nil {
    85  		t.Errorf("expect nil, %v", err)
    86  	}
    87  
    88  	if e, a :=
    89  		[]map[string]*dynamodb.AttributeValue{
    90  			{"key": {S: aws.String("key1")}},
    91  			{"key": {S: aws.String("key2")}},
    92  		}, tokens; !reflect.DeepEqual(e, a) {
    93  		t.Errorf("expect %v, got %v", e, a)
    94  	}
    95  	if e, a :=
    96  		[]map[string]*dynamodb.AttributeValue{
    97  			{"key": {S: aws.String("key1")}},
    98  			{"key": {S: aws.String("key2")}},
    99  			{"key": {S: aws.String("key3")}},
   100  		}, pages; !reflect.DeepEqual(e, a) {
   101  		t.Errorf("expect %v, got %v", e, a)
   102  	}
   103  	if e, a := 3, numPages; e != a {
   104  		t.Errorf("expect %v, got %v", e, a)
   105  	}
   106  	if !gotToEnd {
   107  		t.Errorf("expect true")
   108  	}
   109  	if params.ExclusiveStartKey != nil {
   110  		t.Errorf("expect nil, %v", err)
   111  	}
   112  }
   113  
   114  // Use DynamoDB methods for simplicity
   115  func TestPagination(t *testing.T) {
   116  	db := dynamodb.New(unit.Session)
   117  	tokens, pages, numPages, gotToEnd := []string{}, []string{}, 0, false
   118  
   119  	reqNum := 0
   120  	resps := []*dynamodb.ListTablesOutput{
   121  		{TableNames: []*string{aws.String("Table1"), aws.String("Table2")}, LastEvaluatedTableName: aws.String("Table2")},
   122  		{TableNames: []*string{aws.String("Table3"), aws.String("Table4")}, LastEvaluatedTableName: aws.String("Table4")},
   123  		{TableNames: []*string{aws.String("Table5")}},
   124  	}
   125  
   126  	db.Handlers.Send.Clear() // mock sending
   127  	db.Handlers.Unmarshal.Clear()
   128  	db.Handlers.UnmarshalMeta.Clear()
   129  	db.Handlers.ValidateResponse.Clear()
   130  	db.Handlers.Build.PushBack(func(r *request.Request) {
   131  		in := r.Params.(*dynamodb.ListTablesInput)
   132  		if in == nil {
   133  			tokens = append(tokens, "")
   134  		} else if in.ExclusiveStartTableName != nil {
   135  			tokens = append(tokens, *in.ExclusiveStartTableName)
   136  		}
   137  	})
   138  	db.Handlers.Unmarshal.PushBack(func(r *request.Request) {
   139  		r.Data = resps[reqNum]
   140  		reqNum++
   141  	})
   142  
   143  	params := &dynamodb.ListTablesInput{Limit: aws.Int64(2)}
   144  	err := db.ListTablesPages(params, func(p *dynamodb.ListTablesOutput, last bool) bool {
   145  		numPages++
   146  		for _, t := range p.TableNames {
   147  			pages = append(pages, *t)
   148  		}
   149  		if last {
   150  			if gotToEnd {
   151  				t.Errorf("last=true happened twice")
   152  			}
   153  			gotToEnd = true
   154  		}
   155  		return true
   156  	})
   157  
   158  	if e, a := []string{"Table2", "Table4"}, tokens; !reflect.DeepEqual(e, a) {
   159  		t.Errorf("expect %v, got %v", e, a)
   160  	}
   161  	if e, a := []string{"Table1", "Table2", "Table3", "Table4", "Table5"}, pages; !reflect.DeepEqual(e, a) {
   162  		t.Errorf("expect %v, got %v", e, a)
   163  	}
   164  	if e, a := 3, numPages; e != a {
   165  		t.Errorf("expect %v, got %v", e, a)
   166  	}
   167  	if !gotToEnd {
   168  		t.Errorf("expect true")
   169  	}
   170  	if err != nil {
   171  		t.Errorf("expect nil, %v", err)
   172  	}
   173  	if params.ExclusiveStartTableName != nil {
   174  		t.Errorf("expect nil, %v", err)
   175  	}
   176  }
   177  
   178  // Use DynamoDB methods for simplicity
   179  func TestPaginationEachPage(t *testing.T) {
   180  	db := dynamodb.New(unit.Session)
   181  	tokens, pages, numPages, gotToEnd := []string{}, []string{}, 0, false
   182  
   183  	reqNum := 0
   184  	resps := []*dynamodb.ListTablesOutput{
   185  		{TableNames: []*string{aws.String("Table1"), aws.String("Table2")}, LastEvaluatedTableName: aws.String("Table2")},
   186  		{TableNames: []*string{aws.String("Table3"), aws.String("Table4")}, LastEvaluatedTableName: aws.String("Table4")},
   187  		{TableNames: []*string{aws.String("Table5")}},
   188  	}
   189  
   190  	db.Handlers.Send.Clear() // mock sending
   191  	db.Handlers.Unmarshal.Clear()
   192  	db.Handlers.UnmarshalMeta.Clear()
   193  	db.Handlers.ValidateResponse.Clear()
   194  	db.Handlers.Build.PushBack(func(r *request.Request) {
   195  		in := r.Params.(*dynamodb.ListTablesInput)
   196  		if in == nil {
   197  			tokens = append(tokens, "")
   198  		} else if in.ExclusiveStartTableName != nil {
   199  			tokens = append(tokens, *in.ExclusiveStartTableName)
   200  		}
   201  	})
   202  	db.Handlers.Unmarshal.PushBack(func(r *request.Request) {
   203  		r.Data = resps[reqNum]
   204  		reqNum++
   205  	})
   206  
   207  	params := &dynamodb.ListTablesInput{Limit: aws.Int64(2)}
   208  	req, _ := db.ListTablesRequest(params)
   209  	err := req.EachPage(func(p interface{}, last bool) bool {
   210  		numPages++
   211  		for _, t := range p.(*dynamodb.ListTablesOutput).TableNames {
   212  			pages = append(pages, *t)
   213  		}
   214  		if last {
   215  			if gotToEnd {
   216  				t.Errorf("last=true happened twice")
   217  			}
   218  			gotToEnd = true
   219  		}
   220  
   221  		return true
   222  	})
   223  
   224  	if e, a := []string{"Table2", "Table4"}, tokens; !reflect.DeepEqual(e, a) {
   225  		t.Errorf("expect %v, got %v", e, a)
   226  	}
   227  	if e, a := []string{"Table1", "Table2", "Table3", "Table4", "Table5"}, pages; !reflect.DeepEqual(e, a) {
   228  		t.Errorf("expect %v, got %v", e, a)
   229  	}
   230  	if e, a := 3, numPages; e != a {
   231  		t.Errorf("expect %v, got %v", e, a)
   232  	}
   233  	if !gotToEnd {
   234  		t.Errorf("expect true")
   235  	}
   236  	if err != nil {
   237  		t.Errorf("expect nil, %v", err)
   238  	}
   239  }
   240  
   241  // Use DynamoDB methods for simplicity
   242  func TestPaginationEarlyExit(t *testing.T) {
   243  	db := dynamodb.New(unit.Session)
   244  	numPages, gotToEnd := 0, false
   245  
   246  	reqNum := 0
   247  	resps := []*dynamodb.ListTablesOutput{
   248  		{TableNames: []*string{aws.String("Table1"), aws.String("Table2")}, LastEvaluatedTableName: aws.String("Table2")},
   249  		{TableNames: []*string{aws.String("Table3"), aws.String("Table4")}, LastEvaluatedTableName: aws.String("Table4")},
   250  		{TableNames: []*string{aws.String("Table5")}},
   251  	}
   252  
   253  	db.Handlers.Send.Clear() // mock sending
   254  	db.Handlers.Unmarshal.Clear()
   255  	db.Handlers.UnmarshalMeta.Clear()
   256  	db.Handlers.ValidateResponse.Clear()
   257  	db.Handlers.Unmarshal.PushBack(func(r *request.Request) {
   258  		r.Data = resps[reqNum]
   259  		reqNum++
   260  	})
   261  
   262  	params := &dynamodb.ListTablesInput{Limit: aws.Int64(2)}
   263  	err := db.ListTablesPages(params, func(p *dynamodb.ListTablesOutput, last bool) bool {
   264  		numPages++
   265  		if numPages == 2 {
   266  			return false
   267  		}
   268  		if last {
   269  			if gotToEnd {
   270  				t.Errorf("last=true happened twice")
   271  			}
   272  			gotToEnd = true
   273  		}
   274  		return true
   275  	})
   276  
   277  	if e, a := 2, numPages; e != a {
   278  		t.Errorf("expect %v, got %v", e, a)
   279  	}
   280  	if gotToEnd {
   281  		t.Errorf("expect false")
   282  	}
   283  	if err != nil {
   284  		t.Errorf("expect nil, %v", err)
   285  	}
   286  }
   287  
   288  func TestSkipPagination(t *testing.T) {
   289  	client := s3.New(unit.Session)
   290  	client.Handlers.Send.Clear() // mock sending
   291  	client.Handlers.Unmarshal.Clear()
   292  	client.Handlers.UnmarshalMeta.Clear()
   293  	client.Handlers.ValidateResponse.Clear()
   294  	client.Handlers.Unmarshal.PushBack(func(r *request.Request) {
   295  		r.Data = &s3.HeadBucketOutput{}
   296  	})
   297  
   298  	req, _ := client.HeadBucketRequest(&s3.HeadBucketInput{Bucket: aws.String("bucket")})
   299  
   300  	numPages, gotToEnd := 0, false
   301  	req.EachPage(func(p interface{}, last bool) bool {
   302  		numPages++
   303  		if last {
   304  			gotToEnd = true
   305  		}
   306  		return true
   307  	})
   308  	if e, a := 1, numPages; e != a {
   309  		t.Errorf("expect %v, got %v", e, a)
   310  	}
   311  	if !gotToEnd {
   312  		t.Errorf("expect true")
   313  	}
   314  }
   315  
   316  // Use S3 for simplicity
   317  func TestPaginationTruncation(t *testing.T) {
   318  	client := s3.New(unit.Session)
   319  
   320  	reqNum := 0
   321  	resps := []*s3.ListObjectsOutput{
   322  		{IsTruncated: aws.Bool(true), Contents: []*s3.Object{{Key: aws.String("Key1")}}},
   323  		{IsTruncated: aws.Bool(true), Contents: []*s3.Object{{Key: aws.String("Key2")}}},
   324  		{IsTruncated: aws.Bool(false), Contents: []*s3.Object{{Key: aws.String("Key3")}}},
   325  		{IsTruncated: aws.Bool(true), Contents: []*s3.Object{{Key: aws.String("Key4")}}},
   326  	}
   327  
   328  	client.Handlers.Send.Clear() // mock sending
   329  	client.Handlers.Unmarshal.Clear()
   330  	client.Handlers.UnmarshalMeta.Clear()
   331  	client.Handlers.ValidateResponse.Clear()
   332  	client.Handlers.Unmarshal.PushBack(func(r *request.Request) {
   333  		r.Data = resps[reqNum]
   334  		reqNum++
   335  	})
   336  
   337  	params := &s3.ListObjectsInput{Bucket: aws.String("bucket")}
   338  
   339  	results := []string{}
   340  	err := client.ListObjectsPages(params, func(p *s3.ListObjectsOutput, last bool) bool {
   341  		results = append(results, *p.Contents[0].Key)
   342  		return true
   343  	})
   344  
   345  	if e, a := []string{"Key1", "Key2", "Key3"}, results; !reflect.DeepEqual(e, a) {
   346  		t.Errorf("expect %v, got %v", e, a)
   347  	}
   348  	if err != nil {
   349  		t.Errorf("expect nil, %v", err)
   350  	}
   351  
   352  	// Try again without truncation token at all
   353  	reqNum = 0
   354  	resps[1].IsTruncated = nil
   355  	resps[2].IsTruncated = aws.Bool(true)
   356  	results = []string{}
   357  	err = client.ListObjectsPages(params, func(p *s3.ListObjectsOutput, last bool) bool {
   358  		results = append(results, *p.Contents[0].Key)
   359  		return true
   360  	})
   361  
   362  	if e, a := []string{"Key1", "Key2"}, results; !reflect.DeepEqual(e, a) {
   363  		t.Errorf("expect %v, got %v", e, a)
   364  	}
   365  	if err != nil {
   366  		t.Errorf("expect nil, %v", err)
   367  	}
   368  }
   369  
   370  func TestPaginationNilToken(t *testing.T) {
   371  	client := route53.New(unit.Session)
   372  
   373  	reqNum := 0
   374  	resps := []*route53.ListResourceRecordSetsOutput{
   375  		{
   376  			ResourceRecordSets: []*route53.ResourceRecordSet{
   377  				{Name: aws.String("first.example.com.")},
   378  			},
   379  			IsTruncated:          aws.Bool(true),
   380  			NextRecordName:       aws.String("second.example.com."),
   381  			NextRecordType:       aws.String("MX"),
   382  			NextRecordIdentifier: aws.String("second"),
   383  			MaxItems:             aws.String("1"),
   384  		},
   385  		{
   386  			ResourceRecordSets: []*route53.ResourceRecordSet{
   387  				{Name: aws.String("second.example.com.")},
   388  			},
   389  			IsTruncated:    aws.Bool(true),
   390  			NextRecordName: aws.String("third.example.com."),
   391  			NextRecordType: aws.String("MX"),
   392  			MaxItems:       aws.String("1"),
   393  		},
   394  		{
   395  			ResourceRecordSets: []*route53.ResourceRecordSet{
   396  				{Name: aws.String("third.example.com.")},
   397  			},
   398  			IsTruncated: aws.Bool(false),
   399  			MaxItems:    aws.String("1"),
   400  		},
   401  	}
   402  	client.Handlers.Send.Clear() // mock sending
   403  	client.Handlers.Unmarshal.Clear()
   404  	client.Handlers.UnmarshalMeta.Clear()
   405  	client.Handlers.ValidateResponse.Clear()
   406  
   407  	idents := []string{}
   408  	client.Handlers.Build.PushBack(func(r *request.Request) {
   409  		p := r.Params.(*route53.ListResourceRecordSetsInput)
   410  		idents = append(idents, aws.StringValue(p.StartRecordIdentifier))
   411  
   412  	})
   413  	client.Handlers.Unmarshal.PushBack(func(r *request.Request) {
   414  		r.Data = resps[reqNum]
   415  		reqNum++
   416  	})
   417  
   418  	params := &route53.ListResourceRecordSetsInput{
   419  		HostedZoneId: aws.String("id-zone"),
   420  	}
   421  
   422  	results := []string{}
   423  	err := client.ListResourceRecordSetsPages(params, func(p *route53.ListResourceRecordSetsOutput, last bool) bool {
   424  		results = append(results, *p.ResourceRecordSets[0].Name)
   425  		return true
   426  	})
   427  
   428  	if err != nil {
   429  		t.Errorf("expect nil, %v", err)
   430  	}
   431  	if e, a := []string{"", "second", ""}, idents; !reflect.DeepEqual(e, a) {
   432  		t.Errorf("expect %v, got %v", e, a)
   433  	}
   434  	if e, a := []string{"first.example.com.", "second.example.com.", "third.example.com."}, results; !reflect.DeepEqual(e, a) {
   435  		t.Errorf("expect %v, got %v", e, a)
   436  	}
   437  }
   438  
   439  func TestPaginationNilInput(t *testing.T) {
   440  	// Code generation doesn't have a great way to verify the code is correct
   441  	// other than being run via unit tests in the SDK. This should be fixed
   442  	// So code generation can be validated independently.
   443  
   444  	client := s3.New(unit.Session)
   445  	client.Handlers.Validate.Clear()
   446  	client.Handlers.Send.Clear() // mock sending
   447  	client.Handlers.Unmarshal.Clear()
   448  	client.Handlers.UnmarshalMeta.Clear()
   449  	client.Handlers.ValidateResponse.Clear()
   450  	client.Handlers.Unmarshal.PushBack(func(r *request.Request) {
   451  		r.Data = &s3.ListObjectsOutput{}
   452  	})
   453  
   454  	gotToEnd := false
   455  	numPages := 0
   456  	err := client.ListObjectsPages(nil, func(p *s3.ListObjectsOutput, last bool) bool {
   457  		numPages++
   458  		if last {
   459  			gotToEnd = true
   460  		}
   461  		return true
   462  	})
   463  
   464  	if err != nil {
   465  		t.Fatalf("expect no error, but got %v", err)
   466  	}
   467  	if e, a := 1, numPages; e != a {
   468  		t.Errorf("expect %d number pages but got %d", e, a)
   469  	}
   470  	if !gotToEnd {
   471  		t.Errorf("expect to of gotten to end, did not")
   472  	}
   473  }
   474  
   475  func TestPaginationWithContextNilInput(t *testing.T) {
   476  	// Code generation doesn't have a great way to verify the code is correct
   477  	// other than being run via unit tests in the SDK. This should be fixed
   478  	// So code generation can be validated independently.
   479  
   480  	client := s3.New(unit.Session)
   481  	client.Handlers.Validate.Clear()
   482  	client.Handlers.Send.Clear() // mock sending
   483  	client.Handlers.Unmarshal.Clear()
   484  	client.Handlers.UnmarshalMeta.Clear()
   485  	client.Handlers.ValidateResponse.Clear()
   486  	client.Handlers.Unmarshal.PushBack(func(r *request.Request) {
   487  		r.Data = &s3.ListObjectsOutput{}
   488  	})
   489  
   490  	gotToEnd := false
   491  	numPages := 0
   492  	ctx := &awstesting.FakeContext{DoneCh: make(chan struct{})}
   493  	err := client.ListObjectsPagesWithContext(ctx, nil, func(p *s3.ListObjectsOutput, last bool) bool {
   494  		numPages++
   495  		if last {
   496  			gotToEnd = true
   497  		}
   498  		return true
   499  	})
   500  
   501  	if err != nil {
   502  		t.Fatalf("expect no error, but got %v", err)
   503  	}
   504  	if e, a := 1, numPages; e != a {
   505  		t.Errorf("expect %d number pages but got %d", e, a)
   506  	}
   507  	if !gotToEnd {
   508  		t.Errorf("expect to of gotten to end, did not")
   509  	}
   510  }
   511  
   512  func TestPagination_Standalone(t *testing.T) {
   513  	type testPageInput struct {
   514  		NextToken *string
   515  	}
   516  	type testPageOutput struct {
   517  		Value     *string
   518  		NextToken *string
   519  	}
   520  	type testCase struct {
   521  		Value, PrevToken, NextToken *string
   522  	}
   523  
   524  	type testCaseList struct {
   525  		StopOnSameToken bool
   526  		Cases           []testCase
   527  	}
   528  
   529  	cases := []testCaseList{
   530  		{
   531  			Cases: []testCase{
   532  				{aws.String("FirstValue"), aws.String("InitalToken"), aws.String("FirstToken")},
   533  				{aws.String("SecondValue"), aws.String("FirstToken"), aws.String("SecondToken")},
   534  				{aws.String("ThirdValue"), aws.String("SecondToken"), nil},
   535  			},
   536  			StopOnSameToken: false,
   537  		},
   538  		{
   539  			Cases: []testCase{
   540  				{aws.String("FirstValue"), aws.String("InitalToken"), aws.String("FirstToken")},
   541  				{aws.String("SecondValue"), aws.String("FirstToken"), aws.String("SecondToken")},
   542  				{aws.String("ThirdValue"), aws.String("SecondToken"), aws.String("")},
   543  			},
   544  			StopOnSameToken: false,
   545  		},
   546  		{
   547  			Cases: []testCase{
   548  				{aws.String("FirstValue"), aws.String("InitalToken"), aws.String("FirstToken")},
   549  				{aws.String("SecondValue"), aws.String("FirstToken"), aws.String("SecondToken")},
   550  				{nil, aws.String("SecondToken"), aws.String("SecondToken")},
   551  			},
   552  			StopOnSameToken: true,
   553  		},
   554  		{
   555  			Cases: []testCase{
   556  				{aws.String("FirstValue"), aws.String("InitalToken"), aws.String("FirstToken")},
   557  				{aws.String("SecondValue"), aws.String("FirstToken"), aws.String("SecondToken")},
   558  				{aws.String("SecondValue"), aws.String("SecondToken"), aws.String("SecondToken")},
   559  			},
   560  			StopOnSameToken: true,
   561  		},
   562  	}
   563  
   564  	for _, testcase := range cases {
   565  		c := testcase.Cases
   566  		input := testPageInput{
   567  			NextToken: c[0].PrevToken,
   568  		}
   569  
   570  		svc := awstesting.NewClient()
   571  		i := 0
   572  		p := request.Pagination{
   573  			EndPageOnSameToken: testcase.StopOnSameToken,
   574  			NewRequest: func() (*request.Request, error) {
   575  				r := svc.NewRequest(
   576  					&request.Operation{
   577  						Name: "Operation",
   578  						Paginator: &request.Paginator{
   579  							InputTokens:  []string{"NextToken"},
   580  							OutputTokens: []string{"NextToken"},
   581  						},
   582  					},
   583  					&input, &testPageOutput{},
   584  				)
   585  				// Setup handlers for testing
   586  				r.Handlers.Clear()
   587  				r.Handlers.Build.PushBack(func(req *request.Request) {
   588  					if e, a := len(c), i+1; a > e {
   589  						t.Fatalf("expect no more than %d requests, got %d", e, a)
   590  					}
   591  					in := req.Params.(*testPageInput)
   592  					if e, a := aws.StringValue(c[i].PrevToken), aws.StringValue(in.NextToken); e != a {
   593  						t.Errorf("%d, expect NextToken input %q, got %q", i, e, a)
   594  					}
   595  				})
   596  				r.Handlers.Unmarshal.PushBack(func(req *request.Request) {
   597  					out := &testPageOutput{
   598  						Value: c[i].Value,
   599  					}
   600  					if c[i].NextToken != nil {
   601  						next := *c[i].NextToken
   602  						out.NextToken = aws.String(next)
   603  					}
   604  					req.Data = out
   605  				})
   606  				return r, nil
   607  			},
   608  		}
   609  
   610  		for p.Next() {
   611  			data := p.Page().(*testPageOutput)
   612  
   613  			if e, a := aws.StringValue(c[i].Value), aws.StringValue(data.Value); e != a {
   614  				t.Errorf("%d, expect Value to be %q, got %q", i, e, a)
   615  			}
   616  			if e, a := aws.StringValue(c[i].NextToken), aws.StringValue(data.NextToken); e != a {
   617  				t.Errorf("%d, expect NextToken to be %q, got %q", i, e, a)
   618  			}
   619  
   620  			i++
   621  		}
   622  		if e, a := len(c), i; e != a {
   623  			t.Errorf("expected to process %d pages, did %d", e, a)
   624  		}
   625  		if err := p.Err(); err != nil {
   626  			t.Fatalf("%d, expected no error, got %v", i, err)
   627  		}
   628  	}
   629  }
   630  
   631  // Benchmarks
   632  var benchResps = []*dynamodb.ListTablesOutput{
   633  	{TableNames: []*string{aws.String("TABLE"), aws.String("NXT")}, LastEvaluatedTableName: aws.String("NXT")},
   634  	{TableNames: []*string{aws.String("TABLE"), aws.String("NXT")}, LastEvaluatedTableName: aws.String("NXT")},
   635  	{TableNames: []*string{aws.String("TABLE"), aws.String("NXT")}, LastEvaluatedTableName: aws.String("NXT")},
   636  	{TableNames: []*string{aws.String("TABLE"), aws.String("NXT")}, LastEvaluatedTableName: aws.String("NXT")},
   637  	{TableNames: []*string{aws.String("TABLE"), aws.String("NXT")}, LastEvaluatedTableName: aws.String("NXT")},
   638  	{TableNames: []*string{aws.String("TABLE"), aws.String("NXT")}, LastEvaluatedTableName: aws.String("NXT")},
   639  	{TableNames: []*string{aws.String("TABLE"), aws.String("NXT")}, LastEvaluatedTableName: aws.String("NXT")},
   640  	{TableNames: []*string{aws.String("TABLE"), aws.String("NXT")}, LastEvaluatedTableName: aws.String("NXT")},
   641  	{TableNames: []*string{aws.String("TABLE"), aws.String("NXT")}, LastEvaluatedTableName: aws.String("NXT")},
   642  	{TableNames: []*string{aws.String("TABLE"), aws.String("NXT")}, LastEvaluatedTableName: aws.String("NXT")},
   643  	{TableNames: []*string{aws.String("TABLE"), aws.String("NXT")}, LastEvaluatedTableName: aws.String("NXT")},
   644  	{TableNames: []*string{aws.String("TABLE"), aws.String("NXT")}, LastEvaluatedTableName: aws.String("NXT")},
   645  	{TableNames: []*string{aws.String("TABLE"), aws.String("NXT")}, LastEvaluatedTableName: aws.String("NXT")},
   646  	{TableNames: []*string{aws.String("TABLE")}},
   647  }
   648  
   649  var benchDb = func() *dynamodb.DynamoDB {
   650  	db := dynamodb.New(unit.Session)
   651  	db.Handlers.Send.Clear() // mock sending
   652  	db.Handlers.Unmarshal.Clear()
   653  	db.Handlers.UnmarshalMeta.Clear()
   654  	db.Handlers.ValidateResponse.Clear()
   655  	return db
   656  }
   657  
   658  func BenchmarkCodegenIterator(b *testing.B) {
   659  	reqNum := 0
   660  	db := benchDb()
   661  	db.Handlers.Unmarshal.PushBack(func(r *request.Request) {
   662  		r.Data = benchResps[reqNum]
   663  		reqNum++
   664  	})
   665  
   666  	input := &dynamodb.ListTablesInput{Limit: aws.Int64(2)}
   667  	iter := func(fn func(*dynamodb.ListTablesOutput, bool) bool) error {
   668  		page, _ := db.ListTablesRequest(input)
   669  		for ; page != nil; page = page.NextPage() {
   670  			page.Send()
   671  			out := page.Data.(*dynamodb.ListTablesOutput)
   672  			if result := fn(out, !page.HasNextPage()); page.Error != nil || !result {
   673  				return page.Error
   674  			}
   675  		}
   676  		return nil
   677  	}
   678  
   679  	for i := 0; i < b.N; i++ {
   680  		reqNum = 0
   681  		iter(func(p *dynamodb.ListTablesOutput, last bool) bool {
   682  			return true
   683  		})
   684  	}
   685  }
   686  
   687  func BenchmarkEachPageIterator(b *testing.B) {
   688  	reqNum := 0
   689  	db := benchDb()
   690  	db.Handlers.Unmarshal.PushBack(func(r *request.Request) {
   691  		r.Data = benchResps[reqNum]
   692  		reqNum++
   693  	})
   694  
   695  	input := &dynamodb.ListTablesInput{Limit: aws.Int64(2)}
   696  	for i := 0; i < b.N; i++ {
   697  		reqNum = 0
   698  		req, _ := db.ListTablesRequest(input)
   699  		req.EachPage(func(p interface{}, last bool) bool {
   700  			return true
   701  		})
   702  	}
   703  }