bugsnag-maze-runner 7.22.1 → 7.24.0

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
  SHA256:
3
- metadata.gz: 25918b6a96c58be96023fec6acae1dca6463db11c722fe9ef612f1faa1137f48
4
- data.tar.gz: 68af4db3ee29db253541685361060a0b969371752a22c7c7e7e78bfd51c7dc31
3
+ metadata.gz: f07c9dfdaab73bc584bf02c73a553e949851c700d2d1b54411b1886e936f9b3d
4
+ data.tar.gz: 8894d62125f81610660c6df39a05ad73c03b21cb12d8bff69888e9bc1bec3d1e
5
5
  SHA512:
6
- metadata.gz: 5734aa0594c7286ebe127cb4261b0d029f7241a88c090eea5c52a059459e3f7deed26a3fc14d754155ae8d1000b5001d65b3b903e2d98822ccc6cf7a824573e4
7
- data.tar.gz: 91d4e5a297ec91c2f71bc2f023632eab9c328fe70bcd61bf511f8eab688b7e85c4cba8a9d5dc5ccec3017a2240daee180f6c0364db490e71376389f77cc5a45f
6
+ metadata.gz: fe3934ed47df9f0c0c921f66802b8cccdd8aa06af1e17005079850009bc797107d13f612a4dcdb708868215ffd5bd3ab7b97a08f2e333f04feb9bf70d8728144
7
+ data.tar.gz: 59bf8a127529f30624586d86493f8db88c5b142ef42841dc58082890229925e7cf4e17946855222d0f10c7fd7a6845622d872f2de829c8d5bbcaa321fdaa3e7a
data/bin/maze-runner CHANGED
@@ -26,6 +26,7 @@ require_relative '../lib/maze/client/selenium/base_client'
26
26
  require_relative '../lib/maze/client/selenium/bb_client'
27
27
  require_relative '../lib/maze/client/selenium/bs_client'
28
28
  require_relative '../lib/maze/client/selenium/local_client'
29
+
29
30
  require_relative '../lib/maze/aws_public_ip'
30
31
  require_relative '../lib/maze/compare'
31
32
  require_relative '../lib/maze/docker'
@@ -82,6 +83,7 @@ require_relative '../lib/maze/plugins/bugsnag_reporting_plugin'
82
83
  require_relative '../lib/maze/plugins/cucumber_report_plugin'
83
84
  require_relative '../lib/maze/plugins/error_code_plugin'
84
85
  require_relative '../lib/maze/plugins/global_retry_plugin'
86
+ require_relative '../lib/maze/plugins/datadog_metrics_plugin'
85
87
 
86
88
  # Require monkey-patches after everything else
87
89
  require_relative '../lib/utils/selenium_money_patch'
data/bin/upload-app CHANGED
@@ -3,6 +3,7 @@
3
3
 
4
4
  require_relative '../lib/maze'
5
5
  require_relative '../lib/maze/client/bs_client_utils'
6
+ require_relative '../lib/maze/client/bb_client_utils'
6
7
  require_relative '../lib/maze/logger'
7
8
  require_relative '../lib/maze/helper'
8
9
  require 'optimist'
@@ -11,45 +12,70 @@ require 'net/http'
11
12
 
12
13
  class UploadAppEntry
13
14
  def start(args)
14
- p = Optimist::Parser.new do
15
- text 'Upload app files to BrowserStack'
16
- text ''
17
- text 'Requires BROWSER_STACK_USERNAME and BROWSER_STACK_ACCESS_KEY'
15
+ parser = Optimist::Parser.new do
16
+ text 'Upload app files to a device farm'
18
17
  text ''
19
18
  text 'Usage [OPTIONS]'
20
19
  text ''
21
20
  opt :help,
22
- 'Print this help.'
21
+ 'Print this help.'
23
22
  opt :app,
24
- 'The app to upload.',
25
- :type => :string
23
+ 'The app to upload.',
24
+ type: :string,
25
+ required: true
26
26
  opt :app_id_file,
