Skip to content

Commit 6bd1e31

Browse files
committed
Merge pull request #20 from codebendercc/seleniumbender-python
Seleniumbender python
2 parents 635071a + c15d3d0 commit 6bd1e31

File tree

4 files changed

+219
-7
lines changed

4 files changed

+219
-7
lines changed

bin/env_vars.sh.template

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ export DISQUS_API_PUBLIC=""
1515
export DISQUS_SSO_ID=""
1616
export DISQUS_SSO_USERNAME=""
1717
export DISQUS_SSO_EMAIL=""
18+
export DISQUS_FORUM=""
19+
20+
export AUTHOR_URL=""
1821

1922
export EMAIL=""
2023

bin/seleniumbender.py

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
#!/usr/bin/env python
2+
3+
from __future__ import print_function
4+
import argparse
5+
import os
6+
import re
7+
import subprocess
8+
import sys
9+
import time
10+
11+
SHELL='/bin/bash'
12+
SOURCE = 'codebender_cc'
13+
14+
class Tests:
15+
def __init__(self, url, environment):
16+
self.source=SOURCE
17+
self.url = url
18+
self.environment = os.path.abspath(environment)
19+
self.email = os.getenv('EMAIL', '[email protected]')
20+
self.files_to_ignore = [
21+
'.gitignore'
22+
]
23+
24+
def run(self, operation, libraries=None):
25+
if operation == 'common':
26+
self.common()
27+
elif operation == 'libraries':
28+
self.libraries()
29+
elif operation == 'examples':
30+
self.examples()
31+
elif operation == 'sketches':
32+
self.sketches()
33+
elif operation == 'compile':
34+
self.compile(libraries)
35+
elif operation == 'noplugin':
36+
self.noplugin()
37+
elif operation == 'walkthrough':
38+
self.walkthrough()
39+
elif operation == 'staging':
40+
self.staging()
41+
42+
def run_command(self, command, user_agent=None):
43+
if user_agent:
44+
os.environ['SELENIUM_USER_AGENT'] = user_agent
45+
command = ' '.join(command)
46+
print('command:', command)
47+
return subprocess.call(command, shell=True, executable=SHELL)
48+
49+
def send_mail_no_logs(self, identifier):
50+
command = ['mail', '-s', '"Selenium Tests: {identifier} Failed To Run" {email} <<< "Something went wrong with {identifier} tests. Please check the logs."'.format(identifier=identifier, email=self.email)]
51+
run_command(command)
52+
53+
def send_mail_with_logs(self, identifier):
54+
default_tests_dir = os.path.normpath(os.path.join(os.getcwd(), '..'))
55+
root_dir = os.getenv('ROOTDIR', default_tests_dir)
56+
57+
logs = os.path.join(root_dir, 'logs')
58+
reports = os.path.join(root_dir, 'reports')
59+
log_regexp = re.compile('.+{identifier}.+'.format(identifier=identifier))
60+
61+
try:
62+
logfile = sorted([filename for filename in os.listdir(logs) if filename not in files_to_ignore and log_regexp.match(filename)], reverse=True)[0]
63+
logfile_timestamp = re.match(r'(\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2})-.+', logfile).group(1)
64+
65+
report_regexp = re.compile('report_{timestamp}-{identifier}_(\d+)'.format(timestamp=logfile_timestamp, identifier=identifier))
66+
reportfile = sorted([filename for filename in os.listdir(reports) if filename not in files_to_ignore and report_regexp.match(filename)], reverse=True)[0]
67+
changes = report_regexp.match(reportfile).group(1)
68+
69+
email_date = time.strftime('%Y-%m-%d %H:%M:%S')
70+
71+
command = [
72+
'(echo "Changes since the last time: {changes}";'.format(changes=changes),
73+
'uuencode "{logs}/{logfile}" "{logfile}";'.format(logs=logs, logfile=logfile),
74+
'uuencode "{reports}/{reportfile}" "{reportfile}")'.format(reports=reports, reportfile=reportfile),
75+
'| mail -s "Selenium Tests Report: {identifier} {email_date} Changes: {changes}" {email}'.format(identifier=identifier, email_date=email_date, changes=changes, email=self.email)
76+
]
77+
run_command(command)
78+
except:
79+
pass
80+
81+
def create_command(self, test_directory, *extra_arguments):
82+
return ['tox', 'tests/' + test_directory, '--', '--url={}'.format(TARGETS[self.url]), '--source={}'.format(SOURCE)] + list(extra_arguments)
83+
84+
def common(self, identifier='common'):
85+
command = self.create_command('common', '--plugin')
86+
retval = self.run_command(command)
87+
if retval != 0:
88+
self.send_mail_no_logs(identifier)
89+
90+
def libraries(self, identifier='libraries_fetch'):
91+
command = self.create_command('libraries_fetch', '-F', '--plugin')
92+
self.run_command(command)
93+
self.send_mail_with_logs(identifier)
94+
95+
def examples(self, identifier='libraries_test'):
96+
command = self.create_command('libraries', '-F', '--plugin')
97+
self.run_command(command)
98+
self.send_mail_with_logs(identifier)
99+
100+
def sketches(self, libraries, identifier='cb_compile_tester'):
101+
command = self.create_command('compile_tester', '-F', '--plugin')
102+
self.run_command(command)
103+
self.send_mail_with_logs(identifier)
104+
105+
def compile(self, libraries):
106+
command = self.create_command('target_libraries', '-F', '--plugin', '--libraries={}'.format(libraries))
107+
self.run_command(command)
108+
109+
def noplugin(self, identifier = 'noplugin'):
110+
command = self.create_command('noplugin')
111+
retval = self.run_command(command)
112+
if retval != 0:
113+
self.send_mail_no_logs(identifier)
114+
115+
def walkthrough(self, identifier='walkthrough'):
116+
command = self.create_command('walkthrough', '--plugin')
117+
retvals = []
118+
user_agents = [
119+
'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:43.0) Gecko/20100101 Firefox/43.0 codebender-selenium',
120+
'Mozilla/5.0 (Windows NT 6.1; rv:43.0) Gecko/20100101 Firefox/43.0 codebender-selenium',
121+
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1; rv:43.0) Gecko/20100101 Firefox/43.0 codebender-selenium'
122+
]
123+
for user_agent in user_agents:
124+
retval = self.run_command(command, user_agent=user_agent)
125+
retvals.append(retval)
126+
retval = max(retvals)
127+
if retval != 0:
128+
self.send_mail_no_logs(identifier)
129+
130+
def staging(self):
131+
command = self.create_command('compile_tester', '-F', '--plugin')
132+
self.run_command(command)
133+
134+
OPERATIONS = {
135+
'common':'\tTest site common functionality',
136+
'libraries': 'Visit all libraries and their examples',
137+
'examples': 'Compile all examples',
138+
'sketches': 'Compile sketch of cb_compile_tester user',
139+
'compile': '\tCompile specific examples',
140+
'noplugin': 'Run tests without app/plugin installed',
141+
'walkthrough': 'Run tests for walkthrough',
142+
'staging': '\tRun tests for staging only'
143+
}
144+
145+
TARGETS = {
146+
'live': 'https://codebender.cc',
147+
'staging': 'https://staging.codebender.cc',
148+
'local': 'http://dev.codebender.cc'
149+
}
150+
151+
def main():
152+
available_operations = ['{operation}\t{description}'.format(operation=x, description=OPERATIONS[x]) for x in sorted(OPERATIONS.keys())]
153+
available_targets = ['{target}\t{url}'.format(target=x, url=TARGETS[x]) for x in sorted(TARGETS.keys())]
154+
155+
parser = argparse.ArgumentParser(description="seleniumbender - A command line tool for codebender's Selenium tests",
156+
epilog='Happy testing :)',
157+
formatter_class=argparse.RawTextHelpFormatter)
158+
parser.add_argument('operation',
159+
help='Operation to execute.\nAvailable operations:\n\t{operations}'.format(operations='\n\t'.join(available_operations)))
160+
parser.add_argument('--target',
161+
default='live',
162+
help='Target site for the tests.\nAvailable targets (default: live):\n\t{targets}'.format(targets='\n\t'.join(available_targets)))
163+
parser.add_argument('--config',
164+
default='env_vars.sh',
165+
help='Configuration file to load (default: config.cfg).')
166+
parser.add_argument('--libraries',
167+
default=None,
168+
help='Libraries to test (comma separated machine names) when using option: target')
169+
parser.add_argument('--saucelabs',
170+
action='store_true',
171+
default=False,
172+
help='Use saucelabs as the Selenium server')
173+
174+
# Parse arguments
175+
args = parser.parse_args()
176+
operation = args.operation
177+
if operation not in OPERATIONS:
178+
print('Unsupported operation!\n')
179+
parser.print_help()
180+
sys.exit()
181+
182+
target = args.target
183+
if target not in TARGETS:
184+
print('Unsupported target!\n')
185+
parser.print_help()
186+
sys.exit()
187+
188+
config = args.config
189+
if not os.path.exists(config):
190+
print('Config file:', config, 'does not exist')
191+
sys.exit()
192+
193+
libraries = args.libraries
194+
if operation == 'target' and not libraries:
195+
print('No target libraries specified!\n')
196+
parser.print_help()
197+
sys.exit()
198+
199+
# Read environment variables file
200+
output = subprocess.check_output('source {}; env'.format(config), shell=True, executable=SHELL)
201+
env_vars = dict((line.split('=', 1) for line in output.splitlines()))
202+
os.environ.update(env_vars)
203+
204+
# Run tests
205+
tests = Tests(target, config)
206+
tests.run(operation, libraries=libraries)
207+
208+
if __name__ == '__main__':
209+
main()

