# Copyright (C) 2018 The Android Open Source Project # # 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. # """App Engine local test runner. This program handles properly importing the App Engine SDK so that test modules can use google.appengine.* APIs and the Google App Engine testbed. Example invocation: $ python testrunner.py [--sdk-path ~/google-cloud-sdk] """ import argparse import os import subprocess import sys import unittest def ExecuteOneShellCommand(cmd): """Executes one shell command and returns (stdout, stderr, exit_code). Args: cmd: string, a shell command. Returns: tuple(string, string, int), containing stdout, stderr, exit_code of the shell command. """ p = subprocess.Popen( str(cmd), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() return (stdout, stderr, p.returncode) def fixup_paths(path): """Adds GAE SDK path to system path and appends it to the google path if that already exists.""" # Not all Google packages are inside namespace packages, which means # there might be another non-namespace package named `google` already on # the path and simply appending the App Engine SDK to the path will not # work since the other package will get discovered and used first. # This emulates namespace packages by first searching if a `google` package # exists by importing it, and if so appending to its module search path. try: import google google.__path__.append("{0}/google".format(path)) except ImportError: pass sys.path.insert(0, path) def main(sdk_path, test_path, test_pattern): if not sdk_path: # Get sdk path by running gcloud command. stdout, stderr, _ = ExecuteOneShellCommand( "gcloud info --format='value(installation.sdk_root)'") if stderr: print("Cannot find google cloud sdk path.") return 1 sdk_path = str.strip(stdout) # If the SDK path points to a Google Cloud SDK installation # then we should alter it to point to the GAE platform location. if os.path.exists(os.path.join(sdk_path, 'platform/google_appengine')): sdk_path = os.path.join(sdk_path, 'platform/google_appengine') # Make sure google.appengine.* modules are importable. fixup_paths(sdk_path) # Make sure all bundled third-party packages are available. import dev_appserver dev_appserver.fix_sys_path() # Loading appengine_config from the current project ensures that any # changes to configuration there are available to all tests (e.g. # sys.path modifications, namespaces, etc.) try: import appengine_config (appengine_config) except ImportError: print('Note: unable to import appengine_config.') # Discover and run tests. suite = unittest.loader.TestLoader().discover(test_path, test_pattern) print('Suite', suite) return unittest.TextTestRunner(verbosity=2).run(suite) if __name__ == '__main__': parser = argparse.ArgumentParser( description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument( '--sdk_path', help='The path to the Google App Engine SDK or the Google Cloud SDK.', default=None) parser.add_argument( '--test-path', help='The path to look for tests, defaults to the current directory.', default=os.getcwd()) parser.add_argument( '--test-pattern', help='The file pattern for test modules, defaults to *_test.py.', default='*_test.py') args = parser.parse_args() result = main(args.sdk_path, args.test_path, args.test_pattern) if not result.wasSuccessful(): sys.exit(1)