robot通过解析测试用例文件,对测试用例进行后续操作。在robot3.2.2版本之前直接是将文件内容用list解析为类似于表格的一种格式。词法解析部分从3.2.2版本开始就利用了编译原理当中的token和抽象语法树进行了重写。
token可以理解为一个字段,在robot需要处理的测试用例文件中,每一行中的每一个“单词”,都可以看作一个字段,例如下面这一行:
[Tags] ID=1 ANIMAL=cat COLOR=red SIZE=big
在源码robot/parsing/lexer/tokenizer.py文件中
_space_splitter = re.compile(r'(\s{2,}|\t)', re.UNICODE)
self._space_splitter.split(line)
主要由这两行来分离每一个字段
所以词法解析给出的9个token为
['[Tags]', ' ', 'ID=1',' ','ANIMAL=cat',' ','COLOR=red',' ','SIZE=big']
当然,此处并没有详细描述形成token的过程,不过详情可参考文档:Robot Framework Documentation
文档中的第18页记录获取token的方法,如果想了解详细过程,可以按照文档中的提示写如下测试代码进行调试:
from robot.api.parsing import get_tokens
path = 'example.robot'
for token in get_tokens(path):
print(repr(token))
假如example.robot为以下内容:
*** Test Cases ***
Example
Keyword argument
Second example
Keyword xxx
*** Keywords ***
Keyword
[Arguments] ${arg}
Log ${arg}
解析完成生成的token为:
Token(TESTCASE_HEADER, '*** Test Cases ***', 1, 0)
Token(EOL, '\n', 1, 18)
Token(EOS, '', 1, 19)
Token(TESTCASE_NAME, 'Example', 2, 0)
Token(EOL, '\n', 2, 7)
Token(EOS, '', 2, 8)
Token(SEPARATOR, ' ', 3, 0)
Token(KEYWORD, 'Keyword', 3, 4)
Token(SEPARATOR, ' ', 3, 11)
Token(ARGUMENT, 'argument', 3, 15)
Token(EOL, '\n', 3, 23)
Token(EOS, '', 3, 24)
Token(EOL, '\n', 4, 0)
Token(EOS, '', 4, 1)
创建完token之后,接着就要利用抽象语法树去构建model对象了,构建model是为了将文件中测试套件、测试用例、settings、variables、resource、keywords等信息用类似于tree的结构表现出来,便于后续对用例的其他操作。
model中包含block和statement。statement只是存放相应的token值。block可以简单理解为json串中的key,作为一个标识。而每一个block和statement也可以作为block的value。例:
图中的block有TestCaseSection、TestCase、If,statement有SectionHeader、EmptyLine、TestCaseName,block标识的是测试文件中相应的部分,如:TestCaseSection标识测试文件中的***TestCases***。
每一个block中都包含header和body这两个属性,header存放标识名称,例如:***TestCases***、example case等等。而statement中存放着具体的token。当然也有其他的属性,例如lineno等,其他属性表示此block或statement在文件中对应的位置或者此处有无其他的errors(主要是不存在的section或无法识别的数据)。
robot/parsing/parser/fileparser.py文件中
def parse(self, statement):
parser_class = {
Token.SETTING_HEADER: SettingSectionParser,
Token.VARIABLE_HEADER: VariableSectionParser,
Token.TESTCASE_HEADER: TestCaseSectionParser,
Token.KEYWORD_HEADER: KeywordSectionParser,
Token.COMMENT_HEADER: CommentSectionParser,
Token.ORTHOGONAL_HEADER: OrthogonalSectionParser,
Token.COMMENT: ImplicitCommentSectionParser,
Token.ERROR: ImplicitCommentSectionParser,
Token.EOL: ImplicitCommentSectionParser
}[statement.type]
parser = parser_class(statement)
self.model.sections.append(parser.model)
return parser
定义并创建block,这些block对应文件中*** XXX ***的部分,然后由每一个block再去创建生成block中相应的block或着statement,例如 TestCaseSectionParser,其中header存放testcase的名字,body存放***testcases***中的内容,而其中可能会有多个testcase,每一个testcase又是一个block,以此类推,直至出现具体的token值,把token存放到statement中。
由于本人时间问题,不在这里详细描述创建过程,不过详情可参考文档:Robot Framework Documentation
文档中第19页有一段测试代码,运行调试,进行分析后可以了解详细过程:
import astpretty
from robot.api.parsing import get_model
model = get_model('example.robot')
astpretty.pprint(model)
本人是新手小白,博客中如有讲解不到的地方请多包含。也可以在下方留言,以便及时更正,谢谢。
我是MySoul,期待和你一起进步。
最后附上生成的model示例:
File(
source='F:\\robot4\\demos\\if.robot',
lineno=1,
col_offset=0,
end_lineno=33,
end_col_offset=4,
errors=(),
sections=[
OrthogonalSection(
lineno=1,
col_offset=0,
end_lineno=5,
end_col_offset=1,
errors=(),
header=SectionHeader(lineno=1, col_offset=0, end_lineno=1, end_col_offset=27, errors=(), type='ORTHOGONAL_HEADER', tokens=(Token(ORTHOGONAL_HEADER, '*** Orthogonal Factors ***', 1, 0), Token(EOL, '\n', 1, 26))),
body=[
OrthogonalFactor(lineno=2, col_offset=0, end_lineno=2, end_col_offset=28, errors=(), type='NAME', tokens=(Token(NAME, 'ANIMAL', 2, 0), Token(SEPARATOR, ' ', 2, 6), Token(ARGUMENT, '["cat", "dog"]', 2, 13), Token(EOL, '\n', 2, 27))),
OrthogonalFactor(lineno=3, col_offset=0, end_lineno=3, end_col_offset=38, errors=(), type='NAME', tokens=(Token(NAME, 'COLOR', 3, 0), Token(SEPARATOR, ' ', 3, 5), Token(ARGUMENT, '["red", "green", "blue"]', 3, 13), Token(EOL, '\n', 3, 37))),
OrthogonalFactor(lineno=4, col_offset=0, end_lineno=4, end_col_offset=29, errors=(), type='NAME', tokens=(Token(NAME, 'SIZE', 4, 0), Token(SEPARATOR, ' ', 4, 4), Token(ARGUMENT, '["big", "small"]', 4, 12), Token(EOL, '\n', 4, 28))),
EmptyLine(lineno=5, col_offset=0, end_lineno=5, end_col_offset=1, errors=(), type='EOL', tokens=(Token(EOL, '\n', 5, 0),)),
],
),
VariableSection(
lineno=6,
col_offset=0,
end_lineno=9,
end_col_offset=1,
errors=(),
header=SectionHeader(lineno=6, col_offset=0, end_lineno=6, end_col_offset=18, errors=(), type='VARIABLE HEADER', tokens=(Token(VARIABLE_HEADER, '*** Variables ***', 6, 0), Token(EOL, '\n', 6, 17))),
body=[
Variable(lineno=7, col_offset=0, end_lineno=7, end_col_offset=16, errors=(), type='VARIABLE', tokens=(Token(VARIABLE, '${AGE}', 7, 0), Token(SEPARATOR, ' ', 7, 6), Token(ARGUMENT, '5', 7, 14), Token(EOL, '\n', 7, 15))),
Variable(lineno=8, col_offset=0, end_lineno=8, end_col_offset=18, errors=(), type='VARIABLE', tokens=(Token(VARIABLE, '${NAME}', 8, 0), Token(SEPARATOR, ' ', 8, 7), Token(ARGUMENT, 'dog', 8, 14), Token(EOL, '\n', 8, 17))),
EmptyLine(lineno=9, col_offset=0, end_lineno=9, end_col_offset=1, errors=(), type='EOL', tokens=(Token(EOL, '\n', 9, 0),)),
],
),
TestCaseSection(
lineno=10,
col_offset=0,
end_lineno=29,
end_col_offset=1,
errors=(),
header=SectionHeader(lineno=10, col_offset=0, end_lineno=10, end_col_offset=19, errors=(), type='TESTCASE HEADER', tokens=(Token(TESTCASE_HEADER, '*** Test Cases ***', 10, 0), Token(EOL, '\n', 10, 18))),
body=[
EmptyLine(lineno=11, col_offset=0, end_lineno=11, end_col_offset=1, errors=(), type='EOL', tokens=(Token(EOL, '\n', 11, 0),)),
TestCase(
lineno=12,
col_offset=0,
end_lineno=20,
end_col_offset=1,
errors=(),
header=TestCaseName(lineno=12, col_offset=0, end_lineno=12, end_col_offset=8, errors=(), type='TESTCASE NAME', tokens=(Token(TESTCASE_NAME, 'if case', 12, 0), Token(EOL, '\n', 12, 7))),
body=[
If(
lineno=13,
col_offset=0,
end_lineno=19,
end_col_offset=8,
errors=(),
header=IfHeader(lineno=13, col_offset=0, end_lineno=13, end_col_offset=32, errors=(), type='IF', tokens=(Token(SEPARATOR, ' ', 13, 0), Token(IF, 'IF', 13, 4), Token(SEPARATOR, ' ', 13, 6), Token(ARGUMENT, '"$${ANIMAL}" == "dog"', 13, 10), Token(EOL, '\n', 13, 31))),
body=[KeywordCall(lineno=14, col_offset=0, end_lineno=14, end_col_offset=43, errors=(), type='KEYWORD', tokens=(Token(SEPARATOR, ' ', 14, 0), Token(KEYWORD, 'Log', 14, 8), Token(SEPARATOR, ' ', 14, 11), Token(ARGUMENT, '$${SIZE}$${COLOR}$${ANIMAL}', 14, 15), Token(EOL, '\n', 14, 42)))],
orelse=If(
lineno=15,
col_offset=0,
end_lineno=18,
end_col_offset=23,
errors=(),
header=ElseIfHeader(lineno=15, col_offset=0, end_lineno=15, end_col_offset=36, errors=(), type='ELSE IF', tokens=(Token(SEPARATOR, ' ', 15, 0), Token(ELSE_IF, 'ELSE IF', 15, 4), Token(SEPARATOR, ' ', 15, 11), Token(ARGUMENT, '"$${ANIMAL}" == "cat"', 15, 14), Token(EOL, '\n', 15, 35))),
body=[KeywordCall(lineno=16, col_offset=0, end_lineno=16, end_col_offset=19, errors=(), type='KEYWORD', tokens=(Token(SEPARATOR, ' ', 16, 0), Token(KEYWORD, 'Log', 16, 8), Token(SEPARATOR, ' ', 16, 11), Token(ARGUMENT, 'mew', 16, 15), Token(EOL, '\n', 16, 18)))],
orelse=If(
lineno=17,
col_offset=0,
end_lineno=18,
end_col_offset=23,
errors=(),
header=ElseHeader(lineno=17, col_offset=0, end_lineno=17, end_col_offset=9, errors=(), type='ELSE', tokens=(Token(SEPARATOR, ' ', 17, 0), Token(ELSE, 'ELSE', 17, 4), Token(EOL, '\n', 17, 8))),
body=[KeywordCall(lineno=18, col_offset=0, end_lineno=18, end_col_offset=23, errors=(), type='KEYWORD', tokens=(Token(SEPARATOR, ' ', 18, 0), Token(KEYWORD, 'Log', 18, 8), Token(SEPARATOR, ' ', 18, 11), Token(ARGUMENT, 'unknown', 18, 15), Token(EOL, '\n', 18, 22)))],
orelse=None,
end=None,
),
end=None,
),
end=End(lineno=19, col_offset=0, end_lineno=19, end_col_offset=8, errors=(), type='END', tokens=(Token(SEPARATOR, ' ', 19, 0), Token(END, 'END', 19, 4), Token(EOL, '\n', 19, 7))),
),
EmptyLine(lineno=20, col_offset=0, end_lineno=20, end_col_offset=1, errors=(), type='EOL', tokens=(Token(EOL, '\n', 20, 0),)),
],
),
TestCase(
lineno=21,
col_offset=0,
end_lineno=29,
end_col_offset=1,
errors=(),
header=TestCaseName(lineno=21, col_offset=0, end_lineno=21, end_col_offset=9, errors=(), type='TESTCASE NAME', tokens=(Token(TESTCASE_NAME, 'if case2', 21, 0), Token(EOL, '\n', 21, 8))),
body=[
If(
lineno=22,
col_offset=0,
end_lineno=28,
end_col_offset=8,
errors=(),
header=IfHeader(lineno=22, col_offset=0, end_lineno=22, end_col_offset=32, errors=(), type='IF', tokens=(Token(SEPARATOR, ' ', 22, 0), Token(IF, 'IF', 22, 4), Token(SEPARATOR, ' ', 22, 6), Token(ARGUMENT, '"$${ANIMAL}" == "dog"', 22, 10), Token(EOL, '\n', 22, 31))),
body=[KeywordCall(lineno=23, col_offset=0, end_lineno=23, end_col_offset=20, errors=(), type='KEYWORD', tokens=(Token(SEPARATOR, ' ', 23, 0), Token(KEYWORD, 'Log', 23, 8), Token(SEPARATOR, ' ', 23, 11), Token(ARGUMENT, 'wang', 23, 15), Token(EOL, '\n', 23, 19)))],
orelse=If(
lineno=24,
col_offset=0,
end_lineno=27,
end_col_offset=23,
errors=(),
header=ElseIfHeader(lineno=24, col_offset=0, end_lineno=24, end_col_offset=36, errors=(), type='ELSE IF', tokens=(Token(SEPARATOR, ' ', 24, 0), Token(ELSE_IF, 'ELSE IF', 24, 4), Token(SEPARATOR, ' ', 24, 11), Token(ARGUMENT, '"$${ANIMAL}" == "cat"', 24, 14), Token(EOL, '\n', 24, 35))),
body=[KeywordCall(lineno=25, col_offset=0, end_lineno=25, end_col_offset=19, errors=(), type='KEYWORD', tokens=(Token(SEPARATOR, ' ', 25, 0), Token(KEYWORD, 'Log', 25, 8), Token(SEPARATOR, ' ', 25, 11), Token(ARGUMENT, 'mew', 25, 15), Token(EOL, '\n', 25, 18)))],
orelse=If(
lineno=26,
col_offset=0,
end_lineno=27,
end_col_offset=23,
errors=(),
header=ElseHeader(lineno=26, col_offset=0, end_lineno=26, end_col_offset=9, errors=(), type='ELSE', tokens=(Token(SEPARATOR, ' ', 26, 0), Token(ELSE, 'ELSE', 26, 4), Token(EOL, '\n', 26, 8))),
body=[KeywordCall(lineno=27, col_offset=0, end_lineno=27, end_col_offset=23, errors=(), type='KEYWORD', tokens=(Token(SEPARATOR, ' ', 27, 0), Token(KEYWORD, 'Log', 27, 8), Token(SEPARATOR, ' ', 27, 11), Token(ARGUMENT, 'unknown', 27, 15), Token(EOL, '\n', 27, 22)))],
orelse=None,
end=None,
),
end=None,
),
end=End(lineno=28, col_offset=0, end_lineno=28, end_col_offset=8, errors=(), type='END', tokens=(Token(SEPARATOR, ' ', 28, 0), Token(END, 'END', 28, 4), Token(EOL, '\n', 28, 7))),
),
EmptyLine(lineno=29, col_offset=0, end_lineno=29, end_col_offset=1, errors=(), type='EOL', tokens=(Token(EOL, '\n', 29, 0),)),
],
),
],
),
KeywordSection(
lineno=30,
col_offset=0,
end_lineno=33,
end_col_offset=4,
errors=(),
header=SectionHeader(lineno=30, col_offset=0, end_lineno=30, end_col_offset=17, errors=(), type='KEYWORD HEADER', tokens=(Token(KEYWORD_HEADER, '*** Keywords ***', 30, 0), Token(EOL, '\n', 30, 16))),
body=[
Keyword(
lineno=31,
col_offset=0,
end_lineno=33,
end_col_offset=4,
errors=(),
header=KeywordName(lineno=31, col_offset=0, end_lineno=31, end_col_offset=5, errors=(), type='KEYWORD NAME', tokens=(Token(KEYWORD_NAME, 'Test', 31, 0), Token(EOL, '\n', 31, 4))),
body=[
KeywordCall(lineno=32, col_offset=0, end_lineno=32, end_col_offset=21, errors=(), type='KEYWORD', tokens=(Token(SEPARATOR, ' ', 32, 0), Token(KEYWORD, 'Log', 32, 4), Token(SEPARATOR, ' ', 32, 7), Token(ARGUMENT, 'Test begin', 32, 10), Token(EOL, '\n', 32, 20))),
EmptyLine(lineno=33, col_offset=0, end_lineno=33, end_col_offset=4, errors=(), type='EOL', tokens=(Token(EOL, ' ', 33, 0),)),
],
),
],
),
],
)