27
- 'The file to write the uploaded app ID back to',
28
- :type => :string
27
+ 'The file to write the uploaded app ID back to',
28
+ type: :string
29
+ opt :farm,
30
+ 'The device farm to upload the app to, one of bb (BitBar) or bs (BrowserStack) (default)',
31
+ type: :string,
32
+ default: 'bs'
33
+ opt :username,
34
+ 'Device farm username. Defaults to BROWSER_STACK_DEVICES_USERNAME variable (required for BrowserStack)',
35
+ type: :string
36
+ opt :access_key,
37
+ 'Device farm access key. Defaults to BROWSER_STACK_DEVICES_ACCESS_KEY or BITBAR_ACCESS_KEY environment variables (required)',
38
+ type: :string
29
39
  end
30
40
 
31
- opts = Optimist::with_standard_exception_handling p do
41
+ options = Optimist::with_standard_exception_handling parser do
32
42
  raise Optimist::HelpNeeded if ARGV.empty? # show help screen
33
- p.parse ARGV
43
+ parser.parse ARGV
34
44
  end
35
45
 
36
- # Get browserstack username and access key from the environment
37
- username = ENV['BROWSER_STACK_USERNAME']
38
- access_key = ENV['BROWSER_STACK_ACCESS_KEY']
46
+ # Get username and access key from the environment
47
+ case options[:farm]
48
+ when 'bs'
49
+ options[:username] ||= ENV['BROWSER_STACK_DEVICES_USERNAME'] || ENV['BROWSER_STACK_USERNAME']
50
+ options[:access_key] ||= ENV['BROWSER_STACK_DEVICES_ACCESS_KEY'] ||ENV['BROWSER_STACK_ACCESS_KEY']
51
+ if options[:username].nil?
52
+ $logger.warn 'Browserstack requires username option to be set'
53
+ Optimist::with_standard_exception_handling parser do
54
+ raise Optimist::HelpNeeded
55
+ end
56
+ end
57
+ when 'bb'
58
+ options[:access_key] ||= ENV['BITBAR_ACCESS_KEY']
59
+ end
39
60
 
40
- # Check if BROWSER_STACK_USERNAME or BROWSER_STACK_ACCESS_KEY has been set
41
- if username.nil? || access_key.nil?
42
- $logger.warn "BROWSER_STACK_USERNAME or BROWSER_STACK_ACCESS_KEY has not been set"
43
- Optimist::with_standard_exception_handling p do
61
+ if options[:access_key].nil?
62
+ $logger.warn 'An access_key is required to upload the app'
63
+ Optimist::with_standard_exception_handling parser do
44
64
  raise Optimist::HelpNeeded
45
65
  end
46
66
  end
47
67
 
48
- Maze::Client::BrowserStackClientUtils.upload_app username,
49
- access_key,
50
- opts[:app],
51
- opts[:app_id_file]
52
-
68
+ case options[:farm]
69
+ when 'bs'
70
+ Maze::Client::BrowserStackClientUtils.upload_app options[:username],
71
+ options[:access_key],
72
+ options[:app],
73
+ options[:app_id_file]
74
+ when 'bb'
75
+ Maze::Client::BitBarClientUtils.upload_app options[:access_key],
76
+ options[:app],
77
+ options[:app_id_file]
78
+ end
53
79
  end
54
80
  end
55
81
 
@@ -103,7 +103,7 @@ end
103
103
  # @step_input url [String] The URL to open.
104
104
  When('I open the URL {string}') do |url|
105
105
  begin
106
- open(url, &:read)
106
+ URI.open(url, &:read)
107
107
  rescue OpenURI::HTTPError
108
108
  $logger.debug $!.inspect
109
109
  end
@@ -148,6 +148,12 @@ Then('a span field {string} equals {string}') do |key, expected|
148
148
  Maze.check.includes selected_keys, expected
149
149
  end
150
150
 
