First cut at pagination.
diff --git a/collections_test.go b/collections_test.go
new file mode 100644
index 0000000..4c8674d
--- /dev/null
+++ b/collections_test.go
@@ -0,0 +1,198 @@
+package gophercloud
+
+import (
+ "errors"
+ "fmt"
+ "net/http"
+ "reflect"
+ "testing"
+
+ "github.com/rackspace/gophercloud/testhelper"
+)
+
+// SinglePage sample and test cases.
+
+type SinglePageCollection struct {
+ results []int
+}
+
+func (c SinglePageCollection) Pager() Pager {
+ return SinglePager{}
+}
+
+func AsSingleInts(c Collection) []int {
+ return c.(SinglePageCollection).results
+}
+
+var single = SinglePageCollection{
+ results: []int{1, 2, 3},
+}
+
+func TestEnumerateSinglePaged(t *testing.T) {
+ callCount := 0
+ EachPage(single, func(page Collection) bool {
+ callCount++
+
+ expected := []int{1, 2, 3}
+ actual := AsSingleInts(page)
+ if !reflect.DeepEqual(expected, actual) {
+ t.Errorf("Expected %v, but was %v", expected, actual)
+ }
+ return true
+ })
+
+ if callCount != 1 {
+ t.Errorf("Callback was invoked %d times", callCount)
+ }
+}
+
+func TestAllSinglePaged(t *testing.T) {
+ r, err := AllPages(single)
+ if err != nil {
+ t.Fatalf("Unexpected error when iterating pages: %v", err)
+ }
+
+ expected := []int{1, 2, 3}
+ actual := AsSingleInts(r)
+ if !reflect.DeepEqual(expected, actual) {
+ t.Errorf("Expected %v, but was %v", expected, actual)
+ }
+}
+
+// LinkedPager sample and test cases.
+
+type LinkedCollection struct {
+ PaginationLinks
+
+ service *ServiceClient
+ results []int
+}
+
+func (c LinkedCollection) Links() PaginationLinks {
+ return c.PaginationLinks
+}
+
+func (c LinkedCollection) Service() *ServiceClient {
+ return c.service
+}
+
+func (c LinkedCollection) Interpret(response interface{}) (LinkCollection, error) {
+ fmt.Printf("Interpreting result: %#v\n", response)
+ casted, ok := response.([]interface{})
+ if ok {
+ asInts := make([]int, len(casted))
+ for index, item := range casted {
+ f := item.(float64)
+ asInts[index] = int(f)
+ }
+
+ var nextURL *string
+ switch asInts[0] {
+ case 4:
+ u := testhelper.Server.URL + "/foo?page=3&perPage=3"
+ nextURL = &u
+ case 7:
+ // Leave nextURL as nil.
+ default:
+ return nil, fmt.Errorf("Unexpected resultset: %#v", asInts)
+ }
+
+ result := LinkedCollection{
+ PaginationLinks: PaginationLinks{Next: nextURL},
+ service: c.service,
+ results: asInts,
+ }
+ if nextURL != nil {
+ fmt.Printf("Returning result: %s\n", *nextURL)
+ } else {
+ fmt.Printf("No next link")
+ }
+ return result, nil
+ }
+ return nil, errors.New("Wat")
+}
+
+func (c LinkedCollection) Pager() Pager {
+ return NewLinkPager(c)
+}
+
+func AsLinkedInts(results Collection) []int {
+ return results.(LinkedCollection).results
+}
+
+func createLinked() LinkedCollection {
+ nextURL := testhelper.Server.URL + "/foo?page=2&perPage=3"
+ return LinkedCollection{
+ PaginationLinks: PaginationLinks{Next: &nextURL},
+ service: &ServiceClient{
+ Provider: &ProviderClient{TokenID: "1234"},
+ Endpoint: testhelper.Endpoint(),
+ },
+ results: []int{1, 2, 3},
+ }
+}
+
+func setupLinkedResponses(t *testing.T) {
+ testhelper.Mux.HandleFunc("/foo", func(w http.ResponseWriter, r *http.Request) {
+ testhelper.TestMethod(t, r, "GET")
+ testhelper.TestHeader(t, r, "X-Auth-Token", "1234")
+ w.Header().Add("Content-Type", "application/json")
+
+ r.ParseForm()
+
+ pages := r.Form["page"]
+ if len(pages) != 1 {
+ t.Errorf("Endpoint called with unexpected page: %#v", r.Form)
+ }
+
+ switch pages[0] {
+ case "2":
+ fmt.Fprintf(w, `[4, 5, 6]`)
+ case "3":
+ fmt.Fprintf(w, `[7, 8, 9]`)
+ default:
+ t.Errorf("Endpoint called with unexpected page: %s", pages[0])
+ }
+ })
+}
+
+func TestEnumerateLinked(t *testing.T) {
+ testhelper.SetupHTTP()
+ defer testhelper.TeardownHTTP()
+
+ setupLinkedResponses(t)
+ lc := createLinked()
+
+ callCount := 0
+ err := EachPage(lc, func(page Collection) bool {
+ actual := AsLinkedInts(page)
+ t.Logf("Handler invoked with %v", actual)
+
+ var expected []int
+ switch callCount {
+ case 0:
+ expected = []int{1, 2, 3}
+ case 1:
+ expected = []int{4, 5, 6}
+ case 2:
+ expected = []int{7, 8, 9}
+ default:
+ t.Fatalf("Unexpected call count: %d", callCount)
+ return false
+ }
+
+ if !reflect.DeepEqual(expected, actual) {
+ t.Errorf("Call %d: Expected %#v, but was %#v", callCount, expected, actual)
+ }
+
+ callCount++
+ return true
+ })
+ if err != nil {
+ t.Errorf("Unexpected error for page iteration: %v", err)
+ }
+
+ if callCount != 3 {
+ t.Errorf("Expected 3 calls, but was %d", callCount)
+ }
+}