blob: 941c6dfbcdde61b7b77d1546da8a38269bf5deef [file] [log] [blame]
xixuan82753172017-08-07 09:22:50 -07001# Copyright 2017 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Module for unittest framework.
6
7This program handles properly importing the App Engine SDK so that test modules
8can use google.appengine.* APIs and the Google App Engine testbed.
9
10Example invocation:
11 $ python runner.py ~/google-cloud-sdk [your sdk path]
12"""
13
14import argparse
Xixuan Wuf4e15c42017-08-24 14:06:03 -070015import logging
xixuan82753172017-08-07 09:22:50 -070016import os
17import sys
18import unittest
19
Xixuan Wu3dd37482017-08-21 14:18:36 -070020# Set GOOGLE_CLOUD_SDK_PATH in user's home directory ~/.
21GOOGLE_CLOUD_SDK_PATH = os.path.join(
22 os.path.expanduser('~'), 'google-cloud-sdk')
23
Xixuan Wu7e30c9d2017-09-05 18:46:00 -070024# Mapping between test type and test files' pattern.
25TEST_PATTERN_MAP = {'unittest': '*_unittest.py',
26 'integration': '*_integration_test.py'}
27
xixuan82753172017-08-07 09:22:50 -070028
29def fixup_paths(path):
Xixuan Wu3dd37482017-08-21 14:18:36 -070030 """Adds GAE SDK path to system path and appends it to the google path.
xixuan82753172017-08-07 09:22:50 -070031
Xixuan Wu3dd37482017-08-21 14:18:36 -070032 Not all Google packages are inside namespace packages, which means
33 there might be another non-namespace package named `google` already on
34 the path and simply appending the App Engine SDK to the path will not
35 work since the other package will get discovered and used first.
36 This emulates namespace packages by first searching if a `google` package
37 exists by importing it, and if so appending to its module search path.
38
39 Args:
40 path: the path of GAE SDK.
41 """
42 try:
43 # pylint: disable=g-import-not-at-top
44 import google
45 google.__path__.append('{0}/google'.format(path))
46 except ImportError:
47 pass
48
49 sys.path.insert(0, path)
xixuan82753172017-08-07 09:22:50 -070050
51
Xixuan Wu7e30c9d2017-09-05 18:46:00 -070052def main(input_args):
53 """The main function to run unittest/integration tests.
Xixuan Wuf4e15c42017-08-24 14:06:03 -070054
55 Args:
Xixuan Wu7e30c9d2017-09-05 18:46:00 -070056 input_args: the input args.
Xixuan Wuf4e15c42017-08-24 14:06:03 -070057
58 Returns:
59 a unittest.TextTestRunner object.
60 """
Xixuan Wu7e30c9d2017-09-05 18:46:00 -070061 if input_args.sdk_path != GOOGLE_CLOUD_SDK_PATH:
62 _import_sdk_path(input_args.sdk_path)
xixuan82753172017-08-07 09:22:50 -070063
Xixuan Wu3dd37482017-08-21 14:18:36 -070064 # Loading appengine_config from the current project ensures that any
65 # changes to configuration there are available to all tests (e.g.
66 # sys.path modifications, namespaces, etc.)
67 try:
68 # pylint: disable=g-import-not-at-top
69 import appengine_config
70 # pylint: disable=pointless-statement
71 (appengine_config)
72 except ImportError:
73 print 'Note: unable to import appengine_config.'
xixuan82753172017-08-07 09:22:50 -070074
Xixuan Wu3dd37482017-08-21 14:18:36 -070075 # Discover and run tests.
Xixuan Wu7e30c9d2017-09-05 18:46:00 -070076 if input_args.test_file:
77 suites = unittest.loader.TestLoader().discover(
78 input_args.test_path, input_args.test_file)
Xixuan Wuf4e15c42017-08-24 14:06:03 -070079 else:
Xixuan Wu7e30c9d2017-09-05 18:46:00 -070080 suites = unittest.loader.TestLoader().discover(
81 input_args.test_path, TEST_PATTERN_MAP[input_args.test_type])
Xixuan Wuf4e15c42017-08-24 14:06:03 -070082
83 return unittest.TextTestRunner(verbosity=2).run(suites)
xixuan82753172017-08-07 09:22:50 -070084
xixuan82753172017-08-07 09:22:50 -070085
Xixuan Wu3dd37482017-08-21 14:18:36 -070086def _import_sdk_path(sdk_path):
87 # If the SDK path points to a Google Cloud SDK installation
88 # then we should alter it to point to the GAE platform location.
89 appengine_path = os.path.join(sdk_path, 'platform/google_appengine')
90 sdk_path = appengine_path if os.path.exists(appengine_path) else sdk_path
91
92 # Make sure google.appengine.* modules are importable.
93 fixup_paths(sdk_path)
94
95 # Make sure all bundled third-party packages are available.
96 # pylint: disable=g-import-not-at-top
97 import dev_appserver
98 dev_appserver.fix_sys_path()
99
100
101def _make_parser():
102 """Return unittest parser."""
103 parser = argparse.ArgumentParser(
104 description=__doc__,
105 formatter_class=argparse.RawDescriptionHelpFormatter)
106 parser.add_argument(
107 '--sdk_path',
108 help='The path to the Google App Engine SDK or the Google Cloud SDK.',
109 default=GOOGLE_CLOUD_SDK_PATH)
110 parser.add_argument(
Xixuan Wu7e30c9d2017-09-05 18:46:00 -0700111 '--test_path',
Xixuan Wu3dd37482017-08-21 14:18:36 -0700112 help='The path to look for tests, defaults to the current directory.',
113 default=os.getcwd())
Xixuan Wuf4e15c42017-08-24 14:06:03 -0700114 group = parser.add_mutually_exclusive_group()
115 group.add_argument(
Xixuan Wu7e30c9d2017-09-05 18:46:00 -0700116 '--test_type', choices=['unittest', 'integration'],
117 help=('The test type, including unittest and integration test, defaults '
118 'to unittest'),
119 default='unittest')
Xixuan Wuf4e15c42017-08-24 14:06:03 -0700120 group.add_argument(
121 'test_file',
122 nargs='?',
123 help=('A single test module to test, default to empty string, which '
124 'means the runner will find test modules by test-pattern.'),
125 default='')
126 parser.add_argument(
127 '--debug',
128 action='store_true',
129 help='Display the logging in unittest.')
Xixuan Wu3dd37482017-08-21 14:18:36 -0700130 return parser
xixuan82753172017-08-07 09:22:50 -0700131
132
133if __name__ == '__main__':
Xixuan Wu3dd37482017-08-21 14:18:36 -0700134 unittest_parser = _make_parser()
135 args = unittest_parser.parse_args()
Xixuan Wuf4e15c42017-08-24 14:06:03 -0700136 if args.debug:
137 logging.getLogger().setLevel(logging.DEBUG)
138 else:
139 logging.getLogger().setLevel(logging.CRITICAL)
140
Xixuan Wu3dd37482017-08-21 14:18:36 -0700141 try:
142 _import_sdk_path(args.sdk_path)
143 except ImportError:
144 args.sdk_path = raw_input('Cannot find google SDK in %s. Please specify '
145 'your google SDK path: ' % GOOGLE_CLOUD_SDK_PATH)
xixuan82753172017-08-07 09:22:50 -0700146
Xixuan Wu7e30c9d2017-09-05 18:46:00 -0700147 result = main(args)
Xixuan Wuf4e15c42017-08-24 14:06:03 -0700148
Xixuan Wu3dd37482017-08-21 14:18:36 -0700149 if not result.wasSuccessful():
150 sys.exit(1)