151
+ Then('a span field {string} equals {int}') do |key, expected|
152
+ spans = spans_from_request_list(Maze::Server.list_for('traces'))
153
+ selected_keys = spans.map { |span| span[key] }
154
+ Maze.check.includes selected_keys, expected
155
+ end
156
+
151
157
  Then('a span field {string} matches the regex {string}') do |attribute, pattern|
152
158
  regex = Regexp.new pattern
153
159
  spans = spans_from_request_list(Maze::Server.list_for('traces'))
@@ -156,6 +162,30 @@ Then('a span field {string} matches the regex {string}') do |attribute, pattern|
156
162
  Maze.check.false(selected_attributes.empty?)
157
163
  end
158
164
 
165
+ Then('a span named {string} contains the attributes:') do |span_name, table|
166
+ spans = spans_from_request_list(Maze::Server.list_for('traces'))
167
+ named_spans = spans.find_all { |span| span['name'].eql?(span_name) }
168
+ raise Test::Unit::AssertionFailedError.new "No spans were found with the name #{span_name}" if named_spans.empty?
169
+
170
+ expected_attributes = table.hashes
171
+
172
+ match = false
173
+ named_spans.each do |span|
174
+ matches = expected_attributes.map do |expected_attribute|
175
+ span['attributes'].find_all { |attribute| attribute['key'].eql?(expected_attribute['attribute']) }
176
+ .any? { |attribute| attribute_value_matches?(attribute['value'], expected_attribute['type'], expected_attribute['value']) }
177
+ end
178
+ if matches.all? && !matches.empty?
179
+ match = true
180
+ break
181
+ end
182
+ end
183
+
184
+ unless match
185
+ raise Test::Unit::AssertionFailedError.new "No spans were found containing all of the given attributes"
186
+ end
187
+ end
188
+
159
189
  def spans_from_request_list list
160
190
  return list.remaining
161
191
  .flat_map { |req| req[:body]['resourceSpans'] }
@@ -164,6 +194,30 @@ def spans_from_request_list list
164
194
  .select { |s| !s.nil? }
165
195
  end
166
196
 
197
+ def attribute_value_matches?(attribute_value, expected_type, expected_value)
198
+ # Check that the required value type key is present
199
+ unless attribute_value.keys.include?(expected_type)
200
+ return false
201
+ end
202
+
203
+ case expected_type
204
+ when 'bytesValue', 'stringValue'
205
+ expected_value.eql?(attribute_value[expected_type])
206
+ when 'intValue'
207
+ expected_value.to_i.eql?(attribute_value[expected_type].to_i)
208
+ when 'doubleValue'
209
+ expected_value.to_f.eql?(attribute_value[expected_type])
210
+ when 'boolValue'
211
+ expected_value.eql?('true').eql?(attribute_value[expected_type])
212
+ when 'arrayValue', 'kvlistValue'
213
+ $logger.error('Span attribute validation does not currently support the "arrayValue" or "kvlistValue" types')
214
+ false
215
+ else
216
+ $logger.error("An invalid attribute type was expected: '#{expected_type}'")
217
+ false
218
+ end
219
+ end
220
+
167
221
  def assert_received_spans(span_count, list)
168
222
  timeout = Maze.config.receive_requests_wait
169
223
  wait = Maze::Wait.new(timeout: timeout)
@@ -65,6 +65,9 @@ BeforeAll do
65
65
  # Start document server, if asked for
66
66
  # This must happen after any client hooks have run, so that they can set the server root
67
67
  Maze::DocumentServer.start unless Maze.config.document_server_root.nil?
68
+
69
+ # An initial setup for total success status
70
+ $success = true
68
71
  end
69
72
 
70
73
  # @param config The Cucumber config
@@ -141,6 +144,9 @@ After do |scenario|
141
144
  output_received_requests('invalid requests')
142
145
  end
143
146
 
