Update server test
diff --git a/acceptance/openstack/compute_test.go b/acceptance/openstack/compute_test.go
index 5b35b8f..3cccbc1 100644
--- a/acceptance/openstack/compute_test.go
+++ b/acceptance/openstack/compute_test.go
@@ -6,53 +6,9 @@
 	"github.com/rackspace/gophercloud/openstack/compute/servers"
 	"github.com/rackspace/gophercloud/openstack/compute/images"
 	"github.com/rackspace/gophercloud/openstack/compute/flavors"
-	"github.com/rackspace/gophercloud/openstack/identity"
-	"github.com/rackspace/gophercloud/openstack/utils"
 	"os"
-	"text/tabwriter"
-	"time"
-	"crypto/rand"
 )
 
-type testState struct {
-	o	identity.AuthOptions
-	a	identity.AuthResults
-	sc	*identity.ServiceCatalog
-	eps	[]identity.Endpoint
-	w	*tabwriter.Writer
-}
-
-func setupForList() (*testState, error) {
-	var err error
-
-	ts := new(testState)
-
-	ts.o, err = utils.AuthOptions()
-	if err != nil {
-		return ts, err
-	}
-
-	ts.a, err = identity.Authenticate(ts.o)
-	if err != nil {
-		return ts, err
-	}
-
-	ts.sc, err = identity.GetServiceCatalog(ts.a)
-	if err != nil {
-		return ts, err
-	}
-
-	ts.eps, err = findAllComputeEndpoints(ts.sc)
-	if err != nil {
-		return ts, err
-	}
-
-	ts.w = new(tabwriter.Writer)
-	ts.w.Init(os.Stdout, 2, 8, 2, ' ', 0)
-
-	return ts, nil
-}
-
 func TestListServers(t *testing.T) {
 	ts, err := setupForList()
 	if err != nil {
@@ -173,264 +129,57 @@
 	fmt.Printf("--------\n%d images listed.\n", n)
 }
 
-func findAllComputeEndpoints(sc *identity.ServiceCatalog) ([]identity.Endpoint, error) {
-	ces, err := sc.CatalogEntries()
-	if err != nil {
-		return nil, err
-	}
-
-	for _, ce := range ces {
-		if ce.Type == "compute" {
-			return ce.Endpoints, nil
-		}
-	}
-
-	return nil, fmt.Errorf("Compute endpoint not found.")
-}
-
-func findEndpointForRegion(eps []identity.Endpoint, r string) (string, error) {
-	for _, ep := range eps {
-		if ep.Region == r {
-			return ep.PublicURL, nil
-		}
-	}
-	return "", fmt.Errorf("Unknown region %s", r)
-}
-
 func TestCreateDestroyServer(t *testing.T) {
-	ts, err := setupForList()
+	ts, err := setupForCRUD()
 	if err != nil {
 		t.Error(err)
 		return
 	}
 
-	imageId := os.Getenv("OS_IMAGE_ID")
-	if imageId == "" {
-		t.Error("Expected OS_IMAGE_ID environment variable to be set")
-		return
-	}
-
-	flavorId := os.Getenv("OS_FLAVOR_ID")
-	if flavorId == "" {
-		t.Error("Expected OS_FLAVOR_ID environment variable to be set")
-		return
-	}
-
-	region := os.Getenv("OS_REGION_NAME")
-	if region == "" {
-		region = ts.eps[0].Region
-	}
-
-	ep, err := findEndpointForRegion(ts.eps, region)
+	err = createServer(ts)
 	if err != nil {
 		t.Error(err)
 		return
 	}
 
-	serverName := randomString("ACPTTEST", 16)
-	fmt.Printf("Attempting to create server: %s\n", serverName)
-
-	client := servers.NewClient(ep, ts.a, ts.o)
-
-	cr, err := servers.Create(client, map[string]interface{}{
-		"flavorRef": flavorId,
-		"imageRef": imageId,
-		"name": serverName,
-	})
-	if err != nil {
-		t.Error(err)
-		return
-	}
-
-	createdServer, err := servers.GetServer(cr)
-	if err != nil {
-		t.Error(err)
-		return
-	}
+	// We put this in a defer so that it gets executed even in the face of errors or panics.
 	defer func() {
-		servers.Delete(client, createdServer.Id)
+		servers.Delete(ts.client, ts.createdServer.Id)
 	}()
 
-	timeout := 300
-	for ; timeout > 0; timeout-- {
-		gr, err := servers.GetDetail(client, createdServer.Id)
-		if err != nil {
-			t.Error(err)
-			return
-		}
-
-		gottenServer, err := servers.GetServer(gr)
-		if err != nil {
-			t.Error(err)
-			return
-		}
-
-		if gottenServer.Id != createdServer.Id {
-			t.Error("Created server ID (%s) != gotten server ID (%s)", createdServer.Id, gottenServer.Id)
-			return
-		}
-
-		if gottenServer.Status == "ACTIVE" {
-			fmt.Printf("Server created after %d seconds (approximately)\n", 300-timeout)
-			break
-		}
-		time.Sleep(1*time.Second)
-	}
-	if timeout < 1 {
-		fmt.Printf("I'm not waiting around.\n")
+	err = waitForStatus(ts, "ACTIVE")
+	if err != nil {
+		t.Error(err)
 	}
 }
 
 func TestUpdateServer(t *testing.T) {
-	ts, err := setupForList()
+	ts, err := setupForCRUD()
 	if err != nil {
 		t.Error(err)
 		return
 	}
 
-	imageId := os.Getenv("OS_IMAGE_ID")
-	if imageId == "" {
-		t.Error("Expected OS_IMAGE_ID environment variable to be set")
-		return
-	}
-
-	flavorId := os.Getenv("OS_FLAVOR_ID")
-	if flavorId == "" {
-		t.Error("Expected OS_FLAVOR_ID environment variable to be set")
-		return
-	}
-
-	region := os.Getenv("OS_REGION_NAME")
-	if region == "" {
-		region = ts.eps[0].Region
-	}
-
-	ep, err := findEndpointForRegion(ts.eps, region)
+	err = createServer(ts)
 	if err != nil {
 		t.Error(err)
 		return
 	}
 
-	serverName := randomString("ACPTTEST", 16)
-	fmt.Printf("Attempting to create server: %s\n", serverName)
-
-	client := servers.NewClient(ep, ts.a, ts.o)
-
-	cr, err := servers.Create(client, map[string]interface{}{
-		"flavorRef": flavorId,
-		"imageRef": imageId,
-		"name": serverName,
-	})
-	if err != nil {
-		t.Error(err)
-		return
-	}
-
-	createdServer, err := servers.GetServer(cr)
-	if err != nil {
-		t.Error(err)
-		return
-	}
 	defer func() {
-		servers.Delete(client, createdServer.Id)
+		servers.Delete(ts.client, ts.createdServer.Id)
 	}()
 
-	timeout := 300
-	for ; timeout > 0; timeout-- {
-		gr, err := servers.GetDetail(client, createdServer.Id)
-		if err != nil {
-			t.Error(err)
-			return
-		}
-
-		gottenServer, err := servers.GetServer(gr)
-		if err != nil {
-			t.Error(err)
-			return
-		}
-
-		if gottenServer.Id != createdServer.Id {
-			t.Error("Created server ID (%s) != gotten server ID (%s)", createdServer.Id, gottenServer.Id)
-			return
-		}
-
-		if gottenServer.Status == "ACTIVE" {
-			fmt.Printf("Server created after %d seconds (approximately)\n", 300-timeout)
-			break
-		}
-		time.Sleep(1*time.Second)
-	}
-	if timeout < 1 {
-		fmt.Printf("I'm not waiting around.\n")
-	}
-
-	alternateName := randomString("ACPTTEST", 16)
-	for alternateName == serverName {
-		alternateName = randomString("ACPTTEST", 16)
-	}
-
-	fmt.Println("Attempting to change server name")
-
-	ur, err := servers.Update(client, createdServer.Id, map[string]interface{}{
-		"name": alternateName,
-	})
+	err = waitForStatus(ts, "ACTIVE")
 	if err != nil {
 		t.Error(err)
 		return
 	}
 
-	updatedServer, err := servers.GetServer(ur)
+	err = changeServerName(ts)
 	if err != nil {
 		t.Error(err)
 		return
 	}
-
-	if updatedServer.Id != createdServer.Id {
-		t.Error("Expected updated and created server to share the same ID")
-		return
-	}
-
-	timeout = 300
-	for ; timeout > 0; timeout-- {
-		gr, err := servers.GetDetail(client, createdServer.Id)
-		if err != nil {
-			t.Error(err)
-			return
-		}
-
-		gottenServer, err := servers.GetServer(gr)
-		if err != nil {
-			t.Error(err)
-			return
-		}
-
-		if gottenServer.Id != updatedServer.Id {
-			t.Error("Updated server ID (%s) != gotten server ID (%s)", updatedServer.Id, gottenServer.Id)
-			return
-		}
-
-		if gottenServer.Name == alternateName {
-			fmt.Printf("Server updated after %d seconds (approximately)\n", 300-timeout)
-			break
-		}
-		time.Sleep(1*time.Second)
-	}
-	if timeout < 1 {
-		fmt.Printf("I'm not waiting around.\n")
-	}
-}
-
-// randomString generates a string of given length, but random content.
-// All content will be within the ASCII graphic character set.
-// (Implementation from Even Shaw's contribution on
-// http://stackoverflow.com/questions/12771930/what-is-the-fastest-way-to-generate-a-long-random-string-in-go).
-func randomString(prefix string, n int) string {
-	const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
-	var bytes = make([]byte, n)
-	rand.Read(bytes)
-	for i, b := range bytes {
-		bytes[i] = alphanum[b%byte(len(alphanum))]
-	}
-	return prefix + string(bytes)
 }
 
diff --git a/acceptance/openstack/tools_test.go b/acceptance/openstack/tools_test.go
new file mode 100644
index 0000000..dc2cde8
--- /dev/null
+++ b/acceptance/openstack/tools_test.go
@@ -0,0 +1,243 @@
+package openstack
+
+import (
+	"fmt"
+	"github.com/rackspace/gophercloud/openstack/compute/servers"
+	"github.com/rackspace/gophercloud/openstack/identity"
+	"github.com/rackspace/gophercloud/openstack/utils"
+	"os"
+	"text/tabwriter"
+	"time"
+	"crypto/rand"
+)
+
+var errTimeout = fmt.Errorf("Timeout.")
+
+type testState struct {
+	o	identity.AuthOptions
+	a	identity.AuthResults
+	sc	*identity.ServiceCatalog
+	eps	[]identity.Endpoint
+	w	*tabwriter.Writer
+	imageId	string
+	flavorId string
+	region string
+	ep string
+	client *servers.Client
+	createdServer *servers.Server
+	gottenServer *servers.Server
+	updatedServer *servers.Server
+	serverName string
+	alternateName string
+}
+
+func setupForList() (*testState, error) {
+	var err error
+
+	ts := new(testState)
+
+	ts.o, err = utils.AuthOptions()
+	if err != nil {
+		return ts, err
+	}
+
+	ts.a, err = identity.Authenticate(ts.o)
+	if err != nil {
+		return ts, err
+	}
+
+	ts.sc, err = identity.GetServiceCatalog(ts.a)
+	if err != nil {
+		return ts, err
+	}
+
+	ts.eps, err = findAllComputeEndpoints(ts.sc)
+	if err != nil {
+		return ts, err
+	}
+
+	ts.w = new(tabwriter.Writer)
+	ts.w.Init(os.Stdout, 2, 8, 2, ' ', 0)
+
+	return ts, nil
+}
+
+func setupForCRUD() (*testState, error) {
+	ts, err := setupForList()
+	if err != nil {
+		return ts, err
+	}
+
+	ts.imageId = os.Getenv("OS_IMAGE_ID")
+	if ts.imageId == "" {
+		return ts, fmt.Errorf("Expected OS_IMAGE_ID environment variable to be set")
+	}
+
+	ts.flavorId = os.Getenv("OS_FLAVOR_ID")
+	if ts.flavorId == "" {
+		return ts, fmt.Errorf("Expected OS_FLAVOR_ID environment variable to be set")
+	}
+
+	ts.region = os.Getenv("OS_REGION_NAME")
+	if ts.region == "" {
+		ts.region = ts.eps[0].Region
+	}
+
+	ts.ep, err = findEndpointForRegion(ts.eps, ts.region)
+	if err != nil {
+		return ts, err
+	}
+
+	return ts, err
+}
+
+func findAllComputeEndpoints(sc *identity.ServiceCatalog) ([]identity.Endpoint, error) {
+	ces, err := sc.CatalogEntries()
+	if err != nil {
+		return nil, err
+	}
+
+	for _, ce := range ces {
+		if ce.Type == "compute" {
+			return ce.Endpoints, nil
+		}
+	}
+
+	return nil, fmt.Errorf("Compute endpoint not found.")
+}
+
+func findEndpointForRegion(eps []identity.Endpoint, r string) (string, error) {
+	for _, ep := range eps {
+		if ep.Region == r {
+			return ep.PublicURL, nil
+		}
+	}
+	return "", fmt.Errorf("Unknown region %s", r)
+}
+
+func countDown(ts *testState, timeout int) (bool, int, error) {
+	if timeout < 1 {
+		return false, 0, errTimeout
+	}
+	time.Sleep(1 * time.Second)
+	timeout--
+
+	gr, err := servers.GetDetail(ts.client, ts.createdServer.Id)
+	if err != nil {
+		return false, timeout, err
+	}
+
+	ts.gottenServer, err = servers.GetServer(gr)
+	if err != nil {
+		return false, timeout, err
+	}
+
+	return true, timeout, nil
+}
+
+func createServer(ts *testState) error {
+	ts.serverName = randomString("ACPTTEST", 16)
+	fmt.Printf("Attempting to create server: %s\n", ts.serverName)
+
+	ts.client = servers.NewClient(ts.ep, ts.a, ts.o)
+
+	cr, err := servers.Create(ts.client, map[string]interface{}{
+		"flavorRef": ts.flavorId,
+		"imageRef": ts.imageId,
+		"name": ts.serverName,
+	})
+	if err != nil {
+		return err
+	}
+
+	ts.createdServer, err = servers.GetServer(cr)
+	return err
+}
+
+func waitForStatus(ts *testState, s string) error {
+	var (
+		inProgress bool
+		timeout int
+		err error
+	)
+
+	for inProgress, timeout, err = countDown(ts, 300) ; inProgress ; inProgress, timeout, err = countDown(ts, timeout) {
+		if ts.gottenServer.Id != ts.createdServer.Id {
+			return fmt.Errorf("created server id (%s) != gotten server id (%s)", ts.createdServer.Id, ts.gottenServer.Id)
+		}
+
+		if ts.gottenServer.Status == s {
+			fmt.Printf("Server created after %d seconds (approximately)\n", 300-timeout)
+			break
+		}
+	}
+
+	if err == errTimeout {
+		fmt.Printf("I'm not waiting around.\n")
+		err = nil
+	}
+
+	return err
+}
+
+func changeServerName(ts *testState) error {
+	var (
+		inProgress bool
+		timeout int
+	)
+
+	ts.alternateName = randomString("ACPTTEST", 16)
+	for ts.alternateName == ts.serverName {
+		ts.alternateName = randomString("ACPTTEST", 16)
+	}
+	fmt.Println("Attempting to change server name")
+
+	ur, err := servers.Update(ts.client, ts.createdServer.Id, map[string]interface{}{
+		"name": ts.alternateName,
+	})
+	if err != nil {
+		return err
+	}
+
+	ts.updatedServer, err = servers.GetServer(ur)
+	if err != nil {
+		return err
+	}
+
+	if ts.updatedServer.Id != ts.createdServer.Id {
+		return fmt.Errorf("Expected updated and created server to share the same ID")
+	}
+
+	for inProgress, timeout, err = countDown(ts, 300) ; inProgress ; inProgress, timeout, err = countDown(ts, timeout) {
+		if ts.gottenServer.Id != ts.updatedServer.Id {
+			return fmt.Errorf("Updated server ID (%s) != gotten server ID (%s)", ts.updatedServer.Id, ts.gottenServer.Id)
+		}
+
+		if ts.gottenServer.Name == ts.alternateName {
+			fmt.Printf("Server updated after %d seconds (approximately)\n", 300-timeout)
+			break
+		}
+	}
+
+	if err == errTimeout {
+		fmt.Printf("I'm not waiting around.\n")
+		err = nil
+	}
+
+	return err
+}
+
+// randomString generates a string of given length, but random content.
+// All content will be within the ASCII graphic character set.
+// (Implementation from Even Shaw's contribution on
+// http://stackoverflow.com/questions/12771930/what-is-the-fastest-way-to-generate-a-long-random-string-in-go).
+func randomString(prefix string, n int) string {
+	const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+	var bytes = make([]byte, n)
+	rand.Read(bytes)
+	for i, b := range bytes {
+		bytes[i] = alphanum[b%byte(len(alphanum))]
+	}
+	return prefix + string(bytes)
+}
+