-
Notifications
You must be signed in to change notification settings - Fork 181
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
353 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,39 @@ | ||
# How to check code coverage results in the test suite | ||
# Code Coverage of the server components after a full test suite execution | ||
|
||
## Get the log | ||
In your test suite you must configure JaCoCo, connected to the java components and run the full test suite, to see which blocks of code are triggered. | ||
|
||
* log in to the controller as root; | ||
* go to `spacewalk/testsuite` directory; | ||
* copy recursively `coverage` directory to some web server or local directory; | ||
* open that directory in a web browser. | ||
## Goals | ||
|
||
## Analyze | ||
- Find features not fully covered through our test-suite | ||
- Discover obsolete code (methods or classes), which are never triggered in any workflow. | ||
- Generate a map of features and files(or even piece of code that cover), with the idea of doing reverse engineering and be able to know which Cucumber feature can be broken when we merge a PR. | ||
|
||
***IMPORTANT***: | ||
if you have a lot of failures, the coverage will decrease because the steps were not executed. Ideally, you need to have the test suite results green to evaluate the code coverage correctly and find dead code. | ||
## How to configure JaCoCo in your java server | ||
|
||
Code coverage `result.html` is a great tool, **but** needs a human interpretation of the results. | ||
Start the app with Jacoco Agent enabled on TCP port 6300: | ||
For that we need to edit the file `/etc/sysconfig/tomcat` on the server appending | ||
`-javaagent:/tmp/jacocoagent.jar=output=tcpserver,address=*,port=6300` | ||
to `JAVA_OPTS` variable, then restart tomcat: `systemctl restart tomcat.service` | ||
|
||
## Submit a PR | ||
## How to run the test suite with JaCoCo | ||
|
||
This PR will eliminate dead code or provide a better fix that you had inspired by code coverage. | ||
1. Run the test suite normally, but with the JaCoCo agent enabled | ||
```bash | ||
java -javaagent:/tmp/jacocoagent.jar=output=tcpserver,address=*,port=6300 -jar /tmp/cucumber.jar | ||
``` | ||
2. Then when a test finish we can use JaCoCo CLI to dump results, let's run this from where we have the product code | ||
```bash | ||
java -jar jacococli.jar dump --address <server_fqdn> --destfile /tmp/jacoco.exec --port 6300 --reset | ||
``` | ||
3. After that, we can generate a HTML report | ||
```bash | ||
java -jar jacococli.jar report /tmp/jacoco.exec --html /srv/www/htdocs/pub/jacoco-cucumber-report --xml /srv/www/htdocs/pub/jacoco-cucumber-report.xml --sourcefiles /tmp/uyuni-master/java/code/src --classfiles /srv/tomcat/webapps/rhn/WEB-INF/lib | ||
``` | ||
4. From the XML report we want to obtain a list of (source_file_path:line_number) for each line of code triggered during a Cucumber feature execution. | ||
``` | ||
package name | ||
+ sourcefile name | ||
+ line nr (if mi == 0) | ||
``` | ||
5. We will have a HashMap stored in Redis handled by KeyValueStore class | ||
6. On that HashMap, the key being the Cucumber Feature filepath and the value being a list of (source_file_path:line_number) for each line of code triggered during the execution of that feature. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
25 changes: 25 additions & 0 deletions
25
testsuite/features/step_definitions/system_monitoring_steps.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# Copyright (c) 2024 SUSE LLC. | ||
# Licensed under the terms of the MIT license. | ||
|
||
### This file contains the definitions for the steps extracting and reporting information from the system | ||
|
||
When(/^I report the bootstrap duration for "([^"]*)"$/) do |host| | ||
next unless $quality_intelligence_mode | ||
|
||
duration = last_bootstrap_duration(host) | ||
$quality_intelligence.push_bootstrap_duration(host, duration) | ||
end | ||
|
||
When(/^I report the onboarding duration for "([^"]*)"$/) do |host| | ||
next unless $quality_intelligence_mode | ||
|
||
duration = last_onboarding_duration(host) | ||
$quality_intelligence.push_onboarding_duration(host, duration) | ||
end | ||
|
||
When(/^I report the synchronization duration for "([^"]*)"$/) do |product| | ||
next unless $quality_intelligence_mode | ||
|
||
duration = synchronization_duration(product) | ||
$quality_intelligence.push_synchronization_duration(product, duration) | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
# Copyright (c) 2024 SUSE LLC. | ||
# Licensed under the terms of the MIT license. | ||
require 'redis' | ||
|
||
# Key-Value Store to interact with a NoSQL database | ||
class KeyValueStore | ||
# Initialize a connection with a NoSQL database | ||
# | ||
# @param db_host [String] The hostname of the NoSQL database. | ||
# @param db_port [Integer] The port of the NoSQL database. | ||
# @param db_username [String] The username to authenticate with the NoSQL database. | ||
# @param db_password [String] The password to authenticate with the NoSQL database. | ||
# @return [Redis] A connection with the database. | ||
def initialize(db_host, db_port, db_username, db_password) | ||
begin | ||
raise ArgumentError, 'Database host is required' if db_host.nil? || db_host.empty? | ||
raise ArgumentError, 'Database port is required' if db_port.nil? | ||
raise ArgumentError, 'Database username is required' if db_username.nil? || db_username.empty? | ||
raise ArgumentError, 'Database password is required' if db_password.nil? || db_password.empty? | ||
|
||
@database = Redis.new(host: db_host, port: db_port, username: db_username, password: db_password) | ||
rescue StandardError => e | ||
warn("Error initializing KeyValueStore:\n #{e.full_message}") | ||
raise | ||
end | ||
end | ||
|
||
# Close the connection with the NoSQL database | ||
def close | ||
@database.close | ||
end | ||
|
||
# Add a key-value pair to a Set | ||
# | ||
# @param key [String] The key to add the value to. | ||
# @param value [String] The value to add to the key. | ||
# @param database [Integer] Optional: The database number to select (default: 0). | ||
def add(key, value, database = 0) | ||
begin | ||
@database.select(database) | ||
@database.sadd(key, value) | ||
rescue StandardError => e | ||
warn("Error adding a key-value:\n #{e.full_message}") | ||
raise | ||
end | ||
end | ||
|
||
# Get the value of a key | ||
# | ||
# @param key [String] The key to get the value from. | ||
# @param database [Integer] Optional: The database number to select (default: 0). | ||
# @return [Array<String>] An array with the values of the key. | ||
def get(key, database = 0) | ||
begin | ||
@database.select(database) | ||
@database.smembers(key) | ||
rescue StandardError => e | ||
warn("Error getting a key-value:\n #{e.full_message}") | ||
raise | ||
end | ||
end | ||
|
||
# Remove a key-value pair from a Set | ||
# | ||
# @param key [String] The key to remove the value from. | ||
# @param value [String] The value to remove from the key. | ||
# @param database [Integer] Optional: The database number to select (default: 0). | ||
# @return [Integer] The number of members that were successfully removed | ||
def remove(key, value, database = 0) | ||
begin | ||
@database.select(database) | ||
@database.srem(key, value) | ||
rescue StandardError => e | ||
warn("Error removing a key-value:\n #{e.full_message}") | ||
raise | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# Copyright (c) 2024 SUSE LLC. | ||
# Licensed under the terms of the MIT license. | ||
|
||
require 'net/http' | ||
require 'prometheus/client' | ||
require 'prometheus/client/push' | ||
require 'uri' | ||
|
||
# Metrics Collector handler to push metrics to the Metrics Collector | ||
class MetricsCollectorHandler | ||
def initialize(metrics_collector_url) | ||
@metrics_collector_url = metrics_collector_url | ||
@metrics_collector = Prometheus::Client::Registry.new | ||
end | ||
|
||
# Push a metric to the Metrics Collector, raising an error if the Metrics Collector request fails. | ||
# | ||
# @param job_name [String] the job name to push the metric to | ||
# @param metric_name [String] the metric name to push | ||
# @param metric_value [Integer] the metric value to push | ||
# @param labels [Hash] the labels to add to the metric | ||
# @return [void] | ||
# @raise [SystemCallError] if the Prometheus request fails | ||
def push_metrics(job_name, metric_name, metric_value, labels = {}) | ||
begin | ||
gauge = @metrics_collector.get(metric_name.to_sym) || @metrics_collector.gauge(metric_name.to_sym, docstring: job_name, labels: labels.keys) | ||
gauge.set(metric_value, labels: labels) | ||
Prometheus::Client::Push.new(job: job_name, gateway: @metrics_collector_url).add(@metrics_collector) | ||
rescue StandardError => e | ||
warn("Error pushing the metric #{metric_name} with value #{metric_value}:\n#{e.full_message}") | ||
raise | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.