dragnet 5.2.1

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.
Files changed (86) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/release.yml +45 -0
  3. data/.github/workflows/ruby-linters.yml +41 -0
  4. data/.github/workflows/ruby-tests.yml +38 -0
  5. data/.github/workflows/sphinx-doc.yml +79 -0
  6. data/.gitignore +13 -0
  7. data/.reek.yml +15 -0
  8. data/.rspec +3 -0
  9. data/.rubocop.yml +14 -0
  10. data/.ruby-version +1 -0
  11. data/.travis.yml +6 -0
  12. data/CHANGELOG.md +178 -0
  13. data/Gemfile +18 -0
  14. data/README.md +119 -0
  15. data/Rakefile +8 -0
  16. data/bin/console +14 -0
  17. data/bin/setup +8 -0
  18. data/default.reek +7 -0
  19. data/dragnet.gemspec +39 -0
  20. data/exe/dragnet +9 -0
  21. data/lib/dragnet/base_repository.rb +69 -0
  22. data/lib/dragnet/cli/base.rb +72 -0
  23. data/lib/dragnet/cli/logger.rb +72 -0
  24. data/lib/dragnet/cli/master.rb +173 -0
  25. data/lib/dragnet/cli.rb +8 -0
  26. data/lib/dragnet/errors/error.rb +8 -0
  27. data/lib/dragnet/errors/file_not_found_error.rb +11 -0
  28. data/lib/dragnet/errors/incompatible_repository_error.rb +10 -0
  29. data/lib/dragnet/errors/missing_timestamp_attribute_error.rb +11 -0
  30. data/lib/dragnet/errors/no_mtr_files_found_error.rb +11 -0
  31. data/lib/dragnet/errors/not_a_repository_error.rb +11 -0
  32. data/lib/dragnet/errors/repo_path_not_found_error.rb +10 -0
  33. data/lib/dragnet/errors/unable_to_write_report_error.rb +11 -0
  34. data/lib/dragnet/errors/unknown_export_format_error.rb +11 -0
  35. data/lib/dragnet/errors/validation_error.rb +11 -0
  36. data/lib/dragnet/errors/yaml_format_error.rb +9 -0
  37. data/lib/dragnet/errors.rb +9 -0
  38. data/lib/dragnet/explorer.rb +103 -0
  39. data/lib/dragnet/exporter.rb +130 -0
  40. data/lib/dragnet/exporters/exporter.rb +29 -0
  41. data/lib/dragnet/exporters/html_exporter.rb +158 -0
  42. data/lib/dragnet/exporters/id_generator.rb +35 -0
  43. data/lib/dragnet/exporters/json_exporter.rb +34 -0
  44. data/lib/dragnet/exporters/serializers/repo_serializer.rb +40 -0
  45. data/lib/dragnet/exporters/serializers/test_record_serializer.rb +101 -0
  46. data/lib/dragnet/exporters/serializers/verification_result_serializer.rb +34 -0
  47. data/lib/dragnet/exporters/serializers.rb +12 -0
  48. data/lib/dragnet/exporters/templates/template.html.erb +518 -0
  49. data/lib/dragnet/exporters.rb +13 -0
  50. data/lib/dragnet/helpers/repository_helper.rb +27 -0
  51. data/lib/dragnet/multi_repository.rb +48 -0
  52. data/lib/dragnet/repo.rb +31 -0
  53. data/lib/dragnet/repository.rb +77 -0
  54. data/lib/dragnet/test_record.rb +91 -0
  55. data/lib/dragnet/validator.rb +79 -0
  56. data/lib/dragnet/validators/data_validator.rb +75 -0
  57. data/lib/dragnet/validators/entities/repo_validator.rb +31 -0
  58. data/lib/dragnet/validators/entities/test_record_validator.rb +93 -0
  59. data/lib/dragnet/validators/entities.rb +12 -0
  60. data/lib/dragnet/validators/fields/description_validator.rb +24 -0
  61. data/lib/dragnet/validators/fields/field_validator.rb +69 -0
  62. data/lib/dragnet/validators/fields/files_validator.rb +29 -0
  63. data/lib/dragnet/validators/fields/id_validator.rb +36 -0
  64. data/lib/dragnet/validators/fields/meta_data_field_validator.rb +36 -0
  65. data/lib/dragnet/validators/fields/path_validator.rb +22 -0
  66. data/lib/dragnet/validators/fields/repos_validator.rb +62 -0
  67. data/lib/dragnet/validators/fields/result_validator.rb +33 -0
  68. data/lib/dragnet/validators/fields/sha1_validator.rb +42 -0
  69. data/lib/dragnet/validators/fields.rb +17 -0
  70. data/lib/dragnet/validators/files_validator.rb +85 -0
  71. data/lib/dragnet/validators/repos_validator.rb +74 -0
  72. data/lib/dragnet/validators/validator.rb +20 -0
  73. data/lib/dragnet/validators.rb +12 -0
  74. data/lib/dragnet/verification_result.rb +125 -0
  75. data/lib/dragnet/verifier.rb +64 -0
  76. data/lib/dragnet/verifiers/changes_verifier.rb +70 -0
  77. data/lib/dragnet/verifiers/files_verifier.rb +51 -0
  78. data/lib/dragnet/verifiers/repos_verifier.rb +92 -0
  79. data/lib/dragnet/verifiers/repository_verifier.rb +27 -0
  80. data/lib/dragnet/verifiers/result_verifier.rb +32 -0
  81. data/lib/dragnet/verifiers/test_record_verifier.rb +85 -0
  82. data/lib/dragnet/verifiers/verifier.rb +37 -0
  83. data/lib/dragnet/verifiers.rb +8 -0
  84. data/lib/dragnet/version.rb +5 -0
  85. data/lib/dragnet.rb +18 -0
  86. metadata +190 -0
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../repo'
4
+ require_relative 'field_validator'
5
+
6
+ module Dragnet
7
+ module Validators
8
+ module Fields
9
+ # Validates that the +repos+ attribute in an MTR is valid. This means:
10
+ # * It is either a +Hash+ or an +Array+ of +Hash+es.
11
+ # * The attributes inside each of the +Hash+es are also valid.
12
+ class ReposValidator < Dragnet::Validators::Fields::FieldValidator
13
+ # Validates the MTR's +repos+ field.
14
+ # @param [String] key The name of the key (usually +'repos'+)
15
+ # @param [Object] value The value associated to the attribute.
16
+ # @return [Array<Dragnet::Repo>, nil] If +value+ is a valid +Hash+ or a
17
+ # valid +Array+ of +Hash+es an +Array+ of +Dragnet::Repo+ objects is
18
+ # returned. If +value+ is +nil+, +nil+ is returned.
19
+ # @raise [Dragnet::Errors::ValidationError] If +value+ is not a +Hash+
20
+ # or an +Array+ of +Hash+es or the attributes inside the +Hash+es are
21
+ # invalid.
22
+ # @see Dragnet::Repo#validate
23
+ def validate(key, value)
24
+ return unless value
25
+
26
+ validate_type(key, value, Hash, Array)
27
+
28
+ if value.is_a?(Array)
29
+ return if value.empty?
30
+
31
+ validate_array_types(key, value, Hash)
32
+ else
33
+ # This is needed because trying to apply the splat operator over a
34
+ # Hash will result in an Array of Arrays (one for each of the Hash's
35
+ # key pairs).
36
+ value = [value]
37
+ end
38
+
39
+ create_repos(value)
40
+ end
41
+
42
+ private
43
+
44
+ # @param [Array<Hash>] hashes The array of +Hash+es from which the Repo
45
+ # objects shall be created.
46
+ # @return [Array<Dragnet::Repo>] The array of +Dragnet::Repo+ objects
47
+ # that result from using each of the given +Hash+es as parameters for
48
+ # the constructor.
49
+ # @raise [Dragnet::Errors::ValidationError] If the attributes inside the
50
+ # +Hash+es are invalid.
51
+ # @see Dragnet::Repo#validate
52
+ def create_repos(hashes)
53
+ hashes.map do |hash|
54
+ repo = Dragnet::Repo.new(**hash)
55
+ repo.validate
56
+ repo
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'field_validator'
4
+
5
+ module Dragnet
6
+ module Validators
7
+ module Fields
8
+ # Validates the result field of an MTR Record
9
+ class ResultValidator < Dragnet::Validators::Fields::FieldValidator
10
+ VALID_RESULTS = %w[passed failed].freeze
11
+
12
+ # Validates the MTR's result
13
+ # @param [String] key The name of the key
14
+ # @param [Object] value The value of the key
15
+ # @return [String] The downcase version of the result field.
16
+ # @raise [Dragnet::Errors::ValidationError] If the result is missing, if
17
+ # it isn't a String or is not one of the allowed values for the field.
18
+ def validate(key, value)
19
+ validate_presence(key, value)
20
+ validate_type(key, value, String)
21
+
22
+ value = value.downcase
23
+ return value if VALID_RESULTS.include?(value)
24
+
25
+ validation_error(
26
+ "Invalid value for key result: '#{value}'. "\
27
+ "Valid values are #{VALID_RESULTS.join(', ')}"
28
+ )
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'field_validator'
4
+
5
+ module Dragnet
6
+ module Validators
7
+ module Fields
8
+ # Validates the SHA1 field of a Manual Test Record
9
+ class SHA1Validator < Dragnet::Validators::Fields::FieldValidator
10
+ SHA1_MIN_LENGTH = 7
11
+ SHA1_MAX_LENGTH = 40
12
+ SHA1_REGEX = /\A[0-9a-f]+\Z/.freeze
13
+
14
+ # Validates the SHA1 of the MTR
15
+ # @param [String] key The name of the key
16
+ # @param [Object] value The value of the key
17
+ # @raise [Dragnet::Errors::ValidationError] If the SHA1 is missing, is not
18
+ # and string, is too short or too long or is not a valid hexadecimal
19
+ # string.
20
+ def validate(key, value)
21
+ validate_presence(key, value)
22
+ validate_type(key, value, String)
23
+
24
+ length = value.length
25
+ unless length >= SHA1_MIN_LENGTH && length <= SHA1_MAX_LENGTH
26
+ validation_error(
27
+ "Invalid value for key #{key}: '#{value}'. Expected a string between "\
28
+ "#{SHA1_MIN_LENGTH} and #{SHA1_MAX_LENGTH} characters"
29
+ )
30
+ end
31
+
32
+ return if value.match(SHA1_REGEX)
33
+
34
+ validation_error(
35
+ "Invalid value for key #{key}: '#{value}'. "\
36
+ "Doesn't seem to be a valid hexadecimal string"
37
+ )
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'fields/description_validator'
4
+ require_relative 'fields/files_validator'
5
+ require_relative 'fields/id_validator'
6
+ require_relative 'fields/meta_data_field_validator'
7
+ require_relative 'fields/path_validator'
8
+ require_relative 'fields/repos_validator'
9
+ require_relative 'fields/result_validator'
10
+ require_relative 'fields/sha1_validator'
11
+
12
+ module Dragnet
13
+ module Validators
14
+ # Namespace module for entity fields validators.
15
+ module Fields; end
16
+ end
17
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../errors/file_not_found_error'
4
+ require_relative 'validator'
5
+
6
+ module Dragnet
7
+ module Validators
8
+ # Validates the +files+ key in the given Manual Test Record object.
9
+ # Validates:
10
+ # - That the listed file(s) glob pattern(s) match at least one file in the
11
+ # repository.
12
+ class FilesValidator < Dragnet::Validators::Validator
13
+ attr_reader :test_record, :path
14
+
15
+ # Creates a new instance of the class.
16
+ # @param [Dragnet::TestRecord] test_record The +TestRecord+ object whose
17
+ # files should be validated.
18
+ # @param [Pathname] path The path to the repository where the files are
19
+ # supposed to be located.
20
+ def initialize(test_record, path)
21
+ @test_record = test_record
22
+ @files = test_record.files
23
+ @path = path
24
+ end
25
+
26
+ # Validates the +files+ key in the given data.
27
+ # Updates the +file+ key in the given +data+ to the actual files found in
28
+ # the repository.
29
+ # @raise [Dragnet::Errors::FileNotFoundError] If any of the listed files
30
+ # cannot be found in the given repository path or if a glob pattern
31
+ # doesn't match any files there.
32
+ def validate
33
+ return unless files
34
+
35
+ test_record.files = translate_paths && force_relative_paths && resolve_files
36
+ end
37
+
38
+ private
39
+
40
+ attr_reader :files
41
+
42
+ # Forces all the file paths to be relative by removing the +/+ at the
43
+ # start (if they have one). This is done to ensure that files are always
44
+ # considered relative to the path being checked.
45
+ def force_relative_paths
46
+ @files = files.map do |file|
47
+ file.sub(%r{^/}, '')
48
+ end
49
+ end
50
+
51
+ # Translate the file paths from windows style paths (with \ as path
52
+ # separator) to Unix style paths (with / as path separator).
53
+ # This is done so that the git commands work in all systems.
54
+ def translate_paths
55
+ @files = files.map do |file|
56
+ file.tr('\\', '/')
57
+ end
58
+ end
59
+
60
+ # Resolve the given files by checking for matches in the given repository.
61
+ # Glob patterns are resolved an translated into individual files.
62
+ # @return [Array<Pathname>] The resolved file paths.
63
+ # @raise [Dragnet::Errors::FileNotFoundError] If any of the listed files
64
+ # cannot be found in the given repository path or if a glob pattern
65
+ # doesn't match any files there.
66
+ def resolve_files
67
+ resolved_files = []
68
+
69
+ files.each do |file|
70
+ # Files can be defined as glob patterns
71
+ matched_files = path.glob(file)
72
+
73
+ if matched_files.empty?
74
+ raise Dragnet::Errors::FileNotFoundError,
75
+ "Could not find any files matching #{file} in #{path}"
76
+ end
77
+
78
+ resolved_files += matched_files
79
+ end
80
+
81
+ resolved_files
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../errors/repo_path_not_found_error'
4
+ require_relative 'files_validator'
5
+
6
+ module Dragnet
7
+ module Validators
8
+ # Validates the +Repo+ objects attached to the given +TestRecord+
9
+ class ReposValidator < Dragnet::Validators::Validator
10
+ attr_reader :test_record, :repos, :path
11
+
12
+ # @param [Dragnet::TestRecord] test_record The Test Record to validate.
13
+ # @param [Pathname] path The path where the repositories are supposed to
14
+ # be located.
15
+ def initialize(test_record, path)
16
+ @test_record = test_record
17
+ @path = path
18
+ @repos = test_record.repos
19
+ end
20
+
21
+ # Validates the +Repo+ objects inside the given +TestCase+
22
+ def validate
23
+ return unless repos
24
+
25
+ validate_paths
26
+ end
27
+
28
+ private
29
+
30
+ # Validates the +paths+ of the +Repo+ objects (makes sure the paths
31
+ # exist). Knowing that these paths exist, the +files+ attribute of the
32
+ # +Repo+ object can be validated as well.
33
+ # @raise [Dragnet::Errors::RepoPathNotFoundError] If one or more of the
34
+ # paths cannot be found.
35
+ # @raise [Dragnet::Errors::FileNotFoundError] If any of the files listed
36
+ # in the +files+ attribute do not exist inside the given +path+.
37
+ def validate_paths
38
+ repos.each do |repo|
39
+ repo_path = repo.path = repo.path.gsub('\\', '/')
40
+ repo_path = Pathname.new(repo_path)
41
+
42
+ complete_path = repo_path.absolute? ? repo_path : path / repo_path
43
+
44
+ if complete_path.exist?
45
+ validate_files(repo, complete_path)
46
+ next
47
+ end
48
+
49
+ repo_path_not_found(repo_path)
50
+ end
51
+ end
52
+
53
+ # Validates the existence of the files listed in the +files+ attributes
54
+ # inside the +Repo+ object inside the +Repo+'s +path+.
55
+ # @param [Dragnet::Repo] repo The +Repo+ whose files should be validated.
56
+ # @param [Pathname] complete_path The path to the repository.
57
+ def validate_files(repo, complete_path)
58
+ return unless repo.files
59
+
60
+ Dragnet::Validators::FilesValidator.new(repo, complete_path).validate
61
+ end
62
+
63
+ # Raises a Dragnet::Errors::RepoPathNotFoundError with the appropriate
64
+ # message (which depends on whether the path is absolute or relative).
65
+ # @param [Pathname] repo_path The path that couldn't be found.
66
+ # @raise [Dragnet::Errors::RepoPathNotFoundError] is always raised.
67
+ def repo_path_not_found(repo_path)
68
+ message = "Cannot find the repository path #{repo_path}"
69
+ message += " inside #{path}" if repo_path.relative?
70
+ raise Dragnet::Errors::RepoPathNotFoundError, message
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../errors/yaml_format_error'
4
+
5
+ module Dragnet
6
+ module Validators
7
+ # Base class for all validators.
8
+ class Validator
9
+ private
10
+
11
+ # Raises a +Dragnet::Errors::YAMLFormatError+ with the given message.
12
+ # @param [String] message The message for the exception.
13
+ # @raise [Dragnet::Errors::YAMLFormatError] Is always raised with the
14
+ # given message.
15
+ def yaml_format_error(message)
16
+ raise Dragnet::Errors::YAMLFormatError, message
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'validators/data_validator'
4
+ require_relative 'validators/entities'
5
+ require_relative 'validators/fields'
6
+ require_relative 'validators/files_validator'
7
+ require_relative 'validators/validator'
8
+
9
+ module Dragnet
10
+ # Namespace for Validator classes.
11
+ module Validators; end
12
+ end
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'colorize'
4
+
5
+ require_relative 'errors/missing_timestamp_attribute_error'
6
+
7
+ module Dragnet
8
+ # Holds the verification result of a Manual Test Record
9
+ class VerificationResult
10
+ VALID_STATUSES = %i[passed skipped failed].freeze
11
+
12
+ attr_reader :status, :reason, :started_at, :finished_at
13
+
14
+ # Creates a new instance of the class.
15
+ # @param [Symbol] status The status
16
+ # @param [String] reason
17
+ def initialize(status:, reason: nil)
18
+ self.status = status
19
+ @reason = reason
20
+ end
21
+
22
+ def passed?
23
+ status == :passed
24
+ end
25
+
26
+ def skipped?
27
+ status == :skipped
28
+ end
29
+
30
+ def failed?
31
+ status == :failed
32
+ end
33
+
34
+ # Assigns the given status
35
+ # @param [Symbol] status The status
36
+ # @raise [ArgumentError] If the given status is not one of the accepted
37
+ # valid statuses.
38
+ def status=(status)
39
+ unless VALID_STATUSES.include?(status)
40
+ raise ArgumentError, "Invalid status #{status}."\
41
+ " Valid statuses are: #{VALID_STATUSES.join(', ')}"
42
+ end
43
+
44
+ @status = status
45
+ end
46
+
47
+ # Sets the verification's start time.
48
+ # @param [Time] time The verification's start time.
49
+ # @raise [ArgumentError] If the given +time+ is not an instance of +Time+.
50
+ # @raise [ArgumentError] If +finished_at+ is set and the given +time+ is
51
+ # bigger than or equal to it.
52
+ def started_at=(time)
53
+ validate_time(time)
54
+ raise ArgumentError, 'started_at must be smaller than finished_at' if finished_at && time >= finished_at
55
+
56
+ @runtime = nil
57
+ @started_at = time
58
+ end
59
+
60
+ # Sets the verification's finish time
61
+ # @param [Time] time The verification's finish time.
62
+ # @raise [TypeError] Is an attempt is made to set +finished_at+ before
63
+ # setting +started_at+.
64
+ # @raise [ArgumentError] If the given +time+ is not an instance of +Time+.
65
+ # @raise [ArgumentError] If +started_at+ is set and the given +time+ is
66
+ # smaller than or equal to it.
67
+ def finished_at=(time)
68
+ validate_time(time)
69
+ raise ArgumentError, 'finished_at must be greater than started_at' if started_at && time <= started_at
70
+
71
+ @runtime = nil
72
+ @finished_at = time
73
+ end
74
+
75
+ # @return [Float, nil] The runtime calculated from the started_at and
76
+ # finished_at attributes, if any of them is missing +nil+ is returned
77
+ # instead.
78
+ def runtime
79
+ runtime!
80
+ rescue Dragnet::Errors::MissingTimestampAttributeError
81
+ nil
82
+ end
83
+
84
+ # @return [Float] The runtime calculated from the started_at and finished_at
85
+ # timestamp attributes.
86
+ # @raise [TypeError] If either of these attributes is +nil+
87
+ def runtime!
88
+ @runtime ||= calculate_runtime
89
+ end
90
+
91
+ # @return [String] A string representation of the receiver that can be used
92
+ # to log the result of a verification.
93
+ def log_message
94
+ if passed?
95
+ '✔ PASSED '.colorize(:light_green)
96
+ elsif skipped?
97
+ "#{'⚠ SKIPPED'.colorize(:light_yellow)} #{reason}"
98
+ else
99
+ "#{'✘ FAILED '.colorize(:light_red)} #{reason || 'Unknown reason'}"
100
+ end
101
+ end
102
+
103
+ private
104
+
105
+ # Checks if the given object is a +Time+ and raises an +ArgumentError+ if it
106
+ # isn't.
107
+ # @param [Object] time The object to check.
108
+ # @raise [ArgumentError] If the given object is not a +Time+ object.
109
+ def validate_time(time)
110
+ raise ArgumentError, "Expected a Time object, got #{time.class}" unless time.is_a?(Time)
111
+ end
112
+
113
+ # @return [Float] The runtime calculated from the started_at and finished_at
114
+ # timestamp attributes.
115
+ # @raise [TypeError] If either of these attributes is +nil+
116
+ def calculate_runtime
117
+ if started_at.nil? || finished_at.nil?
118
+ raise Dragnet::Errors::MissingTimestampAttributeError,
119
+ 'Both started_at and finished_at must be set in order to calculate the runtime'
120
+ end
121
+
122
+ finished_at - started_at
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'git'
4
+
5
+ require_relative 'verifiers/test_record_verifier'
6
+
7
+ module Dragnet
8
+ # Executes the verification process on the given Test Records
9
+ class Verifier
10
+ attr_reader :test_records, :path, :logger
11
+
12
+ # Creates a new instance of the class.
13
+ # @param [Array<Hash>] test_records An array with the test records.
14
+ # @param [Dragnet::Repository] repository The repository where the MTR and
15
+ # the source files are stored.
16
+ # @param [#info] logger The logger object to use for output.
17
+ def initialize(test_records:, repository:, logger:)
18
+ @test_records = test_records
19
+ @repository = repository
20
+ @logger = logger
21
+ end
22
+
23
+ # Runs the verify process
24
+ # After the execution of this method each Test Record will get a +:result+
25
+ # key with the result of the verification process. This key contains a hash
26
+ # like the following:
27
+ #
28
+ # result: {
29
+ # status: :passed, # Either :passed, :failed or :skipped
30
+ # reason: 'String' # The reason for the failure (for :failed and :skipped)
31
+ # }
32
+ def verify
33
+ logger.info 'Verifying MTR files...'
34
+ test_records.each do |test_record|
35
+ logger.info "Verifying #{test_record.source_file}"
36
+ verify_mtr(test_record)
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ attr_reader :repository
43
+
44
+ # Verifies the given Manual Test Record
45
+ # Runs the given test record through all the verifiers. If no verifier adds
46
+ # a +result+ key to the Test Record then the method adds one with passed
47
+ # status.
48
+ # @param [Dragnet::TestRecord] test_record The Test Record to verify.
49
+ def verify_mtr(test_record)
50
+ started_at = Time.now.utc
51
+
52
+ verification_result = Dragnet::Verifiers::TestRecordVerifier.new(
53
+ test_record: test_record, repository: repository, test_records: test_records
54
+ ).verify
55
+
56
+ finished_at = Time.now.utc
57
+ verification_result.started_at = started_at
58
+ verification_result.finished_at = finished_at
59
+ test_record.verification_result = verification_result
60
+
61
+ logger.info(verification_result.log_message)
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../verification_result'
4
+ require_relative 'repository_verifier'
5
+
6
+ module Dragnet
7
+ module Verifiers
8
+ # Checks for changes in the repository since the creation of the MTR Record
9
+ class ChangesVerifier < Dragnet::Verifiers::RepositoryVerifier
10
+ attr_reader :test_records
11
+
12
+ # @param [Dragnet::TestRecord] test_record The +TestRecord+ object to
13
+ # verify.
14
+ # @param [Dragnet::Repository] repository A +Dragnet::Repository+ object
15
+ # linked to the repository where the verification should be executed.
16
+ # @param [Array<Hash>] test_records The hash of all the test records. This
17
+ # is used to determine if the changes in the repository are only in the
18
+ # Test Record Files, in which case the Test Records will still be
19
+ # considered valid.
20
+ def initialize(test_record:, repository:, test_records:)
21
+ super(test_record: test_record, repository: repository)
22
+ @test_records = test_records
23
+ end
24
+
25
+ # Runs the verification process. Checks the changes on the repository
26
+ # between the Commit with the SHA1 registered in the MTR and the current
27
+ # HEAD.
28
+ # @return [Dragnet::VerificationResult, nil] A +VerificationResult+ with
29
+ # the details of the changes found in the repository or +nil+ if no
30
+ # changes were found.
31
+ def verify
32
+ diff = repository.diff(sha1, 'HEAD')
33
+ return unless diff.size.positive?
34
+
35
+ find_changes(diff)
36
+ end
37
+
38
+ private
39
+
40
+ # Scans the given diff for changes. If changes are detected then a
41
+ # +:result+ key will be added to the +test_record+. Changes to the MTR
42
+ # files themselves are ignored.
43
+ # @return [Dragnet::VerificationResult, nil] A +VerificationResult+ with
44
+ # the details of the changes found in the repository, or +nil+ if no
45
+ # changes were found.
46
+ def find_changes(diff)
47
+ diff.stats[:files].each do |file, _changes|
48
+ next if mtr_files.include?(file) # Changes to MTR files are ignored.
49
+
50
+ return Dragnet::VerificationResult.new(
51
+ status: :skipped,
52
+ reason: "Changes detected in the repository: #{shorten_sha1(sha1)}..#{shorten_sha1(repository.head.sha)}"\
53
+ " # -- #{file}"
54
+ )
55
+ end
56
+
57
+ nil
58
+ end
59
+
60
+ # @return [Array<Strings>] An array of strings with the paths to all the
61
+ # known MTR files. These will be excluded when checking from changes in
62
+ # the repository.
63
+ def mtr_files
64
+ @mtr_files ||= test_records.map do |test_record|
65
+ test_record.source_file.relative_path_from(path).to_s
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../verification_result'
4
+ require_relative 'repository_verifier'
5
+
6
+ module Dragnet
7
+ module Verifiers
8
+ # Checks if any of the files listed in the MTR have changed since the MTR
9
+ # was created.
10
+ class FilesVerifier < Dragnet::Verifiers::RepositoryVerifier
11
+ # Executes the verification process.
12
+ # Checks the changes in the repository. If a change in one of the files
13
+ # is detected a +:result+ key is added to the MTR, including the detected
14
+ # change.
15
+ # @return [Dragnet::VerificationResult, nil] A +VerificationResult+ object
16
+ # with the detected changes to the listed files or +nil+ if no changes
17
+ # are found.
18
+ def verify
19
+ changes = []
20
+
21
+ files.each do |file|
22
+ diff = repository.diff(sha1, 'HEAD').path(file.to_s)
23
+ next unless diff.size.positive?
24
+
25
+ changes << file
26
+ end
27
+
28
+ result_from(changes) if changes.any?
29
+ end
30
+
31
+ private
32
+
33
+ # @return [Array<String>] The paths to the files listed in the MTR file.
34
+ def files
35
+ @files ||= test_record.files.map do |file|
36
+ file.relative_path_from(path)
37
+ end
38
+ end
39
+
40
+ # Stores the detected changes on the Test Record
41
+ # @param [Array<String>] changes The array of changed files.
42
+ def result_from(changes)
43
+ Dragnet::VerificationResult.new(
44
+ status: :skipped,
45
+ reason: "Changes detected in listed file(s): #{shorten_sha1(sha1)}..#{shorten_sha1(repository.head.sha)}"\
46
+ " -- #{changes.join(' ')}"
47
+ )
48
+ end
49
+ end
50
+ end
51
+ end