diff --git a/error_reporting/nox.py b/error_reporting/nox.py index 746417ccd3e1..1deed376b6e7 100644 --- a/error_reporting/nox.py +++ b/error_reporting/nox.py @@ -64,6 +64,28 @@ def lint_setup_py(session): 'python', 'setup.py', 'check', '--restructuredtext', '--strict') +@nox.session +@nox.parametrize('python_version', ['2.7', '3.6']) +def system_tests(session, python_version): + """Run the system test suite.""" + + # Sanity check: Only run system tests if the environment variable is set. + if not os.environ.get('GOOGLE_APPLICATION_CREDENTIALS', ''): + return + + # Run the system tests against latest Python 2 and Python 3 only. + session.interpreter = 'python{}'.format(python_version) + + # Install all test dependencies, then install this package into the + # virtualenv's dist-packages. + session.install('mock', 'pytest', *LOCAL_DEPS) + session.install('../test_utils/') + session.install('.') + + # Run py.test against the system tests. + session.run('py.test', '-vvv', 'tests/system.py') + + @nox.session def cover(session): """Run the final coverage report. diff --git a/error_reporting/tests/system.py b/error_reporting/tests/system.py new file mode 100644 index 000000000000..3dfafbb6cb07 --- /dev/null +++ b/error_reporting/tests/system.py @@ -0,0 +1,123 @@ +# Copyright 2017 Google Inc. +# +# 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 functools +import operator +import unittest + +from google.cloud import error_reporting +from google.cloud.gapic.errorreporting.v1beta1 import ( + error_stats_service_client) +from google.cloud.proto.devtools.clouderrorreporting.v1beta1 import ( + error_stats_service_pb2) +from google.protobuf.duration_pb2 import Duration + +from test_utils.retry import RetryResult +from test_utils.system import unique_resource_id + + +ERROR_MSG = 'Stackdriver Error Reporting System Test' + + +def setUpModule(): + Config.CLIENT = error_reporting.Client() + + +class Config(object): + """Run-time configuration to be modified at set-up. + + This is a mutable stand-in to allow test set-up to modify + global state. + """ + CLIENT = None + + +def _list_groups(client): + """List Error Groups from the last 60 seconds. + + This class provides a wrapper around making calls to the GAX + API. It's used by the system tests to find the appropriate error group + to verify the error was successfully reported. + + :type client: :class:`~google.cloud.error_reporting.client.Client` + :param client: The client containing a project and credentials. + + :rtype: :class:`~google.gax.ResourceIterator` + :returns: Iterable of :class:`~.error_stats_service_pb2.ErrorGroupStats`. + """ + gax_api = error_stats_service_client.ErrorStatsServiceClient( + credentials=client._credentials) + project_name = gax_api.project_path(client.project) + + time_range = error_stats_service_pb2.QueryTimeRange() + time_range.period = error_stats_service_pb2.QueryTimeRange.PERIOD_1_HOUR + + duration = Duration(seconds=60 * 60) + + return gax_api.list_group_stats( + project_name, time_range, timed_count_duration=duration) + + +def _simulate_exception(class_name, client): + """Simulates an exception to verify it was reported. + + :type class_name: str + :param class_name: The name of a custom error class to + create (and raise). + + :type client: :class:`~google.cloud.error_reporting.client.Client` + :param client: The client that will report the exception. + """ + custom_exc = type(class_name, (RuntimeError,), {}) + try: + raise custom_exc(ERROR_MSG) + except RuntimeError: + client.report_exception() + + +def _get_error_count(class_name, client): + """Counts the number of errors in the group of the test exception. + + :type class_name: str + :param class_name: The name of a custom error class used. + + :type client: :class:`~google.cloud.error_reporting.client.Client` + :param client: The client containing a project and credentials. + + :rtype: int + :returns: Group count for errors that match ``class_name``. If no + match is found, returns :data:`None`. + """ + groups = _list_groups(client) + for group in groups: + if class_name in group.representative.message: + return group.count + + +class TestErrorReporting(unittest.TestCase): + + def test_report_exception(self): + # Get a class name unique to this test case. + class_name = 'RuntimeError' + unique_resource_id('_') + + # Simulate an error: group won't exist until we report + # first exception. + _simulate_exception(class_name, Config.CLIENT) + + is_one = functools.partial(operator.eq, 1) + is_one.__name__ = 'is_one' # partial() has no name. + wrapped_get_count = RetryResult(is_one)(_get_error_count) + + error_count = wrapped_get_count(class_name, Config.CLIENT) + self.assertEqual(error_count, 1)