Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance mazerunner functionality to support CPU metrics tests #381

Merged
merged 1 commit into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions features/default/network.feature
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Feature: Automatic instrumentation spans

Scenario: AutoInstrumentNetworkTracePropagationScenario: Allow All
Given I load scenario "AutoInstrumentNetworkTracePropagationScenario"
And I configure "propagateTraceParentToUrlsMatching" to ".*"
And I configure bugsnag "propagateTraceParentToUrlsMatching" to ".*"
And I invoke "setCallSitesWithCallSiteStrs:" with parameter "?test=1"
And I start bugsnag
And I run the loaded scenario
Expand All @@ -63,7 +63,7 @@ Feature: Automatic instrumentation spans

Scenario: AutoInstrumentNetworkTracePropagationScenario: Allow Some
Given I load scenario "AutoInstrumentNetworkTracePropagationScenario"
And I configure "propagateTraceParentToUrlsMatching" to ".*test.*"
And I configure bugsnag "propagateTraceParentToUrlsMatching" to ".*test.*"
And I invoke "setCallSitesWithCallSiteStrs:" with parameter "?test=1,?temp=1"
And I start bugsnag
And I run the loaded scenario
Expand Down
10 changes: 10 additions & 0 deletions features/fixtures/ios/Fixture/Fixture.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ class Fixture: NSObject, CommandReceiver {
value: command.args["value"] as! String)
self.readyToReceiveCommand = true
break
case "configure_scenario":
self.configureScenario(path: command.args["path"] as! String,
value: command.args["value"] as! String)
self.readyToReceiveCommand = true
break
case "start_bugsnag":
self.startBugsnag()
self.readyToReceiveCommand = true
Expand Down Expand Up @@ -102,6 +107,11 @@ class Fixture: NSObject, CommandReceiver {
scenario!.configureBugsnag(path: path, value: value)
}

private func configureScenario(path: String, value: String) {
logInfo("Configuring scenario [\(path)] to [\(value)]")
scenario!.configureScenario(path: path, value: value)
}

