oyencov 0.0.1.pre

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 829c5d959c386d08c7d81c836251104461d0875fc4fb6e04d1963cd32301235b
4
+ data.tar.gz: 4f14f7447a5278cc84b89d7c6bcd9d4fa97c6ea3f12f4d898e89b042a5de880d
5
+ SHA512:
6
+ metadata.gz: 11a0471aae67aa816d1840f1882eaa206be0ad339ab40fae872567985579a8e84dbae85573b5466083450849b33c30e9ee7a136c48d7ad08d1976849ce46290f
7
+ data.tar.gz: f896668dcbdf7deb0cb392da50f410fca1a0361f20181844fd99753df1ebd83ea3d5d870a9e3790cce60debe8fef09b88430a58149b40ac016c95c7244a3c23e
data/bin/oyencov ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ unless File.exist?('./Gemfile')
4
+ abort 'Please run oyencov from the root of the project.'
5
+ end
6
+
7
+ require 'rubygems'
8
+ begin
9
+ require 'bundler'
10
+ Bundler.setup
11
+ rescue StandardError
12
+ end
13
+
14
+ here = File.expand_path(File.dirname __FILE__)
15
+ $LOAD_PATH << "#{here}/../lib"
16
+
17
+ require "oyencov/cli"
18
+ OyenCov::CLI.start(ARGV)
@@ -0,0 +1,53 @@
1
+ require "faraday"
2
+ require "singleton"
3
+
4
+ module OyenCov
5
+ class APIConnection < Faraday::Connection
6
+ include Singleton
7
+
8
+ def initialize
9
+ super({
10
+ url: (ENV["OYENCOV_API_URL"] || "https://telemetry-api.oyencov.com"),
11
+ headers: {
12
+ "Authorization" => "Bearer #{ENV["OYENCOV_API_KEY"]}",
13
+ "Content-Type" => "application/json",
14
+ "User-Agent" => "oyencov-ruby 0.0.1"
15
+ }
16
+ }) do |f|
17
+ f.request :json
18
+ f.response :json
19
+ end
20
+ end
21
+
22
+ # Used in `background.rb` to determine whether to start background
23
+ def get_data_submission_clearance
24
+ attempts = 3
25
+ begin
26
+ response = get("/v1/data_submission_clearance")
27
+ rescue Faraday::Error => e
28
+ if ENV["OYENCOV_DEBUG"]
29
+ warn(e)
30
+ end
31
+
32
+ if attempts > 0
33
+ attempts -= 1
34
+ sleep(5)
35
+ retry
36
+ end
37
+ nil
38
+ end
39
+
40
+ response
41
+ end
42
+
43
+ def post_runtime_report(body)
44
+ post("/v1/runtime_reports", body)
45
+ rescue Faraday::Error
46
+ false
47
+ end
48
+
49
+ def post_test_report(body)
50
+ post("/v1/test_reports", body)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,81 @@
1
+ require "securerandom"
2
+ require "singleton"
3
+ require_relative "api_connection"
4
+ require_relative "coverage_peek_delta"
5
+
6
+ # Bootstrap the thread that starts Coverage module data collection.
7
+ #
8
+ # Every 60 secs or so:
9
+ # 1. Get the coverage peek_result delta
10
+ # 2. Parse source code and determine which method being run
11
+ # 3. Get controller actions' hits
12
+ # 4. Call the reporter
13
+ #
14
+ # Most of the codes here are inspired by danmayer's coverband gem.
15
+ #
16
+ module OyenCov
17
+ class Background
18
+ @loop_interval = 60 # seconds, can be set from server
19
+ @semaphore = Mutex.new
20
+ @thread = nil
21
+ @reporter = nil
22
+ @api_conn = OyenCov::APIConnection.instance
23
+ @config = OyenCov.config
24
+
25
+ def self.start
26
+ puts "Hello #{Rails.env}"
27
+
28
+ # Start `Coverage` as soon as possible before other codes are loaded
29
+ CoveragePeekDelta.start
30
+
31
+ @thread = Thread.new {
32
+ # Check with backend to get parameters
33
+ sleep(3)
34
+ clearance = @api_conn.get_data_submission_clearance
35
+
36
+ if clearance.nil?
37
+ puts "Unable to obtain oyencov submission clearance. Stopping OyenCov background thread."
38
+ Thread.stop
39
+ end
40
+
41
+ if ENV["OYENCOV_DEBUG"]
42
+ puts(clearance.body)
43
+ end
44
+
45
+ @config.mode == "production" && loop do
46
+ sleep(@loop_interval + 3 - rand(6))
47
+ new_method_hits = CoveragePeekDelta.snapshot_delta
48
+ new_controller_hits = ControllerTracking.snapshot_and_reset!
49
+
50
+ puts new_method_hits
51
+
52
+ runtime_report = {
53
+ git_commit_sha: @config.release,
54
+ controller_action_hits: new_controller_hits,
55
+ method_hits: new_method_hits
56
+ }
57
+ response = @api_conn.post_runtime_report(runtime_report)
58
+
59
+ if response && response.body["status"] == "ok"
60
+ puts "[OyenOnsen] POST runtime_report ok."
61
+ else
62
+ warn "[OyenOnsen] POST runtime_report failed. Stopping background thread."
63
+ Thread.stop
64
+ end
65
+ end # loop
66
+ }
67
+
68
+ @thread.run
69
+
70
+ nil
71
+ end
72
+
73
+ # If production/staging etc, we can exit without further processing.
74
+ # For `test`, persist controller report.
75
+ def self.stop
76
+ @thread.stop
77
+ end
78
+
79
+ private_class_method
80
+ end
81
+ end
@@ -0,0 +1,97 @@
1
+ require "thor"
2
+ require_relative "./api_connection"
3
+ require_relative "./test_report_merger"
4
+ require_relative "./simplecov_resultset_translator"
5
+
6
+ # Bootstrapped from `bin/oyencov`
7
+ #
8
+ #
9
+ module OyenCov
10
+ class CLI < Thor
11
+ desc "translate_simplecov coverage/.resultset.json", "err"
12
+ long_desc <<~TEXT
13
+ If you have parallel test jobs, this command is to be run right after
14
+ finishing the tests, and before artifacts are uploaded.
15
+
16
+ Assumes the --coverage-dir path contains both the simplecov .resultset.json,
17
+ and the oyencov-resultset.json. It parses the .resultset.json file
18
+ generated by simplecov, and append the translated information into the
19
+ pre-existing oyencov-resultset.json.
20
+ TEXT
21
+ # option :coverage_dir, default: "coverage"
22
+ option :simplecov_json_path, default: "coverage/.resultset.json"
23
+ option :oyencov_json_path, default: "coverage/oyencov-resultset.json"
24
+ option :dry_run, type: :boolean, default: false
25
+ def translate_simplecov
26
+ oyencov_json_path = Dir.pwd + "/" + options[:oyencov_json_path]
27
+ simplecov_json_path = Dir.pwd + "/" + options[:simplecov_json_path]
28
+
29
+ # Find existing resultset files
30
+ if File.exist?(oyencov_json_path)
31
+ oyencov_json = File.read(oyencov_json_path)
32
+ else
33
+ warn("Could not find existing oyencov-resultset.json at #{oyencov_json_path}")
34
+ exit(1)
35
+ end
36
+
37
+ warn "Starting to translate simplecov"
38
+
39
+ if File.exist?(simplecov_json_path)
40
+ simplecov_translated_json = OyenCov::SimplecovResultsetTranslator.translate(simplecov_json_path)
41
+ else
42
+ warn("Could not find existing simplecov's .resultset.json at #{simplecov_json_path}")
43
+ exit(1)
44
+ end
45
+
46
+ warn "AFTER translate simplecov"
47
+
48
+ # Attempt merging
49
+ oyencov_resultset = JSON.parse(oyencov_json)
50
+ oyencov_resultset = oyencov_resultset.merge("method_hits" => simplecov_translated_json)
51
+
52
+ # Persist, or dry run stdout?
53
+ new_oyencov_resultset_json = JSON.pretty_generate(oyencov_resultset)
54
+ if options[:dry_run]
55
+ puts new_oyencov_resultset_json
56
+ else
57
+ File.write(oyencov_json_path, new_oyencov_resultset_json)
58
+ end
59
+ end
60
+
61
+ desc "submit tmp/coverage-jsons-*/oyencov-resultset.json",
62
+ "submits the oyencov resultsets data"
63
+ option :files, type: :array, required: true
64
+ option :git_commit_sha, required: true
65
+ option :token
66
+ def submit
67
+ resultset_files = options[:files]
68
+ ENV["OYENCOV_DEBUG"] && if resultset_files.any?
69
+ puts "Found #{resultset_files.join(", ")}"
70
+ else
71
+ puts "No resultset files found"
72
+ exit 1
73
+ end
74
+
75
+ collated_report = OyenCov::TestReportMerger
76
+ .collate_job_reports(options[:files])
77
+ .merge({
78
+ "git_commit_sha" => options[:git_commit_sha]
79
+ })
80
+
81
+ # puts JSON.pretty_generate(collated_report)
82
+
83
+ # Add metadaata
84
+
85
+ ENV["OYENCOV_API_KEY"] ||= options[:token]
86
+ unless ENV["OYENCOV_API_KEY"]
87
+ warn "API token not set. Unable to submit."
88
+ exit(1)
89
+ end
90
+
91
+ connection = OyenCov::APIConnection.instance
92
+ post_response = connection.post_test_report(collated_report)
93
+
94
+ puts post_response.body
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,62 @@
1
+ # We encourage configuring OyenOnsen through environment variables.
2
+ #
3
+ # But some can be set through config/ if they are meant to be uniform across environments.
4
+ module OyenCov
5
+ class Configuration
6
+ ENV_PARAMETERS = %w[
7
+ API_KEY
8
+ API_URL
9
+ MODE
10
+ RELEASE
11
+ TEST_REPORTING_DIR
12
+ TEST_RESULTSET_PATH
13
+ PROGRAM_NAME
14
+ ]
15
+
16
+ attr :api_key, :api_url, :mode, :including_file_paths, :excluding_file_paths, :release, :test_reporting_dir, :test_resultset_path, :program_name
17
+
18
+ def initialize
19
+ reset_to_defaults
20
+ ENV_PARAMETERS.each do |key|
21
+ if (envvar_value = ENV["OYENONSEN_#{key}"])
22
+ instance_variable_set(
23
+ :"@#{key.downcase}", envvar_value
24
+ )
25
+ end
26
+ end
27
+ end
28
+
29
+ def reset_to_defaults
30
+ @api_key = nil
31
+ @api_url = "https://telemetry-api.oyencov.com"
32
+ @mode = ENV["RAILS_ENV"]
33
+ @including_file_paths = %w[app lib]
34
+ @excluding_file_paths = []
35
+ @release = suggest_release
36
+ @test_reporting_dir = "coverage/"
37
+ @test_resultset_path = "coverage/oyencov-resultset.json"
38
+ end
39
+
40
+ private
41
+
42
+ # Lots of ideas came from sentry-ruby, thanks to nate berkopec.
43
+ def suggest_release
44
+ release = `git rev-parse HEAD ||:`.strip
45
+
46
+ if release == "" || release.nil?
47
+ [".source_version", "REVISION"].each do |version_clue|
48
+ if File.exist?(Rails.root.join(version_clue))
49
+ release = File.read(Rails.root.join(version_clue)).strip
50
+ return release
51
+ end
52
+ end
53
+ end
54
+
55
+ release
56
+ end
57
+
58
+ # We need to know if this is rails, sidekiq, rake task etc
59
+ def suggest_program_name
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,22 @@
1
+ # This module is merely the container data structure.
2
+ #
3
+ # The ACTUAL code that tracks controller is in `railtie.rb`
4
+ module OyenCov
5
+ module ControllerTracking
6
+ @hits = {}
7
+
8
+ def self.bump(controller_action_name)
9
+ if @hits[controller_action_name]
10
+ @hits[controller_action_name] += 1
11
+ else
12
+ @hits[controller_action_name] = 1
13
+ end
14
+ end
15
+
16
+ def self.snapshot_and_reset!
17
+ current_hits = @hits
18
+ @hits = {}
19
+ current_hits
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,83 @@
1
+ require "coverage"
2
+ require_relative "method_range_parser"
3
+
4
+ # `CoveragePeekDelta` utility is meant to take...
5
+ #
6
+ # This class won't be governing the state, that will be handled by `OyenCov::Background`. However this will help strip the project path from the hash keys for cleaner reporting.
7
+ module OyenCov
8
+ module CoveragePeekDelta
9
+ PWD = Dir.pwd
10
+
11
+ @@previous_method_hits = {}
12
+
13
+ # We go a bit softer here. If there are other libraries starting
14
+ # coverage then don't throw exception.
15
+ def self.start
16
+ reset!
17
+
18
+ unless Coverage.running?
19
+ Coverage.start
20
+ end
21
+ end
22
+
23
+ # 1. Filter only the keys with PWD.
24
+ # 2. `transform_keys` and remove the PWD
25
+ # 3. Use MRP to get the impt lines.
26
+ #
27
+ # @return [Hash] Method name => line num executions diff from last time
28
+ def self.snapshot_delta
29
+ current_peek = Coverage.peek_result
30
+
31
+ if ENV["OYENONSEN_DEBUG"]
32
+ $stdout.puts "current_peek size = #{current_peek.size}, keys like: #{current_peek.keys[0, 3]}"
33
+ end
34
+
35
+ # Filter into project
36
+ filtered = current_peek.select do |k, _|
37
+ /^#{PWD}/o.match?(k)
38
+ end.transform_keys do |k|
39
+ k.gsub(/#{PWD}\//o, "")
40
+ end
41
+
42
+ if ENV["OYENONSEN_DEBUG"]
43
+ $stdout.puts "filtered size = #{filtered.size}, keys like: #{filtered.keys[0, 3]}"
44
+ end
45
+
46
+ # Filter inside project to just the paths
47
+ filtered = filtered.select do |k, _|
48
+ /^(app|lib)/.match?(k)
49
+ end
50
+
51
+ if ENV["OYENONSEN_DEBUG"]
52
+ $stdout.puts "filtered size = #{filtered.size}, keys like: #{filtered.keys[0, 3]}"
53
+ end
54
+
55
+ # Find the method ranges, set
56
+ current_method_hits = {}
57
+ filtered.each_pair do |fpath, line_hits|
58
+ MethodRangeParser[fpath]&.each_pair do |method_name, line_num|
59
+ # puts [method_name, line_num, line_hits[line_num]]
60
+ next if line_num.nil? || line_hits[line_num].nil?
61
+ current_method_hits[method_name] = line_hits[line_num]
62
+ end
63
+ end
64
+
65
+ # Compare and delta
66
+ new_method_hits = {}
67
+ current_method_hits.each_pair do |method_name, counter|
68
+ if counter.nil?; puts method_name; end
69
+ new_hits = counter - (@@previous_method_hits[method_name] || 0)
70
+ if new_hits > 0
71
+ new_method_hits[method_name] = new_hits
72
+ end
73
+ end
74
+
75
+ @@previous_method_hits = current_method_hits
76
+ new_method_hits
77
+ end
78
+
79
+ def self.reset!
80
+ @@previous_method_hits = {}
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,101 @@
1
+ require "parser/current"
2
+
3
+ # This module helps scanning source code files and get the definition line
4
+ # ranges, so we can count how many times a method has been executed.
5
+ module OyenCov
6
+ class MethodRangeParser < Hash
7
+ @@parsed_files = {}
8
+
9
+ def self.parsed_files
10
+ @@parsed_files
11
+ end
12
+
13
+ # Check cache
14
+ def self.[](filepath)
15
+ @filepath = filepath
16
+ @@parsed_files[@filepath] ||= parse_file(@filepath)
17
+ end
18
+
19
+ private
20
+
21
+ # Considerations:
22
+ # - Some .rb files do not have valid syntax, we can rescue them. However parser
23
+ # stills stderr
24
+ #
25
+ # @return [Hash<String, >] Hash of methods to their children starting line count. The line count can be used to read how often the method is executed from `Coverage.peek_result`
26
+ private_class_method def self.parse_file(filepath)
27
+ traverse_ast(Parser::CurrentRuby.parse(File.read(filepath)))
28
+ .to_h
29
+ .select do |k, v|
30
+ /\.|\#/.match?(k)
31
+ end.transform_keys do |k|
32
+ k.gsub(/^::/, "")
33
+ end
34
+ rescue Parser::SyntaxError
35
+ {}
36
+ end
37
+
38
+ private_class_method def self.declaration_name(node)
39
+ case node.type
40
+ when :begin then ""
41
+ when :defs then ".#{node.children[1]}"
42
+ when :def then "##{node.children[0]}"
43
+ when :class, :module # traverse
44
+ current_name_constant_node = node.children[0]
45
+ full_constant_name = ""
46
+ until current_name_constant_node.children[0].nil?
47
+ full_constant_name = "::#{current_name_constant_node.children[1]}#{full_constant_name}"
48
+ current_name_constant_node = current_name_constant_node.children[0]
49
+ end
50
+ "::#{current_name_constant_node.children[1]}#{full_constant_name}"
51
+ else "Unsupported AST node type: #{node.type}"
52
+ end
53
+ end
54
+
55
+ # @return [Integer]
56
+ private_class_method def self.definition_line_num(node)
57
+ definition_lines = node.children.find do |i|
58
+ Parser::AST::Node === i && i.type == :begin
59
+ end || node.children[-1]
60
+
61
+ if definition_lines.nil?
62
+ nil
63
+ else
64
+ definition_lines.loc.first_line
65
+ end
66
+ end
67
+
68
+ # this be recursion
69
+ # return array of ["node_type/namespace::nam#node_name" => [startline, endline]
70
+ private_class_method def self.traverse_ast(node)
71
+ unless Parser::AST::Node === node
72
+ return nil
73
+ end
74
+
75
+ unless %i[begin module class defs def].include?(node.type)
76
+ return nil
77
+ end
78
+
79
+ node_children = node.children.find do |i|
80
+ Parser::AST::Node === i && i.type == :begin
81
+ end&.children || [node.children[-1]]
82
+
83
+ ownself_name = declaration_name(node)
84
+ ownself_range = definition_line_num(node)
85
+
86
+ children_name_range = []
87
+
88
+ node_children&.each do |cnode|
89
+ if Array === traverse_ast(cnode)
90
+ children_name_range += traverse_ast(cnode)
91
+ end
92
+ end
93
+
94
+ children_name_range.map! do |cnode|
95
+ ["#{ownself_name}#{cnode[0]}", cnode[1]]
96
+ end
97
+
98
+ [[ownself_name, ownself_range]] + children_name_range
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,26 @@
1
+ require_relative "background"
2
+ require_relative "controller_tracking"
3
+ require_relative "test_reporting"
4
+
5
+ module OyenCov
6
+ class Railtie < Rails::Railtie
7
+ initializer "oyencov.configure" do
8
+ # puts "lib/oyencov/railtie.rb initializer oyencov.configure"
9
+ OyenCov::Background.start
10
+ end
11
+
12
+ config.after_initialize do
13
+ # puts "lib/oyencov/railtie.rb config.after_initialize"
14
+ ActiveSupport::Notifications.subscribe("start_processing.action_controller") do |name, start, finish, id, payload|
15
+ # puts(payload)
16
+ ControllerTracking.bump("#{payload[:controller]}##{payload[:action]}")
17
+ end
18
+
19
+ if OyenCov.config.mode == "test"
20
+ at_exit do
21
+ OyenCov::TestReporting.persist_controller_actions!
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,54 @@
1
+ require_relative "./method_range_parser"
2
+ require_relative "./test_report_merger"
3
+
4
+ # This is meant to be run at the end of the fan-out jobs, and the output
5
+ # artefacts are meant to be persisted for the next job in the workflow.
6
+ #
7
+ # It's possible the full codebase isn't pulled in the final summarising
8
+ # job. User can opt to just `gem install oyencov` and do the collation
9
+ # and submission with just the gem.
10
+ #
11
+ # The code should be similar to `CoveragePeekDelta`.
12
+ #
13
+ # A simplecov .resultset.json file looks like this:
14
+ #
15
+ # ```
16
+ # $test_tool (e.g. Minitest, RSpec) # can disregard
17
+ # "coverage" # constant string
18
+ # $rb_filepath # root relative paths
19
+ # [null, 1, 2, null, ...] # raw `Coverage.result` output
20
+ # ```
21
+ #
22
+ module OyenCov
23
+ module SimplecovResultsetTranslator
24
+ PWD = Dir.pwd
25
+
26
+ # @param [String] Root-relative path to the .resultset.json
27
+ # @return [String] JSON file
28
+ def self.translate(resultset_json_path, persist: false)
29
+ # Open up the JSON
30
+ resultset = JSON.parse(File.read(resultset_json_path))
31
+
32
+ # binding.irb
33
+
34
+ # Loop through all the files
35
+ # Set {"method" => runs, ...}
36
+ all_methods_hits = resultset[resultset.keys[0]]["coverage"].each_pair.map do |file_path, file_attr|
37
+ # file_path = file_path.gsub(/#{PWD}\//o, "")
38
+ line_hits = file_attr["lines"]
39
+ methods_hits = MethodRangeParser[file_path]&.each_pair&.map do |method_name, line_num|
40
+ next if line_num.nil? || line_hits[line_num].nil?
41
+ [method_name, line_hits[line_num]]
42
+ end&.compact.to_h
43
+ # methods_hits
44
+ end.reduce(:merge)
45
+
46
+ # Persist to existing oyencov report?
47
+ if persist
48
+ OyenCov::TestReportMerger.create_or_append!(method_hits: all_methods_hits)
49
+ end
50
+
51
+ all_methods_hits
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,71 @@
1
+ require "json"
2
+
3
+ # Currently Oyencov only tracks
4
+ #
5
+ # Simplecov report collation is not handled here.
6
+ module OyenCov
7
+ module TestReportMerger
8
+ def self.create_or_append!(hash_to_merge)
9
+ resultset_path = OyenCov.config.test_resultset_path
10
+
11
+ # Load
12
+ hash_content = if File.exist?(resultset_path)
13
+ JSON.parse(File.read(resultset_path))
14
+ else
15
+ {}
16
+ end
17
+
18
+ hash_content.merge!(hash_to_merge)
19
+
20
+ # Persist
21
+ File.write(resultset_path, JSON.generate(hash_content))
22
+ end
23
+
24
+ # @param [String]
25
+ # @param [String]
26
+ # @return [Hash]
27
+ def self.collate_job_reports(filepath_glob, save_to = nil)
28
+ # Read and parse their JSONs
29
+ job_reports_files = Dir.glob(filepath_glob)
30
+ # binding.irb
31
+ return if job_reports_files.count == 0
32
+
33
+ job_reports = job_reports_files.map do |f|
34
+ JSON.parse(File.read(f))
35
+ end
36
+
37
+ # Add them up
38
+ collated_report = job_reports.reduce({
39
+ "controller_action_hits" => {},
40
+ "method_hits" => {}
41
+ }) do |i, j|
42
+ ij = {}
43
+ i.keys.each do |metric|
44
+ unless j[metric]
45
+ ij[metric] = i[metric]
46
+ next
47
+ end
48
+
49
+ case metric
50
+ when "controller_action_hits", "method_hits"
51
+ ij[metric] = add_hashes(i[metric], j[metric])
52
+ end
53
+ end
54
+
55
+ ij
56
+ end
57
+
58
+ # Persist to filesystem as JSON
59
+ if !!save_to
60
+ end
61
+
62
+ collated_report
63
+ end
64
+
65
+ private_class_method def self.add_hashes(i, j)
66
+ (i.keys | j.keys).map do |key|
67
+ [key, (i[key] || 0) + (j[key] || 0)]
68
+ end.to_h
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,28 @@
1
+ require "securerandom"
2
+
3
+ # For use in CI only, one-off export
4
+ #
5
+ module OyenCov
6
+ class TestReporting
7
+ def self.persist_controller_actions!
8
+ controller_action_hits = OyenCov::ControllerTracking.snapshot_and_reset!
9
+
10
+ # Persist report file
11
+ test_report_dir = File.expand_path(OyenCov.config.test_reporting_dir)
12
+ FileUtils.mkdir_p(test_report_dir)
13
+ test_report_path = OyenCov.config.test_resultset_path
14
+
15
+ report_content = {
16
+ controller_action_hits:
17
+ }
18
+
19
+ report_content_json = JSON.generate(report_content)
20
+
21
+ File.open(test_report_path, "w+") do |f|
22
+ f.puts(report_content_json)
23
+ end
24
+
25
+ puts "[OyenCov] Saved to #{test_report_path}"
26
+ end
27
+ end
28
+ end
data/lib/oyencov.rb ADDED
@@ -0,0 +1,15 @@
1
+ require_relative "oyencov/configuration"
2
+ require_relative "oyencov/simplecov_resultset_translator"
3
+
4
+ # For now, support only Rails. We bootstrap from Railtie.
5
+ module OyenCov
6
+ VERSION = "0.0.1.pre"
7
+
8
+ def self.config
9
+ @config ||= OyenCov::Configuration.new
10
+ end
11
+
12
+ if defined?(Rails::Railtie) && ENV["OYENONSEN_API_KEY"]
13
+ require_relative "oyencov/railtie"
14
+ end
15
+ end
metadata ADDED
@@ -0,0 +1,160 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: oyencov
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.pre
5
+ platform: ruby
6
+ authors:
7
+ - Anonoz Chong
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-08-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: mocha
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: standard
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: faraday
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: parser
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '2'
90
+ - - "<"
91
+ - !ruby/object:Gem::Version
92
+ version: '4.0'
93
+ type: :runtime
94
+ prerelease: false
95
+ version_requirements: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '2'
100
+ - - "<"
101
+ - !ruby/object:Gem::Version
102
+ version: '4.0'
103
+ - !ruby/object:Gem::Dependency
104
+ name: thor
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ type: :runtime
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ description: Runtime and test reporters.
118
+ email: anonoz@oyencov.com
119
+ executables:
120
+ - oyencov
121
+ extensions: []
122
+ extra_rdoc_files: []
123
+ files:
124
+ - bin/oyencov
125
+ - lib/oyencov.rb
126
+ - lib/oyencov/api_connection.rb
127
+ - lib/oyencov/background.rb
128
+ - lib/oyencov/cli.rb
129
+ - lib/oyencov/configuration.rb
130
+ - lib/oyencov/controller_tracking.rb
131
+ - lib/oyencov/coverage_peek_delta.rb
132
+ - lib/oyencov/method_range_parser.rb
133
+ - lib/oyencov/railtie.rb
134
+ - lib/oyencov/simplecov_resultset_translator.rb
135
+ - lib/oyencov/test_report_merger.rb
136
+ - lib/oyencov/test_reporting.rb
137
+ homepage: https://www.oyencov.com
138
+ licenses:
139
+ - MIT
140
+ metadata: {}
141
+ post_install_message:
142
+ rdoc_options: []
143
+ require_paths:
144
+ - lib
145
+ required_ruby_version: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ version: 3.1.0
150
+ required_rubygems_version: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - ">"
153
+ - !ruby/object:Gem::Version
154
+ version: 1.3.1
155
+ requirements: []
156
+ rubygems_version: 3.3.7
157
+ signing_key:
158
+ specification_version: 4
159
+ summary: Client-side telemetry
160
+ test_files: []