moto 0.8.8 → 0.9.1

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: 07c4d34bc9210a0b733b4f611128acaff34fd1a3
4
- data.tar.gz: ab4e1346e86d2844fa5bafd3fe0a6518c939d8d6
3
+ metadata.gz: 4207f74793c80ef13675cb7eb98f8125e7f251ab
4
+ data.tar.gz: f5be5e7a42a02407a03db6a2622d9ffc28af7635
5
5
  SHA512:
6
- metadata.gz: 5026888d22631ff1ae2d42c9347ceae346f0bbbb45870fd7d57595c062697697ea349c421c1b4f67fb079dfe24fed67f9ee45b34dbd78ce30a1bcc466dd10376
7
- data.tar.gz: 834a3b7c45f9c05c84f4e167a26da28a04e377edd0cbbc1a19c3bf6be2f8ab4294a7013ffb97dfc00dba4d46b92eb4ab894d96afb560dbe52dd99f58b57f8a76
6
+ metadata.gz: 925d4a8275fbe9a7c865282c3678161771cd647ae4d770d339984b14083e24885b2a66da7abad143a7d87e34777b925083e137cb5e1a4d0dc05b5eb4652277bf
7
+ data.tar.gz: 2cd6b32278b60a09f4dbcc65ad5f58d5e8da892e0faa1cd2b4e6082d038cf71f10f8338051bd58975cda7ca96050367ea7bcffd2c17e5a340e9fa914d5c05a2c
data/lib/cli.rb CHANGED
@@ -18,6 +18,7 @@ require_relative './runner/test_runner'
18
18
  require_relative './runner/thread_context'
19
19
  require_relative './runner/test_generator'
20
20
  require_relative './test/base'
21
+ require_relative './test/metadata'
21
22
  require_relative './version'
22
23
  require_relative './reporting/listeners/base'
23
24
  require_relative './reporting/listeners/console'
@@ -35,14 +36,16 @@ module Moto
35
36
  class Cli
36
37
  def self.run(argv)
37
38
 
38
- test_paths_absolute = []
39
+ tests_metadata = []
39
40
  directories = argv[:tests]
40
41
  tags = argv[:tags]
41
42
  filters = argv[:filters]
42
43
 
43
44
  if directories
44
45
  directories.each do |directory|
45
- test_paths_absolute += Dir.glob("#{MotoApp::DIR}/tests/#{directory}/**/*.rb")
46
+ Dir.glob("#{MotoApp::DIR}/tests/#{directory}/**/*.rb").each do |test_path|
47
+ tests_metadata << Moto::Test::Metadata.new(test_path)
48
+ end
46
49
  end
47
50
  end
48
51
 
@@ -50,51 +53,40 @@ module Moto
50
53
  tests_total = Dir.glob("#{MotoApp::DIR}/tests/**/*.rb")
51
54
  tests_total.each do |test_path|
52
55
 
