moto 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9148f21a0e6aa5b6a157f4430be6e4669c8ebb5f
4
- data.tar.gz: 493eceb7d79389aace5dfb095167ec1639024505
3
+ metadata.gz: 1c00f9d85d66a288aab3de0089ccb7cd20568dcf
4
+ data.tar.gz: 913cef9df55570bdcbb9c7e4570ce7a36ab6a6aa
5
5
  SHA512:
6
- metadata.gz: 59ed4b2f7ce51f3c1480673de389ff26ca961502474253cca32de6189f0d2fe7404dc937331ede52bfb0c185296ef4fa5bf5c2255502b7d9a58f0283b97aa39c
7
- data.tar.gz: 802ff8c1cef394da74787ea43433ce46f213ab67b48a5abefce6c3a5c48dad7acf846e0292363ba7390bae72c40f2c7f4ae4dd4d5045ac1033eb17973f3fffc2
6
+ metadata.gz: 65932f837806445c3f132b5b01228328f061d63a3247685f3ac5000fed0a6396135a2a92673bc5aa177a8e74fcbf1c5fd00463bb2102cb71c6c52fe7d3d7a7cb
7
+ data.tar.gz: d3bb6d1cf7b48bde43e8c8729aeae70666ec1a53e0208399ec7662ce6b564547bb6c11a863fb1d069a18dfe17d3aac797d636fa5499e6370b0e8b4906170158a
data/bin/moto CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require_relative '../lib/cli'
3
+ require_relative '../lib/parser'
4
4
 
5
- Moto::Cli.run ARGV
5
+ Moto::Parser.run ARGV
@@ -0,0 +1,9 @@
1
+ module Moto
2
+ class AppGenerator
3
+
4
+ def self.run(argv)
5
+
6
+ end
7
+
8
+ end
9
+ end
@@ -2,13 +2,19 @@ module Moto
2
2
  module Assert
3
3
 
4
4
  def assert_equal(a, b)
5
- assert(a==b, "Arguments should be equal: #{a} != #{b}")
5
+ assert(a==b, "Arguments should be equal: #{a} != #{b}.")
6
+ end
7
+
8
+ def assert_true(b)
9
+ assert(b, "Logical condition not met, expecting true, given false.")
6
10
  end
7
11
 
8
12
  def assert(condition, message)
9
13
  unless condition