private func startBugsnag() {
logInfo("Starting bugsnag performance")
scenario!.startBugsnag()
Expand Down
39 changes: 35 additions & 4 deletions features/fixtures/ios/Scenarios/Scenario.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class Scenario: NSObject {
let fixtureConfig: FixtureConfig
var config = BugsnagPerformanceConfiguration.loadConfig()
var pendingMeasurements: [MazerunnerMeasurement] = []
var scenarioConfig: Dictionary<String,String> = [:]

private override init() {
fatalError("do not use the default init of Scenario")
Expand All @@ -32,7 +33,7 @@ class Scenario: NSObject {
config.autoInstrumentAppStarts = false
config.autoInstrumentNetworkRequests = false
config.autoInstrumentViewControllers = false
config.autoInstrumentRendering = false
config.enabledMetrics.rendering = false
config.endpoint = fixtureConfig.tracesURL
config.networkRequestCallback = filterAdminMazeRunnerNetRequests
}
Expand Down Expand Up @@ -91,11 +92,19 @@ class Scenario: NSObject {
}
config.tracePropagationUrls = regexes
break
case "cpuMetrics":
config.enabledMetrics.cpu = (value == "true")
break
default:
fatalError("\(path): Unknown configuration path")
}
}

func configureScenario(path: String, value: String) {
logDebug("Scenario.configureScenario()")
scenarioConfig[path] = value;
}

func startBugsnag() {
logDebug("Scenario.startBugsnag()")
performAndReportDuration({
Expand Down Expand Up @@ -141,7 +150,7 @@ class Scenario: NSObject {
let startDate = Date()
body()
let endDate = Date()

let calendar = Calendar.current
let duration = calendar.dateComponents([.nanosecond], from: startDate, to: endDate)
let metrics = ["duration.nanos": "\(duration.nanosecond ?? 0)"]
Expand All @@ -152,7 +161,7 @@ class Scenario: NSObject {
var request = URLRequest(url: fixtureConfig.metricsURL)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")

var body = metrics
body["metric.measurement"] = name
body["device.manufacturer"] = "Apple"
Expand All @@ -164,12 +173,34 @@ class Scenario: NSObject {
return
}
request.httpBody = jsonData

URLSession.shared.dataTask(with: request).resume()
}

func callReflectUrl(appendingToUrl: String) {
let url = URL(string: appendingToUrl, relativeTo: fixtureConfig.reflectURL)!
URLSession.shared.dataTask(with: url).resume()
}

func toDouble(string: String?) -> Double {
if string == nil {
return 0
}
return Double(string!)!
}

func toTriState(string: String?) -> BSGTriState {
switch string {
case "yes":
(.yes)
case "no":
(.no)
case "unset":
(.unset)
case nil:
(.unset)
default:
fatalError("\(String(describing: string)): Unknown tri-state value")
}
}
}
41 changes: 39 additions & 2 deletions features/steps/app_steps.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,14 @@ def skip_between(os, version_lo, version_hi)
run_command("start_bugsnag", {})
end

When('I configure {string} to {string}') do |config_name, config_value|
# Note: The method will usually be of the form "xyzWithParam:"
When('I configure bugsnag {string} to {string}') do |config_name, config_value|
run_command("configure_bugsnag", { path:config_name, value:config_value })
end

When('I configure scenario {string} to {string}') do |config_name, config_value|
run_command("configure_scenario", { path:config_name, value:config_value })
end

When('I run the loaded scenario') do
run_command("run_loaded_scenario", {})
end
Expand Down Expand Up @@ -93,6 +96,11 @@ def run_command(action, args)
spans.map { |span| Maze::check.false span['attributes'].find { |a| a['key'] == attribute }['value']['boolValue'] }
end

Then('every span attribute {string} does not exist') do |attribute|
spans = spans_from_request_list(Maze::Server.list_for('traces'))
spans.map { |span| Maze.check.nil span['attributes'].find { |a| a['key'] == attribute } }
end

Then('every span bool attribute {string} does not exist') do |attribute|
spans = spans_from_request_list(Maze::Server.list_for('traces'))
spans.map { |span| Maze.check.nil span['attributes'].find { |a| a['key'] == attribute } }
Expand Down Expand Up @@ -192,6 +200,30 @@ def run_command(action, args)
Maze.check.false(attribute_values.empty?)
end


Then('a span float attribute {string} is greater than {float}') do |attribute, expected|
spans = spans_from_request_list(Maze::Server.list_for('traces'))
selected_attributes = spans.map { |span| span['attributes'].find { |a| a['key'].eql?(attribute) && a['value'].has_key?('doubleValue') } }.compact
attribute_values = selected_attributes.map { |a| a['value']['doubleValue'].to_f > expected }
Maze.check.false(attribute_values.empty?)
end

Then('a span float attribute {string} equals {float}') do |attribute, expected|
spans = spans_from_request_list(Maze::Server.list_for('traces'))
selected_attributes = spans.map { |span| span['attributes'].find { |a| a['key'].eql?(attribute) && a['value'].has_key?('doubleValue') } }.compact
attribute_values = selected_attributes.map { |a| a['value']['doubleValue'].to_f == expected }
Maze.check.false(attribute_values.empty?)
end

Then('a span float attribute {string} is less than {float}') do |attribute, expected|
spans = spans_from_request_list(Maze::Server.list_for('traces'))
selected_attributes = spans.map { |span| span['attributes'].find { |a| a['key'].eql?(attribute) && a['value'].has_key?('doubleValue') } }.compact
attribute_values = selected_attributes.map { |a| a['value']['doubleValue'].to_f < expected }
Maze.check.false(attribute_values.empty?)
end



Then('a span array attribute {string} contains the string value {string} at index {int}') do |attribute, expected, index|
value = get_array_value_at_index(attribute, index, 'stringValue')
Maze.check.true(value == expected)
Expand Down Expand Up @@ -245,6 +277,11 @@ def get_array_attribute_contents(attribute)
Maze.check.true(array_contents.empty?)
end

Then('a span array attribute {string} contains {int} elements') do |attribute, count|
array_contents = get_array_attribute_contents(attribute)
Maze.check.true(array_contents.length() == count)
end

Then('a span named {string} is a child of span named {string}') do |name1, name2|
spans = spans_from_request_list(Maze::Server.list_for('traces'))
first_span = spans.find { |span| span['name'] == name1 }
Expand Down