53
- test_body = File.read(test_path)
54
- matches = test_body.match(/^#(\s*)MOTO_TAGS:(.*?)$/)
55
-
56
- if matches
57
- test_tags = matches.to_a[2].gsub(/\s*/, '').split(',')
58
- test_paths_absolute << test_path unless (tags & test_tags).empty?
59
- end
56
+ metadata = Moto::Test::Metadata.new(test_path)
57
+ tests_metadata << metadata unless (tags & metadata.tags).empty?
60
58
 
61
59
  end
62
60
  end
63
61
 
64
62
  # Make sure there are no repetitions in gathered set
65
- test_paths_absolute.uniq!
63
+ tests_metadata.uniq! { |metadata| metadata.test_path }
66
64
 
67
65
  # Tests to be removed due to filtering will be gathered in this array
68
66
  # [].delete(item) cannot be used since it interferes with [].each
69
- filtered_test_paths = []
67
+ unfit_metadata = []
70
68
 
71
69
  # Filter tests by provied tags
70
+ # - test must contain ALL tags specified with -f param
71
+ # - test may contain other tags
72
72
  if filters
73
- test_paths_absolute.each do |test_path|
74
- test_body = File.read(test_path)
75
-
76
- matches = test_body.match(/^#(\s*)MOTO_TAGS:(.*?)$/)
77
-
78
- if matches
79
-
80
- test_tags = matches.to_a[2].gsub(/\s*/, '').split(',')
81
- if (filters & test_tags).empty?
82
- # Test doesn't contain any tags to be filtered upon
83
- filtered_test_paths << test_path
84
- end
85
-
86
- else
87
- # Test has no tags at all
88
- filtered_test_paths << test_path
73
+ tests_metadata.each do |metadata|
74
+
75
+ # If test has no tags at all and filters are set it should be automatically removed
76
+ if metadata.tags.empty?
77
+ unfit_metadata << metadata
78
+ # Otherwise check provided tags and filters for compatibility
79
+ elsif (metadata.tags & filters).length != filters.length
80
+ unfit_metadata << metadata
89
81
  end
90
82
 
91
83
  end
92
84
  end
93
85
 
94
- test_paths_absolute -= filtered_test_paths
86
+ tests_metadata -= unfit_metadata
95
87
 
96
88
  #TODO Display criteria used
97
- if test_paths_absolute.empty?
89
+ if tests_metadata.empty?
98
90
  puts 'No tests found for given arguments.'
99
91
  Kernel.exit(-1)
100
92
  end
@@ -107,9 +99,14 @@ module Moto
107
99
  initializer.init
108
100
  end
109
101
 
110
- test_reporter = Moto::Reporting::TestReporter.new(argv[:listeners], argv[:name])
102
+ run_params = {}
103
+ run_params[:run_name] = argv[:run_name]
104
+ run_params[:suite_name] = argv[:suite_name]
105
+ run_params[:assignee] = argv[:assignee]
106
+
107
+ test_reporter = Moto::Reporting::TestReporter.new(argv[:listeners], run_params)
111
108
 
112
- runner = Moto::Runner::TestRunner.new(test_paths_absolute, test_reporter, argv[:stop_on])
109
+ runner = Moto::Runner::TestRunner.new(tests_metadata, test_reporter, argv[:stop_on])
113
110
  runner.run
114
111
  end
115
112
 
@@ -34,9 +34,11 @@ module Moto
34
34
 
35
35
  # Default options
36
36
  options = {}
37
- options[:listeners] = []
38
- options[:name] = ''
39
- options[:stop_on] = {error: false, fail: false, skip: false}
37
+ options[:listeners] = []
38
+ options[:run_name] = nil
39
+ options[:suite_name] = nil
40
+ options[:assignee] = nil
41
+ options[:stop_on] = {error: false, fail: false, skip: false}
40
42
 
41
43
  # Parse arguments
42
44
  OptionParser.new do |opts|
@@ -45,17 +47,20 @@ module Moto
45
47
  opts.on('-f', '--filters Filters', Array) { |v| options[:filters] = v }
46
48
  opts.on('-l', '--listeners Listeners', Array) { |v| options[:listeners] = v }
47
49
  opts.on('-e', '--environment Environment') { |v| options[:environment] = v }
48
- opts.on('-n', '--name Name') { |v| options[:name] = v }
50
+ opts.on('-r', '--run RunName') { |v| options[:run_name] = v }
51
+ opts.on('-s', '--suite SuiteName') { |v| options[:suite_name] = v }
52
+ opts.on('-a', '--assignee Assignee') { |v| options[:assignee] = v }
49
53
  opts.on('-c', '--config Config') { |v| options[:config_name] = v }
50
54
  opts.on('--stop-on-error') { options[:stop_on][:error] = true }
51
55
  opts.on('--stop-on-fail') { options[:stop_on][:fail] = true }
52
56
  opts.on('--stop-on-skip') { options[:stop_on][:skip] = true }
53
57
  end.parse!
54
58
 
55
- if options[:name].empty?
56
- options[:name] = evaluate_name(options[:tags], options[:tests], options[:filters])
59
+ if options[:run_name].nil?
60
+ options[:run_name] = evaluate_name(options[:tags], options[:tests], options[:filters])
57
61
  end
58
62
 
63
+
59
64
  if options[:environment]
60
65
  Moto::Lib::Config.environment = options[:environment]
61
66
  Moto::Lib::Config.load_configuration(options[:config_name] ? options[:config_name] : 'moto')
@@ -111,25 +116,36 @@ module Moto
111
116
  Moto (#{Moto::VERSION}) CLI Help:
112
117
  moto --version Display current version
113
118
 
114
- moto run:
119
+ MOTO RUN:
115
120
  -t, --tests Tests to be executed.
116
121
  -g, --tags Tags of tests to be executed.
117
122
  Use # MOTO_TAGS: TAGNAME in test to assign tag.
118
123
  -f, --filters Tags that filter tests passed via -t parameter.
119
- Only tests in appropriate directory, having appropriate tag will be executed.
120
- Use # MOTO_TAGS: TAGNAME in test to assign tag.
121
- -l, --listeners Reporters to be used.
122
- Defaults are Moto::Reporting::Listeners::ConsoleDots, Moto::Reporting::Listeners::JunitXml
123
- One reporter that is always used: Moto::Reporting::Listeners::KernelCode
124
+ Only tests in appropriate directory, having all of the specified tags will be executed.
125
+ Use # MOTO_TAGS: TAGNAME1 in test to assign tag.
126
+
127
+
124
128
  -e, --environment Mandatory environment. Environment constants and tests parametrized in certain way depend on this.
125
129
  -c, --config Name of the config, without extension, to be loaded from MotoApp/config/CONFIG_NAME.rb
126
130
  Default: moto (which loads: MotoApp/config/moto.rb)
131
+
132
+
133
+ -l, --listeners Reporters to be used.
134
+ Defaults are Moto::Reporting::Listeners::ConsoleDots, Moto::Reporting::Listeners::JunitXml
135
+ One reporter that is always used: Moto::Reporting::Listeners::KernelCode
136
+ -s, --suitename Name of the test suite to which should aggregate the results of current test run.
137
+ Required when specifying MotoWebUI as one of the listeners.
138
+ -r, --runname Name of the test run to which everything will be reported when using MotoWebUI.
139
+ Default: Value of -g or -t depending on which one was specified.
140
+ -a, --assignee ID of a person responsible for current test run.
141
+ Can have a default value set in config/webui section.
142
+
127
143
  --stop-on-error Moto will stop test execution when an error is encountered in test results
128
144
  --stop-on-fail Moto will stop test execution when a failure is encountered in test results
129
145
  --stop-on-skip Moto will stop test execution when a skip is encountered in test results
130
146
 
131
147
 
132
- moto generate:
148
+ MOTO GENERATE:
133
149
  -t, --test Path and name of the test to be created.
134
150
  Examples:
135
151
  -ttest_name will create MotoApp/tests/test_name/test_name.rb
@@ -5,11 +5,11 @@ module Moto
5
5
  # Base class for listeners that report results of testing 'outside' of the application.
6
6
  class Base
7
7
 
8
- attr_reader :custom_run_name
8
+ attr_reader :run_params
9
9
 
10
- # @param [String] custom_run_name Optional run name to be passed to listeners
11
- def initialize(custom_run_name = '')
12
- @custom_run_name = custom_run_name
10
+ # @param [String] run_params Described in detail in [Moto::Reporting::TestReporter]
11
+ def initialize(run_params)
12
+ @run_params = run_params
13
13
  end
14
14
 
15
15
  # Invoked when whole batch of tests starts
@@ -25,7 +25,7 @@ module Moto
25
25
 
26
26
  # Abstract
27
27
  # Invoked when a single test is started
28
- def start_test(test_status)
28
+ def start_test(test_status, test_metadata)
29
29
  # Abstract
30
30
  end
31
31
 
@@ -17,10 +17,6 @@ module Moto
17
17
  puts " Skipped: #{run_status.tests_skipped.length}"
18
18
  end
19
19
 
20
- def start_test(test_status)
21
- # puts test_status.name
22
- end
23
-
24
20
  def end_test(test_status)
25
21
  puts "\n#{test_status.name}\n\t#{test_status.results.last.message}"
26
22
  end
@@ -13,7 +13,7 @@ module Moto
13
13
  errors: run_status.tests_error.length,
14
14
  failures: run_status.tests_failed.length,
15
15
  skipped: run_status.tests_skipped.length,
16
- name: custom_run_name,
16
+ name: run_params[:run_name],
17
17
  tests: run_status.tests_all.length,
18
18
  time: run_status.duration,
19
19
  timestamp: Time.at(run_status.time_start)
@@ -1,103 +1,177 @@
1
1
  require 'rest-client'
2
2
  require 'sys/uname'
3
+ require 'uri'
3
4
 
4
5
  module Moto
5
6
  module Reporting
6
7
  module Listeners
7
8
  class Webui < Base
8
9
 
9
- REST_MAX_TRIES = 3
10
+ REST_MAX_TRIES = 1
10
11
  REST_TIMEOUT = 15
11
12
 
12
- def start_run
13
+ def initialize(run_params)
14
+ super
15
+
16
+ if run_params[:suite_name].nil?
17
+ raise 'ERROR: Please specify suite name (-s SUITE_NAME) when using MotoWebUI as one of the listeners.'
18
+ end
19
+
20
+ if run_params[:assignee]
21
+ @assignee = run_params[:assignee]
22
+ else
23
+ @assignee = config[:default_assignee]
24
+ end
13
25
 
14
- @url = config[:url]
26
+ @tests = {}
27
+ @url = "#{config[:url]}/api"
15
28
  @send_log_on_pass = config[:send_log_on_pass]
29
+ end
30
+
31
+
32
+ def start_run
33
+
34
+ # Create Suite, if it did exist already nothing new will be created and existing data will be sent in the response
35
+ url_suites = "#{@url}/suites"
36
+ suite_data = {name: run_params[:suite_name]}.to_json
16
37
 
17
- data = {
18
- name: custom_run_name,
19
- result: :running,
20
- cnt_all: nil,
21
- cnt_passed: nil,
22
- cnt_failure: nil,
23
- cnt_error: nil,
24
- cnt_skipped: nil,
25
- user: Sys::Uname.sysname.downcase.include?('windows') ? ENV['USERNAME'] : ENV['LOGNAME'],
26
- host: Sys::Uname.nodename,
27
- pid: Process.pid
38
+ response = try {
39
+ RestClient::Request.execute(method: :post,
40
+ url: URI.escape(url_suites),
41
+ payload: suite_data,
42
+ timeout: REST_TIMEOUT,
43
+ headers: {content_type: :json, accept: :json})
28
44
  }
45
+ suite = JSON.parse(response, symbolize_names: true)
29
46
 
30
- result = try {
31
- RestClient::Request.execute(method: :post, url: "#{@url}/api/runs", payload: data.to_json, timeout: REST_TIMEOUT, headers: {content_type: :json, accept: :json})
47
+ # Store ID of current Suite
48
+ @suite_id = suite[:id]
49
+
50
+ # Prepare data for new TestRun
51
+ url_runs = "#{@url}/suites/#{@suite_id}/runs"
52
+ run_data = {
53
+ name: run_params[:run_name],
54
+ start_time: Time.now
32
55
  }
33
56
 
34
- @run = JSON.parse(result)
35
- @tests = {}
57
+ if @assignee
58
+ run_data[:tester_id] = @assignee
59
+ end
60
+
61
+ run_data = run_data.to_json
62
+
63
+ # Create new TestRun based on prepared data
64
+ response = try {
65
+ RestClient::Request.execute(method: :post,
66
+ url: URI.escape(url_runs),
67
+ payload: run_data,
68
+ timeout: REST_TIMEOUT,
69
+ headers: {content_type: :json, accept: :json})
70
+ }
71
+
72
+ @run = JSON.parse(response, symbolize_names: true)
36
73
  end
37
74
 
75
+
38
76
  def end_run(run_status)
39
- # PUT http://sandbox.dev:3000/api/runs/1
40
- data = {
41
- result: run_status.result,
42
- cnt_all: run_status.tests_all.length,
43
- cnt_passed: run_status.tests_passed.length,
44
- cnt_failure: run_status.tests_failed.length,
45
- cnt_error: run_status.tests_error.length,
46
- cnt_skipped: run_status.tests_skipped.length,
47
- duration: run_status.duration
48
- }
49
77
 
50
- result = try {
51
- RestClient::Request.execute(method: :put, url: "#{@url}/api/runs/#{@run['id']}", payload: data.to_json, timeout: REST_TIMEOUT, headers: {content_type: :json, accept: :json})
78
+ url_run = "#{@url}/suites/#{@suite_id}/runs/#{@run[:id]}"
79
+ run_data = {
80
+ duration: (Time.now.to_f - run_status.time_start).to_i
81
+ }.to_json
82
+
83
+ response = try {
84
+ RestClient::Request.execute(method: :put,
85
+ url: URI.escape(url_run),
86
+ payload: run_data,
87
+ timeout: REST_TIMEOUT,
88
+ headers: {content_type: :json, accept: :json})
52
89
  }
53
- @run = JSON.parse(result)
90
+ @run = JSON.parse(response, symbolize_names: true)
54
91
  end
55
92
 
56
- def start_test(test_status)
57
- # POST http://sandbox.dev:3000/api/tests/create
58
- data = {
59
- name: test_status.name,
60
- class_name: test_status.test_class_name,
61
- log: nil,
62
- run_id: @run['id'],
63
- env: test_status.env,
64
- parameters: test_status.params.to_s,
65
- result: :running,
66
- error: nil,
67
- failures: nil
68
- }
69
93
 
70
- result = try {
71
- RestClient::Request.execute(method: :post, url: "#{@url}/api/tests", payload: data.to_json, timeout: REST_TIMEOUT, headers: {content_type: :json, accept: :json})
94
+ def start_test(test_status, test_metadata)
95
+
96
+ # Prepare data for new Test
97
+ url_tests = "#{@url}/suites/#{@suite_id}/runs/#{@run[:id]}/tests"
98
+ test_data = {
99
+ name: test_status.display_name, #test_status.test_class_name
100
+ start_time: Time.now
72
101
  }
73
- @tests[test_status.name] = JSON.parse(result)
74
- end
75
102
 
76
- def end_test(test_status)
103
+ if test_metadata.ticket_url
104
+ test_data[:ticket_url] = test_metadata.ticket_url
105
+ end
77
106
 
78
- # don't send the log if the test has passed and appropriate flag is set to false
79
- if test_status.results.last.code == Moto::Test::Result::PASSED && !@send_log_on_pass
80
- full_log = nil
81
- else
82
- full_log = File.read(test_status.log_path)
107
+ if test_metadata.tags
108
+ test_data[:tags] = test_metadata.tags.join(',')
83
109
  end
84
110
 
85
- data = {
86
- log: full_log,
87
- result: test_status.results.last.code,
88
- error: test_status.results.last.code == Moto::Test::Result::ERROR ? nil : test_status.results.last.message,
89
- failures: test_failures(test_status),
90
- duration: test_status.duration
111
+ test_data = test_data.to_json
112
+
113
+ # Create new Test based on prepared data
114
+ response = try {
115
+ RestClient::Request.execute(method: :post,
116
+ url: URI.escape(url_tests),
117
+ payload: test_data,
118
+ timeout: REST_TIMEOUT,
119
+ headers: {content_type: :json, accept: :json})
91
120
  }
92
121
 
93
- result = try {
94
- RestClient::Request.execute(method: :put, url: "#{@url}/api/tests/#{@tests[test_status.name]['id']}", payload: data.to_json, timeout: REST_TIMEOUT, headers: {content_type: :json, accept: :json})
122
+ test = JSON.parse(response, symbolize_names: true)
123
+
124
+ # Store Test in a hash with its name as key so later it can be accessed and server side ID can be retrieved
125
+ @tests[test[:name]] = test
126
+ end
127
+
128
+
129
+ def end_test(test_status)
130
+
131
+ url_test = "#{@url}/suites/#{@suite_id}/runs/#{@run[:id]}/tests/#{@tests[test_status.display_name][:id]}"
132
+ test_data = {
133
+ duration: (Time.now.to_f - test_status.time_start).to_i,
134
+ error_message: test_status.results.last.code == Moto::Test::Result::ERROR ? nil : test_status.results.last.message,
135
+ fail_message: test_failures(test_status),
136
+ result_id: webui_result_id(test_status.results.last.code),
137
+ }.to_json
138
+
139
+ # Create new Test based on prepared data
140
+ response = try {
141
+ RestClient::Request.execute(method: :put,
142
+ url: URI.escape(url_test),
143
+ payload: test_data,
144
+ timeout: REST_TIMEOUT,
145
+ headers: {content_type: :json, accept: :json})
95
146
  }
96
- @tests[test_status.name] = JSON.parse(result)
147
+
148
+ test = JSON.parse(response, symbolize_names: true)
149
+
150
+ @tests[test_status.name] = test
151
+
152
+ # Add Log to already existing Test
153
+ if (test_status.results.last.code == Moto::Test::Result::PASSED && @send_log_on_pass) || test_status.results.last.code != Moto::Test::Result::PASSED
154
+
155
+ url_log = "#{url_test}/logs"
156
+ log_data = { text: File.read(test_status.log_path) }.to_json
157
+
158
+ response = try {
159
+ RestClient::Request.execute(method: :post,
160
+ url: URI.escape(url_log),
161
+ payload: log_data,
162
+ timeout: REST_TIMEOUT,
163
+ headers: {content_type: :json, accept: :json})
164
+ }
165
+ response
166
+ end
97
167
  end
98
168
 
99
169
  # @return [String] string with messages of all failures in a test
100
170
  def test_failures(test_status)
171
+ if test_status.results.last.failures.empty?
172
+ return nil
173
+ end
174
+
101
175
  test_status.results.last.failures.join("\n\t")
102
176
  end
103
177
 
@@ -121,6 +195,22 @@ module Moto
121
195
  end
122
196
  private :config
123
197
 
198
+ def webui_result_id(code)
199
+ case code
200
+ when Moto::Test::Result::RUNNING
201
+ 1
202
+ when Moto::Test::Result::PASSED
203
+ 2
204
+ when Moto::Test::Result::FAILURE
205
+ 3
206
+ when Moto::Test::Result::ERROR
207
+ 4
208
+ when Moto::Test::Result::SKIPPED
209
+ 5
210
+ end
211
+ end
212
+ private :webui_result_id
213
+
124
214
  end
125
215
  end
126
216
  end
@@ -0,0 +1,127 @@
1
+ require 'rest-client'
2
+ require 'sys/uname'
3
+
4
+ module Moto
5
+ module Reporting
6
+ module Listeners
7
+ class WebuiDeprecated < Base
8
+
9
+ REST_MAX_TRIES = 3
10
+ REST_TIMEOUT = 15
11
+
12
+ def start_run
13
+
14
+ @url = config[:url]
15
+ @send_log_on_pass = config[:send_log_on_pass]
16
+
17
+ data = {
18
+ name: run_params[:name],
19
+ result: :running,
20
+ cnt_all: nil,
21
+ cnt_passed: nil,
22
+ cnt_failure: nil,
23
+ cnt_error: nil,
24
+ cnt_skipped: nil,
25
+ user: Sys::Uname.sysname.downcase.include?('windows') ? ENV['USERNAME'] : ENV['LOGNAME'],
26
+ host: Sys::Uname.nodename,
27
+ pid: Process.pid
28
+ }
29
+
30
+ result = try {
31
+ RestClient::Request.execute(method: :post, url: "#{@url}/api/runs", payload: data.to_json, timeout: REST_TIMEOUT, headers: {content_type: :json, accept: :json})
32
+ }
33
+
34
+ @run = JSON.parse(result)
35
+ @tests = {}
36
+ end
37
+
38
+ def end_run(run_status)
39
+ # PUT http://sandbox.dev:3000/api/runs/1
40
+ data = {
41
+ result: run_status.result,
42
+ cnt_all: run_status.tests_all.length,
43
+ cnt_passed: run_status.tests_passed.length,
44
+ cnt_failure: run_status.tests_failed.length,
45
+ cnt_error: run_status.tests_error.length,
46
+ cnt_skipped: run_status.tests_skipped.length,
47
+ duration: run_status.duration
48
+ }
49
+
50
+ result = try {
51
+ RestClient::Request.execute(method: :put, url: "#{@url}/api/runs/#{@run['id']}", payload: data.to_json, timeout: REST_TIMEOUT, headers: {content_type: :json, accept: :json})
52
+ }
53
+ @run = JSON.parse(result)
54
+ end
55
+
56
+ def start_test(test_status, test_metadata)
57
+ # POST http://sandbox.dev:3000/api/tests/create
58
+ data = {
59
+ name: test_status.name,
60
+ class_name: test_status.test_class_name,
61
+ log: nil,
62
+ run_id: @run['id'],
63
+ env: test_status.env,
64
+ parameters: test_status.params.to_s,
65
+ result: :running,
66
+ error: nil,
67
+ failures: nil
68
+ }
69
+
70
+ result = try {
71
+ RestClient::Request.execute(method: :post, url: "#{@url}/api/tests", payload: data.to_json, timeout: REST_TIMEOUT, headers: {content_type: :json, accept: :json})
72
+ }
73
+ @tests[test_status.name] = JSON.parse(result)
74
+ end
75
+
76
+ def end_test(test_status)
77
+
78
+ # don't send the log if the test has passed and appropriate flag is set to false
79
+ if test_status.results.last.code == Moto::Test::Result::PASSED && !@send_log_on_pass
80
+ full_log = nil
81
+ else
82
+ full_log = File.read(test_status.log_path)
83
+ end
84
+
85
+ data = {
86
+ log: full_log,
87
+ result: test_status.results.last.code,
88
+ error: test_status.results.last.code == Moto::Test::Result::ERROR ? nil : test_status.results.last.message,
89
+ failures: test_failures(test_status),
90
+ duration: test_status.duration
91
+ }
92
+
93
+ result = try {
94
+ RestClient::Request.execute(method: :put, url: "#{@url}/api/tests/#{@tests[test_status.name]['id']}", payload: data.to_json, timeout: REST_TIMEOUT, headers: {content_type: :json, accept: :json})
95
+ }
96
+ @tests[test_status.name] = JSON.parse(result)
97
+ end
98
+
99
+ # @return [String] string with messages of all failures in a test
100
+ def test_failures(test_status)
101
+ test_status.results.last.failures.join("\n\t")
102
+ end
103
+
104
+ # Tries to execute, without an error, block of code passed to the function.
105
+ # @param block Block of code to be executed up to MAX_REST_TRIES
106
+ def try(&block)
107
+
108
+ tries = REST_MAX_TRIES
109
+
110
+ begin
111
+ yield
112
+ rescue
113
+ tries -= 1
114
+ tries > 0 ? retry : raise
115
+ end
116
+ end
117
+
118
+ # @return [Hash] Hash with config for WebUI
119
+ def config
120
+ Moto::Lib::Config.moto[:test_reporter][:listeners][:webui]
121
+ end
122
+ private :config
123
+
124
+ end
125
+ end
126
+ end
127
+ end
@@ -11,8 +11,11 @@ module Moto
11
11
 
12
12
  # @param [Array] listeners An array of strings, which represent qualified names of classes (listeners) that will be instantiated.
13
13
  # empty array is passed then :default_listeners will be taken from config
14
- # @param [String] custom_run_name Optional, to be passed to listeners during creation
15
- def initialize(listeners, custom_run_name)
14
+ # @param[Hash] run_params Variables specified by the user when parametrizing current moto run
15
+ # suite_name: String Name of the test suite
16
+ # run_name: String Name of the test run, may be custom made or automatically generated
17
+ # assignee: ID of person responsible for test run
18
+ def initialize(listeners, run_params)
16
19
 
17
20
  if listeners.empty?
18
21
  config[:default_listeners].each do |listener_class_name|
@@ -25,7 +28,7 @@ module Moto
25
28
  end
26
29
 
27
30
  @listeners = []
28
- @custom_run_name = custom_run_name
31
+ @run_params = run_params
29
32
  listeners.each { |l| add_listener(l) }
30
33
  end
31
34
 
@@ -33,7 +36,7 @@ module Moto
33
36
  # All listeners on the list will have events reported to them.
34
37
  # @param [Moto::Listener::Base] listener class to be added
35
38
  def add_listener(listener)
36
- @listeners << listener.new(@custom_run_name)
39
+ @listeners << listener.new(@run_params)
37
40
  end
38
41
 
39
42
  # Reports start of the whole run (set of tests) to attached listeners
@@ -57,9 +60,10 @@ module Moto
57
60
 
58
61
  # Reports star of a test to all attached listeners
59
62
  # @param [Moto::Test::Status] test_status of test which's start is to be reported on
60
- def report_start_test(test_status)
63
+ # @param [Moto::Test::Metadata] test_metadata of test which's start is to be reported on
64
+ def report_start_test(test_status, test_metadata)
61
65
  @listeners.each do |l|
62
- l.start_test(test_status)
66
+ l.start_test(test_status, test_metadata)
63
67
  end
64
68
  end
65
69
 
@@ -15,11 +15,11 @@ module Moto
15
15
  # Example: A test with no config file will be returned as an array with single Moto::Test::Base in it.
16
16
  # Example: A test with a config file and 2 sets of parameters there will be returned as array with two elements.
17
17
  #
18
- # @param [String] test_path_absolute Path to the test that is about to be instantiated.
19
- # @return [Array] An array of [Moto::Test::Base] decendants
18
+ # @param [Moto::Test::Metadata] test_metadata Metadata that describes test to be instantiated
19
+ # @return [Array] An array of [Moto::Test::Base] descendants
20
20
  # each entry is a Test with set of parameters injected
21
- def get_test_with_variants(test_path_absolute)
22
- test_path_absolute ? variantize(test_path_absolute) : nil
21
+ def get_test_with_variants(test_metadata)
22
+ variantize(test_metadata)
23
23
  end
24
24
 
25
25
  # Converts test's path to an array of Moto::Base::Test instances that represent all test variants (params)
@@ -28,19 +28,18 @@ module Moto
28
28
  # Config files with ruby code will be evaluated thus if you use any classes in them
29
29
  # they must be required prior to that. That might be done in overloaded app's initializer.
30
30
  #
31
- # @param [String] test_path_absolute Path to the file with test
31
+ # @param [Moto::Test::Metadata] test_metadata Metadata that describes test to be instantiated, contains path to test
32
32
  # @return [Array] array of already initialized test's variants
33
- def variantize(test_path_absolute)
33
+ def variantize(test_metadata)
34
34
  variants = []
35
35
 
36
36
  # TODO CHANGED TEMPORARY
37
37
  #params_path = test_path_absolute.sub(/\.rb\z/, '')
38
- params_directory = File.dirname(test_path_absolute).to_s + '/params/**'
38
+ params_directory = File.dirname(test_metadata.test_path).to_s + '/params/**'
39
39
  param_files_paths = Dir.glob(params_directory)
40
40
  param_files_paths = [nil] if param_files_paths.empty?
41
41
 
42
- #TODO Fix
43
- param_files_paths.each_with_index do |params_path, params_index|
42
+ param_files_paths.each do |params_path|
44
43
 
45
44
  #TODO environment support
46
45
  # Filtering out param sets that are specific to certain envs
@@ -50,9 +49,9 @@ module Moto
50
49
  # end
51
50
 
52
51
  #TODO Name/logname/displayname
53
- test = generate(test_path_absolute)
54
- test.init(params_path, params_index, @internal_counter)
55
- test.log_path = "#{File.dirname(test_path_absolute).to_s}/logs/#{test.name.gsub(/[^0-9A-Za-z.\-]/, '_')}.log"
52
+ test = generate(test_metadata)
53
+ test.init(params_path)
54
+ test.log_path = "#{File.dirname(test_metadata.test_path).to_s}/logs/#{test.name.gsub(/[^0-9A-Za-z.\-]/, '_')}.log"
56
55
  @internal_counter += 1
57
56
 
58
57
  variants << test
@@ -64,24 +63,27 @@ module Moto
64
63
 
65
64
  # Generates test instances
66
65
  # @return [Moto::Test::Base]
67
- def generate(test_path_absolute)
66
+ def generate(test_metadata)
67
+
68
+ test_path = test_metadata.test_path
68
69
 
69
70
  # Checking if it's possible to create test based on provided path. In case something is wrong with
70
71
  # modules structure in class itself Moto::Test::Base will be instantized with raise injected into its run()
71
72
  # so we can have proper reporting and summary even if the test doesn't execute.
72
73
  begin
73
- require test_path_absolute
74
- class_name = test_path_absolute.gsub("#{MotoApp::DIR}/", 'moto_app/').camelize.chomp('.rb').constantize
74
+ require test_path
75
+ class_name = test_path.gsub("#{MotoApp::DIR}/", 'moto_app/').camelize.chomp('.rb').constantize
75
76
  test_object = class_name.new
76
77
  rescue NameError => e
77
78
  class_name = Moto::Test::Base
78
79
  test_object = class_name.new
79
80
 
80
- error_message = "ERROR: Invalid test: #{test_path_absolute.gsub("#{MotoApp::DIR}/", 'moto_app/').camelize.chomp('.rb')}.\nMESSAGE: #{e}"
81
+ error_message = "ERROR: Invalid test: #{test_path.gsub("#{MotoApp::DIR}/", 'moto_app/').camelize.chomp('.rb')}.\nMESSAGE: #{e}"
81
82
  inject_error_to_test(test_object, error_message)
82
83
  end
83
84
 
84
- test_object.static_path = test_path_absolute
85
+ test_object.static_path = test_path
86
+ test_object.metadata = test_metadata
85
87
  test_object
86
88
  end
87
89
  private :generate
@@ -6,13 +6,13 @@ module Moto
6
6
  # Thread safe provider of test instances
7
7
  class TestProvider
8
8
 
9
- # @param [Array] test_paths_absolute
10
- def initialize(test_paths_absolute)
9
+ # @param [Array] tests_metadata
10
+ def initialize(tests_metadata)
11
11
  super()
12
12
  @test_repeats = Moto::Lib::Config.moto[:test_runner][:test_repeats]
13
13
  @current_test_repeat = 1
14
14
  @queue = Queue.new
15
- @test_paths_absolute = test_paths_absolute
15
+ @tests_metadata = tests_metadata
16
16
  @test_generator = TestGenerator.new
17
17
  end
18
18
 
@@ -26,9 +26,10 @@ module Moto
26
26
  def create_tests
27
27
  if @queue.empty?
28
28
 
29
- test_variants = @test_generator.get_test_with_variants(get_test_path)
29
+ test_metadata = get_test_metadata
30
30
 
31
- if test_variants
31
+ if test_metadata
32
+ test_variants = @test_generator.get_test_with_variants(test_metadata)
32
33
  test_variants.each do |test|
33
34
  @queue.push(test)
34
35
  end
@@ -38,12 +39,12 @@ module Moto
38
39
  end
39
40
  private :create_tests
40
41
 
41
- # Returns path to the test while supporting the number of repeats specified by the user
42
- # return [String] Path to the test
43
- def get_test_path
42
+ # Returns metadata of the test while supporting the number of repeats specified by the user
43
+ # return [Moto::Test::Metadata]
44
+ def get_test_metadata
44
45
 
45
46
  if @current_test_repeat == 1
46
- @test_path = @test_paths_absolute.shift
47
+ @test_metadata = @tests_metadata.shift
47
48
  end
48
49
 
49
50
  if @current_test_repeat == @test_repeats
@@ -52,9 +53,9 @@ module Moto
52
53
  @current_test_repeat += 1
53
54
  end
54
55
 
55
- @test_path
56
+ @test_metadata
56
57
  end
57
- private :get_test_path
58
+ private :get_test_metadata
58
59
 
59
60
  # Number of threads waiting for a job
60
61
  def num_waiting
@@ -7,25 +7,25 @@ module Moto
7
7
 
8
8
  attr_reader :test_reporter
9
9
 
10
- # @param [Array] test_paths_absolute Absolute paths to files with tests
10
+ # @param [Array] tests_metadata Collection of [Moto::Test::Metadata] objects describing Tests
11
11
  # @param [Moto::Reporting::TestReporter] test_reporter Reporter of test/run statuses that communicates with external status listeners
12
12
  # @param [Hash] stop_conditions Describe when TestRunner should abnormally stop its execution
13
13
  # :error [Boolean]
14
14
  # :fail [Boolean]
15
15
  # :skip [Boolean]
16
- def initialize(test_paths_absolute, test_reporter, stop_conditions)
17
- @test_paths_absolute = test_paths_absolute
16
+ def initialize(tests_metadata, test_reporter, stop_conditions)
17
+ @tests_metadata = tests_metadata
18
18
  @test_reporter = test_reporter
19
19
  @stop_conditions = stop_conditions
20
20
  end
21
21
 
22
22
  def run
23
- test_provider = TestProvider.new(@test_paths_absolute)
23
+ test_provider = TestProvider.new(@tests_metadata)
24
24
  threads_max = Moto::Lib::Config.moto[:test_runner][:thread_count] || 1
25
25
 
26
26
  # remove log/screenshot files from previous execution
27
- @test_paths_absolute.each do |test_path|
28
- FileUtils.rm_rf("#{File.dirname(test_path)}/logs")
27
+ @tests_metadata.each do |metadata|
28
+ FileUtils.rm_rf("#{File.dirname(metadata.test_path)}/logs")
29
29
  end
30
30
 
31
31
  @test_reporter.report_start_run
@@ -26,7 +26,7 @@ module Moto
26
26
  sleep_time = config[:test_attempt_sleep] || 0
27
27
 
28
28
  # Reporting: start_test
29
- @test_reporter.report_start_test(@test.status)
29
+ @test_reporter.report_start_test(@test.status, @test.metadata)
30
30
 
31
31
  (1..max_attempts).each do |attempt|
32
32
 
@@ -10,6 +10,10 @@ module Moto
10
10
  attr_accessor :static_path
11
11
  attr_accessor :status
12
12
 
13
+ # Contains information specified by user in Test headers, marked with appropriate tags
14
+ # @return [Moto::Test::Metadata]
15
+ attr_accessor :metadata
16
+
13
17
  class << self
14
18
  attr_accessor :_path
15
19
  end
@@ -23,17 +27,19 @@ module Moto
23
27
  end
24
28
 
25
29
  # Initializes test to be executed with specified params and environment
26
- def init(params_path, params_index, global_index)
30
+ def init(params_path)
27
31
  @env = Moto::Lib::Config.environment
28
32
  @params = []
29
33
  @params_path = params_path
30
- #TODO Display name
34
+
31
35
  @name = self.class.to_s.demodulize
32
- @name += "_#{@params_path.split("/")[-1].chomp('.param')}" if @params_path
36
+ @name += "_#{@params_path.split('/')[-1].chomp('.param')}" if @params_path
37
+
33
38
  @status = Moto::Test::Status.new
34
39
  @status.name = @name
35
40
  @status.test_class_name = self.class.name
36
41
  @status.display_name = @status.test_class_name.split('::')[2..-2].join('::')
42
+ @status.display_name += "_#{@params_path.split('/')[-1].chomp('.param')}" if @params_path
37
43
  @status.env = Moto::Lib::Config.environment
38
44
  end
39
45
 
@@ -0,0 +1,77 @@
1
+ module Moto
2
+ module Test
3
+ # Provides tools for accessing metadata embedded in test files
4
+ class Metadata
5
+
6
+ # Absolute test path
7
+ attr_reader :test_path
8
+
9
+ # @param [String] test_path Absolute path to file with test
10
+ def initialize(test_path)
11
+ @test_path = test_path
12
+ end
13
+
14
+ # Text of the file with test
15
+ def text
16
+ if @text.nil?
17
+ @text = ''
18
+
19
+ File.foreach(@test_path) do |line|
20
+
21
+ # Read lines of file until class specification begins
22
+ if line.match(/^\s*(class|module)/)
23
+ break
24
+ end
25
+
26
+ @text += line
27
+ end
28
+
29
+ end
30
+
31
+ @text
32
+ end
33
+ private :text
34
+
35
+ # @return [Array] of [String] which represent contents of #MOTO_TAGS
36
+ def tags
37
+ if @tags.nil?
38
+ matches = text.match(/^#(\s*)MOTO_TAGS:(.*?)$/)
39
+
40
+ if matches
41
+ @tags = matches.to_a[2].gsub(/\s*/, '').split(',')
42
+ else
43
+ @tags = []
44
+ end
45
+ end
46
+
47
+ @tags
48
+ end
49
+
50
+ # @return [String] which represents contents of #TICKET_URL
51
+ def ticket_url
52
+ if @ticket_url.nil?
53
+ matches = text.match(/^#(\s*)TICKET_URL:(.*?)$/)
54
+
55
+ if matches
56
+ @ticket_url = matches.to_a[2].gsub(/\s*/, '')
57
+ else
58
+ @ticket_url = ''
59
+ end
60
+ end
61
+
62
+ @ticket_url
63
+ end
64
+
65
+ # Overriden eql? so various comparisons, array substractions etc. can be perfromed on
66
+ # Metadata objects with them being represented by test's location
67
+ def eql?(other)
68
+ if self.class == other.class
69
+ return self.test_path == other.test_path
70
+ end
71
+
72
+ false
73
+ end
74
+
75
+ end
76
+ end
77
+ end
@@ -1,3 +1,3 @@
1
1
  module Moto
2
- VERSION = '0.8.8'
2
+ VERSION = '0.9.1'
3
3
  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.8.8
4
+ version: 0.9.1
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-09-13 00:00:00.000000000 Z
14
+ date: 2016-11-17 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: activesupport
@@ -99,6 +99,7 @@ files:
99
99
  - lib/reporting/listeners/console_dots.rb
100
100
  - lib/reporting/listeners/junit_xml.rb
101
101
  - lib/reporting/listeners/webui.rb
102
+ - lib/reporting/listeners/webui_deprecated.rb
102
103
  - lib/reporting/run_status.rb
103
104
  - lib/reporting/test_reporter.rb
104
105
  - lib/runner/test_generator.rb
@@ -107,6 +108,7 @@ files:
107
108
  - lib/runner/thread_context.rb
108
109
  - lib/runner_logging.rb
109
110
  - lib/test/base.rb
111
+ - lib/test/metadata.rb
110
112
  - lib/test/result.rb
111
113
  - lib/test/status.rb
112
114
  - lib/test_logging.rb
@@ -131,7 +133,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
131
133
  version: '0'
132
134
  requirements: []
133
135
  rubyforge_project:
134
- rubygems_version: 2.4.5.1
136
+ rubygems_version: 2.6.7
135
137
  signing_key:
136
138
  specification_version: 4
137
139
  summary: Moto - yet another web testing framework