10
- @context.runner.result.add_failure(self, message)
11
- logger.error("ASSERTION FAILED: #{message}")
14
+ line_number = caller.select{ |l| l.match(/\(eval\):\d*:in `run'/) }.first[/\d+/].to_i - 1 # -1 because of added method header in generated class
15
+ full_message = "ASSERTION FAILED in line #{line_number}: #{message}"
16
+ @context.runner.result.add_failure(self, full_message)
17
+ logger.error(full_message)
12
18
  end
13
19
  end
14
20
 
data/lib/cli.rb CHANGED
@@ -10,12 +10,18 @@ require 'pp'
10
10
  require 'yaml'
11
11
  require 'active_support/inflector'
12
12
  require 'active_support/core_ext/object/blank'
13
+ require 'fileutils'
13
14
 
14
15
  require 'bundler/setup'
15
16
  Bundler.require
16
17
 
17
- APP_DIR = Dir.pwd
18
- MOTO_DIR = File.dirname(File.dirname(__FILE__))
18
+ module MotoApp
19
+ DIR = Dir.pwd
20
+ end
21
+
22
+ module Moto
23
+ DIR = File.dirname(File.dirname(__FILE__))
24
+ end
19
25
 
20
26
  require_relative './empty_listener'
21
27
  require_relative './test_logging'
@@ -29,33 +35,29 @@ require_relative './page'
29
35
  require_relative './clients/base'
30
36
  require_relative './listeners/base'
31
37
  require_relative './listeners/console'
38
+ require_relative './listeners/console_dots'
39
+ require_relative './listeners/junit_xml'
32
40
  require_relative './test_generator'
41
+ require_relative './exceptions/moto'
42
+ require_relative './exceptions/test_skipped'
43
+ require_relative './exceptions/test_forced_failure'
44
+ require_relative './exceptions/test_forced_passed'
33
45
 
34
46
  module Moto
35
47
 
36
48
  class Cli
37
49
 
38
50
  def self.run(argv)
39
- test_class_name = argv[0]
40
-
41
- tg = TestGenerator.new(APP_DIR)
42
- t = tg.generate(test_class_name)
43
-
44
- tests = [t]
45
-
46
- # parsing ARGV and creating config will come here
47
- # instantiation of tests for ARGV params also happens here
48
- # instantiate listeners/reporters
49
-
50
- # listeners = []
51
- listeners = [Moto::Listeners::Console]
52
- environments = [:qa, :qa2]
53
- # handle possible syntax error here
54
- config = eval(File.read("#{APP_DIR}/config/moto.rb"))
55
- # overwrite config with values from ARGV (if any given)
56
- # e.g. config[:capybara][:default_driver] = :selenium
57
-
58
- runner = Moto::Runner.new(tests, listeners, environments, config)
51
+ tests = []
52
+ argv[ :tests ].each do |test_name|
53
+ test_class_name = test_name
54
+
55
+ tg = TestGenerator.new(MotoApp::DIR)
56
+ t = tg.generate(test_class_name)
57
+ tests << t
58
+ end
59
+
60
+ runner = Moto::Runner.new(tests, argv[ :reporters ], argv[ :environments ], argv[ :config ])
59
61
  runner.run
60
62
  end
61
63
 
@@ -1,47 +1,47 @@
1
- require 'capybara'
2
-
3
- module Moto
4
- module Clients
5
-
6
- class Website < Moto::Clients::Base
7
-
8
- attr_reader :session
9
-
10
- ignore_logging(:page)
11
- ignore_logging(:context)
12
- ignore_logging(:session)
13
-
14
- def start_run
15
- # TODO: make session driver configurable
16
- @session = Capybara::Session.new(context.runner.config[:capybara][:default_driver])
17
- @pages = {}
18
- end
19
-
20
- def end_run
21
- @session.driver.browser.close # TODO: check that it really works
22
- end
23
-
24
- def start_test(test)
25
- # @context.current_test.logger.info("Hi mom, I'm opening some pages!")
26
- @session.reset_session!
27
- end
28
-
29
- def end_test(test)
30
- @session.reset_session!
31
- end
32
-
33
- def page(p)
34
- page_class_name = "#{self.class.name}Pages::#{p}"
35
- page_class_name.gsub!('Moto::', 'MotoApp::')
36
- if @pages[page_class_name].nil?
37
- a = page_class_name.underscore.split('/')
38
- page_path = a[1..20].join('/')
39
- require "#{APP_DIR}/#{page_path}"
40
- @pages[page_class_name] = page_class_name.constantize.new(self)
41
- end
42
- @pages[page_class_name]
43
- end
44
-
45
- end
46
- end
47
- end
1
+ require 'capybara'
2
+
3
+ module Moto
4
+ module Clients
5
+
6
+ class Website < Moto::Clients::Base
7
+
8
+ attr_reader :session
9
+
10
+ ignore_logging(:page)
11
+ ignore_logging(:context)
12
+ ignore_logging(:session)
13
+
14
+ def start_run
15
+ # TODO: make session driver configurable
16
+ @session = Capybara::Session.new(context.runner.my_config[:capybara][:default_driver])
17
+ @pages = {}
18
+ end
19
+
20
+ def end_run
21
+ @session.driver.browser.close # TODO: check that it really works
22
+ end
23
+
24
+ def start_test(test)
25
+ # @context.current_test.logger.info("Hi mom, I'm opening some pages!")
26
+ @session.reset_session!
27
+ end
28
+
29
+ def end_test(test)
30
+ @session.reset_session!
31
+ end
32
+
33
+ def page(p)
34
+ page_class_name = "#{self.class.name}Pages::#{p}"
35
+ page_class_name.gsub!('Moto::', 'MotoApp::')
36
+ if @pages[page_class_name].nil?
37
+ a = page_class_name.underscore.split('/')
38
+ page_path = a[1..20].join('/')
39
+ require "#{MotoApp::DIR}/lib/#{page_path}"
40
+ @pages[page_class_name] = page_class_name.constantize.new(self)
41
+ end
42
+ @pages[page_class_name]
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,7 @@
1
+ module Moto
2
+ module Exceptions
3
+ class MotoException < RuntimeError
4
+
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Moto
2
+ module Exceptions
3
+ class TestForcedFailure < MotoException
4
+
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Moto
2
+ module Exceptions
3
+ class TestForcedPassed < MotoException
4
+
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Moto
2
+ module Exceptions
3
+ class TestSkipped < MotoException
4
+
5
+ end
6
+ end
7
+ end
@@ -13,6 +13,7 @@ module Moto
13
13
  puts " Passed: #{@runner.result.summary[:cnt_passed]}"
14
14
  puts " Failure: #{@runner.result.summary[:cnt_failure]}"
15
15
  puts " Error: #{@runner.result.summary[:cnt_error]}"
16
+ puts " Skipped: #{@runner.result.summary[:cnt_skipped]}"
16
17
  end
17
18
 
18
19
  def start_test(test)
@@ -0,0 +1,63 @@
1
+ module Moto
2
+ module Listeners
3
+ class ConsoleDots < Base
4
+
5
+ def start_run
6
+ end
7
+
8
+ def end_run
9
+ puts ""
10
+ puts ""
11
+ puts "FINISHED: #{@runner.result.summary[:result]}, duration: #{Time.at(@runner.result.summary[:duration]).utc.strftime("%H:%M:%S")}"
12
+ puts "Tests executed: #{@runner.result.summary[:cnt_all]}"
13
+ puts " Passed: #{@runner.result.summary[:cnt_passed]}"
14
+ puts " Failure: #{@runner.result.summary[:cnt_failure]}"
15
+ puts " Error: #{@runner.result.summary[:cnt_error]}"
16
+ puts " Skipped: #{@runner.result.summary[:cnt_skipped]}"
17
+
18
+ if @runner.result.summary[:cnt_failure] > 0
19
+ puts ""
20
+ puts "FAILURES: "
21
+ @runner.result.summary[:tests_failure].each do |test_name, data|
22
+ puts test_name
23
+ puts "\t#{data[:failures].join("\n\t")}"
24
+ puts ""
25
+ end
26
+ end
27
+
28
+ if @runner.result.summary[:cnt_error] > 0
29
+ puts ""
30
+ puts "ERRORS: "
31
+ @runner.result.summary[:tests_error].each do |test_name, data|
32
+ puts test_name
33
+ puts "\t#{data[:error]}"
34
+ puts ""
35
+ end
36
+ end
37
+
38
+ if @runner.result.summary[:cnt_skipped] > 0
39
+ puts ""
40
+ puts "SKIPPED: "
41
+ @runner.result.summary[:tests_skipped].each do |test_name, data|
42
+ puts test_name
43
+ puts "\t#{data[:error]}"
44
+ puts ""
45
+ end
46
+ end
47
+ end
48
+
49
+ def start_test(test)
50
+ end
51
+
52
+ def end_test(test)
53
+ print case @runner.result[test.name][:result]
54
+ when :passed then "."
55
+ when :failure then "F"
56
+ when :error then "E"
57
+ when :skipped then "S"
58
+ end
59
+ end
60
+
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,37 @@
1
+ require 'nokogiri'
2
+
3
+ module Moto
4
+ module Listeners
5
+ class JunitXml < Base
6
+
7
+ def end_run
8
+ path = @runner.my_config[:output_file]
9
+
10
+ builder = Nokogiri::XML::Builder.new { |xml|
11
+ xml.testsuite(
12
+ errors: @runner.result.summary[:cnt_error],
13
+ failures: @runner.result.summary[:cnt_failure],
14
+ name: "Moto run",
15
+ tests: @runner.result.summary[:cnt_all],
16
+ time: @runner.result.summary[:duration],
17
+ timestamp: Time.at(@runner.result.summary[:started_at])) do
18
+ @runner.result.all.each do |test_name, data|
19
+ xml.testcase(name: test_name, time: data[:duration], classname: data[:class].name, moto_result: data[:result]) do
20
+ if !data[:error].nil?
21
+ xml.error(message: data[:error].message)
22
+ elsif data[:failures].count > 0
23
+ data[:failures].each do |f|
24
+ xml.failure(message: f)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ }
31
+
32
+ File.open(path, 'w') {|f| f.write(builder.to_xml) }
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,61 @@
1
+ require 'optparse'
2
+ require 'yaml'
3
+
4
+ require_relative '../lib/cli'
5
+ require_relative '../lib/app_generator'
6
+
7
+ module Moto
8
+
9
+ class Parser
10
+
11
+ def self.run(argv)
12
+
13
+ # TODO Generate app / Change the way parsing options goes so it doesnt generate them if they`re not needed
14
+ case argv[0]
15
+ when 'run' then Moto::Cli.run run_parse(argv)
16
+ when 'help' then show_help
17
+ when 'generate' then Moto::AppGenerator.run generate_parse(argv)
18
+ end
19
+ end
20
+
21
+ def self.run_parse(argv)
22
+ # puts Moto::DIR
23
+ # Default options
24
+ options = {}
25
+ options[:reporters] = [Moto::Listeners::ConsoleDots, Moto::Listeners::JunitXml]
26
+ options[:config] = eval(File.read("#{MotoApp::DIR}/config/moto.rb"))
27
+ options[:environments] = []
28
+
29
+ # Parse arguments
30
+ # TODO eval ?
31
+ # TODO const
32
+ # TODO reporters should be consts - not strings
33
+ OptionParser.new do |opts|
34
+ opts.on('-t', "--tests Tests", Array) { |v| options[:tests ] = v }
35
+ opts.on('-r', '--reporters Reporters', Array) { |v| options[:reporters] = v }
36
+ opts.on('-e', '--environments Environment', Array) { |v| options[:environments] = v }
37
+ opts.on('-c', '--const Const') { |v| options[:const] = v }
38
+ opts.on('-cfg', '--config Config') { |v| options[:config] = options[:config].merge( eval( v ) ) }
39
+ end.parse!
40
+ return options
41
+ end
42
+
43
+ def self.generate_parse(argv)
44
+ options = {}
45
+ options[:dir]
46
+ end
47
+
48
+ def self.show_help
49
+ puts """
50
+ Moto CLI Help:
51
+ moto run:
52
+ -t, --tests = Tests to be executed. For e.x Tests\Failure\Failure.rb should be passed as Tests::Failure
53
+ -r, --reporter = Reporters to be used. Defaults are Moto::Listeners::ConsoleDots, Moto::Listeners::JunitXml
54
+ -e, --environment etc etc
55
+ moto generate:
56
+ moto generate app_name -d, --dir = directory. Default is current dir.
57
+ """
58
+ end
59
+
60
+ end
61
+ end
@@ -6,6 +6,7 @@ module Moto
6
6
  PASSED = :passed # 0
7
7
  FAILURE = :failure # 1
8
8
  ERROR = :error # 2
9
+ SKIPPED = :skipped # 3
9
10
 
10
11
  attr_reader :summary
11
12
 
@@ -13,6 +14,10 @@ module Moto
13
14
  @results[key]
14
15
  end
15
16
 
17
+ def all
18
+ @results
19
+ end
20
+
16
21
  def initialize(runner)
17
22
  @runner = runner
18
23
  @results = {}
@@ -32,9 +37,14 @@ module Moto
32
37
  @summary[:result] = FAILURE unless @results.values.select{ |v| v[:failures].count > 0 }.empty?
33
38
  @summary[:result] = ERROR unless @results.values.select{ |v| !v[:error].nil? }.empty?
34
39
  @summary[:cnt_all] = @results.count
35
- @summary[:cnt_passed] = @results.values.select{ |v| v[:result] == PASSED }.count
36
- @summary[:cnt_failure] = @results.values.select{ |v| v[:result] == FAILURE }.count
37
- @summary[:cnt_error] = @results.values.select{ |v| v[:result] == ERROR }.count
40
+ @summary[:tests_passed] = @results.select{ |k,v| v[:result] == PASSED }
41
+ @summary[:tests_failure] = @results.select{ |k,v| v[:result] == FAILURE }
42
+ @summary[:tests_error] = @results.select{ |k,v| v[:result] == ERROR }
43
+ @summary[:tests_skipped] = @results.select{ |k,v| v[:result] == SKIPPED }
44
+ @summary[:cnt_passed] = @summary[:tests_passed].count
45
+ @summary[:cnt_failure] = @summary[:tests_failure].count
46
+ @summary[:cnt_error] = @summary[:tests_error].count
47
+ @summary[:cnt_skipped] = @summary[:tests_skipped].count
38
48
  end
39
49
 
40
50
  def start_test(test)
@@ -43,10 +53,21 @@ module Moto
43
53
 
44
54
  def end_test(test)
45
55
  # calculate result basing on errors/failures
56
+ @results[test.name][:finished_at] = Time.now.to_f
46
57
  test.result = PASSED
47
58
  test.result = FAILURE unless @results[test.name][:failures].empty?
48
- test.result = ERROR unless @results[test.name][:error].nil?
49
- @results[test.name][:finished_at] = Time.now.to_f
59
+ unless @results[test.name][:error].nil?
60
+ if @results[test.name][:error].is_a? Moto::Exceptions::TestSkipped
61
+ test.result = SKIPPED
62
+ elsif @results[test.name][:error].is_a? Moto::Exceptions::TestForcedPassed
63
+ test.result = PASSED
64
+ elsif @results[test.name][:error].is_a? Moto::Exceptions::TestForcedFailure
65
+ add_failure(test, @results[test.name][:error].message)
66
+ test.result = FAILURE
67
+ else
68
+ test.result = ERROR
69
+ end
70
+ end
50
71
  @results[test.name][:duration] = @results[test.name][:finished_at] - @results[test.name][:started_at]
51
72
  @results[test.name][:result] = test.result
52
73
  end
@@ -15,12 +15,13 @@ module Moto
15
15
 
16
16
  # TODO: initialize logger from config (yml or just ruby code)
17
17
  # @logger = Logger.new(STDOUT)
18
- @logger = Logger.new(File.open("#{APP_DIR}/moto.log", File::WRONLY | File::APPEND | File::CREAT))
18
+ @logger = Logger.new(File.open("#{MotoApp::DIR}/moto.log", File::WRONLY | File::APPEND | File::CREAT))
19
19
  # @logger.level = Logger::WARN
20
20
 
21
21
  @result = Result.new(self)
22
22
 
23
23
  # TODO: validate envs, maybe no-env should be supported as well?
24
+ environments << :__default if environments.empty?
24
25
  @environments = environments
25
26
 
26
27
  @listeners = []
@@ -30,9 +31,25 @@ module Moto
30
31
  @listeners.unshift(@result)
31
32
  end
32
33
 
34
+ def my_config
35
+ caller_path = caller.first.to_s.split(/:\d/)[0]
36
+ keys = []
37
+ if caller_path.include? MotoApp::DIR
38
+ caller_path.sub!( "#{MotoApp::DIR}/lib/", '' )
39
+ keys << 'moto_app'
40
+ elsif caller_path.include? Moto::DIR
41
+ caller_path.sub!( "#{Moto::DIR}/lib/", '' )
42
+ keys << 'moto'
43
+ end
44
+ caller_path.sub!('.rb', '')
45
+ keys << caller_path.split('/')
46
+ keys.flatten!
47
+ eval "@config#{keys.map{|k| "[:#{k}]" }.join('')}"
48
+ end
49
+
33
50
  def run
34
51
  @listeners.each { |l| l.start_run }
35
- test_slices = @tests.each_slice((@tests.size.to_f/@config[:thread_count]).ceil).to_a
52
+ test_slices = @tests.each_slice((@tests.size.to_f/my_config[:thread_count]).ceil).to_a
36
53
  (0...test_slices.count).each do |i|
37
54
  @threads << Thread.new do
38
55
  tc = ThreadContext.new(self, test_slices[i])
@@ -38,8 +38,13 @@ module Moto
38
38
  end
39
39
 
40
40
  def set_name
41
- return @name = "#{self.class.to_s}/#{@env}" if @params.empty?
42
- return @name = "#{self.class.to_s}/#{@env}/#{@params[:__name]}" if @params.key?(:__name)
41
+ if @env == :__default
42
+ return @name = "#{self.class.to_s}" if @params.empty?
43
+ return @name = "#{self.class.to_s}/#{@params['__name']}" if @params.key?('__name')
44
+ else
45
+ return @name = "#{self.class.to_s}/#{@env}" if @params.empty?
46
+ return @name = "#{self.class.to_s}/#{@env}/#{@params['__name']}" if @params.key?('__name')
47
+ end
43
48
  @name = self.class.to_s
44
49
  end
45
50
 
@@ -73,6 +78,33 @@ module Moto
73
78
  def client(name)
74
79
  @context.client(name)
75
80
  end
81
+
82
+ def skip(msg = nil)
83
+ if msg.nil?
84
+ msg = "Test skipped with no reason given."
85
+ else
86
+ msg = "Skip reason: #{msg}"
87
+ end
88
+ raise Exceptions::TestSkipped.new msg
89
+ end
90
+
91
+ def fail(msg = nil)
92
+ if msg.nil?
93
+ msg = "Test forcibly failed with no reason given."
94
+ else
95
+ msg = "Forced failure, reason: #{msg}"
96
+ end
97
+ raise Exceptions::TestForcedFailure.new msg
98
+ end
99
+
100
+ def pass(msg = nil)
101
+ if msg.nil?
102
+ msg = "Test forcibly passed with no reason given."
103
+ else
104
+ msg = "Forced passed, reason: #{msg}"
105
+ end
106
+ raise Exceptions::TestForcedPassed.new msg
107
+ end
76
108
 
77
109
  end
78
110
  end
@@ -17,7 +17,7 @@ module Moto
17
17
  test_path = (a[1..20]+[a[-1]]).join('/')
18
18
 
19
19
  # TODO: check if this path and constant exists
20
- require "#{APP_DIR}/#{test_path}"
20
+ require "#{MotoApp::DIR}/#{test_path}"
21
21
  test_const = class_name.safe_constantize
22
22
  test_const.new
23
23
  end
@@ -39,17 +39,29 @@ module Moto
39
39
  full_class_name = 'MotoApp::Tests::'+class_name
40
40
  a = full_class_name.underscore.split('/')
41
41
  test_path = (a[1..20]+[a[-1]]).join('/')
42
- test_path = "#{APP_DIR}/#{test_path}.rb"
42
+ test_path = "#{MotoApp::DIR}/#{test_path}.rb"
43
43
 
44
44
  method_body = File.read(test_path) + "\n"
45
45
 
46
+ base = Moto::Test
47
+ base_class_string = method_body.match( /^#\s*BASE_CLASS:\s(\S+)/ )
48
+ unless base_class_string.nil?
49
+ base_class_string = base_class_string[1].strip
50
+
51
+ a = base_class_string.underscore.split('/')
52
+ base_test_path = a[1..20].join('/')
53
+
54
+ require "#{MotoApp::DIR}/lib/#{base_test_path}"
55
+ base = base_class_string.constantize
56
+ end
57
+
46
58
  # MotoApp::Tests::Login::Short
47
59
  consts = full_class_name.split('::')
48
60
  class_name = consts.pop
49
61
 
50
62
  consts.shift 2 # remove Moto::Test as already defined
51
63
  m = create_module_tree(MotoApp::Tests, consts)
52
- cls = Class.new(Moto::Test)
64
+ cls = Class.new(base)
53
65
  m.const_set(class_name.to_sym, cls)
54
66
 
55
67
  test_object = cls.new
@@ -15,7 +15,7 @@ module Moto
15
15
  t.context = self
16
16
  end
17
17
  # TODO: add all *.yml files from that dir
18
- @config = YAML.load_file("#{APP_DIR}/config/const.yml")
18
+ @config = YAML.load_file("#{MotoApp::DIR}/config/const.yml")
19
19
  end
20
20
 
21
21
  def client(name)
@@ -24,13 +24,13 @@ module Moto
24
24
  name_app = 'MotoApp::Clients::' + name
25
25
  name_moto = 'Moto::Clients::' + name
26
26
 
27
- c = try_client(name_app, APP_DIR)
27
+ c = try_client(name_app, "#{MotoApp::DIR}/lib")
28
28
  unless c.nil?
29
29
  @clients[name] = c
30
30
  return c
31
31
  end
32
32
 
33
- c = try_client(name_moto, "#{MOTO_DIR}/lib")
33
+ c = try_client(name_moto, "#{Moto::DIR}/lib")
34
34
  unless c.nil?
35
35
  @clients[name] = c
36
36
  return c
@@ -55,43 +55,64 @@ module Moto
55
55
  end
56
56
 
57
57
  def const(key)
58
- # TODO: add support to consts with no env
59
- @config[@current_test.env.to_s][key.to_s]
58
+ key = key.to_s
59
+ key = "#{@current_test.env.to_s}.#{key}" if @current_test.env != :__default
60
+ code = if key.include? '.'
61
+ "@config#{key.split('.').map{|a| "['#{a}']" }.join('')}"
62
+ else
63
+ "@config['#{key}']"
64
+ end
65
+ begin
66
+ v = eval code
67
+ raise if v.nil?
68
+ rescue
69
+ raise "There is no const defined for key: #{key}. Environment: #{ (@current_test.env == :__default) ? '<none>' : @current_test.env }"
70
+ end
71
+ v
60
72
  end
61
73
 
62
74
  def run
63
75
  @tests.each do |test|
76
+ # remove log files from previous execution
77
+ FileUtils.rm_rf Dir.glob("#{test.dir}/*.log")
78
+ max_attempts = @runner.my_config[:max_attempts] || 1
64
79
  @runner.environments.each do |env|
65
80
  params_path = "#{test.dir}/#{test.filename}.yml"
66
81
  params_all = [{}]
67
- params_all = YAML.load_file(params_path).map{|h| Hash[ h.map{|k,v| [ k.to_sym, v ] } ] } if File.exists?(params_path)
68
- # params_all = YAML.load_file(params_path) if File.exists?(params_path)
82
+ params_all = YAML.load_file(params_path) if File.exists?(params_path)
83
+ # or convert keys to symbols?
84
+ # params_all = YAML.load_file(params_path).map{|h| Hash[ h.map{|k,v| [ k.to_sym, v ] } ] } if File.exists?(params_path)
69
85
  params_all.each do |params|
70
86
  # TODO: add filtering out params that are specific to certain envs
71
- test.init(env, params)
72
- # TODO: log path might be specified (to some extent) by the configuration
73
- @log_path = "#{test.dir}/#{test.name.gsub(/\s+/, '_').gsub('::', '_').gsub('/', '_')}.log"
74
- # TODO: remove log files from previous execution
75
- @logger = Logger.new(File.open(@log_path, File::WRONLY | File::TRUNC | File::CREAT))
76
- # TODO: make logger level configurable
77
- @logger.level = @runner.config[:log_level]
78
- @current_test = test
79
- @runner.listeners.each { |l| l.start_test(test) }
80
- @clients.each_value { |c| c.start_test(test) }
81
- test.before
82
- @logger.info "Start: #{test.name}"
83
- begin
84
- test.run
85
- rescue Exception => e
86
- @logger.error("#{e.class.name}: #{e.message}")
87
- @logger.error(e.backtrace.join("\n"))
88
- @runner.result.add_error(test, e)
89
- end
90
- test.after
91
- @clients.each_value { |c| c.end_test(test) }
92
- @runner.listeners.each { |l| l.end_test(test) }
93
- @logger.info("Result: #{test.result}")
94
- @logger.close
87
+ (1..max_attempts).each do |attempt|
88
+ test.init(env, params)
89
+ # TODO: log path might be specified (to some extent) by the configuration
90
+ @log_path = "#{test.dir}/#{test.name.gsub(/\s+/, '_').gsub('::', '_').gsub('/', '_')}.log"
91
+ @logger = Logger.new(File.open(@log_path, File::WRONLY | File::TRUNC | File::CREAT))
92
+ # TODO: make logger level configurable
93
+ @logger.level = @runner.my_config[:log_level]
94
+ @current_test = test
95
+ @runner.listeners.each { |l| l.start_test(test) }
96
+ @clients.each_value { |c| c.start_test(test) }
97
+ test.before
98
+ @logger.info "Start: #{test.name} attempt #{attempt}/#{max_attempts}"
99
+ begin
100
+ test.run
101
+ rescue Exceptions::TestForcedPassed, Exceptions::TestForcedFailure, Exceptions::TestSkipped => e
102
+ logger.info(e.message)
103
+ @runner.result.add_error(test, e)
104
+ rescue Exception => e
105
+ @logger.error("#{e.class.name}: #{e.message}")
106
+ @logger.error(e.backtrace.join("\n"))
107
+ @runner.result.add_error(test, e)
108
+ end
109
+ test.after
110
+ @clients.each_value { |c| c.end_test(test) }
111
+ @runner.listeners.each { |l| l.end_test(test) }
112
+ @logger.info("Result: #{test.result}")
113
+ @logger.close
114
+ break unless [Result::FAILURE, Result::ERROR].include? test.result
115
+ end # RETRY
95
116
  end
96
117
  end
97
118
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: moto
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bartek Wilczek
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-08-17 00:00:00.000000000 Z
12
+ date: 2015-09-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -25,6 +25,34 @@ dependencies:
25
25
  - - ~>
26
26
  - !ruby/object:Gem::Version
27
27
  version: '3.2'
28
+ - !ruby/object:Gem::Dependency
29
+ name: fileutils
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - '>='
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: nokogiri
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
28
56
  description: This is a development version of a rails philosophy inspired framework
29
57
  for web applications functional testing. It supports (or will support) threading,
30
58
  scenario parametrization, different test environments and much more. Stay tuned
@@ -37,10 +65,12 @@ executables:
37
65
  extensions: []
38
66
  extra_rdoc_files: []
39
67
  files:
68
+ - lib/app_generator.rb
40
69
  - lib/assert.rb
41
70
  - lib/cli.rb
42
71
  - lib/empty_listener.rb
43
72
  - lib/page.rb
73
+ - lib/parser.rb
44
74
  - lib/result.rb
45
75
  - lib/runner.rb
46
76
  - lib/runner_logging.rb
@@ -50,8 +80,14 @@ files:
50
80
  - lib/thread_context.rb
51
81
  - lib/clients/base.rb
52
82
  - lib/clients/website.rb
83
+ - lib/exceptions/moto.rb
84
+ - lib/exceptions/test_forced_failure.rb
85
+ - lib/exceptions/test_forced_passed.rb
86
+ - lib/exceptions/test_skipped.rb
53
87
  - lib/listeners/base.rb
54
88
  - lib/listeners/console.rb
89
+ - lib/listeners/console_dots.rb
90
+ - lib/listeners/junit_xml.rb
55
91
  - bin/moto
56
92
  homepage: https://github.com/bwilczek/moto
57
93
  licenses:
@@ -63,9 +99,9 @@ require_paths:
63
99
  - lib
64
100
  required_ruby_version: !ruby/object:Gem::Requirement
65
101
  requirements:
66
- - - '>='
102
+ - - ~>
67
103
  - !ruby/object:Gem::Version
68
- version: '0'
104
+ version: '2.0'
69
105
  required_rubygems_version: !ruby/object:Gem::Requirement
70
106
  requirements:
71
107
  - - '>='
@@ -73,7 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
73
109
  version: '0'
74
110
  requirements: []
75
111
  rubyforge_project:
76
- rubygems_version: 2.0.15
112
+ rubygems_version: 2.0.14
77
113
  signing_key:
78
114
  specification_version: 4
79
115
  summary: Moto - yet another web testing framework