Class: Roby::Test::TestCase
- Includes:
- Roby::Test, Assertions
- Defined in:
- lib/roby/test/testcase.rb
Overview
This is the base class for running tests which uses a Roby control loop (i.e. plan execution).
Because configuration and planning can be robot-specific, parts of the tests can also be splitted into generic parts and specific parts. The TestCase.robot statement allows to specify that a given test case is specific to a given robot, in which case it is ran only if the call to scripts/test specified a robot which matches (i.e. same name and type).
Finally, two other mode of operation control the way tests are ran
- simulation
-
if the
--simflag is given toscripts/test, the tests are ran under simulation. Otherwise, they are run in live mode (see Roby::Application for a description of simulation and live modes). It is possible to constrain that a given test method is run only in simulation or live mode with the TestCase.sim and TestCase.nosim statements:sim :sim_only def test_sim_only end nosim :live_only def test_live_only end - interactive
-
Sometime, it is hard to actually assess the quality of processing results automatically. In these cases, it is possible to show the user the result of data processing, and then ask if the result is valid by using the #user_validation method. Nonetheless, the tests can be ran in automatic mode, in which the assertions which require user validation are simply skipped. The
--interactiveor-iflags ofscripts/testspecify that user interaction is possible.
Constant Summary collapse
- @@first_time =
true
Constants included from Roby::Test
ASSERT_ANY_EVENTS_TLS, BASE_PORT, DISCOVERY_SERVER, LOCAL_PORT, LOCAL_SERVER, REMOTE_PORT, REMOTE_SERVER, Unit
Constants included from Roby
ROBY_LIB_DIR, ROBY_ROOT_DIR, RX_IN_FRAMEWORK, VERSION
Class Attribute Summary collapse
-
.app_setup ⇒ Object
readonly
Returns the value of attribute app_setup.
Attributes included from Roby::Test
#console_logger, #original_collections, #remote_processes, #timings
Class Method Summary collapse
-
.apply_robot_setup ⇒ Object
Loads the configuration as specified by TestCase.robot.
-
.nosim(*names) ⇒ Object
Do not run
test_nameinside a simulation environmenttest_nameis the name of the method withouttest_. -
.robot(name, kind = name, &block) ⇒ Object
Sets the robot configuration for this test case.
-
.sim(*names) ⇒ Object
Run
test_nameonly inside a simulation environmenttest_nameis the name of the method withouttest_. -
.suite ⇒ Object
:nodoc:.
Instance Method Summary collapse
-
#add_error(*args, &block) ⇒ Object
:nodoc:.
-
#add_failure(*args, &block) ⇒ Object
:nodoc:.
-
#automatic_testing? ⇒ Boolean
Returns true if user interaction is to be disabled during this test.
-
#dataset_file_path(dataset_name, file) ⇒ Object
Returns the full path of the file name into which the log file
fileshould be saved to be referred to as thedataset_namedataset. -
#dataset_prefix ⇒ Object
The directory into which the datasets generated by the current testcase are to be saved.
-
#datasets_dir ⇒ Object
The directory in which datasets are to be saved.
-
#method_config ⇒ Object
:nodoc:.
-
#planner ⇒ Object
Returns a fresh MainPlanner object for the current plan.
-
#progress(value, max = nil) ⇒ Object
Progress report for the curren test.
-
#run(result) ⇒ Object
:nodoc:.
- #sampling(*args, &block) ⇒ Object
-
#save_dataset(files = nil, suffix = '') ⇒ Object
Saves
file, which is taken in the log directory, in the test/datasets directory. -
#setup ⇒ Object
:nodoc:.
- #stats(*args, &block) ⇒ Object
-
#teardown ⇒ Object
:nodoc:.
- #user_interaction ⇒ Object
-
#user_validation(msg) ⇒ Object
Ask for user validation.
Methods included from Assertions
#assert_any_event, #assert_relative_error, #assert_same_position, #assert_succeeds, #control_priority
Methods included from Roby::Test
assert_any_event_result, #assert_doesnt_timeout, #assert_marshallable, #assert_original_error, check_event_assertions, #display_event_structure, #display_timings!, finalize_event_assertions, interrupt_waiting_threads, #new_plan, #plan, #prepare_plan, #process_events, #remote_process, #restore_collections, sampling, #save_collection, stats, #stop_remote_processes, #teardown_plan, #wait_thread_stopped
Methods included from Roby
RelationSpace, app, check_failed_missions, condition_variable, control_thread, each_cycle, each_exception_handler, every, execute, filter_backtrace, format_exception, inside_control?, load_all_relations, log_exception, on_exception, once, outside_control?, poll_state_events, pretty_print_backtrace, return_condition_variable, wait_one_cycle, wait_until
Methods included from ExceptionHandlingObject
#handle_exception, #pass_exception
Class Attribute Details
.app_setup ⇒ Object (readonly)
Returns the value of attribute app_setup.
248 249 250 |
# File 'lib/roby/test/testcase.rb', line 248 def app_setup @app_setup end |
Class Method Details
.apply_robot_setup ⇒ Object
Loads the configuration as specified by TestCase.robot
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 |
# File 'lib/roby/test/testcase.rb', line 263 def self.apply_robot_setup app = Roby.app if @@first_time # Make sure the log directory is empty if File.exists?(app.log_dir) if !Dir.new(app.log_dir).empty? if !STDIN.ask("#{app.log_dir} still exists and must be cleaned before starting. Proceed ? [N,y]", false) raise "user abort" end end FileUtils.rm_rf app.log_dir end @@first_time = false end name, kind, block = app_setup # Silently ignore the test suites which use a different robot if app.robot_name && (app.robot_name != name || app.robot_type != kind) return end app.robot name, kind app.reset app.single app.setup if block block.call end app.control.delete('executive') yield if block_given? end |
.nosim(*names) ⇒ Object
Do not run test_name inside a simulation environment test_name is the name of the method without test_. For instance:
nosim :init
def test_init
end
See also TestCase.sim
368 369 370 371 372 373 |
# File 'lib/roby/test/testcase.rb', line 368 def self.nosim(*names) names.each do |test_name| config = (methods_config[test_name.to_s] ||= Hash.new) config[:mode] = :nosim end end |
.robot(name, kind = name, &block) ⇒ Object
Sets the robot configuration for this test case. If a block is given, it is called between the time the robot configuration is loaded and the time the test methods are started. It can therefore be used to change the robot configuration for the need of this particular test case
256 257 258 259 |
# File 'lib/roby/test/testcase.rb', line 256 def self.robot(name, kind = name, &block) @app_setup = [name, kind, block] apply_robot_setup end |
.sim(*names) ⇒ Object
Run test_name only inside a simulation environment test_name is the name of the method without test_. For instance:
sim :init
def test_init
end
See also TestCase.nosim
383 384 385 386 387 388 |
# File 'lib/roby/test/testcase.rb', line 383 def self.sim(*names) names.each do |test_name| config = (methods_config[test_name.to_s] ||= Hash.new) config[:mode] = :sim end end |
.suite ⇒ Object
:nodoc:
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 |
# File 'lib/roby/test/testcase.rb', line 390 def self.suite # :nodoc: method_names = public_instance_methods(true) tests = method_names.delete_if {|method_name| method_name !~ /^(dataset|test)./} suite = Test::Unit::TestSuite.new(name) tests.sort.each do |test| catch(:invalid_test) do suite << new(test) end end if (suite.empty?) catch(:invalid_test) do suite << new("default_test") end end return suite end |
Instance Method Details
#add_error(*args, &block) ⇒ Object
:nodoc:
456 457 458 459 |
# File 'lib/roby/test/testcase.rb', line 456 def add_error(*args, &block) # :nodoc: @failed_test = true super end |
#add_failure(*args, &block) ⇒ Object
:nodoc:
460 461 462 463 |
# File 'lib/roby/test/testcase.rb', line 460 def add_failure(*args, &block) # :nodoc: @failed_test = true super end |
#automatic_testing? ⇒ Boolean
Returns true if user interaction is to be disabled during this test
317 318 319 |
# File 'lib/roby/test/testcase.rb', line 317 def automatic_testing? Roby.app.automatic_testing? end |
#dataset_file_path(dataset_name, file) ⇒ Object
Returns the full path of the file name into which the log file file should be saved to be referred to as the dataset_name dataset
476 477 478 479 480 481 482 483 484 485 |
# File 'lib/roby/test/testcase.rb', line 476 def dataset_file_path(dataset_name, file) path = File.join(datasets_dir, dataset_name, file) if !File.file?(path) raise "#{path} does not exist" end path rescue flunk("dataset #{dataset_name} has not been generated: #{$!.message}") end |
#dataset_prefix ⇒ Object
The directory into which the datasets generated by the current testcase are to be saved.
471 472 473 |
# File 'lib/roby/test/testcase.rb', line 471 def dataset_prefix "#{Roby.app.robot_name}-#{self.class.name.gsub('TC_', '').underscore}/, '')}" end |
#datasets_dir ⇒ Object
The directory in which datasets are to be saved
466 467 468 |
# File 'lib/roby/test/testcase.rb', line 466 def datasets_dir "#{APP_DIR}/test/datasets" end |
#method_config ⇒ Object
:nodoc:
312 313 314 |
# File 'lib/roby/test/testcase.rb', line 312 def method_config # :nodoc: self.class.case_config.merge(self.class.methods_config[method_name] || Hash.new) end |
#planner ⇒ Object
Returns a fresh MainPlanner object for the current plan
298 299 300 |
# File 'lib/roby/test/testcase.rb', line 298 def planner MainPlanner.new(plan) end |
#progress(value, max = nil) ⇒ Object
Progress report for the curren test. If max is given, then value is assumed to be between 0 and max. Otherwise, value is a float value between 0 and 1 and is displayed as a percentage.
324 325 326 327 328 329 330 331 |
# File 'lib/roby/test/testcase.rb', line 324 def progress(value, max = nil) if max print "\r#{@method_name} progress: #{value}/#{max}" else print "\r#{@method_name} progress: #{"%.2f %%" % [value * 100]}" end STDOUT.flush end |
#run(result) ⇒ Object
:nodoc:
407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 |
# File 'lib/roby/test/testcase.rb', line 407 def run(result) # :nodoc: Roby::Test.waiting_threads.clear self.class.apply_robot_setup do yield if block_given? case method_config[:mode] when :nosim return if Roby.app.simulation? when :sim return unless Roby.app.simulation? end @failed_test = false begin Roby.app.run do super end rescue Exception => e if @_result add_error(e) else raise end end keep_logdir = @failed_test || Roby.app.testing_keep_logs? save_logdir = (@failed_test && automatic_testing?) || Roby.app.testing_keep_logs? if save_logdir subdir = @failed_test ? 'failures' : 'results' basedir = File.join(APP_DIR, 'test', subdir) dirname = Roby::Application.unique_dirname(basedir, dataset_prefix) if Roby.app.testing_overwrites_logs? dirname.gsub! /\.\d+$/, '' FileUtils.rm_rf dirname end FileUtils.mv Roby.app.log_dir, dirname end if !keep_logdir FileUtils.rm_rf Roby.app.log_dir end end rescue Exception puts "testcase #{method_name} teardown failed with\n#{$!.full_message}" end |
#sampling(*args, &block) ⇒ Object
513 |
# File 'lib/roby/test/testcase.rb', line 513 def sampling(*args, &block); Test.sampling(*args, &block) end |
#save_dataset(files = nil, suffix = '') ⇒ Object
Saves file, which is taken in the log directory, in the test/datasets directory. The data set is saved as ‘robot-testname-testmethod-suffix’
490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 |
# File 'lib/roby/test/testcase.rb', line 490 def save_dataset(files = nil, suffix = '') destname = dataset_prefix destname << "-#{suffix}" unless suffix.empty? dir = File.join(datasets_dir, destname) if File.exists?(dir) relative_dir = dir.gsub(/^#{Regexp.quote(APP_DIR)}/, '') unless STDIN.ask("\r#{relative_dir} already exists. Delete ? [N,y]", false) raise "user abort" end FileUtils.rm_rf dir end FileUtils.mkdir_p(dir) files ||= Dir.entries(Roby.app.log_dir).find_all do |path| File.file? File.join(Roby.app.log_dir, path) end [*files].each do |path| FileUtils.mv "#{Roby.app.log_dir}/#{path}", dir end end |
#setup ⇒ Object
:nodoc:
302 303 304 305 |
# File 'lib/roby/test/testcase.rb', line 302 def setup # :nodoc: super Roby::Test.waiting_threads << Thread.current end |
#stats(*args, &block) ⇒ Object
514 |
# File 'lib/roby/test/testcase.rb', line 514 def stats(*args, &block); Test.stats(*args, &block) end |
#teardown ⇒ Object
:nodoc:
307 308 309 310 |
# File 'lib/roby/test/testcase.rb', line 307 def teardown # :nodoc: Roby::Test.waiting_threads.delete(Thread.current) super end |
#user_interaction ⇒ Object
333 334 335 336 337 338 339 340 341 342 343 |
# File 'lib/roby/test/testcase.rb', line 333 def user_interaction return unless automatic_testing? test_result = catch(:validation_result) do yield return end if test_result flunk(*test_result) end end |
#user_validation(msg) ⇒ Object
Ask for user validation. The method first yields, and then asks the user if the showed dataset is nominal. If the tests are ran in automated mode (#automatic_testing? returns true), it does nothing.
349 350 351 352 353 354 355 356 357 358 |
# File 'lib/roby/test/testcase.rb', line 349 def user_validation(msg) return if automatic_testing? assert_block(msg) do STDOUT.puts "Now validating #{msg}" yield STDIN.ask("\rIs the result OK ? [N,y]", false) end end |