diff_test 0.2.0
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 +7 -0
- data/.standard.yml +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +208 -0
- data/Rakefile +10 -0
- data/lib/diff_test/api_client.rb +38 -0
- data/lib/diff_test/configuration.rb +178 -0
- data/lib/diff_test/file_hash_computer.rb +21 -0
- data/lib/diff_test/helper.rb +37 -0
- data/lib/diff_test/impacted_file_tracker.rb +34 -0
- data/lib/diff_test/integrations/integration.rb +29 -0
- data/lib/diff_test/integrations/minitest/integration.rb +26 -0
- data/lib/diff_test/integrations/minitest/lifecycle.rb +77 -0
- data/lib/diff_test/integrations/rails_js/annotator.rb +43 -0
- data/lib/diff_test/integrations/rails_js/body_processor.rb +69 -0
- data/lib/diff_test/integrations/rails_js/integration.rb +25 -0
- data/lib/diff_test/integrations/rails_js/js_files.rake +48 -0
- data/lib/diff_test/integrations/rails_js/middleware.rb +43 -0
- data/lib/diff_test/integrations/rails_js/processing_skip_analyzer.rb +31 -0
- data/lib/diff_test/integrations/rails_js/railtie.rb +17 -0
- data/lib/diff_test/js_version_hash_computer.rb +24 -0
- data/lib/diff_test/project_version_hash_computer.rb +26 -0
- data/lib/diff_test/should_run_decider.rb +44 -0
- data/lib/diff_test/test_execution.rb +92 -0
- data/lib/diff_test/test_suite_execution.rb +114 -0
- data/lib/diff_test/trackers/base.rb +23 -0
- data/lib/diff_test/trackers/js_file.rb +7 -0
- data/lib/diff_test/trackers/ruby_file.rb +50 -0
- data/lib/diff_test/trackers/singleton_base.rb +43 -0
- data/lib/diff_test/version.rb +5 -0
- data/lib/diff_test.rb +32 -0
- data/sig/diff_test.rbs +4 -0
- metadata +107 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
module DiffTest
|
2
|
+
module Integrations
|
3
|
+
module RailsJs
|
4
|
+
class Annotator
|
5
|
+
def self.annotate_file(file_path)
|
6
|
+
expanded_file_path = DiffTest::Helper.expand_path(file_path)
|
7
|
+
content = File.read(expanded_file_path)
|
8
|
+
content = annotate(content, file_path)
|
9
|
+
File.write(expanded_file_path, content)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.unannotate_file(file_path)
|
13
|
+
expanded_file_path = DiffTest::Helper.expand_path(file_path)
|
14
|
+
content = File.read(expanded_file_path)
|
15
|
+
content = unannotate(content)
|
16
|
+
File.write(expanded_file_path, content)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.annotate(content, file_path)
|
20
|
+
return content if annotated?(content)
|
21
|
+
|
22
|
+
coffee = file_path.end_with?('.coffee')
|
23
|
+
comment = coffee ? '#' : '//'
|
24
|
+
safe_method_call = coffee ? '?' : '?.'
|
25
|
+
|
26
|
+
annotation = "#{comment} Automatically added by DiffTest\n(globalThis || window)?.diffTestTrackJsFile#{safe_method_call}(\"#{file_path}\");\n#{comment} Automatically added by DiffTest\n"
|
27
|
+
|
28
|
+
annotation + content
|
29
|
+
end
|
30
|
+
|
31
|
+
UNANNOTATE_REGEX = /(\/\/|#) Automatically added by DiffTest.*?(\/\/|#) Automatically added by DiffTest\n/m
|
32
|
+
def self.unannotate(content)
|
33
|
+
content.gsub!(UNANNOTATE_REGEX, '')
|
34
|
+
content
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.annotated?(content)
|
38
|
+
content.include?('diffTestTrackJsFile')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module DiffTest
|
2
|
+
module Integrations
|
3
|
+
module RailsJs
|
4
|
+
class BodyProcessor
|
5
|
+
HEAD_TAG_REGEX = /<head( [^<]+)?>/
|
6
|
+
|
7
|
+
attr_reader :content_length, :new_body
|
8
|
+
|
9
|
+
def initialize(body, options)
|
10
|
+
@body = body
|
11
|
+
@options = options
|
12
|
+
end
|
13
|
+
|
14
|
+
def process!(env)
|
15
|
+
@env = env
|
16
|
+
@body.close if @body.respond_to?(:close)
|
17
|
+
|
18
|
+
@new_body = []
|
19
|
+
@body.each { |line| @new_body << line.to_s }
|
20
|
+
|
21
|
+
@content_length = 0
|
22
|
+
@script_added = false
|
23
|
+
|
24
|
+
@new_body = @new_body.map do |line|
|
25
|
+
if !@script_added && line['<head']
|
26
|
+
line = line.gsub(HEAD_TAG_REGEX) { |match| %(#{match}#{script}) }
|
27
|
+
|
28
|
+
@script_added = true
|
29
|
+
end
|
30
|
+
|
31
|
+
@content_length += line.bytesize
|
32
|
+
line
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def script
|
37
|
+
<<~HTML
|
38
|
+
<script>
|
39
|
+
var owner = globalThis || window
|
40
|
+
owner.diffTestTrackedJsFiles = owner.diffTestTrackedJsFiles || [];
|
41
|
+
owner.diffTestTrackJsFile = function(file_path) {
|
42
|
+
const path = "/__diff_test_js_beacon";
|
43
|
+
const payload = JSON.stringify({ test_id: #{DiffTest::TestExecution.current.id.to_json}, file_path: file_path });
|
44
|
+
|
45
|
+
owner.diffTestTrackedJsFiles.push(file_path);
|
46
|
+
if (owner.navigator && owner.navigator.sendBeacon) {
|
47
|
+
// Use sendBeacon in capable browsers/environments
|
48
|
+
window.navigator.sendBeacon(path, payload);
|
49
|
+
} else {
|
50
|
+
// Fallback: Use fetch from Service Worker or other Worker
|
51
|
+
fetch(path, {
|
52
|
+
method: 'POST',
|
53
|
+
headers: { 'Content-Type': 'application/json' },
|
54
|
+
body: payload
|
55
|
+
}).catch((err) => {
|
56
|
+
// Handle error logging or silent fail
|
57
|
+
console.error('diff_test_js_beacon_failure', err);
|
58
|
+
});
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
owner.diffTestTrackJsFileAuto = owner.diffTestTrackJsFile;
|
63
|
+
</script>
|
64
|
+
HTML
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative '../integration'
|
2
|
+
|
3
|
+
module DiffTest
|
4
|
+
module Integrations
|
5
|
+
module RailsJs
|
6
|
+
class Integration < DiffTest::Integrations::Integration
|
7
|
+
def gem_name
|
8
|
+
'rails'
|
9
|
+
end
|
10
|
+
|
11
|
+
def minimum_compatible_version
|
12
|
+
'4.0.0'
|
13
|
+
end
|
14
|
+
|
15
|
+
def loaded?
|
16
|
+
supported? && defined?(::Rails)
|
17
|
+
end
|
18
|
+
|
19
|
+
def integrate
|
20
|
+
require_relative 'railtie'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require_relative 'annotator'
|
2
|
+
|
3
|
+
namespace :diff_test do
|
4
|
+
desc 'Annotate your js files with autoTrackJsFile call'
|
5
|
+
task annotate: :environment do |_, args|
|
6
|
+
DiffTest.configuration.tracked_js_files.each do |file_path|
|
7
|
+
DiffTest::Integrations::RailsJs::Annotator.annotate_file(file_path)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
task unannotate: :environment do |_, args|
|
12
|
+
DiffTest.configuration.tracked_js_files.each do |file_path|
|
13
|
+
DiffTest::Integrations::RailsJs::Annotator.unannotate_file(file_path)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
task __auto_annotate_before_precompile: :environment do |_, args|
|
18
|
+
next unless DiffTest.configuration.auto_annotate_js_files?
|
19
|
+
next unless Rails.env.test?
|
20
|
+
|
21
|
+
Rake::Task['diff_test:annotate'].invoke
|
22
|
+
end
|
23
|
+
|
24
|
+
task __auto_unannotate_before_clobber: :environment do |_, args|
|
25
|
+
next unless DiffTest.configuration.auto_annotate_js_files?
|
26
|
+
next unless Rails.env.test?
|
27
|
+
|
28
|
+
Rake::Task['diff_test:unannotate'].invoke
|
29
|
+
end
|
30
|
+
|
31
|
+
task __auto_annotate_before_test_prepare: :environment do |_, args|
|
32
|
+
next unless DiffTest.configuration.auto_annotate_js_files?
|
33
|
+
next unless Rails.env.test?
|
34
|
+
|
35
|
+
Rake::Task['diff_test:annotate'].invoke
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
Rake::Task['assets:precompile'].enhance(['diff_test:__auto_annotate_before_precompile']) if Rake::Task.task_defined?('assets:precompile')
|
40
|
+
Rake::Task['assets:clobber'].enhance(['diff_test:__auto_unannotate_before_clobber']) if Rake::Task.task_defined?('assets:clobber')
|
41
|
+
|
42
|
+
if Rake::Task.task_defined?("test:prepare")
|
43
|
+
Rake::Task["test:prepare"].enhance(["diff_test:__auto_annotate_before_test_prepare"])
|
44
|
+
elsif Rake::Task.task_defined?("spec:prepare")
|
45
|
+
Rake::Task["spec:prepare"].enhance(["diff_test:__auto_annotate_before_test_prepare"])
|
46
|
+
elsif Rake::Task.task_defined?("db:test:prepare")
|
47
|
+
Rake::Task["db:test:prepare"].enhance(["diff_test:__auto_annotate_before_test_prepare"])
|
48
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require_relative 'processing_skip_analyzer'
|
2
|
+
require_relative 'body_processor'
|
3
|
+
|
4
|
+
module DiffTest
|
5
|
+
module Integrations
|
6
|
+
module RailsJs
|
7
|
+
class Middleware
|
8
|
+
def initialize(app)
|
9
|
+
@app = app
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
dup._call(env)
|
14
|
+
end
|
15
|
+
|
16
|
+
def _call(env)
|
17
|
+
path = env['PATH_INFO'] || ''
|
18
|
+
|
19
|
+
if path.include?('__diff_test_js_beacon')
|
20
|
+
body = env['rack.input'].read
|
21
|
+
json = JSON.parse(body)
|
22
|
+
DiffTest::Trackers::JsFile.record_by_test_id(json['test_id'], json['file_path'])
|
23
|
+
[204, {}, []]
|
24
|
+
else
|
25
|
+
env.delete('HTTP_IF_NONE_MATCH')
|
26
|
+
env.delete('HTTP_IF_MODIFIED_SINCE')
|
27
|
+
|
28
|
+
status, headers, body = result = @app.call(env)
|
29
|
+
|
30
|
+
return result if ProcessingSkipAnalyzer.skip_processing?(result, env, @options)
|
31
|
+
|
32
|
+
processor = BodyProcessor.new(body, @options)
|
33
|
+
processor.process!(env)
|
34
|
+
|
35
|
+
headers['content-length'] = processor.content_length.to_s
|
36
|
+
|
37
|
+
[status, headers, processor.new_body]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module DiffTest
|
2
|
+
module Integrations
|
3
|
+
module RailsJs
|
4
|
+
class ProcessingSkipAnalyzer
|
5
|
+
def self.skip_processing?(result, env, options)
|
6
|
+
new(result, env, options).skip_processing?
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(result, env, options)
|
10
|
+
@env = env
|
11
|
+
@options = options
|
12
|
+
|
13
|
+
@status, @headers, @body = result
|
14
|
+
end
|
15
|
+
|
16
|
+
def skip_processing?
|
17
|
+
!html? || chunked?
|
18
|
+
end
|
19
|
+
|
20
|
+
def chunked?
|
21
|
+
@headers['Transfer-Encoding'] == 'chunked'
|
22
|
+
end
|
23
|
+
|
24
|
+
def html?
|
25
|
+
content_type = @headers['Content-Type'] || @headers['content-type']
|
26
|
+
content_type&.include?('text/html')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require_relative 'middleware'
|
2
|
+
module DiffTest
|
3
|
+
module Integrations
|
4
|
+
module RailsJs
|
5
|
+
class Railtie < Rails::Railtie
|
6
|
+
initializer 'diff_test.middleware' do |app|
|
7
|
+
app.middleware.insert_before 0, DiffTest::Integrations::RailsJs::Middleware if Rails.env.test?
|
8
|
+
end
|
9
|
+
|
10
|
+
rake_tasks do
|
11
|
+
path = File.expand_path(__dir__)
|
12
|
+
load "#{path}/js_files.rake"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'json'
|
2
|
+
require_relative 'integrations/rails_js/annotator'
|
3
|
+
|
4
|
+
module DiffTest
|
5
|
+
class JsVersionHashComputer
|
6
|
+
def self.compute
|
7
|
+
new.compute
|
8
|
+
end
|
9
|
+
|
10
|
+
def compute
|
11
|
+
string = file_paths_to_use.map do |file_path|
|
12
|
+
[file_path, FileHashComputer.compute_relative(file_path)]
|
13
|
+
end.sort_by(&:first).to_json
|
14
|
+
|
15
|
+
Digest::SHA256.hexdigest(string)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def file_paths_to_use
|
21
|
+
DiffTest.configuration.tracked_js_files
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module DiffTest
|
4
|
+
class ProjectVersionHashComputer
|
5
|
+
def self.compute
|
6
|
+
new.compute
|
7
|
+
end
|
8
|
+
|
9
|
+
def compute
|
10
|
+
string = file_paths_to_use.map do |file_path|
|
11
|
+
[file_path, XXhash.xxh64_file(Helper.expand_path(file_path))]
|
12
|
+
end.sort_by(&:first).to_json
|
13
|
+
|
14
|
+
Digest::SHA256.hexdigest(string)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def file_paths_to_use
|
20
|
+
DiffTest.configuration.project_files -
|
21
|
+
DiffTest.configuration.ignored_files -
|
22
|
+
DiffTest.configuration.tracked_files -
|
23
|
+
DiffTest.configuration.tracked_js_files
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module DiffTest
|
2
|
+
class ShouldRunDecider
|
3
|
+
def initialize
|
4
|
+
previous_compatible_test_executions = DiffTest::TestSuiteExecution.current.previous_compatible_test_executions
|
5
|
+
@impacted_file_path_by_id = previous_compatible_test_executions['impacted_file_path_by_id']
|
6
|
+
@test_suite_executions_by_id = previous_compatible_test_executions['test_suite_executions_by_id']
|
7
|
+
@test_executions_by_test = previous_compatible_test_executions['test_executions_by_test']
|
8
|
+
end
|
9
|
+
|
10
|
+
def should_run?(test_id, system_test:)
|
11
|
+
previous_test_executions = @test_executions_by_test[test_id] || []
|
12
|
+
|
13
|
+
# If there are any previous test executions run whose impacted files are the same as now
|
14
|
+
# Assume its safe to skip
|
15
|
+
has_previous_execution_with_no_impacted_changes = previous_test_executions.any? do |previous_test_execution|
|
16
|
+
hashes_match = previous_test_execution['test_execution_impacted_files'].all? do |test_execution_impacted_file|
|
17
|
+
impacted_file_id = test_execution_impacted_file['impacted_file_id'].to_s
|
18
|
+
file_hash = test_execution_impacted_file['file_hash']
|
19
|
+
impacted_file_path = @impacted_file_path_by_id[impacted_file_id]
|
20
|
+
|
21
|
+
DiffTest::FileHashComputer.compute_relative(impacted_file_path) == file_hash
|
22
|
+
end
|
23
|
+
next false unless hashes_match
|
24
|
+
next true unless system_test
|
25
|
+
|
26
|
+
test_suite_execution = @test_suite_executions_by_id[previous_test_execution['test_suite_execution_id'].to_s]
|
27
|
+
next true if test_suite_execution['all_js_files_annotated']
|
28
|
+
next true if test_suite_execution['js_version_hash'] == DiffTest::TestSuiteExecution.current.js_version_hash
|
29
|
+
|
30
|
+
false
|
31
|
+
end
|
32
|
+
|
33
|
+
!has_previous_execution_with_no_impacted_changes
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.current
|
37
|
+
@current ||= DiffTest::ShouldRunDecider.new
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.should_run?(test_id, system_test:)
|
41
|
+
current.should_run?(test_id, system_test: system_test)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require_relative 'should_run_decider'
|
2
|
+
|
3
|
+
module DiffTest
|
4
|
+
class TestExecution
|
5
|
+
attr_reader :impacted_file_tracker
|
6
|
+
|
7
|
+
def initialize(test_file_path:, test_name:)
|
8
|
+
@test_file_path = test_file_path
|
9
|
+
@test_name = test_name
|
10
|
+
@result = :queued
|
11
|
+
@started_at = Time.now
|
12
|
+
@stopped_at = Time.now
|
13
|
+
@impacted_file_tracker = DiffTest::ImpactedFileTracker.new
|
14
|
+
self.class.set(id, self)
|
15
|
+
end
|
16
|
+
|
17
|
+
def should_run?
|
18
|
+
return @should_run unless @should_run.nil?
|
19
|
+
@should_run = DiffTest::ShouldRunDecider.should_run?(id, system_test: system_test?)
|
20
|
+
end
|
21
|
+
|
22
|
+
def save_payload
|
23
|
+
{
|
24
|
+
test: id,
|
25
|
+
result: @result,
|
26
|
+
runtime_ms: runtime_ms,
|
27
|
+
impacted_files: @impacted_file_tracker.save_payload,
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def id
|
32
|
+
@id ||= DiffTest::Helper.test_id(@test_file_path, @test_name)
|
33
|
+
end
|
34
|
+
|
35
|
+
def start
|
36
|
+
@impacted_file_tracker.start
|
37
|
+
@started_at = Time.now
|
38
|
+
end
|
39
|
+
|
40
|
+
def stop
|
41
|
+
@impacted_file_tracker.stop
|
42
|
+
@stopped_at = Time.now
|
43
|
+
finish
|
44
|
+
end
|
45
|
+
|
46
|
+
def finish
|
47
|
+
DiffTest::TestSuiteExecution.current.record_test_execution(self)
|
48
|
+
end
|
49
|
+
|
50
|
+
def not_run!
|
51
|
+
@result = :not_run
|
52
|
+
end
|
53
|
+
|
54
|
+
def skipped!
|
55
|
+
@result = :skipped
|
56
|
+
end
|
57
|
+
|
58
|
+
def passed!
|
59
|
+
@result = :passed
|
60
|
+
end
|
61
|
+
|
62
|
+
def failed!
|
63
|
+
@result = :failed
|
64
|
+
end
|
65
|
+
|
66
|
+
def runtime_ms
|
67
|
+
return 0 if @stopped_at.nil? || @started_at.nil?
|
68
|
+
(@stopped_at - @started_at) * 1000
|
69
|
+
end
|
70
|
+
|
71
|
+
def system_test?
|
72
|
+
DiffTest.configuration.system_test_paths.include?(@test_file_path)
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.set(test_id, instance)
|
76
|
+
@by_id ||= {}
|
77
|
+
@by_id[test_id] = instance
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.get(test_id)
|
81
|
+
@by_id[test_id]
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.current
|
85
|
+
@current
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.current=(instance)
|
89
|
+
@current = instance
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require_relative 'project_version_hash_computer'
|
2
|
+
require_relative 'js_version_hash_computer'
|
3
|
+
|
4
|
+
module DiffTest
|
5
|
+
class TestSuiteExecution
|
6
|
+
def initialize(tests:)
|
7
|
+
@tests = tests.compact
|
8
|
+
@test_executions = []
|
9
|
+
|
10
|
+
find_or_create_on_server
|
11
|
+
end
|
12
|
+
|
13
|
+
def previous_compatible_test_executions
|
14
|
+
DiffTest::ApiClient.post("/test_suite_executions/#{id}/previous_compatible_test_executions", body: { tests: @tests })
|
15
|
+
end
|
16
|
+
|
17
|
+
def ensure_application_eager_loaded!
|
18
|
+
return if @eager_loaded
|
19
|
+
@eager_loaded = true
|
20
|
+
::Rails.application.eager_load! if defined?(::Rails)
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def record_test_execution(test_execution)
|
25
|
+
@test_executions << test_execution
|
26
|
+
end
|
27
|
+
|
28
|
+
def run?(test_file_path, test_name)
|
29
|
+
should_run_decider.run?(test_file_path, test_name)
|
30
|
+
end
|
31
|
+
|
32
|
+
def save
|
33
|
+
save_test_execution_results_on_server
|
34
|
+
end
|
35
|
+
|
36
|
+
def js_version_hash
|
37
|
+
@js_version_hash ||= DiffTest::JsVersionHashComputer.compute
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.current
|
41
|
+
@current
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.current=(value)
|
45
|
+
@current = value
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.find_or_create(tests)
|
49
|
+
self.current ||= new(tests:)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def id
|
55
|
+
@id || raise('Not created')
|
56
|
+
end
|
57
|
+
|
58
|
+
def find_or_create_on_server
|
59
|
+
response = DiffTest::ApiClient.post("/test_suite_executions", body: payload)
|
60
|
+
raise "Failed to create test suite execution: #{response.inspect}" unless response.success?
|
61
|
+
@id = response['id']
|
62
|
+
end
|
63
|
+
|
64
|
+
def save_test_execution_results_on_server
|
65
|
+
raise "Expected to have at least one test execution" if @test_executions.empty?
|
66
|
+
response = DiffTest::ApiClient.post("/test_suite_executions/#{id}/test_executions/bulk_create", body: { test_executions: @test_executions.map(&:save_payload) })
|
67
|
+
end
|
68
|
+
|
69
|
+
def payload
|
70
|
+
{
|
71
|
+
key: key,
|
72
|
+
project_version_hash: project_version_hash,
|
73
|
+
js_version_hash: js_version_hash,
|
74
|
+
all_js_files_annotated: all_js_files_annotated?,
|
75
|
+
rev_sha: rev_sha,
|
76
|
+
commit_title: commit_title,
|
77
|
+
branch_name: branch_name,
|
78
|
+
ci_platform: DiffTest.configuration.ci_platform,
|
79
|
+
meta: meta,
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
def key
|
84
|
+
ENV['CIRCLE_WORKFLOW_ID'] || SecureRandom.hex(48) || rev_sha
|
85
|
+
end
|
86
|
+
|
87
|
+
def project_version_hash
|
88
|
+
@project_version_hash ||= DiffTest::ProjectVersionHashComputer.compute
|
89
|
+
end
|
90
|
+
|
91
|
+
def all_js_files_annotated?
|
92
|
+
@all_js_files_annotated ||= DiffTest.configuration.tracked_js_files.all? do |file_path|
|
93
|
+
file_path = DiffTest::Helper.expand_path(file_path)
|
94
|
+
DiffTest::Integrations::RailsJs::Annotator.annotated?(File.read(file_path))
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def rev_sha
|
99
|
+
@rev_sha ||= `git rev-parse HEAD`.strip
|
100
|
+
end
|
101
|
+
|
102
|
+
def commit_title
|
103
|
+
@commit_title ||= `git log -1 --pretty=%B`.strip
|
104
|
+
end
|
105
|
+
|
106
|
+
def branch_name
|
107
|
+
@branch_name ||= `git rev-parse --abbrev-ref HEAD`.strip
|
108
|
+
end
|
109
|
+
|
110
|
+
def meta
|
111
|
+
{}
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module DiffTest
|
2
|
+
module Trackers
|
3
|
+
class Base
|
4
|
+
attr_reader :impacted_files
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@impacted_files = Set.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def enable
|
11
|
+
raise NotImplementedError
|
12
|
+
end
|
13
|
+
|
14
|
+
def disable
|
15
|
+
raise NotImplementedError
|
16
|
+
end
|
17
|
+
|
18
|
+
def record(path)
|
19
|
+
@impacted_files << path
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
module DiffTest
|
3
|
+
module Trackers
|
4
|
+
class RubyFile < DiffTest::Trackers::Base
|
5
|
+
@@location_by_constant = {}
|
6
|
+
|
7
|
+
def enable
|
8
|
+
@seen_constants = Set.new
|
9
|
+
@tracepoint = TracePoint.new(:call, :c_call, :b_call) do |event|
|
10
|
+
if DiffTest::Helper.file_in_project?(event.path)
|
11
|
+
record(DiffTest::Helper.relative_path_from_project_root(event.path))
|
12
|
+
end
|
13
|
+
|
14
|
+
constant = event.defined_class
|
15
|
+
constant = event.self if constant == Class && event.method_id == :new
|
16
|
+
|
17
|
+
unless @seen_constants.include?(constant)
|
18
|
+
@seen_constants.add(constant)
|
19
|
+
constant = constant.attached_object if constant&.singleton_class?
|
20
|
+
|
21
|
+
path = @@location_by_constant[constant]
|
22
|
+
|
23
|
+
if path.nil?
|
24
|
+
if constant
|
25
|
+
path = begin
|
26
|
+
DiffTest::Helper.const_source_path(constant.name)
|
27
|
+
rescue StandardError
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
end
|
31
|
+
path = false if !path || !DiffTest::Helper.file_in_project?(path)
|
32
|
+
path = DiffTest::Helper.relative_path_from_project_root(path) if path
|
33
|
+
@@location_by_constant[constant] = path
|
34
|
+
end
|
35
|
+
|
36
|
+
record(path) if path
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
@tracepoint.enable
|
41
|
+
end
|
42
|
+
|
43
|
+
def disable
|
44
|
+
@tracepoint.disable
|
45
|
+
@tracepoint = nil
|
46
|
+
@seen_constants = nil
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|