moto 0.0.61 → 0.7.0
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.
- checksums.yaml +4 -4
- data/lib/cli.rb +0 -1
- data/lib/clients/base.rb +5 -11
- data/lib/clients/clients_manager.rb +51 -0
- data/lib/clients/website.rb +15 -23
- data/lib/page.rb +3 -12
- data/lib/runner/test_generator.rb +3 -10
- data/lib/runner/test_runner.rb +5 -4
- data/lib/runner/thread_context.rb +19 -64
- data/lib/runner_logging.rb +2 -3
- data/lib/test/base.rb +9 -4
- data/lib/test_logging.rb +2 -2
- data/lib/version.rb +1 -1
- metadata +3 -3
- data/lib/forward_context_methods.rb +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5c6fc26ad79a37b98a47def96713f98aeb645d7a
|
4
|
+
data.tar.gz: 319b20fd048182b400a165c5f947eca5da265850
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b7094905897ffa05fd13dcc0e170d3f9a9faf54b76a27aaff5776f353d664c035f786bab3fe9b8585ce594584a3c03c88f1d852e35e991787eb5eece832134dc
|
7
|
+
data.tar.gz: 8fb71b7424b346314b29cd76f232207db2fc3f19214adfbbbd3e2f9239896e8610b568ca3e192759e2ed87bb649b2971059a7f9098db31cb2f82cc9cdb885b92
|
data/lib/cli.rb
CHANGED
data/lib/clients/base.rb
CHANGED
@@ -3,23 +3,12 @@ module Moto
|
|
3
3
|
|
4
4
|
class Base
|
5
5
|
include Moto::EmptyListener
|
6
|
-
include Moto::ForwardContextMethods
|
7
6
|
|
8
7
|
# include Moto::RunnerLogging
|
9
8
|
include Moto::TestLogging
|
10
9
|
|
11
|
-
attr_reader :context
|
12
|
-
|
13
10
|
ignore_logging(:handle_test_exception)
|
14
11
|
|
15
|
-
def initialize(context)
|
16
|
-
@context = context
|
17
|
-
end
|
18
|
-
|
19
|
-
def init
|
20
|
-
# abstract
|
21
|
-
end
|
22
|
-
|
23
12
|
def handle_test_exception(test, exception)
|
24
13
|
# abstract
|
25
14
|
end
|
@@ -31,6 +20,11 @@ module Moto
|
|
31
20
|
Moto::Lib::Config.environment_const(key)
|
32
21
|
end
|
33
22
|
|
23
|
+
# Access client defined in Moto or MotoApp via it's name
|
24
|
+
def client(name)
|
25
|
+
Thread.current['clients_manager'].client(name)
|
26
|
+
end
|
27
|
+
|
34
28
|
end
|
35
29
|
end
|
36
30
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Moto
|
2
|
+
module Lib
|
3
|
+
module Clients
|
4
|
+
class ClientsManager
|
5
|
+
|
6
|
+
attr_reader :clients
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@clients = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def client(name)
|
13
|
+
return @clients[name] if @clients.key? name
|
14
|
+
|
15
|
+
name_app = 'MotoApp::Lib::Clients::' + name
|
16
|
+
name_moto = 'Moto::Clients::' + name
|
17
|
+
|
18
|
+
client = try_client(name_app, "#{MotoApp::DIR}/")
|
19
|
+
if client
|
20
|
+
@clients[name] = client
|
21
|
+
return client
|
22
|
+
end
|
23
|
+
|
24
|
+
client = try_client(name_moto, "#{Moto::DIR}/lib/")
|
25
|
+
if client
|
26
|
+
@clients[name] = client
|
27
|
+
return client
|
28
|
+
end
|
29
|
+
|
30
|
+
raise "Could not find client class for name: #{name}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def try_client(name, dir)
|
34
|
+
client_path = name.underscore.split('/')[1..-1].join('/')
|
35
|
+
|
36
|
+
if File.file?(dir + client_path + '.rb')
|
37
|
+
require dir + client_path
|
38
|
+
client_const = name.constantize
|
39
|
+
instance = client_const.new
|
40
|
+
instance.start_run
|
41
|
+
instance
|
42
|
+
else
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
private :try_client
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/clients/website.rb
CHANGED
@@ -6,15 +6,25 @@ module Moto
|
|
6
6
|
|
7
7
|
class Website < Moto::Clients::Base
|
8
8
|
|
9
|
-
attr_reader :session
|
10
|
-
|
11
|
-
ignore_logging(:page)
|
12
|
-
ignore_logging(:context)
|
13
9
|
ignore_logging(:session)
|
14
10
|
|
15
|
-
def
|
11
|
+
def initialize
|
16
12
|
register_grid_driver
|
17
13
|
register_chrome_driver
|
14
|
+
|
15
|
+
if config[:default_selector]
|
16
|
+
Capybara.default_selector = config[:default_selector]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns Capybara session, if none was available for this thread it will create one.
|
21
|
+
def session
|
22
|
+
if Thread.current['capybara_session'].nil?
|
23
|
+
Thread.current['capybara_session'] = Capybara::Session.new(config[:default_driver])
|
24
|
+
Thread.current['capybara_session'].driver.browser.manage.window.maximize
|
25
|
+
end
|
26
|
+
|
27
|
+
Thread.current['capybara_session']
|
18
28
|
end
|
19
29
|
|
20
30
|
# @return [Hash] Config section for Capybara driver.
|
@@ -24,13 +34,7 @@ module Moto
|
|
24
34
|
private :config
|
25
35
|
|
26
36
|
def start_run
|
27
|
-
# TODO: make session driver configurable
|
28
|
-
if config[:default_selector]
|
29
|
-
Capybara.default_selector = config[:default_selector]
|
30
|
-
end
|
31
37
|
|
32
|
-
Thread.current['capybara_session'] = Capybara::Session.new(config[:default_driver])
|
33
|
-
@pages = {}
|
34
38
|
end
|
35
39
|
|
36
40
|
def end_run
|
@@ -44,18 +48,6 @@ module Moto
|
|
44
48
|
def end_test(test)
|
45
49
|
Thread.current['capybara_session'].reset_session!
|
46
50
|
end
|
47
|
-
#TODO fix moto to use Lib module
|
48
|
-
def page(p)
|
49
|
-
page_class_name = "#{self.class.name}::Pages::#{p}"
|
50
|
-
page_class_name.gsub!('Moto::', 'MotoApp::Lib::')
|
51
|
-
if @pages[page_class_name].nil?
|
52
|
-
a = page_class_name.underscore.split('/')
|
53
|
-
page_path = a[1..-1].join('/')
|
54
|
-
require "#{MotoApp::DIR}/#{page_path}"
|
55
|
-
@pages[page_class_name] = page_class_name.constantize.new(self)
|
56
|
-
end
|
57
|
-
@pages[page_class_name]
|
58
|
-
end
|
59
51
|
|
60
52
|
def register_grid_driver
|
61
53
|
grid_config = config[:grid]
|
data/lib/page.rb
CHANGED
@@ -1,23 +1,14 @@
|
|
1
1
|
module Moto
|
2
|
-
|
3
2
|
class Page
|
4
3
|
|
5
4
|
include Moto::TestLogging
|
6
|
-
include Moto::ForwardContextMethods
|
7
5
|
|
8
6
|
ignore_logging :const
|
9
7
|
ignore_logging :session
|
10
8
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
def initialize(website)
|
15
|
-
@website = website
|
16
|
-
@context = @website.context
|
17
|
-
end
|
18
|
-
|
19
|
-
def page(p)
|
20
|
-
@context.client(@website.class.name.split('::').pop).page(p)
|
9
|
+
# Returns Capybara's session by means of on-the-fly client&session creation.
|
10
|
+
def session
|
11
|
+
Thread.current['clients_manager'].client('Website').session
|
21
12
|
end
|
22
13
|
|
23
14
|
def raise_unless_loaded
|
@@ -91,25 +91,18 @@ module Moto
|
|
91
91
|
# Generates test instances, based on fully defined class file
|
92
92
|
# @return [Moto::Test::Base]
|
93
93
|
def generate_for_full_class_code(test_path_absolute)
|
94
|
-
|
95
|
-
begin
|
96
|
-
require test_path_absolute
|
97
|
-
rescue Exception
|
98
|
-
# will catch an error with non existent base class in test and stop it from breaking whole batch of tests
|
99
|
-
# no need to handle it here, next begin/rescue clause in this function will finish the job
|
100
|
-
end
|
101
|
-
|
102
94
|
# Checking if it's possible to create test based on provided path. In case something is wrong with
|
103
95
|
# modules structure in class itself Moto::Test::Base will be instantized with raise injected into its run()
|
104
96
|
# so we can have proper reporting and summary even if the test doesn't execute.
|
105
97
|
begin
|
98
|
+
require test_path_absolute
|
106
99
|
class_name = test_path_absolute.gsub("#{MotoApp::DIR}/", 'moto_app/').camelize.chomp('.rb').constantize
|
107
100
|
test_object = class_name.new
|
108
|
-
rescue NameError
|
101
|
+
rescue NameError => e
|
109
102
|
class_name = Moto::Test::Base
|
110
103
|
test_object = class_name.new
|
111
104
|
|
112
|
-
error_message = "ERROR: Invalid
|
105
|
+
error_message = "ERROR: Invalid test: #{test_path_absolute.gsub("#{MotoApp::DIR}/", 'moto_app/').camelize.chomp('.rb')}.\nMESSAGE: #{e}"
|
113
106
|
inject_error_to_test(test_object, error_message)
|
114
107
|
end
|
115
108
|
|
data/lib/runner/test_runner.rb
CHANGED
@@ -5,15 +5,11 @@ module Moto
|
|
5
5
|
module Runner
|
6
6
|
class TestRunner
|
7
7
|
|
8
|
-
attr_reader :logger
|
9
8
|
attr_reader :test_reporter
|
10
9
|
|
11
10
|
def initialize(test_paths_absolute, test_reporter)
|
12
11
|
@test_paths_absolute = test_paths_absolute
|
13
12
|
@test_reporter = test_reporter
|
14
|
-
|
15
|
-
# TODO: initialize logger from config (yml or just ruby code)
|
16
|
-
@logger = Logger.new(File.open("#{MotoApp::DIR}/moto.log", File::WRONLY | File::APPEND | File::CREAT))
|
17
13
|
end
|
18
14
|
|
19
15
|
def run
|
@@ -30,10 +26,15 @@ module Moto
|
|
30
26
|
# Create as many threads as we're allowed by the config file.
|
31
27
|
# test_provider.get_test - will invoke Queue.pop thus putting the thread to sleep
|
32
28
|
# once there is no more work.
|
29
|
+
|
30
|
+
Thread.abort_on_exception = true
|
31
|
+
|
33
32
|
(1..threads_max).each do |index|
|
34
33
|
Thread.new do
|
35
34
|
Thread.current[:id] = index
|
36
35
|
loop do
|
36
|
+
Thread.current['clients_manager'] = Moto::Lib::Clients::ClientsManager.new
|
37
|
+
|
37
38
|
tc = ThreadContext.new(test_provider.get_test, @test_reporter)
|
38
39
|
tc.run
|
39
40
|
end
|
@@ -1,103 +1,58 @@
|
|
1
1
|
require 'erb'
|
2
2
|
require 'fileutils'
|
3
3
|
require_relative '../../lib/config'
|
4
|
+
require_relative '../../lib/clients/clients_manager'
|
4
5
|
|
5
6
|
module Moto
|
6
7
|
module Runner
|
7
8
|
class ThreadContext
|
8
9
|
|
9
|
-
# all resources specific for single thread will be initialized here. E.g. browser session
|
10
|
-
attr_reader :logger
|
11
10
|
attr_reader :test
|
12
11
|
|
13
12
|
def initialize(test, test_reporter)
|
14
13
|
@test = test
|
15
|
-
@clients = {}
|
16
|
-
@test.context = self
|
17
14
|
@test_reporter = test_reporter
|
18
15
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
def client(name)
|
24
|
-
return @clients[name] if @clients.key? name
|
25
|
-
|
26
|
-
name_app = 'MotoApp::Lib::Clients::' + name
|
27
|
-
name_moto = 'Moto::Clients::' + name
|
28
|
-
|
29
|
-
c = try_client(name_app, "#{MotoApp::DIR}/")
|
30
|
-
unless c.nil?
|
31
|
-
@clients[name] = c
|
32
|
-
return c
|
16
|
+
log_directory = File.dirname(@test.log_path)
|
17
|
+
if !File.directory?(log_directory)
|
18
|
+
FileUtils.mkdir_p(log_directory)
|
33
19
|
end
|
34
20
|
|
35
|
-
|
36
|
-
|
37
|
-
@clients[name] = c
|
38
|
-
return c
|
39
|
-
end
|
40
|
-
raise "Could not find client class for name #{name}"
|
21
|
+
Thread.current['logger'] = Logger.new(File.open(@test.log_path, File::WRONLY | File::TRUNC | File::CREAT))
|
22
|
+
Thread.current['logger'].level = config[:test_log_level] || Logger::DEBUG
|
41
23
|
end
|
42
24
|
|
43
|
-
def try_client(name, dir)
|
44
|
-
begin
|
45
|
-
a = name.underscore.split('/')
|
46
|
-
client_path = a[1..-1].join('/')
|
47
|
-
require "#{dir}/#{client_path}"
|
48
|
-
client_const = name.constantize
|
49
|
-
instance = client_const.new(self)
|
50
|
-
instance.init
|
51
|
-
instance.start_run
|
52
|
-
instance.start_test(@test)
|
53
|
-
return instance
|
54
|
-
rescue Exception => e
|
55
|
-
# puts e
|
56
|
-
# puts e.backtrace
|
57
|
-
return nil
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
#
|
62
25
|
def run
|
63
26
|
max_attempts = config[:test_attempt_max] || 1
|
64
27
|
sleep_time = config[:test_attempt_sleep] || 0
|
65
28
|
|
66
|
-
log_directory = File.dirname(@test.log_path)
|
67
|
-
if !File.directory?(log_directory)
|
68
|
-
FileUtils.mkdir_p(log_directory)
|
69
|
-
end
|
70
|
-
|
71
|
-
@logger = Logger.new(File.open(@test.log_path, File::WRONLY | File::TRUNC | File::CREAT))
|
72
|
-
@logger.level = config[:test_log_level] || Logger::DEBUG
|
73
|
-
|
74
29
|
# Reporting: start_test
|
75
30
|
@test_reporter.report_start_test(@test.status)
|
76
31
|
|
77
32
|
(1..max_attempts).each do |attempt|
|
78
33
|
|
79
|
-
|
34
|
+
Thread.current['clients_manager'].clients.each_value { |c| c.start_test(@test) }
|
80
35
|
@test.before
|
81
|
-
|
36
|
+
Thread.current['logger'].info("Start: #{@test.name} attempt #{attempt}/#{max_attempts}")
|
82
37
|
|
83
38
|
begin
|
84
39
|
@test.run_test
|
85
40
|
rescue Exceptions::TestForcedPassed, Exceptions::TestForcedFailure, Exceptions::TestSkipped => e
|
86
|
-
|
41
|
+
Thread.current['logger'].info(e.message)
|
87
42
|
rescue Exception => e
|
88
|
-
|
89
|
-
|
90
|
-
|
43
|
+
Thread.current['logger'].error("#{e.class.name}: #{e.message}")
|
44
|
+
Thread.current['logger'].error(e.backtrace.join("\n"))
|
45
|
+
Thread.current['clients_manager'].clients.each_value { |c| c.handle_test_exception(@test, e) }
|
91
46
|
end
|
92
47
|
|
93
48
|
@test.after
|
94
|
-
|
49
|
+
Thread.current['clients_manager'].clients.each_value { |c| c.end_test(@test) }
|
95
50
|
|
96
|
-
|
51
|
+
Thread.current['logger'].info("Result: #{@test.status.results.last.code}")
|
97
52
|
|
98
|
-
# test should have another attempt
|
99
|
-
|
100
|
-
|
53
|
+
# test should have another attempt in case of an error / failure / none at all
|
54
|
+
if (@test.status.results.last.code == Moto::Test::Result::ERROR && !Moto::Lib::Config.moto[:test_reattempt_on_error]) ||
|
55
|
+
(@test.status.results.last.code == Moto::Test::Result::FAILURE && !Moto::Lib::Config.moto[:test_reattempt_on_fail] )
|
101
56
|
break
|
102
57
|
end
|
103
58
|
|
@@ -109,12 +64,12 @@ module Moto
|
|
109
64
|
end # Make another attempt
|
110
65
|
|
111
66
|
# Close and flush stream to file
|
112
|
-
|
67
|
+
Thread.current['logger'].close
|
113
68
|
|
114
69
|
# Reporting: end_test
|
115
70
|
@test_reporter.report_end_test(@test.status)
|
116
71
|
|
117
|
-
|
72
|
+
Thread.current['clients_manager'].clients.each_value { |c| c.end_run }
|
118
73
|
|
119
74
|
end
|
120
75
|
|
data/lib/runner_logging.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
module Moto
|
2
2
|
module RunnerLogging
|
3
3
|
|
4
|
-
|
5
4
|
# TODO: merge it somehow with TestLogging. Parametrize logger object?
|
6
5
|
def self.included(cls)
|
7
6
|
def cls.method_added(name)
|
@@ -14,9 +13,9 @@ module Moto
|
|
14
13
|
original_method = "original_#{name}"
|
15
14
|
alias_method original_method, name
|
16
15
|
define_method(name) do |*args|
|
17
|
-
|
16
|
+
Thread.current['logger'].debug("#{self.class.name}::#{__callee__} ENTER >>> #{args}") unless excluded_methods.include? name
|
18
17
|
result = send original_method, *args
|
19
|
-
|
18
|
+
Thread.current['logger'].debug("#{self.class.name}::#{__callee__} LEAVE <<< #{result} ") unless excluded_methods.include? name
|
20
19
|
result
|
21
20
|
end
|
22
21
|
@added = false
|
data/lib/test/base.rb
CHANGED
@@ -4,10 +4,7 @@ module Moto
|
|
4
4
|
module Test
|
5
5
|
class Base
|
6
6
|
|
7
|
-
include Moto::ForwardContextMethods
|
8
|
-
|
9
7
|
attr_reader :name
|
10
|
-
attr_writer :context
|
11
8
|
attr_reader :env
|
12
9
|
attr_reader :params
|
13
10
|
attr_accessor :static_path
|
@@ -156,7 +153,7 @@ module Moto
|
|
156
153
|
end
|
157
154
|
|
158
155
|
status.log_failure("ASSERTION FAILED in line #{line_number}: #{message}")
|
159
|
-
logger.error(message)
|
156
|
+
Thread.current['logger'].error(message)
|
160
157
|
end
|
161
158
|
end
|
162
159
|
|
@@ -167,6 +164,14 @@ module Moto
|
|
167
164
|
Moto::Lib::Config.environment_const(key)
|
168
165
|
end
|
169
166
|
|
167
|
+
def client(name)
|
168
|
+
Thread.current['clients_manager'].client(name)
|
169
|
+
end
|
170
|
+
|
171
|
+
def session
|
172
|
+
client('Website').session
|
173
|
+
end
|
174
|
+
|
170
175
|
end
|
171
176
|
end
|
172
177
|
end
|
data/lib/test_logging.rb
CHANGED
@@ -36,10 +36,10 @@ module Moto
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
39
|
-
|
39
|
+
Thread.current['logger'].debug("ENTER >>> #{self.class.name}::#{__callee__}(#{args})") unless skip_logging
|
40
40
|
result = send original_method, *args
|
41
41
|
# Below is the hack to properly escape binary data (if any manages to make it to logs)
|
42
|
-
|
42
|
+
Thread.current['logger'].debug("LEAVE <<< #{self.class.name}::#{__callee__} => #{[result].to_s[1..-2]}") unless skip_logging
|
43
43
|
result
|
44
44
|
end
|
45
45
|
@added = false
|
data/lib/version.rb
CHANGED
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
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bartek Wilczek
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2016-05-
|
14
|
+
date: 2016-05-26 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: activesupport
|
@@ -87,6 +87,7 @@ files:
|
|
87
87
|
- lib/app_generator.rb
|
88
88
|
- lib/cli.rb
|
89
89
|
- lib/clients/base.rb
|
90
|
+
- lib/clients/clients_manager.rb
|
90
91
|
- lib/clients/website.rb
|
91
92
|
- lib/config.rb
|
92
93
|
- lib/empty_listener.rb
|
@@ -94,7 +95,6 @@ files:
|
|
94
95
|
- lib/exceptions/test_forced_failure.rb
|
95
96
|
- lib/exceptions/test_forced_passed.rb
|
96
97
|
- lib/exceptions/test_skipped.rb
|
97
|
-
- lib/forward_context_methods.rb
|
98
98
|
- lib/initializer.rb
|
99
99
|
- lib/page.rb
|
100
100
|
- lib/parser.rb
|
@@ -1,19 +0,0 @@
|
|
1
|
-
module Moto
|
2
|
-
module ForwardContextMethods
|
3
|
-
# Access client object instance for given class name.
|
4
|
-
def client(name)
|
5
|
-
@context.client(name)
|
6
|
-
end
|
7
|
-
|
8
|
-
# Write message to test execution log file. See Ruby Logger class for details.
|
9
|
-
def logger
|
10
|
-
@context.logger
|
11
|
-
end
|
12
|
-
|
13
|
-
# Access currently executed test
|
14
|
-
def test
|
15
|
-
@context.test
|
16
|
-
end
|
17
|
-
|
18
|
-
end
|
19
|
-
end
|