codebender_testing/disqus.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
import re
1313

1414

15-
FORUM = 'codebender-cc'
16-
AUTHOR_URL = 'https://codebender.cc/user/codebender'
15+
FORUM = os.getenv('DISQUS_FORUM', 'codebender-cc')
16+
AUTHOR_URL = os.getenv('AUTHOR_URL', 'https://codebender.cc/user/codebender')
1717
DISQUS_REQUESTS_PER_HOUR = 1000
1818
DISQUS_WAIT = (DISQUS_REQUESTS_PER_HOUR / 60) / 60
1919
CHANGE_LOG = 'examples_compile_log.json'
@@ -111,7 +111,7 @@ def handle_library_comment(self, library, current_date, log):
111111
return log
112112

113113
def handle_example_comment(self, url, results, current_date, log):
114-
identifier = url.replace('https://codebender.cc', '')
114+
identifier = re.sub(r'https*://.*codebender.cc', '', url)
115115
identifier = 'ident:' + identifier
116116
try:
117117
log[url]['comment'] = False
@@ -176,7 +176,6 @@ def create_post(self, thread_id, message):
176176
self.last_post = message
177177
elif re.match(r'^.+\.$', self.last_post):
178178
message = message[:-1]
179-
self.last_post = message
180179

181180
comment_status = False
182181

