robotframework 7.1rc2__zip → 7.2b1__zip
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {robotframework-7.1rc2 → robotframework-7.2b1}/BUILD.rst +7 -7
- {robotframework-7.1rc2 → robotframework-7.2b1}/PKG-INFO +2 -2
- {robotframework-7.1rc2 → robotframework-7.2b1}/setup.py +1 -1
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/api/__init__.py +33 -9
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/api/deco.py +8 -6
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/api/interfaces.py +66 -15
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/api/parsing.py +72 -68
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/conf/languages.py +4 -3
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/conf/settings.py +11 -10
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/errors.py +3 -3
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/htmlfilewriter.py +3 -0
- robotframework-7.2b1/src/robot/htmldata/libdoc/libdoc.html +408 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/rebot/log.html +1 -1
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/rebot/report.html +1 -1
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/rebot/testdata.js +1 -1
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/template.py +0 -2
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdoc.py +19 -11
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdocpkg/htmlwriter.py +7 -4
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdocpkg/model.py +7 -5
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdocpkg/writer.py +2 -2
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libraries/BuiltIn.py +121 -32
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libraries/Collections.py +20 -7
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/__init__.py +3 -3
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/body.py +48 -57
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/control.py +35 -4
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/message.py +13 -8
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/modelobject.py +26 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/statistics.py +13 -9
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/stats.py +12 -14
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/suitestatistics.py +8 -9
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/tags.py +10 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/tagstatistics.py +7 -8
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/testcase.py +1 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/testsuite.py +1 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/totalstatistics.py +14 -21
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/visitor.py +29 -3
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/__init__.py +3 -2
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/console/dotted.py +6 -3
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/console/highlighting.py +18 -10
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/debugfile.py +3 -3
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/filelogger.py +7 -3
- robotframework-7.2b1/src/robot/output/jsonlogger.py +338 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/librarylogger.py +12 -11
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/listeners.py +49 -47
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/logger.py +33 -20
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/loggerapi.py +6 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/loggerhelper.py +29 -57
- robotframework-7.2b1/src/robot/output/loglevel.py +54 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/output.py +21 -17
- robotframework-7.2b1/src/robot/output/outputfile.py +174 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/pyloggingconf.py +2 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/xmllogger.py +40 -169
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/lexer/blocklexers.py +25 -12
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/lexer/statementlexers.py +16 -7
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/lexer/tokens.py +2 -1
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/model/__init__.py +1 -1
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/model/blocks.py +34 -2
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/model/statements.py +32 -2
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/parser/blockparsers.py +7 -2
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/rebot.py +10 -2
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/reporting/jsmodelbuilders.py +1 -1
- robotframework-7.2b1/src/robot/reporting/outputwriter.py +30 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/result/__init__.py +1 -1
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/result/executionerrors.py +9 -9
- robotframework-7.2b1/src/robot/result/executionresult.py +331 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/result/messagefilter.py +14 -10
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/result/model.py +90 -18
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/result/resultbuilder.py +2 -5
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/result/xmlelementhandlers.py +20 -9
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/run.py +18 -6
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/__init__.py +1 -1
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/arguments/embedded.py +7 -6
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/arguments/typeconverters.py +14 -2
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/arguments/typeinfo.py +23 -4
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/bodyrunner.py +41 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/builder/settings.py +2 -1
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/builder/transformers.py +46 -20
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/context.py +22 -11
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/librarykeyword.py +1 -1
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/librarykeywordrunner.py +29 -23
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/model.py +31 -4
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/outputcapture.py +2 -1
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/signalhandler.py +7 -3
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/status.py +49 -38
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/statusreporter.py +1 -1
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/suiterunner.py +22 -15
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/userkeywordrunner.py +4 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/importer.py +39 -23
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/unic.py +7 -8
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/variables/evaluation.py +21 -2
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/variables/replacer.py +20 -24
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/variables/scopes.py +19 -9
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/variables/variables.py +4 -1
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/version.py +1 -1
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robotframework.egg-info/PKG-INFO +2 -2
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robotframework.egg-info/SOURCES.txt +3 -4
- robotframework-7.1rc2/src/robot/htmldata/libdoc/doc_formatting.css +0 -74
- robotframework-7.1rc2/src/robot/htmldata/libdoc/libdoc.css +0 -747
- robotframework-7.1rc2/src/robot/htmldata/libdoc/libdoc.html +0 -702
- robotframework-7.1rc2/src/robot/htmldata/libdoc/print.css +0 -11
- robotframework-7.1rc2/src/robot/htmldata/libdoc/pygments.css +0 -162
- robotframework-7.1rc2/src/robot/reporting/outputwriter.py +0 -48
- robotframework-7.1rc2/src/robot/result/executionresult.py +0 -201
- {robotframework-7.1rc2 → robotframework-7.2b1}/AUTHORS.rst +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/CONTRIBUTING.rst +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/COPYRIGHT.txt +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/INSTALL.rst +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/LICENSE.txt +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/MANIFEST.in +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/README.rst +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/setup.cfg +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/__init__.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/__main__.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/api/exceptions.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/api/logger.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/api/py.typed +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/conf/__init__.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/conf/gatherfailed.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/__init__.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/common/__init__.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/common/js_disabled.css +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/common/storage.js +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/jsonwriter.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/lib/__init__.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/lib/jquery.highlight.min.js +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/lib/jquery.min.js +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/lib/jquery.tablesorter.min.js +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/lib/jquery.tmpl.min.js +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/lib/jsxcompressor.min.js +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/libdoc/__init__.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/rebot/__init__.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/rebot/common.css +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/rebot/doc_formatting.css +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/rebot/fileloading.js +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/rebot/log.css +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/rebot/log.js +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/rebot/model.js +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/rebot/print.css +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/rebot/report.css +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/rebot/util.js +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/rebot/view.js +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/testdoc/__init__.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/testdoc/testdoc.css +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/testdoc/testdoc.html +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdocpkg/__init__.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdocpkg/builder.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdocpkg/consoleviewer.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdocpkg/datatypes.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdocpkg/htmlutils.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdocpkg/jsonbuilder.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdocpkg/jsonwriter.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdocpkg/output.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdocpkg/robotbuilder.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdocpkg/standardtypes.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdocpkg/xmlbuilder.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdocpkg/xmlwriter.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libraries/DateTime.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libraries/Dialogs.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libraries/Easter.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libraries/OperatingSystem.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libraries/Process.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libraries/Remote.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libraries/Screenshot.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libraries/String.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libraries/Telnet.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libraries/XML.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libraries/__init__.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libraries/dialogs_py.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/configurer.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/filter.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/fixture.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/itemlist.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/keyword.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/metadata.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/modifier.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/namepatterns.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/tagsetter.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/console/__init__.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/console/quiet.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/console/verbose.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/stdoutlogsplitter.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/__init__.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/lexer/__init__.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/lexer/context.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/lexer/lexer.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/lexer/settings.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/lexer/tokenizer.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/model/visitor.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/parser/__init__.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/parser/fileparser.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/parser/parser.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/suitestructure.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/pythonpathsetter.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/reporting/__init__.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/reporting/expandkeywordmatcher.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/reporting/jsbuildingcontext.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/reporting/jsexecutionresult.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/reporting/jswriter.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/reporting/logreportwriters.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/reporting/resultwriter.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/reporting/stringcache.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/reporting/xunitwriter.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/result/configurer.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/result/flattenkeywordmatcher.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/result/keywordremover.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/result/merger.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/result/modeldeprecation.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/result/suiteteardownfailed.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/result/visitor.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/arguments/__init__.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/arguments/argumentconverter.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/arguments/argumentmapper.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/arguments/argumentparser.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/arguments/argumentresolver.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/arguments/argumentspec.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/arguments/argumentvalidator.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/arguments/customconverters.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/arguments/typeinfoparser.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/arguments/typevalidator.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/builder/__init__.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/builder/builders.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/builder/parsers.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/dynamicmethods.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/importer.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/invalidkeyword.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/keywordfinder.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/keywordimplementation.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/libraryscopes.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/namespace.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/randomizer.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/resourcemodel.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/runkwregister.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/testlibraries.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/timeouts/__init__.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/timeouts/nosupport.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/timeouts/posix.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/timeouts/windows.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/testdoc.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/__init__.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/application.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/argumentparser.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/asserts.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/charwidth.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/compress.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/connectioncache.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/dotdict.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/encoding.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/encodingsniffer.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/error.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/escaping.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/etreewrapper.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/filereader.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/frange.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/htmlformatters.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/markuputils.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/markupwriters.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/match.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/misc.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/normalizing.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/notset.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/platform.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/recommendations.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/restreader.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/robotenv.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/robotinspect.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/robotio.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/robotpath.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/robottime.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/robottypes.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/setter.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/sortable.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/text.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/typehints.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/variables/__init__.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/variables/assigner.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/variables/filesetter.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/variables/finders.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/variables/notfound.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/variables/resolvable.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/variables/search.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/variables/store.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/variables/tablesetter.py +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robotframework.egg-info/dependency_links.txt +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robotframework.egg-info/entry_points.txt +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/src/robotframework.egg-info/top_level.txt +0 -0
- {robotframework-7.1rc2 → robotframework-7.2b1}/tasks.py +0 -0
|
@@ -22,7 +22,7 @@ from typing import cast, Iterator, Sequence, TextIO, Union
|
|
|
22
22
|
from robot.utils import file_writer, test_or_task
|
|
23
23
|
|
|
24
24
|
from .statements import (Break, Continue, ElseHeader, ElseIfHeader, End, ExceptHeader,
|
|
25
|
-
Error, FinallyHeader, ForHeader, IfHeader, KeywordCall,
|
|
25
|
+
Error, FinallyHeader, ForHeader, GroupHeader, IfHeader, KeywordCall,
|
|
26
26
|
KeywordName, Node, ReturnSetting, ReturnStatement,
|
|
27
27
|
SectionHeader, Statement, TemplateArguments, TestCaseName,
|
|
28
28
|
TryHeader, Var, WhileHeader)
|
|
@@ -99,7 +99,7 @@ class Block(Container, ABC):
|
|
|
99
99
|
def _body_is_empty(self):
|
|
100
100
|
# This works with tests, keywords, and blocks inside them, not with sections.
|
|
101
101
|
valid = (KeywordCall, TemplateArguments, Var, Continue, Break, ReturnSetting,
|
|
102
|
-
ReturnStatement, NestedBlock, Error)
|
|
102
|
+
Group, ReturnStatement, NestedBlock, Error)
|
|
103
103
|
return not any(isinstance(node, valid) for node in self.body)
|
|
104
104
|
|
|
105
105
|
|
|
@@ -328,6 +328,7 @@ class Try(NestedBlock):
|
|
|
328
328
|
if self.type == Token.TRY:
|
|
329
329
|
self._validate_structure()
|
|
330
330
|
self._validate_end()
|
|
331
|
+
TemplatesNotAllowed('TRY').check(self)
|
|
331
332
|
|
|
332
333
|
def _validate_body(self):
|
|
333
334
|
if self._body_is_empty():
|
|
@@ -395,6 +396,21 @@ class While(NestedBlock):
|
|
|
395
396
|
self.errors += ('WHILE loop cannot be empty.',)
|
|
396
397
|
if not self.end:
|
|
397
398
|
self.errors += ('WHILE loop must have closing END.',)
|
|
399
|
+
TemplatesNotAllowed('WHILE').check(self)
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
class Group(NestedBlock):
|
|
403
|
+
header: GroupHeader
|
|
404
|
+
|
|
405
|
+
@property
|
|
406
|
+
def name(self) -> str:
|
|
407
|
+
return self.header.name
|
|
408
|
+
|
|
409
|
+
def validate(self, ctx: 'ValidationContext'):
|
|
410
|
+
if self._body_is_empty():
|
|
411
|
+
self.errors += ('GROUP cannot be empty.',)
|
|
412
|
+
if not self.end:
|
|
413
|
+
self.errors += ('GROUP must have closing END.',)
|
|
398
414
|
|
|
399
415
|
|
|
400
416
|
class ModelWriter(ModelVisitor):
|
|
@@ -504,3 +520,19 @@ class LastStatementFinder(ModelVisitor):
|
|
|
504
520
|
|
|
505
521
|
def visit_Statement(self, statement: Statement):
|
|
506
522
|
self.statement = statement
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
class TemplatesNotAllowed(ModelVisitor):
|
|
526
|
+
|
|
527
|
+
def __init__(self, kind: str):
|
|
528
|
+
self.kind = kind
|
|
529
|
+
self.found = False
|
|
530
|
+
|
|
531
|
+
def check(self, model: Node):
|
|
532
|
+
self.found = False
|
|
533
|
+
self.visit(model)
|
|
534
|
+
if self.found:
|
|
535
|
+
model.errors += (f'{self.kind} does not support templates.',)
|
|
536
|
+
|
|
537
|
+
def visit_TemplateArguments(self, node: None):
|
|
538
|
+
self.found = True
|
|
@@ -1204,6 +1204,35 @@ class WhileHeader(Statement):
|
|
|
1204
1204
|
self._validate_options()
|
|
1205
1205
|
|
|
1206
1206
|
|
|
1207
|
+
@Statement.register
|
|
1208
|
+
class GroupHeader(Statement):
|
|
1209
|
+
type = Token.GROUP
|
|
1210
|
+
|
|
1211
|
+
@classmethod
|
|
1212
|
+
def from_params(cls, name: str = '',
|
|
1213
|
+
indent: str = FOUR_SPACES, separator: str = FOUR_SPACES,
|
|
1214
|
+
eol: str = EOL) -> 'GroupHeader':
|
|
1215
|
+
tokens = [Token(Token.SEPARATOR, indent),
|
|
1216
|
+
Token(Token.GROUP)]
|
|
1217
|
+
if name:
|
|
1218
|
+
tokens.extend(
|
|
1219
|
+
[Token(Token.SEPARATOR, separator),
|
|
1220
|
+
Token(Token.ARGUMENT, name)]
|
|
1221
|
+
)
|
|
1222
|
+
tokens.append(Token(Token.EOL, eol))
|
|
1223
|
+
return cls(tokens)
|
|
1224
|
+
|
|
1225
|
+
@property
|
|
1226
|
+
def name(self) -> str:
|
|
1227
|
+
return ', '.join(self.get_values(Token.ARGUMENT))
|
|
1228
|
+
|
|
1229
|
+
def validate(self, ctx: 'ValidationContext'):
|
|
1230
|
+
names = self.get_values(Token.ARGUMENT)
|
|
1231
|
+
if len(names) > 1:
|
|
1232
|
+
self.errors += (f"GROUP accepts only one argument as name, got {len(names)} "
|
|
1233
|
+
f"arguments {seq2str(names)}.",)
|
|
1234
|
+
|
|
1235
|
+
|
|
1207
1236
|
@Statement.register
|
|
1208
1237
|
class Var(Statement):
|
|
1209
1238
|
type = Token.VAR
|
|
@@ -1343,8 +1372,9 @@ class Config(Statement):
|
|
|
1343
1372
|
|
|
1344
1373
|
@property
|
|
1345
1374
|
def language(self) -> 'Language|None':
|
|
1346
|
-
value = self.
|
|
1347
|
-
|
|
1375
|
+
value = ' '.join(self.get_values(Token.CONFIG))
|
|
1376
|
+
lang = value.split(':', 1)[1].strip()
|
|
1377
|
+
return Language.from_name(lang) if lang else None
|
|
1348
1378
|
|
|
1349
1379
|
|
|
1350
1380
|
@Statement.register
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
from abc import ABC, abstractmethod
|
|
17
17
|
|
|
18
18
|
from ..lexer import Token
|
|
19
|
-
from ..model import (Block, Container, End, For, If, Keyword, NestedBlock,
|
|
19
|
+
from ..model import (Block, Container, End, For, Group, If, Keyword, NestedBlock,
|
|
20
20
|
Statement, TestCase, Try, While)
|
|
21
21
|
|
|
22
22
|
|
|
@@ -44,10 +44,11 @@ class BlockParser(Parser, ABC):
|
|
|
44
44
|
super().__init__(model)
|
|
45
45
|
self.parsers: 'dict[str, type[NestedBlockParser]]' = {
|
|
46
46
|
Token.FOR: ForParser,
|
|
47
|
+
Token.WHILE: WhileParser,
|
|
47
48
|
Token.IF: IfParser,
|
|
48
49
|
Token.INLINE_IF: IfParser,
|
|
49
50
|
Token.TRY: TryParser,
|
|
50
|
-
Token.
|
|
51
|
+
Token.GROUP: GroupParser
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
def handles(self, statement: Statement) -> bool:
|
|
@@ -101,6 +102,10 @@ class WhileParser(NestedBlockParser):
|
|
|
101
102
|
model: While
|
|
102
103
|
|
|
103
104
|
|
|
105
|
+
class GroupParser(NestedBlockParser):
|
|
106
|
+
model: Group
|
|
107
|
+
|
|
108
|
+
|
|
104
109
|
class IfParser(NestedBlockParser):
|
|
105
110
|
model: If
|
|
106
111
|
|
|
@@ -122,7 +122,6 @@ Options
|
|
|
122
122
|
--processemptysuite Processes output also if the top level suite is
|
|
123
123
|
empty. Useful e.g. with --include/--exclude when it
|
|
124
124
|
is not an error that there are no matches.
|
|
125
|
-
Use --skiponfailure when starting execution instead.
|
|
126
125
|
-d --outputdir dir Where to create output files. The default is the
|
|
127
126
|
directory where Rebot is run from and the given path
|
|
128
127
|
is considered relative to that unless it is absolute.
|
|
@@ -183,7 +182,6 @@ Options
|
|
|
183
182
|
pattern. Documentation is shown in `Test Details` and
|
|
184
183
|
also as a tooltip in `Statistics by Tag`. Pattern can
|
|
185
184
|
use `*`, `?` and `[]` as wildcards like --test.
|
|
186
|
-
Documentation can contain formatting like --doc.
|
|
187
185
|
Examples: --tagdoc mytag:Example
|
|
188
186
|
--tagdoc "owner-*:Original author"
|
|
189
187
|
--tagstatlink pattern:link:title * Add external links into `Statistics by
|
|
@@ -307,6 +305,16 @@ ROBOT_SYSLOG_LEVEL Log level to use when writing to the syslog file.
|
|
|
307
305
|
Available levels are the same as for --loglevel
|
|
308
306
|
command line option and the default is INFO.
|
|
309
307
|
|
|
308
|
+
Return Codes
|
|
309
|
+
============
|
|
310
|
+
|
|
311
|
+
0 All tests passed.
|
|
312
|
+
1-249 Returned number of tests failed.
|
|
313
|
+
250 250 or more failures.
|
|
314
|
+
251 Help or version information printed.
|
|
315
|
+
252 Invalid data or command line options.
|
|
316
|
+
255 Unexpected internal error.
|
|
317
|
+
|
|
310
318
|
Examples
|
|
311
319
|
========
|
|
312
320
|
|
|
@@ -25,7 +25,7 @@ STATUSES = {'FAIL': 0, 'PASS': 1, 'SKIP': 2, 'NOT RUN': 3}
|
|
|
25
25
|
KEYWORD_TYPES = {'KEYWORD': 0, 'SETUP': 1, 'TEARDOWN': 2,
|
|
26
26
|
'FOR': 3, 'ITERATION': 4, 'IF': 5, 'ELSE IF': 6, 'ELSE': 7,
|
|
27
27
|
'RETURN': 8, 'VAR': 9, 'TRY': 10, 'EXCEPT': 11, 'FINALLY': 12,
|
|
28
|
-
'WHILE': 13, '
|
|
28
|
+
'WHILE': 13, 'GROUP': 14, 'CONTINUE': 15, 'BREAK': 16, 'ERROR': 17}
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
class JsModelBuilder:
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Copyright 2008-2015 Nokia Networks
|
|
2
|
+
# Copyright 2016- Robot Framework Foundation
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
from robot.output.xmllogger import XmlLogger, LegacyXmlLogger
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class OutputWriter(XmlLogger):
|
|
20
|
+
generator = 'Rebot'
|
|
21
|
+
|
|
22
|
+
def end_result(self, result):
|
|
23
|
+
self.close()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class LegacyOutputWriter(LegacyXmlLogger):
|
|
27
|
+
generator = 'Rebot'
|
|
28
|
+
|
|
29
|
+
def end_result(self, result):
|
|
30
|
+
self.close()
|
|
@@ -38,7 +38,7 @@ __ http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#
|
|
|
38
38
|
"""
|
|
39
39
|
|
|
40
40
|
from .executionresult import Result
|
|
41
|
-
from .model import (Break, Continue, Error, For, ForIteration, If, IfBranch, Keyword,
|
|
41
|
+
from .model import (Break, Continue, Error, For, ForIteration, Group, If, IfBranch, Keyword,
|
|
42
42
|
Message, Return, TestCase, TestSuite, Try, TryBranch, Var, While,
|
|
43
43
|
WhileIteration)
|
|
44
44
|
from .resultbuilder import ExecutionResult, ExecutionResultBuilder
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
# See the License for the specific language governing permissions and
|
|
14
14
|
# limitations under the License.
|
|
15
15
|
|
|
16
|
+
from typing import Iterator, Sequence
|
|
17
|
+
|
|
16
18
|
from robot.model import ItemList, Message
|
|
17
19
|
from robot.utils import setter
|
|
18
20
|
|
|
@@ -24,31 +26,29 @@ class ExecutionErrors:
|
|
|
24
26
|
"""
|
|
25
27
|
id = 'errors'
|
|
26
28
|
|
|
27
|
-
def __init__(self, messages=
|
|
28
|
-
#: A :class:`list-like object <robot.model.itemlist.ItemList>` of
|
|
29
|
-
#: :class:`~robot.model.message.Message` instances.
|
|
29
|
+
def __init__(self, messages: Sequence[Message] = ()):
|
|
30
30
|
self.messages = messages
|
|
31
31
|
|
|
32
32
|
@setter
|
|
33
|
-
def messages(self, messages):
|
|
33
|
+
def messages(self, messages) -> ItemList[Message]:
|
|
34
34
|
return ItemList(Message, {'parent': self}, items=messages)
|
|
35
35
|
|
|
36
|
-
def add(self, other):
|
|
36
|
+
def add(self, other: 'ExecutionErrors'):
|
|
37
37
|
self.messages.extend(other.messages)
|
|
38
38
|
|
|
39
39
|
def visit(self, visitor):
|
|
40
40
|
visitor.visit_errors(self)
|
|
41
41
|
|
|
42
|
-
def __iter__(self):
|
|
42
|
+
def __iter__(self) -> Iterator[Message]:
|
|
43
43
|
return iter(self.messages)
|
|
44
44
|
|
|
45
|
-
def __len__(self):
|
|
45
|
+
def __len__(self) -> int:
|
|
46
46
|
return len(self.messages)
|
|
47
47
|
|
|
48
|
-
def __getitem__(self, index):
|
|
48
|
+
def __getitem__(self, index) -> Message:
|
|
49
49
|
return self.messages[index]
|
|
50
50
|
|
|
51
|
-
def __str__(self):
|
|
51
|
+
def __str__(self) -> str:
|
|
52
52
|
if not self:
|
|
53
53
|
return 'No execution errors'
|
|
54
54
|
if len(self) == 1:
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
# Copyright 2008-2015 Nokia Networks
|
|
2
|
+
# Copyright 2016- Robot Framework Foundation
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
from datetime import datetime
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import overload, TextIO
|
|
19
|
+
|
|
20
|
+
from robot.errors import DataError
|
|
21
|
+
from robot.model import Statistics
|
|
22
|
+
from robot.model.modelobject import JsonDumper, JsonLoader # FIXME: Expose via `robot.model` or move to `robot.utils`.
|
|
23
|
+
from robot.utils import setter
|
|
24
|
+
from robot.version import get_full_version
|
|
25
|
+
|
|
26
|
+
from .executionerrors import ExecutionErrors
|
|
27
|
+
from .model import TestSuite
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def is_json_source(source) -> bool:
|
|
31
|
+
if isinstance(source, bytes):
|
|
32
|
+
# ISO-8859-1 is most likely *not* the right encoding, but decoding bytes
|
|
33
|
+
# with it always succeeds and characters we care about ought to be correct
|
|
34
|
+
# at least if the right encoding is UTF-8 or any ISO-8859-x encoding.
|
|
35
|
+
source = source.decode('ISO-8859-1')
|
|
36
|
+
if isinstance(source, str):
|
|
37
|
+
source = source.strip()
|
|
38
|
+
first, last = (source[0], source[-1]) if source else ('', '')
|
|
39
|
+
if (first, last) == ('{', '}'):
|
|
40
|
+
return True
|
|
41
|
+
if (first, last) == ('<', '>'):
|
|
42
|
+
return False
|
|
43
|
+
path = Path(source)
|
|
44
|
+
elif isinstance(source, Path):
|
|
45
|
+
path = source
|
|
46
|
+
elif hasattr(source, 'name') and isinstance(source.name, str):
|
|
47
|
+
path = Path(source.name)
|
|
48
|
+
else:
|
|
49
|
+
return False
|
|
50
|
+
return bool(path and path.suffix.lower() == '.json')
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class Result:
|
|
54
|
+
"""Test execution results.
|
|
55
|
+
|
|
56
|
+
Can be created based on XML output files using the
|
|
57
|
+
:func:`~.resultbuilder.ExecutionResult`
|
|
58
|
+
factory method. Also returned by the
|
|
59
|
+
:meth:`robot.running.TestSuite.run <robot.running.model.TestSuite.run>`
|
|
60
|
+
method.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
def __init__(self, source: 'Path|str|None' = None,
|
|
64
|
+
suite: 'TestSuite|None' = None,
|
|
65
|
+
errors: 'ExecutionErrors|None' = None,
|
|
66
|
+
rpa: 'bool|None' = None,
|
|
67
|
+
generator: str = 'unknown',
|
|
68
|
+
generation_time: 'datetime|str|None' = None):
|
|
69
|
+
self.source = Path(source) if isinstance(source, str) else source
|
|
70
|
+
self.suite = suite or TestSuite()
|
|
71
|
+
self.errors = errors or ExecutionErrors()
|
|
72
|
+
self.rpa = rpa
|
|
73
|
+
self.generator = generator
|
|
74
|
+
self.generation_time = generation_time
|
|
75
|
+
self._status_rc = True
|
|
76
|
+
self._stat_config = {}
|
|
77
|
+
|
|
78
|
+
@setter
|
|
79
|
+
def rpa(self, rpa: 'bool|None') -> 'bool|None':
|
|
80
|
+
if rpa is not None:
|
|
81
|
+
self._set_suite_rpa(self.suite, rpa)
|
|
82
|
+
return rpa
|
|
83
|
+
|
|
84
|
+
def _set_suite_rpa(self, suite, rpa):
|
|
85
|
+
suite.rpa = rpa
|
|
86
|
+
for child in suite.suites:
|
|
87
|
+
self._set_suite_rpa(child, rpa)
|
|
88
|
+
|
|
89
|
+
@setter
|
|
90
|
+
def generation_time(self, timestamp: 'datetime|str|None') -> 'datetime|None':
|
|
91
|
+
if datetime is None:
|
|
92
|
+
return None
|
|
93
|
+
if isinstance(timestamp, str):
|
|
94
|
+
return datetime.fromisoformat(timestamp)
|
|
95
|
+
return timestamp
|
|
96
|
+
|
|
97
|
+
@property
|
|
98
|
+
def statistics(self) -> Statistics:
|
|
99
|
+
"""Execution statistics.
|
|
100
|
+
|
|
101
|
+
Statistics are created based on the contained ``suite`` and possible
|
|
102
|
+
:func:`configuration <configure>`.
|
|
103
|
+
|
|
104
|
+
Statistics are created every time this property is accessed. Saving
|
|
105
|
+
them to a variable is thus often a good idea to avoid re-creating
|
|
106
|
+
them unnecessarily::
|
|
107
|
+
|
|
108
|
+
from robot.api import ExecutionResult
|
|
109
|
+
|
|
110
|
+
result = ExecutionResult('output.xml')
|
|
111
|
+
result.configure(stat_config={'suite_stat_level': 2,
|
|
112
|
+
'tag_stat_combine': 'tagANDanother'})
|
|
113
|
+
stats = result.statistics
|
|
114
|
+
print(stats.total.failed)
|
|
115
|
+
print(stats.total.passed)
|
|
116
|
+
print(stats.tags.combined[0].total)
|
|
117
|
+
"""
|
|
118
|
+
return Statistics(self.suite, rpa=self.rpa, **self._stat_config)
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def return_code(self) -> int:
|
|
122
|
+
"""Execution return code.
|
|
123
|
+
|
|
124
|
+
By default, returns the number of failed tests or tasks (max 250),
|
|
125
|
+
but can be :func:`configured <configure>` to always return 0.
|
|
126
|
+
"""
|
|
127
|
+
if self._status_rc:
|
|
128
|
+
return min(self.suite.statistics.failed, 250)
|
|
129
|
+
return 0
|
|
130
|
+
|
|
131
|
+
@property
|
|
132
|
+
def generated_by_robot(self) -> bool:
|
|
133
|
+
return self.generator.split()[0].upper() == 'ROBOT'
|
|
134
|
+
|
|
135
|
+
def configure(self, status_rc=True, suite_config=None, stat_config=None):
|
|
136
|
+
"""Configures the result object and objects it contains.
|
|
137
|
+
|
|
138
|
+
:param status_rc: If set to ``False``, :attr:`return_code` always
|
|
139
|
+
returns 0.
|
|
140
|
+
:param suite_config: A dictionary of configuration options passed
|
|
141
|
+
to :meth:`~.result.testsuite.TestSuite.configure` method of
|
|
142
|
+
the contained ``suite``.
|
|
143
|
+
:param stat_config: A dictionary of configuration options used when
|
|
144
|
+
creating :attr:`statistics`.
|
|
145
|
+
"""
|
|
146
|
+
if suite_config:
|
|
147
|
+
self.suite.configure(**suite_config)
|
|
148
|
+
self._status_rc = status_rc
|
|
149
|
+
self._stat_config = stat_config or {}
|
|
150
|
+
|
|
151
|
+
@classmethod
|
|
152
|
+
def from_json(cls, source: 'str|bytes|TextIO|Path',
|
|
153
|
+
rpa: 'bool|None' = None) -> 'Result':
|
|
154
|
+
"""Construct a result object from JSON data.
|
|
155
|
+
|
|
156
|
+
The data is given as the ``source`` parameter. It can be:
|
|
157
|
+
|
|
158
|
+
- a string (or bytes) containing the data directly,
|
|
159
|
+
- an open file object where to read the data from, or
|
|
160
|
+
- a path (``pathlib.Path`` or string) to a UTF-8 encoded file to read.
|
|
161
|
+
|
|
162
|
+
Data can contain either:
|
|
163
|
+
|
|
164
|
+
- full result data (contains suite information, execution errors, etc.)
|
|
165
|
+
got, for example, from the :meth:`to_json` method, or
|
|
166
|
+
- only suite information got, for example, from
|
|
167
|
+
:meth:`result.testsuite.TestSuite.to_json <TestSuite.to_json>`.
|
|
168
|
+
|
|
169
|
+
:attr:`statistics` are populated automatically based on suite information
|
|
170
|
+
and thus ignored if they are present in the data.
|
|
171
|
+
|
|
172
|
+
The ``rpa`` argument can be used to override the RPA mode. The mode is
|
|
173
|
+
got from the data by default.
|
|
174
|
+
|
|
175
|
+
New in Robot Framework 7.2.
|
|
176
|
+
"""
|
|
177
|
+
try:
|
|
178
|
+
data = JsonLoader().load(source)
|
|
179
|
+
except (TypeError, ValueError) as err:
|
|
180
|
+
raise DataError(f'Loading JSON data failed: {err}')
|
|
181
|
+
if 'suite' in data:
|
|
182
|
+
result = cls._from_full_json(data)
|
|
183
|
+
else:
|
|
184
|
+
result = cls._from_suite_json(data)
|
|
185
|
+
result.rpa = data.get('rpa', False) if rpa is None else rpa
|
|
186
|
+
if isinstance(source, Path):
|
|
187
|
+
result.source = source
|
|
188
|
+
elif isinstance(source, str) and source[0] != '{' and Path(source).exists():
|
|
189
|
+
result.source = Path(source)
|
|
190
|
+
return result
|
|
191
|
+
|
|
192
|
+
@classmethod
|
|
193
|
+
def _from_full_json(cls, data) -> 'Result':
|
|
194
|
+
return Result(suite=TestSuite.from_dict(data['suite']),
|
|
195
|
+
errors=ExecutionErrors(data.get('errors')),
|
|
196
|
+
generator=data.get('generator'),
|
|
197
|
+
generation_time=data.get('generated'))
|
|
198
|
+
|
|
199
|
+
@classmethod
|
|
200
|
+
def _from_suite_json(cls, data) -> 'Result':
|
|
201
|
+
return Result(suite=TestSuite.from_dict(data))
|
|
202
|
+
|
|
203
|
+
@overload
|
|
204
|
+
def to_json(self, file: None = None, *,
|
|
205
|
+
include_statistics: bool = True,
|
|
206
|
+
ensure_ascii: bool = False, indent: int = 0,
|
|
207
|
+
separators: 'tuple[str, str]' = (',', ':')) -> str:
|
|
208
|
+
...
|
|
209
|
+
|
|
210
|
+
@overload
|
|
211
|
+
def to_json(self, file: 'TextIO|Path|str', *,
|
|
212
|
+
include_statistics: bool = True,
|
|
213
|
+
ensure_ascii: bool = False, indent: int = 0,
|
|
214
|
+
separators: 'tuple[str, str]' = (',', ':')) -> None:
|
|
215
|
+
...
|
|
216
|
+
|
|
217
|
+
def to_json(self, file: 'None|TextIO|Path|str' = None, *,
|
|
218
|
+
include_statistics: bool = True,
|
|
219
|
+
ensure_ascii: bool = False, indent: int = 0,
|
|
220
|
+
separators: 'tuple[str, str]' = (',', ':')) -> 'str|None':
|
|
221
|
+
"""Serialize results into JSON.
|
|
222
|
+
|
|
223
|
+
The ``file`` parameter controls what to do with the resulting JSON data.
|
|
224
|
+
It can be:
|
|
225
|
+
|
|
226
|
+
- ``None`` (default) to return the data as a string,
|
|
227
|
+
- an open file object where to write the data to, or
|
|
228
|
+
- a path (``pathlib.Path`` or string) to a file where to write
|
|
229
|
+
the data using UTF-8 encoding.
|
|
230
|
+
|
|
231
|
+
The ``include_statistics`` controls including statistics information
|
|
232
|
+
in the resulting JSON data. Statistics are not needed if the serialized
|
|
233
|
+
JSON data is converted back to a ``Result`` object, but they can be
|
|
234
|
+
useful for external tools.
|
|
235
|
+
|
|
236
|
+
The remaining optional parameters are used for JSON formatting.
|
|
237
|
+
They are passed directly to the underlying json__ module, but
|
|
238
|
+
the defaults differ from what ``json`` uses.
|
|
239
|
+
|
|
240
|
+
New in Robot Framework 7.2.
|
|
241
|
+
|
|
242
|
+
__ https://docs.python.org/3/library/json.html
|
|
243
|
+
"""
|
|
244
|
+
data = {'generator': get_full_version('Rebot'),
|
|
245
|
+
'generated': datetime.now().isoformat(),
|
|
246
|
+
'rpa': self.rpa,
|
|
247
|
+
'suite': self.suite.to_dict()}
|
|
248
|
+
if include_statistics:
|
|
249
|
+
data['statistics'] = self.statistics.to_dict()
|
|
250
|
+
data['errors'] = self.errors.messages.to_dicts()
|
|
251
|
+
return JsonDumper(ensure_ascii=ensure_ascii, indent=indent,
|
|
252
|
+
separators=separators).dump(data, file)
|
|
253
|
+
|
|
254
|
+
def save(self, target=None, legacy_output=False):
|
|
255
|
+
"""Save results as XML or JSON file.
|
|
256
|
+
|
|
257
|
+
:param target: Target where to save results to. Can be a path
|
|
258
|
+
(``pathlib.Path`` or ``str``) or an open file object. If omitted,
|
|
259
|
+
uses the :attr:`source` which overwrites the original file.
|
|
260
|
+
:param legacy_output: Save XML results in Robot Framework 6.x compatible
|
|
261
|
+
format. New in Robot Framework 7.0.
|
|
262
|
+
|
|
263
|
+
File type is got based on the ``target``. The type is JSON if the ``target``
|
|
264
|
+
is a path that has a ``.json`` suffix or if it is an open file that has
|
|
265
|
+
a ``name`` attribute with a ``.json`` suffix. Otherwise, the type is XML.
|
|
266
|
+
|
|
267
|
+
It is also possible to use :meth:`to_json` for JSON serialization. Compared
|
|
268
|
+
to this method, it allows returning the JSON in addition to writing it
|
|
269
|
+
into a file, and it also supports customizing JSON formatting.
|
|
270
|
+
|
|
271
|
+
Support for saving results in JSON is new in Robot Framework 7.0.
|
|
272
|
+
Originally only suite information was saved in that case, but starting
|
|
273
|
+
from Robot Framework 7.2, also JSON results contain full result data
|
|
274
|
+
including, for example, execution errors and statistics.
|
|
275
|
+
"""
|
|
276
|
+
from robot.reporting.outputwriter import LegacyOutputWriter, OutputWriter
|
|
277
|
+
|
|
278
|
+
target = target or self.source
|
|
279
|
+
if not target:
|
|
280
|
+
raise ValueError('Path required.')
|
|
281
|
+
if is_json_source(target):
|
|
282
|
+
self.to_json(target)
|
|
283
|
+
else:
|
|
284
|
+
writer = OutputWriter if not legacy_output else LegacyOutputWriter
|
|
285
|
+
self.visit(writer(target, rpa=self.rpa))
|
|
286
|
+
|
|
287
|
+
def visit(self, visitor):
|
|
288
|
+
"""An entry point to visit the whole result object.
|
|
289
|
+
|
|
290
|
+
:param visitor: An instance of :class:`~.visitor.ResultVisitor`.
|
|
291
|
+
|
|
292
|
+
Visitors can gather information, modify results, etc. See
|
|
293
|
+
:mod:`~robot.result` package for a simple usage example.
|
|
294
|
+
|
|
295
|
+
Notice that it is also possible to call :meth:`result.suite.visit
|
|
296
|
+
<robot.result.testsuite.TestSuite.visit>` if there is no need to
|
|
297
|
+
visit the contained ``statistics`` or ``errors``.
|
|
298
|
+
"""
|
|
299
|
+
visitor.visit_result(self)
|
|
300
|
+
|
|
301
|
+
def handle_suite_teardown_failures(self):
|
|
302
|
+
"""Internal usage only."""
|
|
303
|
+
if self.generated_by_robot:
|
|
304
|
+
self.suite.handle_suite_teardown_failures()
|
|
305
|
+
|
|
306
|
+
def set_execution_mode(self, other):
|
|
307
|
+
"""Set execution mode based on other result. Internal usage only."""
|
|
308
|
+
if other.rpa is None:
|
|
309
|
+
pass
|
|
310
|
+
elif self.rpa is None:
|
|
311
|
+
self.rpa = other.rpa
|
|
312
|
+
elif self.rpa is not other.rpa:
|
|
313
|
+
this, that = ('task', 'test') if other.rpa else ('test', 'task')
|
|
314
|
+
raise DataError("Conflicting execution modes. File '%s' has %ss "
|
|
315
|
+
"but files parsed earlier have %ss. Use '--rpa' "
|
|
316
|
+
"or '--norpa' options to set the execution mode "
|
|
317
|
+
"explicitly." % (other.source, this, that))
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
class CombinedResult(Result):
|
|
321
|
+
"""Combined results of multiple test executions."""
|
|
322
|
+
|
|
323
|
+
def __init__(self, results=None):
|
|
324
|
+
super().__init__()
|
|
325
|
+
for result in results or ():
|
|
326
|
+
self.add_result(result)
|
|
327
|
+
|
|
328
|
+
def add_result(self, other):
|
|
329
|
+
self.set_execution_mode(other)
|
|
330
|
+
self.suite.suites.append(other.suite)
|
|
331
|
+
self.errors.add(other.errors)
|
|
@@ -13,21 +13,25 @@
|
|
|
13
13
|
# See the License for the specific language governing permissions and
|
|
14
14
|
# limitations under the License.
|
|
15
15
|
|
|
16
|
-
from robot
|
|
16
|
+
from robot import output
|
|
17
17
|
|
|
18
|
-
from
|
|
18
|
+
from .visitor import ResultVisitor
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
class MessageFilter(
|
|
21
|
+
class MessageFilter(ResultVisitor):
|
|
22
|
+
|
|
23
|
+
def __init__(self, level='TRACE'):
|
|
24
|
+
log_level = output.LogLevel(level or 'TRACE')
|
|
25
|
+
self.log_all = log_level.level == 'TRACE'
|
|
26
|
+
self.is_logged = log_level.is_logged
|
|
22
27
|
|
|
23
|
-
def __init__(self, log_level=None):
|
|
24
|
-
self.is_logged = IsLogged(log_level or 'TRACE')
|
|
25
28
|
|
|
26
29
|
def start_suite(self, suite):
|
|
27
|
-
if self.
|
|
30
|
+
if self.log_all:
|
|
28
31
|
return False
|
|
29
32
|
|
|
30
|
-
def
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
def start_body_item(self, item):
|
|
34
|
+
if hasattr(item, 'body'):
|
|
35
|
+
for msg in item.body.filter(messages=True):
|
|
36
|
+
if not self.is_logged(msg):
|
|
37
|
+
item.body.remove(msg)
|