| Index: pkg/gcloud/test/storage/e2e_test.dart | 
| diff --git a/pkg/gcloud/test/storage/e2e_test.dart b/pkg/gcloud/test/storage/e2e_test.dart | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..fabbf48dff7b3d2b7f77c4ec33f45dd7f28f1d61 | 
| --- /dev/null | 
| +++ b/pkg/gcloud/test/storage/e2e_test.dart | 
| @@ -0,0 +1,271 @@ | 
| +// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file | 
| +// for details. All rights reserved. Use of this source code is governed by a | 
| +// BSD-style license that can be found in the LICENSE file. | 
| + | 
| +library gcloud.storage; | 
| + | 
| +import 'dart:async'; | 
| + | 
| +import 'package:gcloud/storage.dart'; | 
| +import 'package:googleapis/common/common.dart' as common; | 
| +import 'package:unittest/unittest.dart'; | 
| + | 
| +import '../common_e2e.dart'; | 
| + | 
| +String generateBucketName() { | 
| +  var id = new DateTime.now().millisecondsSinceEpoch; | 
| +  return 'dart-e2e-test-$id'; | 
| +} | 
| + | 
| +bool testDetailedApiError(e) => e is common.DetailedApiRequestError; | 
| + | 
| +// Generate a list just above the limit when changing to resumable upload. | 
| +const int MB = 1024 * 1024; | 
| +const int maxNormalUpload = 1 * MB; | 
| +const int minResumableUpload = maxNormalUpload + 1; | 
| +var bytesResumableUpload = | 
| +    new List.generate(minResumableUpload, (e) => e & 255); | 
| + | 
| +runTests(Storage storage, Bucket testBucket) { | 
| +  group('bucket', () { | 
| +    test('create-info-delete', () { | 
| +      var bucketName = generateBucketName(); | 
| +      return storage.createBucket(bucketName).then(expectAsync((result) { | 
| +        expect(result, isNull); | 
| +        return storage.bucketInfo(bucketName).then(expectAsync((info) { | 
| +          expect(info.bucketName, bucketName); | 
| +          expect(info.etag, isNotNull); | 
| +          expect(info.created is DateTime, isTrue); | 
| +          expect(info.id, isNotNull); | 
| +          return storage.deleteBucket(bucketName) | 
| +              .then(expectAsync((result) { | 
| +                expect(result, isNull); | 
| +              })); | 
| +        })); | 
| +      })); | 
| +    }); | 
| + | 
| +    test('create-with-predefined-acl-delete', () { | 
| +      Future<Acl> test(predefinedAcl, expectedLength) { | 
| +        var bucketName = generateBucketName(); | 
| +        return storage.createBucket(bucketName, predefinedAcl: predefinedAcl) | 
| +            .then(expectAsync((result) { | 
| +              expect(result, isNull); | 
| +              return storage.bucketInfo(bucketName).then(expectAsync((info) { | 
| +                var acl = info.acl; | 
| +                expect(info.bucketName, bucketName); | 
| +                expect(acl.entries.length, expectedLength); | 
| +                return storage.deleteBucket(bucketName) | 
| +                    .then(expectAsync((result) { | 
| +                      expect(result, isNull); | 
| +                    })); | 
| +              })); | 
| +        })); | 
| +      } | 
| + | 
| +      return Future.forEach([ | 
| +          // TODO: Figure out why some returned ACLs are empty. | 
| +          () => test(PredefinedAcl.authenticatedRead, 0), | 
| +          // [test, [PredefinedAcl.private, 0]],  // TODO: Cannot delete. | 
| +          () => test(PredefinedAcl.projectPrivate, 3), | 
| +          () => test(PredefinedAcl.publicRead, 0), | 
| +          () => test(PredefinedAcl.publicReadWrite, 0) | 
| +      ], (f) => f().then(expectAsync((_) {}))); | 
| +    }); | 
| + | 
| +    test('create-error', () { | 
| +      var bucketName = generateBucketName(); | 
| + | 
| +      storage.createBucket('goog-reserved').catchError(expectAsync((e) { | 
| +        expect(e, isNotNull); | 
| +      }), test: testDetailedApiError); | 
| +    }); | 
| +  }); | 
| + | 
| +  // TODO: Remove solo_ here when the rate-limit issue have been resolved. | 
| +  solo_group('object', () { | 
| +    // Run all object tests in the same bucket to try to avoid the rate-limit | 
| +    // for creating and deleting buckets while testing. | 
| +    Future withTestBucket(function) { | 
| +      return function(testBucket).whenComplete(() { | 
| +        // TODO: Clean the bucket. | 
| +      }); | 
| +    } | 
| + | 
| +    test('create-read-delete', () { | 
| +      Future test(name, bytes) { | 
| +      return withTestBucket((Bucket bucket) { | 
| +        return bucket.writeBytes('test', bytes).then(expectAsync((info) { | 
| +          expect(info, isNotNull); | 
| +          return bucket.read('test') | 
| +              .fold([], (p, e) => p..addAll(e)) | 
| +              .then(expectAsync((result) { | 
| +                expect(result, bytes); | 
| +                return bucket.delete('test').then(expectAsync((result) { | 
| +                  expect(result, isNull); | 
| +              })); | 
| +          })); | 
| +        })); | 
| +      }); | 
| +      } | 
| + | 
| +      return Future.forEach([ | 
| +          () => test('test-1', [1, 2, 3]), | 
| +          () => test('test-2', bytesResumableUpload) | 
| +     ], (f) => f().then(expectAsync((_) {}))); | 
| +    }); | 
| + | 
| +    test('create-with-predefined-acl-delete', () { | 
| +      return withTestBucket((Bucket bucket) { | 
| +        Future test(objectName, predefinedAcl, expectedLength) { | 
| +          var bucketName = generateBucketName(); | 
| +          return bucket.writeBytes( | 
| +              objectName, [1, 2, 3], predefinedAcl: predefinedAcl) | 
| +              .then(expectAsync((result) { | 
| +                expect(result, isNotNull); | 
| +                return bucket.info(objectName).then(expectAsync((info) { | 
| +                  var acl = info.metadata.acl; | 
| +                  expect(info.name, objectName); | 
| +                  expect(info.etag, isNotNull); | 
| +                  expect(acl.entries.length, expectedLength); | 
| +                  return bucket.delete(objectName).then(expectAsync((result) { | 
| +                    expect(result, isNull); | 
| +                  })); | 
| +                })); | 
| +          })); | 
| +        } | 
| + | 
| +        return Future.forEach([ | 
| +            () => test('test-1', PredefinedAcl.authenticatedRead, 2), | 
| +            () => test('test-2', PredefinedAcl.private, 1), | 
| +            () => test('test-3', PredefinedAcl.projectPrivate, 4), | 
| +            () => test('test-4', PredefinedAcl.publicRead, 2), | 
| +            () => test('test-5', PredefinedAcl.bucketOwnerFullControl, 2), | 
| +            () => test('test-6', PredefinedAcl.bucketOwnerRead, 2) | 
| +        ], (f) => f().then(expectAsync((_) {}))); | 
| +      }); | 
| +    }); | 
| + | 
| +    test('create-with-acl-delete', () { | 
| +      return withTestBucket((Bucket bucket) { | 
| +        Future test(objectName, acl, expectedLength) { | 
| +          var bucketName = generateBucketName(); | 
| +          return bucket.writeBytes(objectName, [1, 2, 3], acl: acl) | 
| +              .then(expectAsync((result) { | 
| +                expect(result, isNotNull); | 
| +                return bucket.info(objectName).then(expectAsync((info) { | 
| +                  var acl = info.metadata.acl; | 
| +                  expect(info.name, objectName); | 
| +                  expect(info.etag, isNotNull); | 
| +                  expect(acl.entries.length, expectedLength); | 
| +                  return bucket.delete(objectName).then(expectAsync((result) { | 
| +                    expect(result, isNull); | 
| +                  })); | 
| +                })); | 
| +          })); | 
| +        } | 
| + | 
| +        Acl acl1 = new Acl( | 
| +            [new AclEntry(AclScope.allAuthenticated, AclPermission.WRITE)]); | 
| +        Acl acl2 = new Acl( | 
| +            [new AclEntry(AclScope.allUsers, AclPermission.WRITE), | 
| +             new AclEntry(new AccountScope('sgjesse@google.com'), | 
| +                          AclPermission.WRITE)]); | 
| +        Acl acl3 = new Acl( | 
| +            [new AclEntry(AclScope.allUsers, AclPermission.WRITE), | 
| +             new AclEntry(new AccountScope('sgjesse@google.com'), | 
| +                          AclPermission.WRITE), | 
| +             new AclEntry(new AccountScope('misc@dartlang.org'), | 
| +                          AclPermission.READ)]); | 
| +        Acl acl4 = new Acl( | 
| +            [new AclEntry(AclScope.allUsers, AclPermission.WRITE), | 
| +             new AclEntry(new AccountScope('sgjesse@google.com'), | 
| +                          AclPermission.WRITE), | 
| +             new AclEntry(new GroupScope('misc@dartlang.org'), | 
| +                          AclPermission.READ), | 
| +             new AclEntry(new DomainScope('dartlang.org'), | 
| +                          AclPermission.FULL_CONTROL)]); | 
| + | 
| +        return Future.forEach([ | 
| +            () => test('test-1', acl1, 1), | 
| +            () => test('test-2', acl2, 2), | 
| +            () => test('test-3', acl3, 3), | 
| +            () => test('test-4', acl4, 4) | 
| +        ], (f) => f().then(expectAsync((_) {}))); | 
| +      }); | 
| +    }); | 
| + | 
| +    test('create-with-metadata-delete', () { | 
| +      return withTestBucket((Bucket bucket) { | 
| +        Future test(objectName, metadata, bytes) { | 
| +          var bucketName = generateBucketName(); | 
| +          return bucket.writeBytes(objectName, bytes, metadata: metadata) | 
| +              .then(expectAsync((result) { | 
| +                expect(result, isNotNull); | 
| +                return bucket.info(objectName).then(expectAsync((info) { | 
| +                  var acl = info.metadata.acl; | 
| +                  expect(info.name, objectName); | 
| +                  expect(info.length, bytes.length); | 
| +                  expect(info.updated is DateTime, isTrue); | 
| +                  expect(info.md5Hash, isNotNull); | 
| +                  expect(info.crc32CChecksum, isNotNull); | 
| +                  expect(info.downloadLink is Uri, isTrue); | 
| +                  expect(info.generation.objectGeneration, isNotNull); | 
| +                  expect(info.generation.metaGeneration, 1); | 
| +                  expect(info.metadata.contentType, metadata.contentType); | 
| +                  expect(info.metadata.cacheControl, metadata.cacheControl); | 
| +                  expect(info.metadata.contentDisposition, | 
| +                         metadata.contentDisposition); | 
| +                  expect(info.metadata.contentEncoding, | 
| +                         metadata.contentEncoding); | 
| +                  expect(info.metadata.contentLanguage, | 
| +                         metadata.contentLanguage); | 
| +                  expect(info.metadata.custom, metadata.custom); | 
| +                  return bucket.delete(objectName).then(expectAsync((result) { | 
| +                    expect(result, isNull); | 
| +                  })); | 
| +                })); | 
| +          })); | 
| +        } | 
| + | 
| +        var metadata1 = new ObjectMetadata(contentType: 'text/plain'); | 
| +        var metadata2 = new ObjectMetadata( | 
| +            contentType: 'text/plain', | 
| +            cacheControl: 'no-cache', | 
| +            contentDisposition: 'attachment; filename="test.txt"', | 
| +            contentEncoding: 'gzip', | 
| +            contentLanguage: 'da', | 
| +            custom: {'a': 'b', 'c': 'd'}); | 
| + | 
| +        return Future.forEach([ | 
| +            () => test('test-1', metadata1, [65, 66, 67]), | 
| +            () => test('test-2', metadata2, [65, 66, 67]), | 
| +            () => test('test-3', metadata1, bytesResumableUpload), | 
| +            () => test('test-4', metadata2, bytesResumableUpload) | 
| +        ], (f) => f().then(expectAsync((_) {}))); | 
| +      }); | 
| +    }); | 
| +  }); | 
| +} | 
| + | 
| +main() { | 
| +  withAuthClient(Storage.SCOPES, (String project, httpClient) { | 
| +    var testBucket = generateBucketName(); | 
| + | 
| +    // Share the same storage connection for all tests. | 
| +    var storage = new Storage(httpClient, project); | 
| + | 
| +    // Create a shared bucket for all object tests. | 
| +    return storage.createBucket(testBucket).then((_) { | 
| +      return runE2EUnittest(() { | 
| +        runTests(storage, storage.bucket(testBucket)); | 
| +      }).whenComplete(() { | 
| +        // Deleting a bucket relies on eventually consistent behaviour, hence | 
| +        // the delay in attempt to prevent test flakiness. | 
| +        return new Future.delayed(STORAGE_LIST_DELAY, () { | 
| +          return storage.deleteBucket(testBucket); | 
| +        }); | 
| +      }); | 
| +    }); | 
| +  }); | 
| +} | 
|  |