@@ -189,6 +188,7 @@ def create_post(self, thread_id, message):
189188
thread=response[0]['id'],
190189
message=message, method='POST')
191190
if response['raw_message'] == message:
191+
self.last_post = message
192192
comment_status = True
193193
except Exception as error:
194194
print 'Error:', error
@@ -200,7 +200,6 @@ def update_post(self, post_id, message):
200200
self.last_post = message
201201
elif re.match(r'^.+\.$', self.last_post):
202202
message = message[:-1]
203-
self.last_post = message
204203

205204
comment_status = False
206205

@@ -211,6 +210,7 @@ def update_post(self, post_id, message):
211210
post=post_id,
212211
message=message, method='POST')
213212
if response['raw_message'] == message:
213+
self.last_post = message
214214
comment_status = True
215215
except Exception as error:
216216
print 'Error:', error

codebender_testing/utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ def compile_sketches(self, sketches, iframe=False, project_view=False, logfile=N
552552

553553
urls_visited = {}
554554
last_log = read_last_log(compile_type)
555-
if last_log['log']:
555+
if compile_type != 'target_library' and last_log['log']:
556556
# resume previous compile
557557
log_time = strptime(last_log['timestamp'], '%Y-%m-%d_%H-%M-%S')
558558
log_entry = last_log['log']
@@ -650,7 +650,7 @@ def compile_sketches(self, sketches, iframe=False, project_view=False, logfile=N
650650
return
651651

652652
# Generate a report if requested
653-
if create_report:
653+
if compile_type != 'target_library' and create_report:
654654
report_creator(compile_type, log_entry, log_file)
655655
print '\nTest duration:', int(toc - tic), 'sec'
656656

0 commit comments

Comments
 (0)