147
+ # Keep a global record of the total test status for reporting purposes
148
+ $success = !scenario.failed?
149
+
144
150
  # Log all received requests to file
145
151
  Maze::MazeOutput.new(scenario).write_requests if Maze.config.file_log
146
152
 
@@ -70,25 +70,37 @@ module Maze
70
70
  #
71
71
  # @return [Hash] A hash containing the capabilities.
72
72
  def dashboard_capabilities
73
- # Attempt to use the current git repo as the project name
74
- output, status = Maze::Runner.run_command('git rev-parse --show-toplevel')
75
- if status == 0
76
- project = File.basename(output[0].strip)
73
+
74
+ # Determine project name
75
+ if ENV['BUILDKITE']
76
+ $logger.info 'Using BUILDKITE_PIPELINE_SLUG for BitBar project name'
77
+ project = ENV['BUILDKITE_PIPELINE_SLUG']
77
78
  else
78
- if ENV['BUILDKITE']
79
- project = ENV['BUILDKITE_PIPELINE_SLUG']
79
+ # Attempt to use the current git repo
80
+ output, status = Maze::Runner.run_command('git rev-parse --show-toplevel')
81
+ if status == 0
82
+ project = File.basename(output[0].strip)
80
83
  else
81
84
  $logger.warn 'Unable to determine project name, consider running Maze Runner from within a Git repository'
82
85
  project = 'Unknown'
83
86
  end
84
87
  end
85
88
 
89
+ # Test run
86
90
  if ENV['BUILDKITE']
87
- test_run = "#{ENV['BUILDKITE_BUILD_NUMBER']} - #{ENV['BUILDKITE_LABEL']}"
91
+ bk_retry = ENV['BUILDKITE_RETRY_COUNT']
92
+ retry_string = if !bk_retry.nil? && bk_retry.to_i > 1
93
+ " (#{bk_retry})"
94
+ else
95
+ ''
96
+ end
97
+ test_run = "#{ENV['BUILDKITE_BUILD_NUMBER']} - #{ENV['BUILDKITE_LABEL']}#{retry_string}"
88
98
  else
89
99
  test_run = Maze.run_uuid
90
100
  end
91
101
 
