-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add step, next, continue, list and longlist commands
Showing
12 changed files
with
463 additions
and
95 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,8 @@ | |
build | ||
dist | ||
*.pyc | ||
*.txt | ||
*.robot | ||
log.html | ||
output.xml | ||
report.html | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
class SingletonContext: | ||
in_step_mode = False | ||
current_runner = None | ||
current_runner_step = None | ||
current_source_path = '' | ||
current_source_lineno = 0 | ||
last_command = '' | ||
|
||
def __new__(cls): | ||
if not hasattr(cls, 'instance'): | ||
cls.instance = super(SingletonContext, cls).__new__(cls) | ||
return cls.instance | ||
|
||
|
||
context = SingletonContext() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
from robot.version import get_version | ||
|
||
ROBOT_VERION_RUNNER_GET_STEP_LINENO = '3.2' | ||
|
||
|
||
class RobotNeedUpgrade(Exception): | ||
"""Need upgrade robotframework.""" | ||
|
||
|
||
def check_version(): | ||
if get_version() < ROBOT_VERION_RUNNER_GET_STEP_LINENO: | ||
raise RobotNeedUpgrade | ||
|
||
|
||
def print_source_lines(source_file, lineno, before_and_after=5): | ||
check_version() | ||
|
||
if not source_file or not lineno: | ||
return | ||
|
||
lines = open(source_file).readlines() | ||
start_index = max(1, lineno - before_and_after - 1) | ||
end_index = min(len(lines) + 1, lineno + before_and_after) | ||
_print_lines(lines, start_index, end_index, lineno) | ||
|
||
|
||
def print_test_case_lines(source_file, lineno): | ||
check_version() | ||
|
||
if not source_file or not lineno: | ||
return | ||
|
||
lines = open(source_file).readlines() | ||
current_lineno = lineno | ||
|
||
# find the first line of current test case | ||
line_index = current_lineno - 1 | ||
while line_index >= 0: | ||
line_index -= 1 | ||
line = lines[line_index] | ||
if not _inside_test_case_block(line): | ||
break | ||
start_index = line_index | ||
|
||
# find the last line of current test case | ||
line_index = current_lineno - 1 | ||
while line_index < len(lines): | ||
line = lines[line_index] | ||
if not _inside_test_case_block(line): | ||
break | ||
line_index += 1 | ||
end_index = line_index | ||
|
||
_print_lines(lines, start_index, end_index, lineno) | ||
|
||
|
||
def _inside_test_case_block(line): | ||
if line.startswith('#'): | ||
return True | ||
elif line.startswith(' '): | ||
return True | ||
elif line.startswith('\t'): | ||
return True | ||
return False | ||
|
||
|
||
def _print_lines(lines, start_index, end_index, current_lineno): | ||
display_lines = lines[start_index:end_index] | ||
for lineno, line in enumerate(display_lines, start_index + 1): | ||
current_line_sign = '' | ||
if lineno == current_lineno: | ||
current_line_sign = '->' | ||
print('{:>3} {:2}\t{}'.format(lineno, | ||
current_line_sign, | ||
line.rstrip())) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import inspect | ||
|
||
from .globals import context | ||
|
||
|
||
class RobotLibraryStepListenerMixin: | ||
ROBOT_LISTENER_API_VERSION = 2 | ||
|
||
def __init__(self): | ||
super(RobotLibraryStepListenerMixin, self).__init__() | ||
self.ROBOT_LIBRARY_LISTENER = [self] | ||
|
||
def _start_keyword(self, name, attrs): | ||
context.current_source_path = '' | ||
context.current_source_lineno = 0 | ||
|
||
if not is_step_mode(): | ||
return | ||
|
||
find_runner_step() | ||
step = context.current_runner_step | ||
|
||
if hasattr(step, 'lineno'): | ||
path = step.source | ||
lineno = step.lineno | ||
lineno_0_based = lineno - 1 | ||
context.current_source_path = path | ||
context.current_source_lineno = lineno | ||
print('> {}({})'.format(path, lineno)) | ||
line = (open(path).readlines()[lineno_0_based].strip()) | ||
print('-> {}'.format(line)) | ||
|
||
if attrs['assign']: | ||
assign = '%s = ' % ', '.join(attrs['assign']) | ||
else: | ||
assign = '' | ||
name = '{}.{}'.format(attrs['libname'], attrs['kwname']) | ||
|
||
translated = '{}{} {}'.format(assign, name, ' '.join(attrs['args'])) | ||
print('=> {}'.format(translated)) | ||
|
||
# callback debug interface | ||
self.debug() | ||
|
||
|
||
def find_runner_step(): | ||
stack = inspect.stack() | ||
for frame in stack: | ||
if frame.function == 'run_steps': | ||
arginfo = inspect.getargvalues(frame.frame) | ||
context.current_runner = arginfo.locals.get('runner') | ||
context.current_runner_step = arginfo.locals.get('step') | ||
|
||
|
||
def set_step_mode(on=True): | ||
context.in_step_mode = on | ||
|
||
|
||
def is_step_mode(): | ||
return context.in_step_mode |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
*** Settings *** | ||
Library DebugLibrary | ||
|
||
** test case ** | ||
test1 | ||
debug | ||
log to console working | ||
@{list} = Create List hello world | ||
|
||
test2 | ||
log to console another test case | ||
log to console end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,75 +1,168 @@ | ||
#!/usr/bin/env python | ||
|
||
import os | ||
import tempfile | ||
import unittest | ||
from os.path import abspath, dirname, join | ||
|
||
import pexpect | ||
from robot.version import get_version | ||
|
||
TIMEOUT_SECONDS = 2 | ||
|
||
child = None | ||
|
||
|
||
def check_result(pattern): | ||
index = child.expect([pattern, pexpect.EOF, pexpect.TIMEOUT], | ||
timeout=TIMEOUT_SECONDS) | ||
try: | ||
assert index == 0 | ||
except AssertionError: | ||
print('\n==== Screen buffer raw ====\n', | ||
child._buffer.getvalue(), | ||
'\n^^^^ Screen buffer raw ^^^^') | ||
print('==== Screen buffer ====\n', | ||
child._buffer.getvalue().decode('utf8'), | ||
'\n^^^^ Screen buffer ^^^^') | ||
raise | ||
|
||
def functional_testing(): | ||
child = pexpect.spawn('/usr/bin/env python -m DebugLibrary.shell') | ||
child.expect('Enter interactive shell', timeout=TIMEOUT_SECONDS * 3) | ||
|
||
def check_result(pattern): | ||
index = child.expect([pattern, pexpect.EOF, pexpect.TIMEOUT], | ||
timeout=TIMEOUT_SECONDS) | ||
try: | ||
assert index == 0 | ||
except AssertionError: | ||
print('Screen buffer: ', child._buffer.getvalue()) | ||
raise | ||
def check_prompt(keys, pattern): | ||
child.write(keys) | ||
check_result(pattern) | ||
child.write('\003') # ctrl-c: reset inputs | ||
|
||
def check_prompt(keys, pattern): | ||
child.write(keys) | ||
check_result(pattern) | ||
child.write('\003') # ctrl-c: reset inputs | ||
|
||
def check_command(command, pattern): | ||
child.sendline(command) | ||
check_result(pattern) | ||
def check_command(command, pattern): | ||
child.sendline(command) | ||
check_result(pattern) | ||
|
||
|
||
def base_functional_testing(): | ||
global child | ||
child = pexpect.spawn('/usr/bin/env python -m DebugLibrary.shell') | ||
child.expect('Enter interactive shell', timeout=TIMEOUT_SECONDS * 3) | ||
|
||
# auto complete | ||
check_prompt('key\t', 'keywords') | ||
check_prompt('key\t', 'Keyword Should Exist') | ||
check_prompt('buil\t', 'Library: BuiltIn') | ||
check_prompt('builtin.\t', 'Call Method') | ||
check_prompt('get\t', 'Get Count') | ||
check_prompt('get\t', 'Get Time') | ||
check_prompt('key\t', | ||
'keywords') | ||
check_prompt('key\t', | ||
'Keyword Should Exist') | ||
check_prompt('buil\t', | ||
'Library: BuiltIn') | ||
check_prompt('builtin.\t', | ||
'Call Method') | ||
check_prompt('get\t', | ||
'Get Count') | ||
check_prompt('get\t', | ||
'Get Time') | ||
|
||
# keyword | ||
check_command('log to console hello', 'hello') | ||
check_command('get time', '.*-.*-.* .*:.*:.*') | ||
check_prompt('g', 'et time') | ||
check_command('help keywords', 'Print keywords of libraries') | ||
check_command('k builtin', 'Sleep') | ||
check_command('d sleep', 'Pauses the test executed for the given time') | ||
check_command('log to console hello', | ||
'hello') | ||
check_command('get time', | ||
'.*-.*-.* .*:.*:.*') | ||
check_prompt('g', | ||
'et time') | ||
check_command('help keywords', | ||
'Print keywords of libraries') | ||
check_command('k builtin', | ||
'Sleep') | ||
check_command('d sleep', | ||
'Pauses the test executed for the given time') | ||
|
||
# var | ||
check_command('@{{list}} = Create List hello world', | ||
"@{{list}} = ['helo', 'world']") | ||
check_command('${list}', "['helo', 'world']") | ||
check_command('${list}', | ||
"['helo', 'world']") | ||
check_command('&{dict} = Create Dictionary name=admin', | ||
"&{dict} = {'name': 'admin'}") | ||
check_command('${dict.name}', 'admin') | ||
check_command('${dict.name}', | ||
'admin') | ||
|
||
# fail-safe | ||
check_command('fail', 'AssertionError') | ||
check_command('nothing', "No keyword with name 'nothing' found.") | ||
check_command('fail', | ||
'AssertionError') | ||
check_command('nothing', | ||
"No keyword with name 'nothing' found.") | ||
|
||
return 'OK' | ||
|
||
|
||
def step_functional_testing(): | ||
global child | ||
path = join(dirname(abspath(__file__)), 'step.robot') | ||
child = pexpect.spawn('/usr/bin/env robot {}'.format(path)) | ||
child.expect('Enter interactive shell', timeout=TIMEOUT_SECONDS * 3) | ||
|
||
check_command('list', | ||
'Please run `step` or `next` command first.') | ||
|
||
support_source_lineno = get_version() >= '3.2' | ||
|
||
if support_source_lineno: | ||
check_command('s', # step | ||
'/DebugLibrary/tests/step.robot\(7\).*' | ||
'-> log to console working.*' | ||
'=> BuiltIn.Log To Console working') | ||
check_command('l', # list | ||
' 7 -> log to console working') | ||
check_command('n', # next | ||
'/DebugLibrary/tests/step.robot\(8\).*' | ||
'@.* = Create List hello world.*' | ||
'@.* = BuiltIn\.Create List hello world') | ||
check_command('', # just repeat last command | ||
'/DebugLibrary/tests/step.robot\(11\).*' | ||
'-> log to console another test case.*' | ||
'=> BuiltIn.Log To Console another test case') | ||
check_command('l', # list | ||
' 6 debug.*' | ||
' 7 log to console working.*' | ||
' 8 @.* = Create List hello world.*' | ||
' 9.*' | ||
' 10 test2.*' | ||
' 11 -> log to console another test case.*' | ||
' 12 log to console end') | ||
check_command('ll', # longlist | ||
' 10 test2.*' | ||
' 11 -> log to console another test case.*' | ||
' 12 log to console end') | ||
else: | ||
check_command('s', # step | ||
'=> BuiltIn.Log To Console working') | ||
check_command('l', # list | ||
'Please upgrade robotframework') | ||
check_command('n', # next | ||
'@.* = BuiltIn\.Create List hello world') | ||
check_command('', # repeat last command | ||
'=> BuiltIn.Log To Console another test case') | ||
|
||
# exit | ||
check_command('c', # continue | ||
'Exit shell.*' | ||
'another test case.*' | ||
'end') | ||
|
||
return 'OK' | ||
|
||
|
||
class FunctionalTestCase(unittest.TestCase): | ||
def test_functional(self): | ||
assert functional_testing() == 'OK' | ||
def test_base_functional(self): | ||
assert base_functional_testing() == 'OK' | ||
|
||
def test_step_functional(self): | ||
assert step_functional_testing() == 'OK' | ||
|
||
|
||
def suite(): | ||
suite = unittest.TestSuite() | ||
suite.addTest(FunctionalTestCase('test_functional')) | ||
suite.addTest(FunctionalTestCase('test_base_functional')) | ||
suite.addTest(FunctionalTestCase('test_step_functional')) | ||
return suite | ||
|
||
|
||
if __name__ == '__main__': | ||
print(functional_testing()) | ||
print(base_functional_testing()) | ||
print(step_functional_testing()) |