OLD | NEW |
| (Empty) |
1 #!/usr/bin/env python | |
2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
3 # Use of this source code is governed by a BSD-style license that can be | |
4 # found in the LICENSE file. | |
5 | |
6 """Setups a local Rietveld instance to test against a live server for | |
7 integration tests. | |
8 | |
9 It makes sure Google AppEngine SDK is found, download Rietveld and Django code | |
10 if necessary and starts the server on a free inbound TCP port. | |
11 """ | |
12 | |
13 import optparse | |
14 import os | |
15 import socket | |
16 import sys | |
17 import time | |
18 | |
19 sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) | |
20 import subprocess2 | |
21 | |
22 | |
23 class Failure(Exception): | |
24 pass | |
25 | |
26 | |
27 def test_port(port): | |
28 s = socket.socket() | |
29 try: | |
30 return s.connect_ex(('127.0.0.1', port)) == 0 | |
31 finally: | |
32 s.close() | |
33 | |
34 | |
35 def find_free_port(): | |
36 # Test to find an available port starting at 8080. | |
37 port = 8080 | |
38 max_val = (2<<16) | |
39 while test_port(port) and port < max_val: | |
40 port += 1 | |
41 if port == max_val: | |
42 raise Failure('Having issues finding an available port') | |
43 return port | |
44 | |
45 | |
46 class LocalRietveld(object): | |
47 """Downloads everything needed to run a local instance of Rietveld.""" | |
48 | |
49 def __init__(self, base_dir=None): | |
50 # Paths | |
51 self.base_dir = base_dir | |
52 if not self.base_dir: | |
53 self.base_dir = os.path.dirname(os.path.abspath(__file__)) | |
54 # TODO(maruel): This should be in /tmp but that would mean having to fetch | |
55 # everytime. This test is already annoyingly slow. | |
56 self.rietveld = os.path.join(self.base_dir, '_rietveld') | |
57 self.test_server = None | |
58 self.port = None | |
59 | |
60 # Find the GAE SDK | |
61 previous_dir = '' | |
62 self.sdk_path = '' | |
63 base_dir = self.base_dir | |
64 while base_dir != previous_dir: | |
65 previous_dir = base_dir | |
66 self.sdk_path = os.path.join(base_dir, 'google_appengine') | |
67 if not os.path.isfile(os.path.join(self.sdk_path, 'VERSION')): | |
68 base_dir = os.path.dirname(base_dir) | |
69 self.dev_app = os.path.join(self.sdk_path, 'dev_appserver.py') | |
70 | |
71 def install_prerequisites(self): | |
72 # First, verify the Google AppEngine SDK is available. | |
73 if not os.path.isfile(self.dev_app): | |
74 raise Failure( | |
75 'Install google_appengine sdk in %s or higher up' % self.base_dir) | |
76 | |
77 # Second, checkout rietveld if not available. | |
78 if not os.path.isdir(self.rietveld): | |
79 print('Checking out rietveld...') | |
80 try: | |
81 subprocess2.check_call( | |
82 ['svn', 'co', '-q', 'http://rietveld.googlecode.com/svn/trunk@681', | |
83 self.rietveld]) | |
84 except (OSError, subprocess2.CalledProcessError), e: | |
85 raise Failure('Failed to checkout rietveld\n%s' % e) | |
86 else: | |
87 print('Syncing rietveld...') | |
88 try: | |
89 subprocess2.check_call( | |
90 ['svn', 'up', '-q', '-r', '681'], cwd=self.rietveld) | |
91 except (OSError, subprocess2.CalledProcessError), e: | |
92 raise Failure('Failed to sync rietveld\n%s' % e) | |
93 | |
94 def start_server(self, verbose=False): | |
95 self.install_prerequisites() | |
96 self.port = find_free_port() | |
97 if verbose: | |
98 pipe = None | |
99 else: | |
100 pipe = subprocess2.VOID | |
101 cmd = [ | |
102 sys.executable, | |
103 self.dev_app, | |
104 '--skip_sdk_update_check', | |
105 '.', | |
106 '--port=%d' % self.port, | |
107 '--datastore_path=' + os.path.join(self.rietveld, 'tmp.db'), | |
108 '-c'] | |
109 | |
110 # CHEAP TRICK | |
111 # By default you only want to bind on loopback but I'm testing over a | |
112 # headless computer so it's useful to be able to access the test instance | |
113 # remotely. | |
114 if os.environ.get('GAE_LISTEN_ALL', '') == 'true': | |
115 cmd.extend(('-a', '0.0.0.0')) | |
116 | |
117 self.test_server = subprocess2.Popen( | |
118 cmd, stdout=pipe, stderr=pipe, cwd=self.rietveld) | |
119 # Loop until port 127.0.0.1:port opens or the process dies. | |
120 while not test_port(self.port): | |
121 self.test_server.poll() | |
122 if self.test_server.returncode is not None: | |
123 raise Failure( | |
124 'Test rietveld instance failed early on port %s' % | |
125 self.port) | |
126 time.sleep(0.01) | |
127 | |
128 def stop_server(self): | |
129 if self.test_server: | |
130 self.test_server.kill() | |
131 self.test_server.wait() | |
132 self.test_server = None | |
133 self.port = None | |
134 | |
135 | |
136 def main(): | |
137 parser = optparse.OptionParser() | |
138 parser.add_option('-v', '--verbose', action='store_true') | |
139 options, args = parser.parse_args() | |
140 if args: | |
141 parser.error('Unknown arguments: %s' % ' '.join(args)) | |
142 instance = LocalRietveld() | |
143 try: | |
144 instance.start_server(verbose=options.verbose) | |
145 print 'Local rietveld instance started on port %d' % instance.port | |
146 while True: | |
147 time.sleep(0.1) | |
148 finally: | |
149 instance.stop_server() | |
150 | |
151 | |
152 if __name__ == '__main__': | |
153 main() | |
OLD | NEW |