102
+ $logger.info "BitBar project name: #{project}"
103
+ $logger.info "BitBar test run: #{test_run}"
92
104
  {
93
105
  'bitbar:options' => {
94
106
  bitbar_project: project,
@@ -41,7 +41,9 @@ module Maze
41
41
  all_devices = query_api(path, query)
42
42
 
43
43
  $logger.debug "All available devices in group #{device_group_id}: #{JSON.pretty_generate(all_devices)}"
44
+ Maze::Plugins::DatadogMetricsPlugin.send_gauge('bitbar.device.available', all_devices['data'].size, [Maze.config.device])
44
45
  filtered_devices = all_devices['data'].reject { |device| device['locked'] }
46
+ Maze::Plugins::DatadogMetricsPlugin.send_gauge('bitbar.device.unlocked', filtered_devices.size, [Maze.config.device])
45
47
  return filtered_devices.size, filtered_devices.sample
46
48
  end
47
49
 
@@ -17,7 +17,8 @@ module Maze
17
17
  # Uploads an app to BitBar for later consumption
18
18
  # @param api_key [String] The BitBar API key
19
19
  # @param app [String] A path to the application file
20
- def upload_app(api_key, app)
20
+ # @param app_id_file [String] the file to write the uploaded app url to BitBar
21
+ def upload_app(api_key, app, app_id_file=nil)
21
22
  uuid_regex = /\A[0-9]+\z/
22
23
 
23
24
  if uuid_regex.match? app
@@ -53,6 +54,12 @@ module Maze
53
54
  raise
54
55
  end
55
56
  end
57
+
58
+ unless app_id_file.nil?
59
+ $logger.info "Writing uploaded app id to #{app_id_file}"
60
+ File.write(Maze::Helper.expand_path(app_id_file), app_uuid)
61
+ end
62
+
56
63
  app_uuid
57
64
  end
58
65
 
@@ -6,6 +6,10 @@ module Maze
6
6
  raise 'Method not implemented by this class'
7
7
  end
8
8
 
9
+ def log_run_outro
10
+ raise 'Method not implemented by this class'
11
+ end
12
+
9
13
  def stop_session
10
14
  Maze.driver&.driver_quit
11
15
  end
@@ -27,6 +27,15 @@ module Maze
27
27
  Maze.driver.start_driver
28
28
  end
29
29
 
30
+ def log_run_outro
31
+ api_client = BitBarApiClient.new(Maze.config.access_key)
32
+
33
+ $logger.info 'Selenium session created:'
34
+ id = Maze.driver.session_id
35
+ link = api_client.get_device_session_ui_link(id)
36
+ $logger.info Maze::LogUtil.linkify link, 'BitBar session(s)' if link
37
+ end
38
+
30
39
  def stop_session
31
40
  super
32
41
  Maze::Client::BitBarClientUtils.stop_local_tunnel
@@ -59,6 +59,10 @@ module Maze
59
59
  Maze::Client::BrowserStackClientUtils.stop_local_tunnel
60
60
  end
61
61
 
62
+ def log_run_outro
63
+ log_session_info
64
+ end
65
+
62
66
  private
63
67
 
64
68
  # Determines and returns sensible project and build capabilities
@@ -92,6 +92,13 @@ module Maze
92
92
  end
93
93
  end
94
94
 
95
+ # Returns the driver session ID
96
+ #
97
+ # @returns [String] The session ID of the selenium session
98
+ def session_id
99
+ @driver.session_id
100
+ end
101
+
95
102
  private
96
103
 
97
104
  # Creates and starts the selenium driver
@@ -6,6 +6,7 @@ module Maze
6
6
  @client
7
7
 
8
8
  def before_all
9
+ Maze::Plugins::DatadogMetricsPlugin.send_increment('appium.test_started')
9
10
  @client = Maze::Client::Appium.start
10
11
  end
11
12
 
@@ -30,6 +31,11 @@ module Maze
30
31
 
31
32
  def after_all
32
33
  @client&.log_run_outro
34
+ if $success
35
+ Maze::Plugins::DatadogMetricsPlugin.send_increment('appium.test_succeeded')
36
+ else
37
+ Maze::Plugins::DatadogMetricsPlugin.send_increment('appium.test_failed')
38
+ end
33
39
  end
34
40
 
35
41
  def at_exit
@@ -7,8 +7,12 @@ module Maze
7
7
  @client = Maze::Client::Selenium.start
8
8
  end
9
9
 
10
+ def after_all
11
+ @client&.log_run_outro
12
+ end
13
+
10
14
  def at_exit
11
- @client.stop_session
15
+ @client&.stop_session
12
16
  end
13
17
  end
14
18
  end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'datadog/statsd'
4
+
5
+ module Maze
6
+ module Plugins
7
+ # Enables metrics to be reported to Datadog via a StatsD proxy
8
+ class DatadogMetricsPlugin
9
+
10
+ class << self
11
+ # Sends a gauge metric to Datadog
12
+ #
13
+ # @param metric [String] The identifier of the metric
14
+ # @param value [Integer] The value of the metric
15
+ # @param tags [Array] An array of strings with which to tag the metric
16
+ def send_gauge(metric, value, tags=[])
17
+ return unless logging?
18
+ stats_dog.gauge(metric, value, tags: tags)
19
+ end
20
+
21
+ # Sends an increment metric to Datadog
22
+ #
23
+ # @param metric [String] The identifier of the metric
24
+ # @param tags [Array] An array of strings with which to tag the metric
25
+ def send_increment(metric, tags=[])
26
+ return unless logging?
27
+ stats_dog.increment(metric, tags: tags)
28
+ end
29
+
30
+ private
31
+
32
+ # Whether metrics should be delivered to Datadog
33
+ #
34
+ # @returns [Boolean] Whether metrics should be sent
35
+ def logging?
36
+ ENV['BUILDKITE']
37
+ end
38
+
39
+ # Returns or initialises the DogStatsD instance
40
+ #
41
+ # @returns [Datadog::Statsd] The DogStatsD instance
42
+ def stats_dog
43
+ @stats_dog ||= initialize_stats_dog
44
+ end
45
+
46
+ # Initializes the DogStatsD instance, connecting to the buildkite agent Datadog agent
47
+ #
48
+ # @returns [Datadog::Statsd] The newly created DogStatsD instance
49
+ def initialize_stats_dog
50
+ tags = []
51
+ tags << Maze.config.device.to_s if Maze.config.device
52
+ tags << Maze.config.farm.to_s if Maze.config.farm
53
+ @stats_dog = Datadog::Statsd.new(aws_instance_ip, 8125, tags: tags, namespace: 'maze-runner')
54
+
55
+ at_exit do
56
+ @stats_dog.close
57
+ end
58
+
59
+ @stats_dog
60
+ end
61
+
62
+ # Retrieves the internal ipv4 address of the AWS buildkite instance the maze-runner container is run upon
63
+ #
64
+ # @returns [String] The local ipv4 address the Datadog agent is running on
65
+ def aws_instance_ip
66
+ `curl --silent -XGET http://169.254.169.254/latest/meta-data/local-ipv4`
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
data/lib/maze.rb CHANGED
@@ -7,7 +7,7 @@ require_relative 'maze/timers'
7
7
  # Glues the various parts of MazeRunner together that need to be accessed globally,
8
8
  # providing an alternative to the proliferation of global variables or singletons.
9
9
  module Maze
10
- VERSION = '7.22.1'
10
+ VERSION = '7.24.0'
11
11
 
12
12
  class << self
13
13
  attr_accessor :check, :driver, :internal_hooks, :mode, :start_time, :dynamic_retry, :public_address, :run_uuid
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bugsnag-maze-runner
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.22.1
4
+ version: 7.24.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steve Kirkland
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-03-15 00:00:00.000000000 Z
11
+ date: 2023-03-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cucumber
@@ -164,6 +164,20 @@ dependencies:
164
164
  - - "~>"
165
165
  - !ruby/object:Gem::Version
166
166
  version: 0.9.6
167
+ - !ruby/object:Gem::Dependency
168
+ name: dogstatsd-ruby
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: 5.5.0
174
+ type: :runtime
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: 5.5.0
167
181
  - !ruby/object:Gem::Dependency
168
182
  name: optimist
169
183
  requirement: !ruby/object:Gem::Requirement
@@ -401,6 +415,7 @@ files:
401
415
  - lib/maze/option/validator.rb
402
416
  - lib/maze/plugins/bugsnag_reporting_plugin.rb
403
417
  - lib/maze/plugins/cucumber_report_plugin.rb
418
+ - lib/maze/plugins/datadog_metrics_plugin.rb
404
419
  - lib/maze/plugins/error_code_plugin.rb
405
420
  - lib/maze/plugins/global_retry_plugin.rb
406
421
  - lib/maze/proxy.rb
@@ -425,11 +440,11 @@ files:
425
440
  - lib/maze/wait.rb
426
441
  - lib/utils/deep_merge.rb
427
442
  - lib/utils/selenium_money_patch.rb
428
- homepage:
443
+ homepage:
429
444
  licenses:
430
445
  - MIT
431
446
  metadata: {}
432
- post_install_message:
447
+ post_install_message:
433
448
  rdoc_options: []
434
449
  require_paths:
435
450
  - lib
@@ -445,7 +460,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
445
460
  version: '0'
446
461
  requirements: []
447
462
  rubygems_version: 3.1.6
448
- signing_key:
463
+ signing_key:
449
464
  specification_version: 4
450
465
  summary: Bugsnag API request validation harness
451
466
  test_files: []