From 7e922316c0386c57291bb4d5115d440d60551b1a Mon Sep 17 00:00:00 2001 From: Jon Wayne Parrott Date: Tue, 23 Feb 2016 10:41:43 -0800 Subject: [PATCH] Moving common testing tools to gcp-repo-tools. * Updating travis.yml for new GAE tool. * Updating tox.ini to use new tools. * Updating imports in various tests to use new tools. * Updating root conftest.py to provide configuration. * Updating secrets.tar with client-secrets, needed for incoming bigquery tests. --- .gitignore | 1 + .travis.yml | 10 +- appengine/bigquery/main_test.py | 4 +- appengine/conftest.py | 62 +++++------ appengine/mailgun/main_test.py | 25 +++-- appengine/storage/main_test.py | 2 +- bigquery/api/async_query_test.py | 2 +- .../api/export_data_to_cloud_storage_test.py | 20 ++-- bigquery/api/getting_started_test.py | 2 +- bigquery/api/list_datasets_projects_test.py | 2 +- bigquery/api/load_data_by_post_test.py | 10 +- bigquery/api/load_data_from_csv_test.py | 9 +- bigquery/api/streaming_test.py | 2 +- bigquery/api/sync_query_test.py | 2 +- .../blog_test.py | 6 +- .../wiki_test.py | 6 +- cloud_logging/api/list_logs_test.py | 2 +- compute/api/create_instance_test.py | 8 +- conftest.py | 23 +++- datastore/api/snippets_test.py | 8 +- datastore/api/tasks_test.py | 18 ++-- monitoring/api/auth_test.py | 2 +- scripts/encrypt-secrets.sh | 2 +- secrets.tar.enc | Bin 9760 -> 14368 bytes storage/api/compose_objects_test.py | 2 +- storage/api/list_objects_test.py | 2 +- testing/__init__.py | 25 ----- testing/appengine.py | 99 ------------------ testing/cloud.py | 76 -------------- testing/flaky.py | 36 ------- testing/utils.py | 70 ------------- tox.ini | 19 ++-- 32 files changed, 139 insertions(+), 418 deletions(-) delete mode 100644 testing/__init__.py delete mode 100644 testing/appengine.py delete mode 100644 testing/cloud.py delete mode 100644 testing/flaky.py delete mode 100644 testing/utils.py diff --git a/.gitignore b/.gitignore index 184297e92b1..dd8bffdf608 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ managed_vms/django_tutorial/static/* lib testing/resources/test-env.sh testing/resources/service-account.json +testing/resources/client-secrets.json secrets.tar .cache junit.xml diff --git a/.travis.yml b/.travis.yml index b43a7de8d63..4a147705cd5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,8 @@ sudo: false language: python services: - - memcached - - mysql +- memcached +- mysql branches: only: - master @@ -13,15 +13,15 @@ env: global: - PATH=${PATH}:${HOME}/gcloud/google-cloud-sdk/bin - GOOGLE_APPLICATION_CREDENTIALS=${TRAVIS_BUILD_DIR}/testing/resources/service-account.json + - GOOGLE_CLIENT_SECRETS=${TRAVIS_BUILD_DIR}/testing/resources/client-secrets.json - GAE_PYTHONPATH=${HOME}/.cache/google_appengine - - secure: YIowCOMJ97rTcehKVT6Gi3u0Etm8s9+TBRGsNPJLgSF2zZdsh9IHcIc+tMDUMR3lpOe8y2a060RuODQcRsW1W1LIHej+ZE/gv6vATT6qNA3eKfKmZ9AyrpBO0fTOHlHrGBuU9ktBPR+iqvnq8MLWjnUozPFMJbuNBFITU7JP8jc= + - GAE_ROOT=${HOME}/.cache/ + - secure: Orp9Et2TIwCG/Hf59aa0NUDF1pNcwcS4TFulXX175918cFREOzf/cNZNg+Ui585ZRFjbifZdc858tVuCVd8XlxQPXQgp7bwB7nXs3lby3LYg4+HD83Gaz7KOWxRLWVor6IVn8OxeCzwl6fJkdmffsTTO9csC4yZ7izHr+u7hiO4= before_install: - openssl aes-256-cbc -k "$secrets_password" -in secrets.tar.enc -out secrets.tar -d - tar xvf secrets.tar install: - pip install tox coverage -- pip install -e git+https://github.com/GoogleCloudPlatform/python-repo-tools#egg=python-repo-tools -- gcp-python-repo-tools download-appengine-sdk `dirname "${GAE_PYTHONPATH}"` script: - source ${TRAVIS_BUILD_DIR}/testing/resources/test-env.sh - tox diff --git a/appengine/bigquery/main_test.py b/appengine/bigquery/main_test.py index 4d22d67d243..77e192b2dac 100644 --- a/appengine/bigquery/main_test.py +++ b/appengine/bigquery/main_test.py @@ -14,7 +14,7 @@ import re -from apiclient.http import HttpMock +from googleapiclient.http import HttpMock import main import mock import pytest @@ -23,7 +23,7 @@ @pytest.fixture def app(cloud_config, testbed): - main.PROJECTID = cloud_config.GCLOUD_PROJECT + main.PROJECTID = cloud_config.project return webtest.TestApp(main.app) diff --git a/appengine/conftest.py b/appengine/conftest.py index ff92c6fc95e..4fd85dcc906 100644 --- a/appengine/conftest.py +++ b/appengine/conftest.py @@ -1,35 +1,27 @@ -import pytest -import testing.appengine - - -def pytest_configure(config): - testing.appengine.setup_sdk_imports() - - -def pytest_runtest_call(item): - testing.appengine.import_appengine_config() - - -@pytest.yield_fixture -def testbed(): - testbed = testing.appengine.setup_testbed() - yield testbed - testbed.deactivate() - - -@pytest.fixture -def login(testbed): - def _login(email='user@example.com', id='123', is_admin=False): - testbed.setup_env( - user_email=email, - user_id=id, - user_is_admin='1' if is_admin else '0', - overwrite=True) - return _login - - -@pytest.fixture -def run_tasks(testbed): - def _run_tasks(app): - testing.appengine.run_tasks(testbed, app) - return _run_tasks +# Copyright 2015 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Import py.test hooks and fixtures for App Engine +from gcp.testing.appengine import ( + login, + pytest_configure, + pytest_runtest_call, + run_tasks, + testbed) + +(login) +(pytest_configure) +(pytest_runtest_call) +(run_tasks) +(testbed) diff --git a/appengine/mailgun/main_test.py b/appengine/mailgun/main_test.py index 8270716b84f..de92f476f29 100644 --- a/appengine/mailgun/main_test.py +++ b/appengine/mailgun/main_test.py @@ -12,12 +12,19 @@ # See the License for the specific language governing permissions and # limitations under the License. +from googleapiclient.http import HttpMockSequence +import httplib2 import main +import mock import pytest -from testing import Http2Mock import webtest +class HttpMockSequenceWithCredentials(HttpMockSequence): + def add_credentials(self, *args): + pass + + @pytest.fixture def app(): return webtest.TestApp(main.app) @@ -29,27 +36,31 @@ def test_get(app): def test_post(app): - http = Http2Mock(responses=[{}]) + http = HttpMockSequenceWithCredentials([ + ({'status': '200'}, '')]) + patch_http = mock.patch.object(httplib2, 'Http', lambda: http) - with http: + with patch_http: response = app.post('/', { 'recipient': 'jonwayne@google.com', 'submit': 'Send simple email'}) assert response.status_int == 200 - http = Http2Mock(responses=[{}]) + http = HttpMockSequenceWithCredentials([ + ({'status': '200'}, '')]) - with http: + with patch_http: response = app.post('/', { 'recipient': 'jonwayne@google.com', 'submit': 'Send complex email'}) assert response.status_int == 200 - http = Http2Mock(responses=[{'status': 500, 'body': 'Test error'}]) + http = HttpMockSequenceWithCredentials([ + ({'status': '500'}, 'Test error')]) - with http, pytest.raises(Exception): + with patch_http, pytest.raises(Exception): app.post('/', { 'recipient': 'jonwayne@google.com', 'submit': 'Send simple email'}) diff --git a/appengine/storage/main_test.py b/appengine/storage/main_test.py index fd9b9fd32b1..e68fe66a16d 100644 --- a/appengine/storage/main_test.py +++ b/appengine/storage/main_test.py @@ -18,7 +18,7 @@ def test_get(cloud_config): - main.BUCKET_NAME = cloud_config.GCLOUD_PROJECT + main.BUCKET_NAME = cloud_config.project app = webtest.TestApp(main.app) response = app.get('/') diff --git a/bigquery/api/async_query_test.py b/bigquery/api/async_query_test.py index 8aabadd0fed..f8e9f06891d 100644 --- a/bigquery/api/async_query_test.py +++ b/bigquery/api/async_query_test.py @@ -22,7 +22,7 @@ def test_async_query(cloud_config, capsys): 'GROUP BY corpus;') main( - project_id=cloud_config.GCLOUD_PROJECT, + project_id=cloud_config.project, query_string=query, batch=False, num_retries=5, diff --git a/bigquery/api/export_data_to_cloud_storage_test.py b/bigquery/api/export_data_to_cloud_storage_test.py index 86204fc05f5..72a41069199 100644 --- a/bigquery/api/export_data_to_cloud_storage_test.py +++ b/bigquery/api/export_data_to_cloud_storage_test.py @@ -12,19 +12,19 @@ # limitations under the License. from export_data_to_cloud_storage import main -from testing import mark_flaky +from gcp.testing.flaky import flaky DATASET_ID = 'test_dataset' TABLE_ID = 'test_table' -@mark_flaky +@flaky def test_export_table_csv(cloud_config): cloud_storage_output_uri = \ - 'gs://{}/output.csv'.format(cloud_config.CLOUD_STORAGE_BUCKET) + 'gs://{}/output.csv'.format(cloud_config.storage_bucket) main( cloud_storage_output_uri, - cloud_config.GCLOUD_PROJECT, + cloud_config.project, DATASET_ID, TABLE_ID, num_retries=5, @@ -32,13 +32,13 @@ def test_export_table_csv(cloud_config): export_format="CSV") -@mark_flaky +@flaky def test_export_table_json(cloud_config): cloud_storage_output_uri = \ - 'gs://{}/output.json'.format(cloud_config.CLOUD_STORAGE_BUCKET) + 'gs://{}/output.json'.format(cloud_config.storage_bucket) main( cloud_storage_output_uri, - cloud_config.GCLOUD_PROJECT, + cloud_config.project, DATASET_ID, TABLE_ID, num_retries=5, @@ -46,13 +46,13 @@ def test_export_table_json(cloud_config): export_format="NEWLINE_DELIMITED_JSON") -@mark_flaky +@flaky def test_export_table_avro(cloud_config): cloud_storage_output_uri = \ - 'gs://{}/output.avro'.format(cloud_config.CLOUD_STORAGE_BUCKET) + 'gs://{}/output.avro'.format(cloud_config.storage_bucket) main( cloud_storage_output_uri, - cloud_config.GCLOUD_PROJECT, + cloud_config.project, DATASET_ID, TABLE_ID, num_retries=5, diff --git a/bigquery/api/getting_started_test.py b/bigquery/api/getting_started_test.py index d0ff01b71e0..8f0866f46d1 100644 --- a/bigquery/api/getting_started_test.py +++ b/bigquery/api/getting_started_test.py @@ -17,7 +17,7 @@ def test_main(cloud_config, capsys): - main(cloud_config.GCLOUD_PROJECT) + main(cloud_config.project) out, _ = capsys.readouterr() diff --git a/bigquery/api/list_datasets_projects_test.py b/bigquery/api/list_datasets_projects_test.py index 588086807b0..e096a0342b2 100644 --- a/bigquery/api/list_datasets_projects_test.py +++ b/bigquery/api/list_datasets_projects_test.py @@ -17,7 +17,7 @@ def test_main(cloud_config, capsys): - main(cloud_config.GCLOUD_PROJECT) + main(cloud_config.project) out, _ = capsys.readouterr() diff --git a/bigquery/api/load_data_by_post_test.py b/bigquery/api/load_data_by_post_test.py index 876c8748c9f..ca4d1ea38e9 100644 --- a/bigquery/api/load_data_by_post_test.py +++ b/bigquery/api/load_data_by_post_test.py @@ -13,14 +13,14 @@ import re +from gcp.testing.flaky import flaky from load_data_by_post import load_data -from testing import mark_flaky DATASET_ID = 'ephemeral_test_dataset' TABLE_ID = 'load_data_by_post' -@mark_flaky +@flaky def test_load_csv_data(cloud_config, resource, capsys): schema_path = resource('schema.json') data_path = resource('data.csv') @@ -28,7 +28,7 @@ def test_load_csv_data(cloud_config, resource, capsys): load_data( schema_path, data_path, - cloud_config.GCLOUD_PROJECT, + cloud_config.project, DATASET_ID, TABLE_ID ) @@ -39,7 +39,7 @@ def test_load_csv_data(cloud_config, resource, capsys): r'Waiting for job to finish.*Job complete.', re.DOTALL), out) -@mark_flaky +@flaky def test_load_json_data(cloud_config, resource, capsys): schema_path = resource('schema.json') data_path = resource('data.json') @@ -47,7 +47,7 @@ def test_load_json_data(cloud_config, resource, capsys): load_data( schema_path, data_path, - cloud_config.GCLOUD_PROJECT, + cloud_config.project, DATASET_ID, TABLE_ID ) diff --git a/bigquery/api/load_data_from_csv_test.py b/bigquery/api/load_data_from_csv_test.py index 0cc64adb423..6e7fc4323db 100644 --- a/bigquery/api/load_data_from_csv_test.py +++ b/bigquery/api/load_data_from_csv_test.py @@ -11,22 +11,21 @@ # See the License for the specific language governing permissions and # limitations under the License. - +from gcp.testing.flaky import flaky from load_data_from_csv import main -from testing import mark_flaky DATASET_ID = 'test_dataset' TABLE_ID = 'test_import_table' -@mark_flaky +@flaky def test_load_table(cloud_config, resource): cloud_storage_input_uri = 'gs://{}/data.csv'.format( - cloud_config.CLOUD_STORAGE_BUCKET) + cloud_config.storage_bucket) schema_file = resource('schema.json') main( - cloud_config.GCLOUD_PROJECT, + cloud_config.project, DATASET_ID, TABLE_ID, schema_file=schema_file, diff --git a/bigquery/api/streaming_test.py b/bigquery/api/streaming_test.py index de22c58d202..66347da7cc0 100644 --- a/bigquery/api/streaming_test.py +++ b/bigquery/api/streaming_test.py @@ -27,7 +27,7 @@ def test_stream_row_to_bigquery(cloud_config, resource, capsys): streaming.get_rows = lambda: rows streaming.main( - cloud_config.GCLOUD_PROJECT, + cloud_config.project, DATASET_ID, TABLE_ID, num_retries=5) diff --git a/bigquery/api/sync_query_test.py b/bigquery/api/sync_query_test.py index 7f1d196f2b6..02d84f7c9c1 100644 --- a/bigquery/api/sync_query_test.py +++ b/bigquery/api/sync_query_test.py @@ -22,7 +22,7 @@ def test_sync_query(cloud_config, capsys): 'GROUP BY corpus;') main( - project_id=cloud_config.GCLOUD_PROJECT, + project_id=cloud_config.project, query=query, timeout=30, num_retries=5) diff --git a/blog/introduction_to_data_models_in_cloud_datastore/blog_test.py b/blog/introduction_to_data_models_in_cloud_datastore/blog_test.py index 4959229e7b2..16f41c883ec 100644 --- a/blog/introduction_to_data_models_in_cloud_datastore/blog_test.py +++ b/blog/introduction_to_data_models_in_cloud_datastore/blog_test.py @@ -12,9 +12,9 @@ # limitations under the License. from blog import main -from testing import mark_flaky +from gcp.testing.flaky import flaky -@mark_flaky +@flaky def test_main(cloud_config): - main(cloud_config.GCLOUD_PROJECT) + main(cloud_config.project) diff --git a/blog/introduction_to_data_models_in_cloud_datastore/wiki_test.py b/blog/introduction_to_data_models_in_cloud_datastore/wiki_test.py index a4ff93d97f0..7db45420ebe 100644 --- a/blog/introduction_to_data_models_in_cloud_datastore/wiki_test.py +++ b/blog/introduction_to_data_models_in_cloud_datastore/wiki_test.py @@ -11,10 +11,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -from testing import mark_flaky +from gcp.testing.flaky import flaky from wiki import main -@mark_flaky +@flaky def test_main(cloud_config): - main(cloud_config.GCLOUD_PROJECT) + main(cloud_config.project) diff --git a/cloud_logging/api/list_logs_test.py b/cloud_logging/api/list_logs_test.py index 031f0680662..f7f94cf84b1 100644 --- a/cloud_logging/api/list_logs_test.py +++ b/cloud_logging/api/list_logs_test.py @@ -17,6 +17,6 @@ def test_main(cloud_config, capsys): - list_logs.main(cloud_config.GCLOUD_PROJECT) + list_logs.main(cloud_config.project) out, _ = capsys.readouterr() assert re.search(re.compile(r'.*', re.S), out) diff --git a/compute/api/create_instance_test.py b/compute/api/create_instance_test.py index 70de82a33c1..f85dcf09d0e 100644 --- a/compute/api/create_instance_test.py +++ b/compute/api/create_instance_test.py @@ -14,14 +14,14 @@ import re from create_instance import main -from testing import mark_flaky +from gcp.testing.flaky import flaky -@mark_flaky +@flaky def test_main(cloud_config, capsys): main( - cloud_config.GCLOUD_PROJECT, - cloud_config.CLOUD_STORAGE_BUCKET, + cloud_config.project, + cloud_config.storage_bucket, 'us-central1-f', 'test-instance', wait=False) diff --git a/conftest.py b/conftest.py index 049118194e6..92a808d07b0 100644 --- a/conftest.py +++ b/conftest.py @@ -1,13 +1,30 @@ import os import pytest -from testing.cloud import Config, get_resource_path + + +class Namespace: + def __init__(self, **kwargs): + self.__dict__.update(kwargs) @pytest.fixture(scope='session') def cloud_config(): - """Provides a configuration option as a proxy to environment variables.""" - return Config() + """Provides a configuration object as a proxy to environment variables.""" + return Namespace( + project=os.environ.get('GCLOUD_PROJECT'), + storage_bucket=os.environ.get('CLOUD_STORAGE_BUCKET'), + client_secrets=os.environ.get('GOOGLE_CLIENT_SECRETS')) + + +def get_resource_path(resource, local_path): + local_resource_path = os.path.join(local_path, 'resources', *resource) + + if os.path.exists(local_resource_path): + return local_resource_path + else: + raise EnvironmentError('Resource {} not found.'.format( + os.path.join(*resource))) @pytest.fixture(scope='module') diff --git a/datastore/api/snippets_test.py b/datastore/api/snippets_test.py index 9bb65dbbc3d..974578f9ef4 100644 --- a/datastore/api/snippets_test.py +++ b/datastore/api/snippets_test.py @@ -14,9 +14,9 @@ import time from gcloud import datastore +from gcp.testing.flaky import flaky import pytest import snippets -from testing import mark_flaky class CleanupClient(datastore.Client): @@ -43,19 +43,19 @@ def put_multi(self, *args, **kwargs): @pytest.yield_fixture def client(cloud_config): - client = CleanupClient(cloud_config.GCLOUD_PROJECT) + client = CleanupClient(cloud_config.project) yield client client.cleanup() @pytest.yield_fixture def waiting_client(cloud_config): - client = WaitingClient(cloud_config.GCLOUD_PROJECT) + client = WaitingClient(cloud_config.project) yield client client.cleanup() -@mark_flaky +@flaky class TestDatastoreSnippets: # These tests mostly just test the absence of exceptions. def test_incomplete_key(self, client): diff --git a/datastore/api/tasks_test.py b/datastore/api/tasks_test.py index 98358997030..d8901d8f244 100644 --- a/datastore/api/tasks_test.py +++ b/datastore/api/tasks_test.py @@ -12,14 +12,14 @@ # limitations under the License. from gcloud import datastore +from gcp.testing.flaky import flaky import pytest import tasks -from testing import mark_flaky @pytest.yield_fixture def client(cloud_config): - client = datastore.Client(cloud_config.GCLOUD_PROJECT) + client = datastore.Client(cloud_config.project) yield client @@ -29,12 +29,12 @@ def client(cloud_config): [x.key for x in client.query(kind='Task').fetch()]) -@mark_flaky +@flaky def test_create_client(cloud_config): - tasks.create_client(cloud_config.GCLOUD_PROJECT) + tasks.create_client(cloud_config.project) -@mark_flaky +@flaky def test_add_task(client): task_key = tasks.add_task(client, 'Test task') task = client.get(task_key) @@ -42,7 +42,7 @@ def test_add_task(client): assert task['description'] == 'Test task' -@mark_flaky +@flaky def test_mark_done(client): task_key = tasks.add_task(client, 'Test task') tasks.mark_done(client, task_key.id) @@ -51,7 +51,7 @@ def test_mark_done(client): assert task['done'] -@mark_flaky +@flaky def test_list_tasks(client): task1_key = tasks.add_task(client, 'Test task 1') task2_key = tasks.add_task(client, 'Test task 2') @@ -59,14 +59,14 @@ def test_list_tasks(client): assert [x.key for x in task_list] == [task1_key, task2_key] -@mark_flaky +@flaky def test_delete_task(client): task_key = tasks.add_task(client, 'Test task 1') tasks.delete_task(client, task_key.id) assert client.get(task_key) is None -@mark_flaky +@flaky def test_format_tasks(client): task1_key = tasks.add_task(client, 'Test task 1') tasks.add_task(client, 'Test task 2') diff --git a/monitoring/api/auth_test.py b/monitoring/api/auth_test.py index 4d0d37a029a..917e33e870d 100644 --- a/monitoring/api/auth_test.py +++ b/monitoring/api/auth_test.py @@ -17,7 +17,7 @@ def test_main(cloud_config, capsys): - auth.main(cloud_config.GCLOUD_PROJECT) + auth.main(cloud_config.project) output, _ = capsys.readouterr() assert re.search( diff --git a/scripts/encrypt-secrets.sh b/scripts/encrypt-secrets.sh index 56e6f2d63a1..6bdbf9421d4 100755 --- a/scripts/encrypt-secrets.sh +++ b/scripts/encrypt-secrets.sh @@ -17,7 +17,7 @@ read -s -p "Enter password for encryption: " password echo -tar cvf secrets.tar testing/resources/{service-account.json,test-env.sh} +tar cvf secrets.tar testing/resources/{service-account.json,client-secrets.json,test-env.sh} openssl aes-256-cbc -k "$password" -in secrets.tar -out secrets.tar.enc rm secrets.tar diff --git a/secrets.tar.enc b/secrets.tar.enc index 24e60b17ab7835f709fa9c459d9e37f73218f14d..3dc345619bd616c4f343a6169f80fe48524acdc0 100644 GIT binary patch literal 14368 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�H6wOq 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 a$FOK;G^$@eVKUo)Xm3P+YQPbgk!1*uqWXLQ literal 9760 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