| 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 |