2018-11-03 19:33:19 +03:00
|
|
|
#!/usr/bin/python3
|
|
|
|
|
2018-11-04 13:57:14 +03:00
|
|
|
import sys, getopt, yaml, time
|
2018-11-03 19:33:19 +03:00
|
|
|
|
2018-11-03 20:14:43 +03:00
|
|
|
from monkeyfarmer import Browser
|
|
|
|
|
2019-02-16 13:56:28 +03:00
|
|
|
class DriverBrowser(Browser):
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
super(DriverBrowser, self).__init__(*args, **kwargs)
|
|
|
|
self.auth = []
|
|
|
|
|
|
|
|
def add_auth(self, url, realm, username, password):
|
|
|
|
self.auth.append((url, realm, username, password))
|
|
|
|
|
|
|
|
def remove_auth(self, url, realm, username, password):
|
|
|
|
keep = []
|
|
|
|
def matches(a,b):
|
|
|
|
if a is None or b is None:
|
|
|
|
return True
|
|
|
|
return a == b
|
|
|
|
for (iurl, irealm, iusername, ipassword) in self.auth:
|
|
|
|
if not (matches(url, iurl) or
|
|
|
|
matches(realm, irealm) or
|
|
|
|
matches(username, iusername) or
|
|
|
|
matches(password, ipassword)):
|
|
|
|
keep.append((iurl, irealm, iusername, ipassword))
|
|
|
|
self.auth = keep
|
|
|
|
|
|
|
|
def handle_ready_login(self, logwin):
|
|
|
|
# We have logwin.{url,username,password,realm}
|
|
|
|
# We must logwin.send_{username,password}(xxx)
|
|
|
|
# We may logwin.go()
|
|
|
|
# We may logwin.destroy()
|
|
|
|
def matches(a,b):
|
|
|
|
if a is None or b is None:
|
|
|
|
return True
|
|
|
|
return a == b
|
|
|
|
candidates = []
|
|
|
|
for (url, realm, username, password) in self.auth:
|
|
|
|
score = 0
|
|
|
|
if matches(url, logwin.url):
|
|
|
|
score += 1
|
|
|
|
if matches(realm, logwin.realm):
|
|
|
|
score += 1
|
|
|
|
if matches(username, logwin.username):
|
|
|
|
score += 1
|
|
|
|
if score > 0:
|
|
|
|
candidates.append((score, username, password))
|
|
|
|
if candidates:
|
|
|
|
candidates.sort()
|
|
|
|
(score, username, password) = candidates[-1]
|
|
|
|
print("401: Found candidate {}/{} with score {}".format(username, password, score))
|
|
|
|
logwin.send_username(username)
|
|
|
|
logwin.send_password(password)
|
|
|
|
logwin.go()
|
|
|
|
else:
|
|
|
|
print("401: No candidate found, cancelling login box")
|
|
|
|
logwin.destroy()
|
|
|
|
|
2018-11-03 19:33:19 +03:00
|
|
|
def print_usage():
|
|
|
|
print('Usage:')
|
|
|
|
print(' ' + sys.argv[0] + ' -m <path to monkey> -t <path to test>')
|
|
|
|
|
|
|
|
def parse_argv(argv):
|
|
|
|
path_monkey = ''
|
|
|
|
path_test = ''
|
|
|
|
try:
|
|
|
|
opts, args = getopt.getopt(argv,"hm:t:",["monkey=","test="])
|
|
|
|
except getopt.GetoptError:
|
|
|
|
print_usage()
|
|
|
|
sys.exit(2)
|
|
|
|
for opt, arg in opts:
|
|
|
|
if opt == '-h':
|
|
|
|
print_usage()
|
|
|
|
sys.exit()
|
|
|
|
elif opt in ("-m", "--monkey"):
|
|
|
|
path_monkey = arg
|
|
|
|
elif opt in ("-t", "--test"):
|
|
|
|
path_test = arg
|
|
|
|
|
|
|
|
if path_monkey == '':
|
|
|
|
print_usage()
|
|
|
|
sys.exit()
|
|
|
|
if path_test == '':
|
|
|
|
print_usage()
|
|
|
|
sys.exit()
|
|
|
|
|
|
|
|
return path_monkey, path_test
|
|
|
|
|
|
|
|
def load_test_plan(path):
|
|
|
|
plan = []
|
|
|
|
with open(path, 'r') as stream:
|
|
|
|
try:
|
|
|
|
plan = (yaml.load(stream))
|
|
|
|
except:
|
|
|
|
print (exc)
|
|
|
|
return plan
|
|
|
|
|
|
|
|
def get_indent(ctx):
|
|
|
|
return ' ' * ctx["depth"];
|
|
|
|
|
|
|
|
def print_test_plan_info(ctx, plan):
|
|
|
|
print('Running test: [' + plan["group"] + '] ' + plan["title"])
|
|
|
|
|
2018-11-03 20:14:43 +03:00
|
|
|
def assert_browser(ctx):
|
|
|
|
assert(ctx['browser'].started)
|
|
|
|
assert(not ctx['browser'].stopped)
|
2018-11-04 16:13:23 +03:00
|
|
|
|
|
|
|
def conds_met(ctx, conds):
|
|
|
|
for cond in conds:
|
|
|
|
status = cond['status']
|
|
|
|
window = cond['window']
|
|
|
|
assert(status == "complete") # TODO: Add more status support?
|
|
|
|
if window == "*all*":
|
|
|
|
for win in ctx['windows'].items():
|
2018-11-04 17:45:46 +03:00
|
|
|
if win[1].throbbing:
|
2018-11-04 16:13:23 +03:00
|
|
|
return False
|
|
|
|
else:
|
|
|
|
win = ctx['windows'][window]
|
|
|
|
if win.throbbing:
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
2018-11-03 19:33:19 +03:00
|
|
|
def run_test_step_action_launch(ctx, step):
|
|
|
|
print(get_indent(ctx) + "Action: " + step["action"])
|
2018-11-03 20:14:43 +03:00
|
|
|
assert(ctx.get('browser') is None)
|
|
|
|
assert(ctx.get('windows') is None)
|
2019-02-16 13:56:28 +03:00
|
|
|
ctx['browser'] = DriverBrowser(monkey_cmd=[ctx["monkey"]], quiet=True)
|
2018-11-03 20:14:43 +03:00
|
|
|
assert_browser(ctx)
|
|
|
|
ctx['windows'] = dict()
|
2018-11-03 19:33:19 +03:00
|
|
|
|
|
|
|
def run_test_step_action_window_new(ctx, step):
|
|
|
|
print(get_indent(ctx) + "Action: " + step["action"])
|
2018-11-03 20:14:43 +03:00
|
|
|
tag = step['tag']
|
|
|
|
assert_browser(ctx)
|
|
|
|
assert(ctx['windows'].get(tag) is None)
|
|
|
|
ctx['windows'][tag] = ctx['browser'].new_window(url=step.get('url'))
|
2018-11-03 19:33:19 +03:00
|
|
|
|
|
|
|
def run_test_step_action_window_close(ctx, step):
|
|
|
|
print(get_indent(ctx) + "Action: " + step["action"])
|
2018-11-03 20:14:43 +03:00
|
|
|
assert_browser(ctx)
|
|
|
|
tag = step['window']
|
|
|
|
assert(ctx['windows'].get(tag) is not None)
|
|
|
|
win = ctx['windows'].pop(tag)
|
|
|
|
win.kill()
|
|
|
|
win.wait_until_dead()
|
|
|
|
assert(win.alive == False)
|
2018-11-03 19:33:19 +03:00
|
|
|
|
|
|
|
def run_test_step_action_navigate(ctx, step):
|
|
|
|
print(get_indent(ctx) + "Action: " + step["action"])
|
2018-11-03 20:14:43 +03:00
|
|
|
assert_browser(ctx)
|
2018-11-04 14:07:36 +03:00
|
|
|
assert(step.get('url') is not None)
|
2018-11-03 20:14:43 +03:00
|
|
|
tag = step['window']
|
2018-11-04 14:07:36 +03:00
|
|
|
print(get_indent(ctx) + " " + tag + " --> " + step['url'])
|
2018-11-03 20:14:43 +03:00
|
|
|
win = ctx['windows'].get(tag)
|
|
|
|
assert(win is not None)
|
|
|
|
win.go(step['url'])
|
2018-11-03 19:33:19 +03:00
|
|
|
|
|
|
|
def run_test_step_action_sleep_ms(ctx, step):
|
|
|
|
print(get_indent(ctx) + "Action: " + step["action"])
|
2018-11-04 17:34:31 +03:00
|
|
|
conds = step['conditions']
|
|
|
|
sleep_time = step['time']
|
|
|
|
sleep = 0
|
|
|
|
have_repeat = False
|
|
|
|
if isinstance(sleep_time, str):
|
|
|
|
assert(ctx['repeats'].get(sleep_time) is not None)
|
|
|
|
repeat = ctx['repeats'].get(sleep_time)
|
|
|
|
sleep = repeat["i"] / 1000
|
|
|
|
start = repeat["start"]
|
|
|
|
have_repeat = True
|
|
|
|
else:
|
|
|
|
sleep = time / 1000
|
|
|
|
start = time.time()
|
|
|
|
|
|
|
|
while True:
|
|
|
|
slept = time.time() - start
|
|
|
|
if conds_met(ctx, conds):
|
|
|
|
if have_repeat:
|
|
|
|
ctx['repeats'][sleep_time]["loop"] = False
|
|
|
|
print(get_indent(ctx) + " Condition met after {}s".format(slept))
|
|
|
|
break
|
|
|
|
elif slept > sleep:
|
|
|
|
print(get_indent(ctx) + " Condition not met after {}s".format(sleep))
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
ctx['browser'].farmer.loop(once=True)
|
2018-11-03 19:33:19 +03:00
|
|
|
|
|
|
|
def run_test_step_action_block(ctx, step):
|
|
|
|
print(get_indent(ctx) + "Action: " + step["action"])
|
2018-11-03 20:14:43 +03:00
|
|
|
conds = step['conditions']
|
|
|
|
assert_browser(ctx)
|
|
|
|
|
2018-11-04 16:13:23 +03:00
|
|
|
while not conds_met(ctx, conds):
|
2018-11-03 20:14:43 +03:00
|
|
|
ctx['browser'].farmer.loop(once=True)
|
2018-11-03 19:33:19 +03:00
|
|
|
|
|
|
|
def run_test_step_action_repeat(ctx, step):
|
|
|
|
print(get_indent(ctx) + "Action: " + step["action"])
|
2018-11-04 17:34:31 +03:00
|
|
|
tag = step['tag']
|
|
|
|
assert(ctx['repeats'].get(tag) is None)
|
|
|
|
ctx['repeats'][tag] = {
|
|
|
|
"i": step["min"],
|
|
|
|
"step": step["step"],
|
|
|
|
"loop": True,
|
|
|
|
}
|
|
|
|
while ctx['repeats'][tag]["loop"]:
|
|
|
|
ctx['repeats'][tag]["start"] = time.time()
|
|
|
|
ctx["depth"] += 1
|
|
|
|
for s in step["steps"]:
|
|
|
|
run_test_step(ctx, s)
|
|
|
|
ctx['repeats'][tag]["i"] += ctx['repeats'][tag]["step"]
|
|
|
|
ctx["depth"] -= 1
|
2018-11-03 19:33:19 +03:00
|
|
|
|
2018-11-03 20:14:43 +03:00
|
|
|
def run_test_step_action_plot_check(ctx, step):
|
|
|
|
print(get_indent(ctx) + "Action: " + step["action"])
|
|
|
|
assert_browser(ctx)
|
|
|
|
win = ctx['windows'][step['window']]
|
|
|
|
checks = step['checks']
|
|
|
|
all_text = []
|
|
|
|
bitmaps = []
|
|
|
|
for plot in win.redraw():
|
|
|
|
if plot[0] == 'TEXT':
|
|
|
|
all_text.extend(plot[6:])
|
|
|
|
if plot[0] == 'BITMAP':
|
|
|
|
bitmaps.append(plot[1:])
|
|
|
|
all_text = " ".join(all_text)
|
|
|
|
for check in checks:
|
|
|
|
if 'text-contains' in check.keys():
|
|
|
|
print("Check {} in {}".format(repr(check['text-contains']),repr(all_text)))
|
|
|
|
assert(check['text-contains'] in all_text)
|
2019-02-16 13:56:28 +03:00
|
|
|
elif 'text-not-contains' in check.keys():
|
|
|
|
print("Check {} NOT in {}".format(repr(check['text-not-contains']),repr(all_text)))
|
|
|
|
assert(check['text-not-contains'] not in all_text)
|
2018-11-03 20:14:43 +03:00
|
|
|
elif 'bitmap-count' in check.keys():
|
|
|
|
assert(len(bitmaps) == int(check['bitmap-count']))
|
|
|
|
else:
|
|
|
|
raise AssertionError("Unknown check: {}".format(repr(check)))
|
|
|
|
|
2018-11-03 19:33:19 +03:00
|
|
|
def run_test_step_action_timer_start(ctx, step):
|
|
|
|
print(get_indent(ctx) + "Action: " + step["action"])
|
2018-11-04 13:57:14 +03:00
|
|
|
tag = step['tag']
|
|
|
|
assert_browser(ctx)
|
|
|
|
assert(ctx['timers'].get(tag) is None)
|
|
|
|
ctx['timers'][tag] = {}
|
|
|
|
ctx['timers'][tag]["start"] = time.time()
|
2018-11-03 19:33:19 +03:00
|
|
|
|
|
|
|
def run_test_step_action_timer_stop(ctx, step):
|
|
|
|
print(get_indent(ctx) + "Action: " + step["action"])
|
2018-11-04 13:57:14 +03:00
|
|
|
timer = step['timer']
|
|
|
|
assert_browser(ctx)
|
|
|
|
assert(ctx['timers'].get(timer) is not None)
|
|
|
|
taken = time.time() - ctx['timers'][timer]["start"]
|
|
|
|
print(get_indent(ctx) + " " + timer + " took: " + str(taken) + "s")
|
|
|
|
ctx['timers'][timer]["taken"] = taken
|
2018-11-03 19:33:19 +03:00
|
|
|
|
|
|
|
def run_test_step_action_timer_check(ctx, step):
|
|
|
|
print(get_indent(ctx) + "Action: " + step["action"])
|
2018-11-04 13:57:14 +03:00
|
|
|
condition = step["condition"].split()
|
|
|
|
assert(len(condition) == 3)
|
|
|
|
timer1 = ctx['timers'].get(condition[0])
|
|
|
|
timer2 = ctx['timers'].get(condition[2])
|
|
|
|
assert(timer1 is not None)
|
|
|
|
assert(timer2 is not None)
|
|
|
|
assert(timer1["taken"] is not None)
|
|
|
|
assert(timer2["taken"] is not None)
|
|
|
|
assert(condition[1] in ('<', '>'))
|
|
|
|
if condition[1] == '<':
|
|
|
|
assert(timer1["taken"] < timer2["taken"])
|
|
|
|
elif condition[1] == '>':
|
|
|
|
assert(timer1["taken"] > timer2["taken"])
|
2018-11-03 19:33:19 +03:00
|
|
|
|
2019-02-16 13:56:28 +03:00
|
|
|
def run_test_step_action_add_auth(ctx, step):
|
|
|
|
print(get_indent(ctx) + "Action:" + step["action"])
|
|
|
|
assert_browser(ctx)
|
|
|
|
browser = ctx['browser']
|
|
|
|
browser.add_auth(step.get("url"), step.get("realm"),
|
|
|
|
step.get("username"), step.get("password"))
|
|
|
|
|
|
|
|
|
|
|
|
def run_test_step_action_remove_auth(ctx, step):
|
|
|
|
print(get_indent(ctx) + "Action:" + step["action"])
|
|
|
|
assert_browser(ctx)
|
|
|
|
browser = ctx['browser']
|
|
|
|
browser.remove_auth(step.get("url"), step.get("realm"),
|
|
|
|
step.get("username"), step.get("password"))
|
|
|
|
|
2018-11-03 19:33:19 +03:00
|
|
|
def run_test_step_action_quit(ctx, step):
|
|
|
|
print(get_indent(ctx) + "Action: " + step["action"])
|
2018-11-03 20:14:43 +03:00
|
|
|
assert_browser(ctx)
|
|
|
|
browser = ctx.pop('browser')
|
|
|
|
windows = ctx.pop('windows')
|
|
|
|
assert(browser.quit_and_wait())
|
2018-11-03 19:33:19 +03:00
|
|
|
|
|
|
|
step_handlers = {
|
|
|
|
"launch": run_test_step_action_launch,
|
|
|
|
"window-new": run_test_step_action_window_new,
|
|
|
|
"window-close": run_test_step_action_window_close,
|
|
|
|
"navigate": run_test_step_action_navigate,
|
|
|
|
"sleep-ms": run_test_step_action_sleep_ms,
|
|
|
|
"block": run_test_step_action_block,
|
|
|
|
"repeat": run_test_step_action_repeat,
|
|
|
|
"timer-start": run_test_step_action_timer_start,
|
|
|
|
"timer-stop": run_test_step_action_timer_stop,
|
|
|
|
"timer-check": run_test_step_action_timer_check,
|
2018-11-03 20:14:43 +03:00
|
|
|
"plot-check": run_test_step_action_plot_check,
|
2019-02-16 13:56:28 +03:00
|
|
|
"add-auth": run_test_step_action_add_auth,
|
|
|
|
"remove-auth": run_test_step_action_remove_auth,
|
2018-11-03 19:33:19 +03:00
|
|
|
"quit": run_test_step_action_quit,
|
|
|
|
}
|
|
|
|
|
|
|
|
def run_test_step(ctx, step):
|
|
|
|
step_handlers[step["action"]](ctx, step)
|
|
|
|
|
|
|
|
def walk_test_plan(ctx, plan):
|
|
|
|
ctx["depth"] = 0
|
2018-11-04 16:10:27 +03:00
|
|
|
ctx["timers"] = dict()
|
2018-11-04 17:34:31 +03:00
|
|
|
ctx['repeats'] = dict()
|
2018-11-03 19:33:19 +03:00
|
|
|
for step in plan["steps"]:
|
|
|
|
run_test_step(ctx, step)
|
|
|
|
|
2019-02-16 17:57:11 +03:00
|
|
|
def run_test_plan(ctx, plan):
|
|
|
|
print_test_plan_info(ctx, plan)
|
|
|
|
walk_test_plan(ctx, plan)
|
|
|
|
|
|
|
|
def run_preloaded_test(path_monkey, plan):
|
|
|
|
ctx = {
|
|
|
|
"monkey": path_monkey,
|
|
|
|
}
|
|
|
|
run_test_plan(ctx, plan)
|
2018-11-03 19:33:19 +03:00
|
|
|
|
|
|
|
def main(argv):
|
|
|
|
ctx = {}
|
|
|
|
path_monkey, path_test = parse_argv(argv)
|
|
|
|
plan = load_test_plan(path_test)
|
2018-11-03 20:14:43 +03:00
|
|
|
ctx["monkey"] = path_monkey
|
2018-11-03 19:33:19 +03:00
|
|
|
|
|
|
|
# Some python weirdness to get to main().
|
|
|
|
if __name__ == "__main__":
|
2018-11-03 20:14:43 +03:00
|
|
|
main(sys.argv[1:])
|