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,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'errors/unable_to_write_report_error'
4
+ require_relative 'errors/unknown_export_format_error'
5
+ require_relative 'exporters/html_exporter'
6
+ require_relative 'exporters/json_exporter'
7
+
8
+ module Dragnet
9
+ # The base exporter class, receives an array of test records, an array of
10
+ # errors and an array of file names and exports the results to the given
11
+ # files. (For each file the format is deduced from its file name).
12
+ class Exporter
13
+ KNOWN_FORMATS = {
14
+ 'HTML' => { extensions: %w[.html .htm], exporter: Dragnet::Exporters::HTMLExporter },
15
+ 'JSON' => { extensions: %w[.json], exporter: Dragnet::Exporters::JSONExporter }
16
+ }.freeze
17
+
18
+ attr_reader :test_records, :errors, :repository, :targets, :logger
19
+
20
+ # Creates a new instance of the class.
21
+ # @param [Array<Dragnet::TestRecord>] test_records The array of MTRs that
22
+ # should be included in the reports.
23
+ # @param [Array<Hash>] errors An array of Hashes with the data of the MTR
24
+ # files that did not pass the validation process.
25
+ # @param [Dragnet::Repository, Dragnet::MultiRepository] repository The
26
+ # repository where the MTR files and the source code are stored.
27
+ # @param [Array<String>] targets The array of target files. For each of them
28
+ # the format of the export will be deduced from the file's extension.
29
+ # @param [#info, #debug] logger A logger object to use for output.
30
+ def initialize(test_records:, errors:, repository:, targets:, logger:)
31
+ @test_records = test_records
32
+ @errors = errors
33
+ @repository = repository
34
+ @targets = targets
35
+ @logger = logger
36
+ end
37
+
38
+ # Starts the export process.
39
+ # @raise [Dragnet::Errors::UnableToWriteReportError] If one of the target
40
+ # files cannot be created, opened, or if the output cannot be written to
41
+ # it.
42
+ def export
43
+ logger.info 'Starting export process...'
44
+ log_target_files
45
+
46
+ formats.each do |format, targets|
47
+ exporter = KNOWN_FORMATS.dig(format, :exporter).new(
48
+ test_records: test_records, errors: errors, repository: repository, logger: logger
49
+ )
50
+
51
+ text = exporter.export
52
+ write_output(text, format, targets)
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ # Writes the given text output with the given format to the given targets.
59
+ # @param [String] text The text output to write.
60
+ # @param [String] format The format of the target file.
61
+ # @param [Array<String>] targets The paths of the target files the output
62
+ # should be written to.
63
+ # @raise [Dragnet::Errors::UnableToWriteReportError] If one of the target
64
+ # files cannot be created, opened, or if the output cannot be written to
65
+ # it.
66
+ def write_output(text, format, targets)
67
+ targets.each do |target|
68
+ logger.info "Writing #{format} output to #{target}..."
69
+
70
+ begin
71
+ start = Time.now
72
+ bytes = File.write(target, text)
73
+ elapsed = Time.now - start
74
+
75
+ logger.debug("Ok (#{bytes} bytes written in #{elapsed} seconds)")
76
+ rescue SystemCallError => e
77
+ raise Dragnet::Errors::UnableToWriteReportError,
78
+ "Unable to write report output to #{target}: #{e.message}"
79
+ end
80
+ end
81
+ end
82
+
83
+ # Writes a log entry with the files that will be written as a result of the
84
+ # export process (each with its corresponding format).
85
+ def log_target_files
86
+ files_with_formats = formats.flat_map do |format, targets|
87
+ targets.map { |target| "\t * #{target} as #{format}" }
88
+ end
89
+
90
+ logger.debug "Target files are:\n#{files_with_formats.join("\n")}"
91
+ end
92
+
93
+ # @return [Hash] A hash whose keys are known formats and whose values are
94
+ # arrays of target files.
95
+ def formats
96
+ @formats ||= deduce_formats
97
+ end
98
+
99
+ # Deduces the format of each target file (given its extension) and relates
100
+ # them to their corresponding formats.
101
+ # @return [Hash] A hash whose keys are known formats and whose values are
102
+ # arrays of target files.
103
+ def deduce_formats
104
+ formats = {}
105
+
106
+ targets.each do |target|
107
+ extension = File.extname(target).downcase
108
+ format, = KNOWN_FORMATS.find { |_name, config| config[:extensions].include?(extension) }
109
+ unknown_format_error(extension) unless format
110
+
111
+ formats[format] ||= []
112
+ formats[format] << target
113
+ end
114
+
115
+ formats
116
+ end
117
+
118
+ # Raises a +Dragnet::Errors::UnknownExportFormatError+ with the proper error
119
+ # message.
120
+ # @param [String] extension The extension of the given target file.
121
+ # @raise [Dragnet::Errors::UnknownExportFormatError] is always raised.
122
+ def unknown_format_error(extension)
123
+ allowed_extensions = KNOWN_FORMATS.flat_map { |_format, config| config[:extensions] }
124
+
125
+ raise Dragnet::Errors::UnknownExportFormatError,
126
+ "Unknown export format: '#{extension}'. Valid export formats are: "\
127
+ "#{allowed_extensions.join(', ')}"
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dragnet
4
+ module Exporters
5
+ # Base class for all exporter classes.
6
+ class Exporter
7
+ attr_reader :test_records, :errors, :repository, :logger
8
+
9
+ # @param [Array<Hash>] test_records The array of test records.
10
+ # @param [Array<Hash>] errors The array of errors.
11
+ # @param [Dragnet::Repository, Dragnet::MultiRepository] repository The
12
+ # repository where the MTR files and the source code are stored.
13
+ # @param [#info] logger A logger object to use for output.
14
+ def initialize(test_records:, errors:, repository:, logger:)
15
+ @test_records = test_records
16
+ @errors = errors
17
+ @repository = repository
18
+ @logger = logger
19
+ end
20
+
21
+ # @raise [NotImplementedError] Is always raised. Subclasses are expected
22
+ # to override this method.
23
+ def export
24
+ raise NotImplementedError,
25
+ "'export' method not implemented for class #{self.class}"
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'erb'
4
+
5
+ require_relative '../helpers/repository_helper'
6
+ require_relative 'exporter'
7
+
8
+ module Dragnet
9
+ module Exporters
10
+ # Creates an HTML report from the given Test Records and Errors data.
11
+ class HTMLExporter < Dragnet::Exporters::Exporter
12
+ include Dragnet::Helpers::RepositoryHelper
13
+
14
+ TEMPLATE = File.join(__dir__, 'templates', 'template.html.erb').freeze
15
+
16
+ # Generates the report and returns it as a string.
17
+ # @return [String] The generated HTML report.
18
+ def export
19
+ logger.info "Generating HTML report from template: #{TEMPLATE}..."
20
+ ERB.new(File.read(TEMPLATE)).result(binding)
21
+ end
22
+
23
+ private
24
+
25
+ # Returns the percentage that +num1+ represents with respect to +num2+
26
+ # @param [Integer, Float] num1 A number.
27
+ # @param [Integer, Float] num2 A number.
28
+ # @return [Integer, Float] The percentage that +num1+ represents with
29
+ # respect to +num2+ rounded to two decimal places.
30
+ def percentage(num1, num2)
31
+ return 0.0 if num1.zero? || num2.zero?
32
+
33
+ ((num1.to_f / num2) * 100).round(2)
34
+ end
35
+
36
+ # @param [Dragnet::Repository] repository The repository whose branches
37
+ # should be retrieved.
38
+ # @return [Array<String>] An array with the names of the branches that
39
+ # "contain" the current head of the repository (may be empty).
40
+ def software_branches(repository)
41
+ # (uniq needed because of remote/local branches)
42
+ repository.branches_with_head.map(&:name).uniq
43
+ rescue Git::GitExecuteError => e
44
+ logger.warn "Failed to read branches information from the repository at #{repository.path}"
45
+ logger.warn e.message
46
+ []
47
+ end
48
+
49
+ # Method used to memoize the output of the +group_by_requirement+ method.
50
+ # @see #group_by_requirement
51
+ # @return [Hash] A hash whose keys are the requirement IDs and whose
52
+ # values are arrays of MTRs
53
+ def test_records_by_requirement
54
+ @test_records_by_requirement ||= group_by_requirement
55
+ end
56
+
57
+ # Groups the MTRs by the requirement(s) they are covering, if a MTR covers
58
+ # more than one requirement it will be added to all of them, if a
59
+ # requirement is covered by more than one MTR the requirement will end up
60
+ # with more than one MTR, example:
61
+ #
62
+ # {
63
+ # 'ESR_REQ_9675' => [MTR1],
64
+ # 'ESR_REQ_1879' => [MTR2, MTR3]
65
+ # 'ESR_REQ_4714' => [MTR3]
66
+ # }
67
+ #
68
+ # @return [Hash] A hash whose keys are the requirement IDs and whose
69
+ # values are arrays of MTRs
70
+ def group_by_requirement
71
+ tests_by_requirement = {}
72
+
73
+ test_records.each do |test_record|
74
+ ids = *test_record.id
75
+ ids.each do |id|
76
+ tests_by_requirement[id] ||= []
77
+ tests_by_requirement[id] << test_record
78
+ end
79
+ end
80
+
81
+ tests_by_requirement
82
+ end
83
+
84
+ # Returns the HTML code needed to render the Review Status of a MTR as a
85
+ # badge.
86
+ # @param [Dragnet::TestRecord] test_record The Test Record.
87
+ # @return [String] The HTML code to display the Test Record's review
88
+ # status as a badge.
89
+ def review_status_badge(test_record)
90
+ if test_record.review_status
91
+ color = test_record.reviewed? ? 'green' : 'red'
92
+ review_status = test_record.review_status.capitalize
93
+ else
94
+ color = 'gray'
95
+ review_status = '(unknown)'
96
+ end
97
+
98
+ badge_html(color, review_status)
99
+ end
100
+
101
+ # Returns the HTML code needed to display the verification result of a MTR
102
+ # (the color and the text inside the badge are picked in accordance to the
103
+ # given result).
104
+ # @param [Dragnet::VerificationResult] verification_result The result of
105
+ # the verification for a given +TestRecord+
106
+ # @return [String] The HTML code needed to display the result as a badge.
107
+ def verification_result_badge(verification_result)
108
+ badge_html(
109
+ verification_result_color(verification_result),
110
+ verification_result.status.capitalize
111
+ )
112
+ end
113
+
114
+ # Returns a color that depends on the verification result for a Test
115
+ # Record. To be used on HTML elements.
116
+ # @param [Dragnet::VerificationResult] verification_result The
117
+ # +VerificationResult+ object.
118
+ # @return [String] The corresponding color (depends on the +status+ field
119
+ # of the +VerificationResult+ object).
120
+ def verification_result_color(verification_result)
121
+ case verification_result.status
122
+ when :passed
123
+ 'green'
124
+ when :skipped
125
+ 'yellow'
126
+ else
127
+ 'red'
128
+ end
129
+ end
130
+
131
+ # Returns the color that should be used for the highlight line on the left
132
+ # of the card given the result of the MTR's verification.
133
+ # @param [Dragnet::VerificationResult] verification_result The
134
+ # +VerificationResult+ object associated with the +TestRecord+ being
135
+ # rendered on the card.
136
+ def card_color(verification_result)
137
+ verification_result_color(verification_result)
138
+ end
139
+
140
+ # Returns the HTML string to produce a Badge
141
+ # @param [String] color The color of the badge.
142
+ # @param [String] text The text that goes inside the badge.
143
+ # @return [String] The HTML code to produce a badge with the given color
144
+ # and text.
145
+ def badge_html(color, text)
146
+ "<span class=\"badge bg-#{color}\">#{text}</span>"
147
+ end
148
+
149
+ # Converts the ID (+String+) or IDs (+Array<String>+) of a +TestRecord+
150
+ # object into a string that can be safely rendered in the HTML report.
151
+ # @param [Dragnet::TestRecord] test_record The +TestRecord+ object.
152
+ # @return [String] A string with the ID or IDs of the +TestRecord+ object.
153
+ def test_record_id_to_string(test_record)
154
+ Array(test_record.id).join(', ')
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest'
4
+
5
+ require_relative '../helpers/repository_helper'
6
+
7
+ module Dragnet
8
+ module Exporters
9
+ # Generates unique IDs for the Manual Test Records by hashing some of their
10
+ # properties into a hexadecimal SHA1.
11
+ class IDGenerator
12
+ include Dragnet::Helpers::RepositoryHelper
13
+
14
+ attr_reader :repository
15
+
16
+ # @param [Dragnet::Repository] repository The repository where the MTR
17
+ # files are located. This allows the SHA1 to be calculated with relative
18
+ # paths to the MTRs' files.
19
+ def initialize(repository)
20
+ @repository = repository
21
+ end
22
+
23
+ # Calculates the ID of the given MTR
24
+ # @param [Dragnet::TestRecord] test_record The record for which the ID
25
+ # should be calculated.
26
+ # @return [String] The ID for the given +TestRecord+.
27
+ # :reek:FeatureEnvy (Cannot be done in the TestRecord itself because it needs the Repository)
28
+ def id_for(test_record)
29
+ string = "#{relative_to_repo(test_record.source_file)}#{test_record.id}"
30
+ # noinspection RubyMismatchedReturnType (This is never nil)
31
+ Digest::SHA1.hexdigest(string)[0...16]
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ require_relative 'exporter'
6
+ require_relative 'id_generator'
7
+ require_relative 'serializers/test_record_serializer'
8
+
9
+ module Dragnet
10
+ module Exporters
11
+ # Exports the results for the Manual Test Record verification to a JSON
12
+ # string.
13
+ class JSONExporter < ::Dragnet::Exporters::Exporter
14
+ # @return [String] A JSON string containing an array of objects, one for
15
+ # each Test Record.
16
+ def export
17
+ logger.info 'Exporting data to JSON'
18
+ test_records.map do |test_record|
19
+ ::Dragnet::Exporters::Serializers::TestRecordSerializer
20
+ .new(test_record, repository).serialize
21
+ .merge(id: id_generator.id_for(test_record))
22
+ end.to_json
23
+ end
24
+
25
+ private
26
+
27
+ # @return [Dragnet::Exporters::IDGenerator] An instance of the IDGenerator
28
+ # class that can be used to calculate the ID for the exported MTRs.
29
+ def id_generator
30
+ @id_generator ||= ::Dragnet::Exporters::IDGenerator.new(repository)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+ require 'active_support/core_ext/object/blank'
5
+
6
+ module Dragnet
7
+ module Exporters
8
+ module Serializers
9
+ # Serializes a +Repo+ object into a +Hash+
10
+ class RepoSerializer
11
+ attr_reader :repo
12
+
13
+ # @param [Dragnet::Repo] repo The +Repo+ object to serialize.
14
+ def initialize(repo)
15
+ @repo = repo
16
+ end
17
+
18
+ # Serializes the given +Repo+ object.
19
+ # @return [Hash] A +Hash+ representing the given +Repo+ object.
20
+ def serialize
21
+ {
22
+ path: repo.path,
23
+ sha1: repo.sha1
24
+ }.tap do |hash|
25
+ hash[:files] = serialize_files if repo.files.present?
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ # Serializes the array of files attached to the +Repo+
32
+ # @return [Array<String>] The array of file names (without the path to
33
+ # the repository).
34
+ def serialize_files
35
+ repo.files.map { |file| file.to_s.gsub("#{repo.path}/", '') }
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+ require 'active_support/core_ext/object/blank'
5
+
6
+ require_relative '../../helpers/repository_helper'
7
+ require_relative 'repo_serializer'
8
+ require_relative 'verification_result_serializer'
9
+
10
+ module Dragnet
11
+ module Exporters
12
+ module Serializers
13
+ # Serializes a +TestRecord+ object into a +Hash+.
14
+ class TestRecordSerializer
15
+ include ::Dragnet::Helpers::RepositoryHelper
16
+
17
+ attr_reader :test_record, :repository
18
+
19
+ # @param [Dragnet::TestRecord] test_record The +TestRecord+ object to
20
+ # serialize.
21
+ # @param [Dragnet::RepositoryBase] repository The +Repository+ object
22
+ # associated with the +TestRecord+. Used to render file paths relative
23
+ # to the repository instead of as absolute paths.
24
+ def initialize(test_record, repository)
25
+ @test_record = test_record
26
+ @repository = repository
27
+ end
28
+
29
+ # rubocop:disable Metrics/AbcSize (because of the Hash)
30
+ # rubocop:disable Metrics/CyclomaticComplexity (because of the conditionals)
31
+ # rubocop:disable Metrics/PerceivedComplexity (because of the conditionals)
32
+ # rubocop:disable Metrics/MethodLength (because of he Hash)
33
+
34
+ # @return [Hash] A +Hash+ representing the given +TestRecord+ object.
35
+ def serialize
36
+ {
37
+ refs: Array(test_record.id),
38
+ result: test_record.result,
39
+ review_status: render_review_status,
40
+ verification_result: serialized_verification_result,
41
+
42
+ # TODO: Remove the started_at and finished_at attributes after solving
43
+ # https://esrlabs.atlassian.net/browse/JAY-493
44
+ started_at: serialized_verification_result[:started_at],
45
+ finished_at: serialized_verification_result[:finished_at]
46
+ }.tap do |hash|
47
+ hash[:sha1] = test_record.sha1 if test_record.sha1.present?
48
+ hash[:owner] = Array(test_record.name).join(', ') if test_record.name.present?
49
+ hash[:description] = test_record.description if test_record.description.present?
50
+ hash[:test_method] = Array(test_record.test_method) if test_record.test_method.present?
51
+
52
+ if test_record.tc_derivation_method.present?
53
+ hash[:tc_derivation_method] = Array(test_record.tc_derivation_method)
54
+ end
55
+
56
+ hash[:review_comments] = test_record.review_comments if test_record.review_comments.present?
57
+ hash[:findings] = test_record.findings if test_record.findings?
58
+ hash[:files] = serialize_files if test_record.files.present?
59
+ hash[:repos] = serialize_repos if test_record.repos.present?
60
+ end
61
+ end
62
+
63
+ # rubocop:enable Metrics/AbcSize
64
+ # rubocop:enable Metrics/CyclomaticComplexity
65
+ # rubocop:enable Metrics/PerceivedComplexity
66
+ # rubocop:enable Metrics/MethodLength
67
+
68
+ private
69
+
70
+ # Renders the +TestRecord+'s review status
71
+ # @return [String] The review status, either +'not_reviewed'+ or +'reviewed'+
72
+ def render_review_status
73
+ "#{test_record.reviewed? ? nil : 'not_'}reviewed"
74
+ end
75
+
76
+ # Serializes the files listed in the given +TestRecord+
77
+ # @return [Array<String>] An array of strings, one for each listed file.
78
+ def serialize_files
79
+ test_record.files.map { |file| relative_to_repo(file).to_s }
80
+ end
81
+
82
+ # Serializes the +Repo+ objects attached to the +TestRecord+
83
+ # @return [Array<Hash>] An array of +Hash+es representing each of the
84
+ # +Repo+ objects associated with the +TestRecord+
85
+ def serialize_repos
86
+ test_record.repos.map { |repo| ::Dragnet::Exporters::Serializers::RepoSerializer.new(repo).serialize }
87
+ end
88
+
89
+ # Serializes the +VerificationResult+ object attached to the given
90
+ # +TestRecord+
91
+ # @return [Hash] A +Hash+ representation of the +VerificationResult+
92
+ # object.
93
+ def serialized_verification_result
94
+ @serialized_verification_result ||= ::Dragnet::Exporters::Serializers::VerificationResultSerializer.new(
95
+ test_record.verification_result
96
+ ).serialize
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dragnet
4
+ module Exporters
5
+ module Serializers
6
+ # Serializes a +VerificationResult+ object into a +Hash+.
7
+ class VerificationResultSerializer
8
+ attr_reader :verification_result
9
+
10
+ # Format used to serialize the +VerificationResult+'s date/time attributes.
11
+ DATE_FORMAT = '%F %T %z'
12
+
13
+ # @param [Dragnet::VerificationResult] verification_result The
14
+ # +VerificationResult+ object to serialize.
15
+ def initialize(verification_result)
16
+ @verification_result = verification_result
17
+ end
18
+
19
+ # @return [Hash] The +Hash+ representation of the given
20
+ # +VerificationResult+ object.
21
+ def serialize
22
+ {
23
+ status: verification_result.status,
24
+ started_at: verification_result.started_at.strftime(DATE_FORMAT),
25
+ finished_at: verification_result.finished_at.strftime(DATE_FORMAT),
26
+ runtime: verification_result.runtime
27
+ }.tap do |hash|
28
+ hash[:reason] = verification_result.reason if verification_result.reason
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'serializers/repo_serializer'
4
+ require_relative 'serializers/test_record_serializer'
5
+ require_relative 'serializers/verification_result_serializer'
6
+
7
+ module Dragnet
8
+ module Exporters
9
+ # Namespace for the serializer classes used by the exporters.
10
+ module Serializers; end
11
+ end
12
+ end