blob: b2687c84f4432ef6adcea92d298c27e90d4be160 [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
xixuan82753172017-08-07 09:22:50 -070024
25def fixup_paths(path):
Xixuan Wu3dd37482017-08-21 14:18:36 -070026 """Adds GAE SDK path to system path and appends it to the google path.
xixuan82753172017-08-07 09:22:50 -070027
Xixuan Wu3dd37482017-08-21 14:18:36 -070028 Not all Google packages are inside namespace packages, which means
29 there might be another non-namespace package named `google` already on
30 the path and simply appending the App Engine SDK to the path will not
31 work since the other package will get discovered and used first.
32 This emulates namespace packages by first searching if a `google` package
33 exists by importing it, and if so appending to its module search path.
34
35 Args:
36 path: the path of GAE SDK.
37 """
38 try:
39 # pylint: disable=g-import-not-at-top
40 import google
41 google.__path__.append('{0}/google'.format(path))
42 except ImportError:
43 pass
44
45 sys.path.insert(0, path)
xixuan82753172017-08-07 09:22:50 -070046
47
Xixuan Wuf4e15c42017-08-24 14:06:03 -070048def main(sdk_path, test_path, test_pattern, test_file):
49 """The main function to run unittest for given test_pattern/test_file.
50
51 Args:
52 sdk_path: the google sdk path.
53 test_path: the path of test files to run.
54 test_pattern: the file name patterns for testing files.
55 test_file: the single file for testing.
56
57 Returns:
58 a unittest.TextTestRunner object.
59 """
Xixuan Wu3dd37482017-08-21 14:18:36 -070060 if sdk_path != GOOGLE_CLOUD_SDK_PATH:
61 _import_sdk_path(sdk_path)
xixuan82753172017-08-07 09:22:50 -070062
Xixuan Wu3dd37482017-08-21 14:18:36 -070063 # Loading appengine_config from the current project ensures that any
64 # changes to configuration there are available to all tests (e.g.
65 # sys.path modifications, namespaces, etc.)
66 try:
67 # pylint: disable=g-import-not-at-top
68 import appengine_config
69 # pylint: disable=pointless-statement
70 (appengine_config)
71 except ImportError:
72 print 'Note: unable to import appengine_config.'
xixuan82753172017-08-07 09:22:50 -070073
Xixuan Wu3dd37482017-08-21 14:18:36 -070074 # Discover and run tests.
Xixuan Wuf4e15c42017-08-24 14:06:03 -070075 if test_file:
76 suites = unittest.loader.TestLoader().discover(test_path, test_file)
77 else:
78 suites = unittest.loader.TestLoader().discover(test_path, test_pattern)
79
80 return unittest.TextTestRunner(verbosity=2).run(suites)
xixuan82753172017-08-07 09:22:50 -070081
xixuan82753172017-08-07 09:22:50 -070082
Xixuan Wu3dd37482017-08-21 14:18:36 -070083def _import_sdk_path(sdk_path):
84 # If the SDK path points to a Google Cloud SDK installation
85 # then we should alter it to point to the GAE platform location.
86 appengine_path = os.path.join(sdk_path, 'platform/google_appengine')
87 sdk_path = appengine_path if os.path.exists(appengine_path) else sdk_path
88
89 # Make sure google.appengine.* modules are importable.
90 fixup_paths(sdk_path)
91
92 # Make sure all bundled third-party packages are available.
93 # pylint: disable=g-import-not-at-top
94 import dev_appserver
95 dev_appserver.fix_sys_path()
96
97
98def _make_parser():
99 """Return unittest parser."""
100 parser = argparse.ArgumentParser(
101 description=__doc__,
102 formatter_class=argparse.RawDescriptionHelpFormatter)
103 parser.add_argument(
104 '--sdk_path',
105 help='The path to the Google App Engine SDK or the Google Cloud SDK.',
106 default=GOOGLE_CLOUD_SDK_PATH)
107 parser.add_argument(
108 '--test-path',
109 help='The path to look for tests, defaults to the current directory.',
110 default=os.getcwd())
Xixuan Wuf4e15c42017-08-24 14:06:03 -0700111 group = parser.add_mutually_exclusive_group()
112 group.add_argument(
Xixuan Wu3dd37482017-08-21 14:18:36 -0700113 '--test-pattern',
114 help='The file pattern for test modules, defaults to *_test.py.',
115 default='*_test.py')
Xixuan Wuf4e15c42017-08-24 14:06:03 -0700116 group.add_argument(
117 'test_file',
118 nargs='?',
119 help=('A single test module to test, default to empty string, which '
120 'means the runner will find test modules by test-pattern.'),
121 default='')
122 parser.add_argument(
123 '--debug',
124 action='store_true',
125 help='Display the logging in unittest.')
Xixuan Wu3dd37482017-08-21 14:18:36 -0700126 return parser
xixuan82753172017-08-07 09:22:50 -0700127
128
129if __name__ == '__main__':
Xixuan Wu3dd37482017-08-21 14:18:36 -0700130 unittest_parser = _make_parser()
131 args = unittest_parser.parse_args()
Xixuan Wuf4e15c42017-08-24 14:06:03 -0700132 if args.debug:
133 logging.getLogger().setLevel(logging.DEBUG)
134 else:
135 logging.getLogger().setLevel(logging.CRITICAL)
136
Xixuan Wu3dd37482017-08-21 14:18:36 -0700137 try:
138 _import_sdk_path(args.sdk_path)
139 except ImportError:
140 args.sdk_path = raw_input('Cannot find google SDK in %s. Please specify '
141 'your google SDK path: ' % GOOGLE_CLOUD_SDK_PATH)
xixuan82753172017-08-07 09:22:50 -0700142
Xixuan Wuf4e15c42017-08-24 14:06:03 -0700143 result = main(args.sdk_path, args.test_path, args.test_pattern,
144 args.test_file)
145
Xixuan Wu3dd37482017-08-21 14:18:36 -0700146 if not result.wasSuccessful():
147 sys.exit(1)