Robot Framework
builders.py
Go to the documentation of this file.
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 import os
17 
18 from robot.errors import DataError
19 from robot.output import LOGGER
20 from robot.parsing import SuiteStructureBuilder, SuiteStructureVisitor
21 
22 from .parsers import RobotParser, NoInitFileDirectoryParser, RestParser
23 from .settings import Defaults
24 
25 
26 
48 
49 
72  def __init__(self, included_suites=None, included_extensions=('robot',),
73  rpa=None, lang=None, allow_empty_suite=False,
74  process_curdir=True):
75  self.rparpa = rpa
76  self.langlang = lang
77  self.included_suitesincluded_suites = included_suites
78  self.included_extensionsincluded_extensions = included_extensions
79  self.allow_empty_suiteallow_empty_suite = allow_empty_suite
80  self.process_curdirprocess_curdir = process_curdir
81 
82 
86  def build(self, *paths):
87  structure = SuiteStructureBuilder(self.included_extensionsincluded_extensions,
88  self.included_suitesincluded_suites).build(paths)
89  parser = SuiteStructureParser(self.included_extensionsincluded_extensions,
90  self.rparpa, self.langlang, self.process_curdirprocess_curdir)
91  suite = parser.parse(structure)
92  if not self.included_suitesincluded_suites and not self.allow_empty_suiteallow_empty_suite:
93  self._validate_test_counts_validate_test_counts(suite, multisource=len(paths) > 1)
94  suite.remove_empty_suites(preserve_direct_children=len(paths) > 1)
95  return suite
96 
97  def _validate_test_counts(self, suite, multisource=False):
98  def validate(suite):
99  if not suite.has_tests:
100  raise DataError(f"Suite '{suite.name}' contains no tests or tasks.")
101  if not multisource:
102  validate(suite)
103  else:
104  for s in suite.suites:
105  validate(s)
106 
107 
109 
110  def __init__(self, included_extensions, rpa=None, lang=None, process_curdir=True):
111  self.rparpa = rpa
112  self._rpa_given_rpa_given = rpa is not None
113  self.suitesuite = None
114  self._stack_stack = []
115  self.parsersparsers = self._get_parsers_get_parsers(included_extensions, lang, process_curdir)
116 
117  def _get_parsers(self, extensions, lang, process_curdir):
118  robot_parser = RobotParser(lang, process_curdir)
119  rest_parser = RestParser(lang, process_curdir)
120  parsers = {
122  'robot': robot_parser,
123  'rst': rest_parser,
124  'rest': rest_parser
125  }
126  for ext in extensions:
127  if ext not in parsers:
128  parsers[ext] = robot_parser
129  return parsers
130 
131  def _get_parser(self, extension):
132  try:
133  return self.parsersparsers[extension]
134  except KeyError:
135  return self.parsersparsers['robot']
136 
137  def parse(self, structure):
138  structure.visit(self)
139  self.suitesuite.rpa = self.rparpa
140  return self.suitesuite
141 
142  def visit_file(self, structure):
143  LOGGER.info(f"Parsing file '{structure.source}'.")
144  suite, _ = self._build_suite_build_suite(structure)
145  if self._stack_stack:
146  self._stack_stack[-1][0].suites.append(suite)
147  else:
148  self.suitesuite = suite
149 
150  def start_directory(self, structure):
151  if structure.source:
152  LOGGER.info(f"Parsing directory '{structure.source}'.")
153  suite, defaults = self._build_suite_build_suite(structure)
154  if self.suitesuite is None:
155  self.suitesuite = suite
156  else:
157  self._stack_stack[-1][0].suites.append(suite)
158  self._stack_stack.append((suite, defaults))
159 
160  def end_directory(self, structure):
161  suite, _ = self._stack_stack.pop()
162  if suite.rpa is None and suite.suites:
163  suite.rpa = suite.suites[0].rpa
164 
165  def _build_suite(self, structure):
166  parent_defaults = self._stack_stack[-1][-1] if self._stack_stack else None
167  source = structure.source
168  defaults = Defaults(parent_defaults)
169  parser = self._get_parser_get_parser(structure.extension)
170  try:
171  if structure.is_directory:
172  suite = parser.parse_init_file(structure.init_file or source, defaults)
173  else:
174  suite = parser.parse_suite_file(source, defaults)
175  if not suite.tests:
176  LOGGER.info(f"Data source '{source}' has no tests or tasks.")
177  self._validate_execution_mode_validate_execution_mode(suite)
178  except DataError as err:
179  raise DataError(f"Parsing '{source}' failed: {err.message}")
180  return suite, defaults
181 
182  def _validate_execution_mode(self, suite):
183  if self._rpa_given_rpa_given:
184  suite.rpa = self.rparpa
185  elif suite.rpa is None:
186  pass
187  elif self.rparpa is None:
188  self.rparpa = suite.rpa
189  elif self.rparpa is not suite.rpa:
190  this, that = ('tasks', 'tests') if suite.rpa else ('tests', 'tasks')
191  raise DataError(f"Conflicting execution modes. File has {this} "
192  f"but files parsed earlier have {that}. Fix headers "
193  f"or use '--rpa' or '--norpa' options to set the "
194  f"execution mode explicitly.")
195 
196 
198 
199  def __init__(self, lang=None, process_curdir=True):
200  self.langlang = lang
201  self.process_curdirprocess_curdir = process_curdir
202 
203  def build(self, source):
204  LOGGER.info(f"Parsing resource file '{source}'.")
205  resource = self._parse_parse(source)
206  if resource.imports or resource.variables or resource.keywords:
207  LOGGER.info(f"Imported resource file '{source}' ({len(resource.keywords)} "
208  f"keywords).")
209  else:
210  LOGGER.warn(f"Imported resource file '{source}' is empty.")
211  return resource
212 
213  def _parse(self, source):
214  if os.path.splitext(source)[1].lower() in ('.rst', '.rest'):
215  return RestParser(self.langlang, self.process_curdirprocess_curdir).parse_resource_file(source)
216  return RobotParser(self.langlang, self.process_curdirprocess_curdir).parse_resource_file(source)
def __init__(self, lang=None, process_curdir=True)
Definition: builders.py:199
def _get_parsers(self, extensions, lang, process_curdir)
Definition: builders.py:117
def __init__(self, included_extensions, rpa=None, lang=None, process_curdir=True)
Definition: builders.py:110
Builder to construct TestSuite objects based on data on the disk.
Definition: builders.py:47
def _validate_test_counts(self, suite, multisource=False)
Definition: builders.py:97
def __init__(self, included_suites=None, included_extensions=('robot',), rpa=None, lang=None, allow_empty_suite=False, process_curdir=True)
:param include_suites: List of suite names to include.
Definition: builders.py:74
def build(self, *paths)
:param paths: Paths to test data files or directories.
Definition: builders.py:86