All Files (99.54% covered at 13.72 hits/line)
64 files in total.
1085 relevant lines.
1080 lines covered and
5 lines missed
-
# frozen_string_literal: true
-
-
1
require 'dragnet/version'
-
-
1
require_relative 'dragnet/cli'
-
1
require_relative 'dragnet/errors'
-
1
require_relative 'dragnet/explorer'
-
1
require_relative 'dragnet/exporter'
-
1
require_relative 'dragnet/exporters'
-
1
require_relative 'dragnet/multi_repository'
-
1
require_relative 'dragnet/repo'
-
1
require_relative 'dragnet/validator'
-
1
require_relative 'dragnet/validators'
-
1
require_relative 'dragnet/verifiers'
-
-
# Main namespace for the gem
-
1
module Dragnet
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'errors/incompatible_repository_error'
-
-
1
module Dragnet
-
# Base class for Dragnet's repository classes.
-
1
class BaseRepository
-
1
attr_reader :path
-
-
# @param [Pathname] path The path were the repository is located.
-
1
def initialize(path:)
-
72
@path = path
-
end
-
-
# @raise [Dragnet::Errors::IncompatibleRepositoryError] Is always raised
-
1
def git
-
1
incompatible_repository(__method__)
-
end
-
-
# @raise [Dragnet::Errors::IncompatibleRepositoryError] Is always raised
-
1
def branch
-
1
incompatible_repository(__method__)
-
end
-
-
# @raise [Dragnet::Errors::IncompatibleRepositoryError] Is always raised
-
1
def branches
-
1
incompatible_repository(__method__)
-
end
-
-
# @raise [Dragnet::Errors::IncompatibleRepositoryError] Is always raised
-
1
def diff
-
1
incompatible_repository(__method__)
-
end
-
-
# @raise [Dragnet::Errors::IncompatibleRepositoryError] Is always raised
-
1
def head
-
1
incompatible_repository(__method__)
-
end
-
-
# @raise [Dragnet::Errors::IncompatibleRepositoryError] Is always raised
-
1
def remote_uri_path
-
1
incompatible_repository(__method__)
-
end
-
-
# @raise [Dragnet::Errors::IncompatibleRepositoryError] Is always raised
-
1
def repositories
-
1
incompatible_repository(__method__)
-
end
-
-
# @raise [Dragnet::Errors::IncompatibleRepositoryError] Is always raised
-
1
def branches_with(_commit)
-
1
incompatible_repository(__method__)
-
end
-
-
# @raise [Dragnet::Errors::IncompatibleRepositoryError] Is always raised
-
1
def branches_with_head
-
1
incompatible_repository(__method__)
-
end
-
-
1
private
-
-
# @param [String] message The message for the raised error.
-
# @raise [Dragnet::Errors::IncompatibleRepositoryError] Is always raised
-
# with the given message.
-
1
def incompatible_repository(message)
-
9
raise Dragnet::Errors::IncompatibleRepositoryError, message
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'cli/master.rb'
-
-
1
module Dragnet
-
# Namespace for the Gem's CLI
-
1
module CLI; end
-
end
-
# frozen_string_literal: true
-
-
1
require 'active_support'
-
1
require 'active_support/core_ext/hash'
-
1
require 'colorize'
-
1
require 'thor'
-
1
require 'yaml'
-
-
1
require_relative 'logger'
-
-
1
module Dragnet
-
1
module CLI
-
# Base class for all CLI classes.
-
1
class Base < Thor
-
1
include Thor::Actions
-
-
# Exit status codes
-
1
E_CONFIG_LOAD_ERROR = 1
-
-
1
attr_reader :configuration, :logger
-
-
1
class_option :configuration, aliases: :c, desc: 'Configuration file',
-
default: '.dragnet.yaml', required: true
-
-
1
class_option :quiet, aliases: :q, default: false, type: :boolean,
-
desc: 'Suppresses all terminal output (except for critical errors)'
-
-
# Tells Thor to return an unsuccessful return code (different from 0) if
-
# an error is raised.
-
1
def self.exit_on_failure?
-
true
-
end
-
-
# Creates a new instance of the class. Called by Thor when a command is
-
# executed. Creates a logger for the class passing Thor's shell to it
-
# (Thor's shell handles the output to the console)
-
1
def initialize(*args)
-
57
super
-
57
@logger = Dragnet::CLI::Logger.new(shell)
-
end
-
-
1
private
-
-
# @return [String] Returns the name of the configuration file (passed via
-
# the -c command line switch).
-
1
def configuration_file
-
109
@configuration_file ||= options[:configuration]
-
end
-
-
# Loads the configuration from the given configuration file. (This is a
-
# dumb loader, it basically loads the whole YAML file into a hash, no
-
# parsing, validation or checking takes place)
-
1
def load_configuration
-
53
logger.info "Loading configuration file #{configuration_file}..."
-
53
@configuration = YAML.safe_load(File.read(configuration_file)).deep_symbolize_keys
-
rescue StandardError => e
-
3
fatal_error("Unable to load the given configuration file: '#{configuration_file}'", e, E_CONFIG_LOAD_ERROR)
-
end
-
-
# Prints the given message alongside the message of the given exception
-
# and then terminates the process with the given exit code.
-
# @param [String] message The error message.
-
# @param [Exception] exception The exception that caused the fatal error.
-
# @param [exit_code] exit_code The exit code.
-
1
def fatal_error(message, exception, exit_code)
-
20
puts 'Error: '.colorize(:light_red) + message
-
20
puts " #{exception.message}"
-
20
exit(exit_code)
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require 'colorize'
-
-
1
module Dragnet
-
1
module CLI
-
# A logger for the CLI. It uses the +say+ method in Thor's +Shell+ class to
-
# print the messages to the output, honoring the status of the +quiet+
-
# command line switch.
-
1
class Logger
-
1
attr_reader :shell, :log_level
-
-
1
LEVELS = { debug: 0, info: 1, warn: 2, error: 3 }.freeze
-
1
DEFAULT_LOG_LEVEL = :info
-
1
PADDING_STRING = ' '
-
1
PADDING_WIDTH = 7
-
-
# Creates a new instance of the class.
-
# @param [Thor::Shell::Basic] shell A reference to Thor's +Shell+ this
-
# will be used to send the output to the terminal in which Thor was
-
# started.
-
# @param [Symbol] log_level The log level for the logger. The higher the
-
# level the less output will be printed.
-
# @see LEVELS
-
1
def initialize(shell, log_level = DEFAULT_LOG_LEVEL)
-
17
raise ArgumentError, "Unknown logger level: #{log_level}" unless LEVELS.keys.include?(log_level)
-
-
16
@log_level = LEVELS[log_level]
-
16
@shell = shell
-
end
-
-
# Prints a message with log level +debug+
-
# @param [String] message The message to print
-
1
def debug(message)
-
3
output(:debug, :green, message)
-
end
-
-
# Prints a message with log level +info+
-
# @param [String] message The message to print
-
1
def info(message)
-
3
output(:info, :blue, message)
-
end
-
-
# Prints a message with log level +warn+
-
# @param [String] message The message to print
-
1
def warn(message)
-
3
output(:warn, :yellow, message)
-
end
-
-
# Prints a message with log level +error+
-
# @param [String] message The message to print
-
1
def error(message)
-
2
output(:error, :red, message)
-
end
-
-
1
private
-
-
# Prints the given message with the given level and text color (only the
-
# name of the level will be colored).
-
# @param [Symbol] level The log level
-
# @param [Symbol] color The color to use. One of the colors available for
-
# the +#colorize+ method.
-
# @param [String] message The message to print.
-
# @see Colorize::InstanceMethods#colorize
-
1
def output(level, color, message)
-
11
return unless log_level <= LEVELS[level]
-
-
8
shell.say "#{level.to_s.capitalize}:".ljust(PADDING_WIDTH, PADDING_STRING).colorize(color) + message
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative '../explorer'
-
1
require_relative '../exporter'
-
1
require_relative '../multi_repository'
-
1
require_relative '../repository'
-
1
require_relative '../validator'
-
1
require_relative '../verifier'
-
1
require_relative '../version'
-
1
require_relative 'base'
-
-
1
module Dragnet
-
1
module CLI
-
# Entry point class for the Dragnet CLI. Includes all the commands and
-
# sub-commands of the CLI.
-
#
-
# The class should not contain any logic, everything should be delegated to
-
# helper classes as soon as possible. Only exceptions are error handling and
-
# message printing.
-
1
class Master < Dragnet::CLI::Base
-
1
E_MISSING_PARAMETER_ERROR = 2
-
1
E_NO_MTR_FILES_FOUND = 3
-
1
E_GIT_ERROR = 4
-
1
E_EXPORT_ERROR = 5
-
1
E_INCOMPATIBLE_REPOSITORY = 6
-
-
1
E_ERRORS_DETECTED = 16
-
1
E_FAILED_TESTS = 32
-
-
1
map %w[--version -v] => :version
-
-
1
desc '--version', 'Prints the current version of the Gem'
-
1
method_option :'number-only',
-
aliases: 'n', type: :boolean,
-
desc: 'If given, only the version number will be printed'
-
1
def version
-
4
if options[:'number-only']
-
2
say Dragnet::VERSION
-
else
-
2
say "Dragnet #{Dragnet::VERSION}"
-
2
say "Copyright (c) #{Time.now.year} ESR Labs GmbH esrlabs.com"
-
end
-
end
-
-
1
desc 'check [PATH]', 'Executes the verification procedure. '\
-
'Loads the given configuration file and executes the verify procedure on the given path '\
-
'(defaults to the value of the "path" key in the configuration file or the current '\
-
'working directory if none of them is given)'
-
1
method_option :export,
-
aliases: 'e', type: :string, repeatable: true,
-
desc: 'If given, the results of the verification procedure will be exported to'\
-
' the given file. The format of the export will be deducted from the given'\
-
" file's name"
-
1
method_option :'multi-repo',
-
aliases: '-m', type: :boolean, default: false,
-
desc: 'Enables the multi-repo compatibility mode. This prevents Dragnet from assuming'\
-
' that [PATH] refers to a Git repository allowing it to run even if that is not the case.'\
-
" Using this option will cause Dragnet to raise an error if it finds a MTR which doesn't"\
-
" have a 'repos' attribute"
-
1
def check(path = nil)
-
53
load_configuration
-
50
self.path = path
-
-
50
files = explore
-
44
test_records, errors = validate(files)
-
44
verify(test_records)
-
-
39
export(test_records, errors) if options[:export]
-
-
33
exit_code = 0
-
33
exit_code |= E_ERRORS_DETECTED if errors.any?
-
39
exit_code |= E_FAILED_TESTS unless test_records.all? { |test_record| test_record.verification_result.passed? }
-
-
33
exit(exit_code) if exit_code.positive? # doing exit(0) will stop RSpec execution.
-
end
-
-
1
private
-
-
# Runs the explorer on the given path.
-
# @return [Array<Pathname>] The array of found MTR files.
-
1
def explore
-
50
glob_patterns = configuration[:glob_patterns]
-
-
begin
-
50
explorer = Dragnet::Explorer.new(path: path, glob_patterns: glob_patterns, logger: logger)
-
47
explorer.files
-
6
rescue ArgumentError => e
-
3
fatal_error('Initialization error. Missing or malformed parameter.', e, E_MISSING_PARAMETER_ERROR)
-
rescue Dragnet::Errors::NoMTRFilesFoundError => e
-
3
fatal_error('No MTR Files found.', e, E_NO_MTR_FILES_FOUND)
-
end
-
end
-
-
# Executes the validator on the given MTR files.
-
# @param [Array<Pathname>] files The files to run the validator on.
-
# @return [Array (Array<Dragnet::TestRecord>, Array<Hash>)] An array.
-
# - The first element is an array of +TestRecord+s with the MTR data.
-
# One for each valid MTR file.
-
# - The second element contains the errors occurred during the
-
# validation process. Can be an empty array.
-
1
def validate(files)
-
44
validator = Dragnet::Validator.new(files: files, path: path, logger: logger)
-
44
[validator.validate, validator.errors]
-
end
-
-
# Executes the verification on the given MTRs
-
# @param [Array<Dragnet::TestRecord>] test_records The array of MTRs on
-
# which the verification should be executed.
-
1
def verify(test_records)
-
44
verifier = Dragnet::Verifier.new(test_records: test_records, repository: repository, logger: logger)
-
41
verifier.verify
-
rescue ArgumentError => e
-
3
fatal_error("Could not open the specified path: #{path} as a Git Repository", e, E_GIT_ERROR)
-
rescue Dragnet::Errors::IncompatibleRepositoryError => e
-
2
incompatible_repository_error(e)
-
end
-
-
# Executes the export process.
-
# @param [Array<Dragnet::TestRecord>] test_records The validated and
-
# verified test records.
-
# @param [Array<Hashes>] errors The array of Hashes with the MTR files
-
# that didn't pass the validation process.
-
1
def export(test_records, errors)
-
9
exporter = Dragnet::Exporter.new(
-
test_records: test_records, errors: errors, repository: repository, targets: options[:export], logger: logger
-
)
-
-
9
exporter.export
-
rescue Dragnet::Errors::UnknownExportFormatError, Dragnet::Errors::UnableToWriteReportError => e
-
4
fatal_error('Export failed', e, E_EXPORT_ERROR)
-
rescue Dragnet::Errors::IncompatibleRepositoryError => e
-
2
incompatible_repository_error(e)
-
end
-
-
# @return [Pathname] The path of the directory where the verification
-
# process should be executed.
-
1
def path
-
141
@path || set_fallback_path
-
end
-
-
# @param [Pathname, String] path The path of the directory where the
-
# verification process should be executed.
-
1
def path=(path)
-
94
@path = path ? Pathname.new(path) : nil
-
end
-
-
# @raise [ArgumentError] If the given path is not a valid git repository.
-
# @return [Dragnet::Repository, Dragnet::MultiRepository] One of the
-
# possible Repository objects.
-
1
def repository
-
53
@repository ||= create_repository
-
end
-
-
# Creates the appropriate Repository object in accordance to the status of
-
# the +multi-repo+ command line option.
-
# @return [Dragnet::MultiRepository] If +multi-repo+ was set to +true+
-
# @return [Dragnet::Repository] If +multi_repo+ was set to +false+
-
1
def create_repository
-
44
options[:'multi-repo'] ? Dragnet::MultiRepository.new(path: path) : Dragnet::Repository.new(path: path)
-
end
-
-
# Prints a message and exits with the proper error code when a
-
# +Dragnet::Errors::IncompatibleRepositoryError+ is raised.
-
# @param [Dragnet::Errors::IncompatibleRepositoryError] error The raised
-
# error.
-
1
def incompatible_repository_error(error)
-
4
fatal_error('Incompatible git operation:', error, E_INCOMPATIBLE_REPOSITORY)
-
end
-
-
# Called when no path has been given by the user explicitly. The method
-
# uses the configured path or the current working directory as a fallback.
-
# @return [Pathname] The constructed fallback path.
-
1
def set_fallback_path
-
# The && causes Ruby to return the value of +@path+ AFTER it has been
-
# assigned (converted to a Pathname)
-
44
(self.path = configuration[:path] || Dir.pwd) && @path
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'errors/not_a_repository_error'
-
1
require_relative 'errors/repo_path_not_found_error'
-
-
1
module Dragnet
-
# Namespace to house all the error classes for the gem.
-
1
module Errors; end
-
end
-
# frozen_string_literal: true
-
-
1
module Dragnet
-
1
module Errors
-
# Base class for all errors raised by the gem.
-
1
class Error < StandardError; end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'error'
-
-
1
module Dragnet
-
1
module Errors
-
# An error to be raised when one of the files referenced by a MTR File
-
# doesn't exist in the repository.
-
1
class FileNotFoundError < Dragnet::Errors::Error; end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
module Dragnet
-
1
module Errors
-
# An error to be raised when an attempt is made to perform an action on a
-
# multi-repo set-up which can only be performed on a single-repo set-up.
-
# For example, trying to perform a +diff+ operation on the multi-repo root.
-
1
class IncompatibleRepositoryError < Dragnet::Errors::Error; end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'error'
-
-
1
module Dragnet
-
1
module Errors
-
# An error to be raised when an attempt is made to retrieve the runtime
-
# when one or more timestamp attributes are missing.
-
1
class MissingTimestampAttributeError < Dragnet::Errors::Error; end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'error'
-
-
1
module Dragnet
-
1
module Errors
-
# An error to be raised when the +Explorer+ is unable to locate MTR files
-
# with the given glob patterns inside the specified path.
-
1
class NoMTRFilesFoundError < Dragnet::Errors::Error; end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'error'
-
-
1
module Dragnet
-
1
module Errors
-
# An error to be raised when the path of a repository entry in a MTR with
-
# multiple repositories doesn't point to an actual git repository.
-
1
class NotARepositoryError < Dragnet::Errors::Error; end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'error'
-
-
1
module Dragnet
-
1
module Errors
-
# An error to raise when the +path+ given for a +repos+ entry cannot be found.
-
1
class RepoPathNotFoundError < Dragnet::Errors::Error; end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'error'
-
-
1
module Dragnet
-
1
module Errors
-
# An error to be raised when Dragnet cannot write to one of the given export
-
# files.
-
1
class UnableToWriteReportError < Dragnet::Errors::Error; end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'error'
-
-
1
module Dragnet
-
1
module Errors
-
# An error to be raised when an export target file is given for which the
-
# format is unknown (cannot be deduced from its extension).
-
1
class UnknownExportFormatError < Dragnet::Errors::Error; end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'error'
-
-
1
module Dragnet
-
1
module Errors
-
# An error to be raised when an attempt is made to create an entity with
-
# invalid data.
-
1
class ValidationError < Dragnet::Errors::Error; end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
module Dragnet
-
1
module Errors
-
# An error to be raised when there is a formatting problem with a YAML file.
-
# For example a missing key. (This error doesn't cover Syntax Errors)
-
1
class YAMLFormatError < Dragnet::Errors::Error; end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'errors/no_mtr_files_found_error'
-
-
1
module Dragnet
-
# This class searches for Manual Test Record files inside a given path by
-
# using the given Glob patterns.
-
1
class Explorer
-
1
attr_reader :path, :glob_patterns, :logger
-
-
# Creates a new instance of the class.
-
# @param [Pathname] path The path that should be explored.
-
# @param [String, Array<String>] glob_patterns The glob pattern or glob
-
# patterns to use when exploring the specified path.
-
# @param [#info] logger A logger object to use for output.
-
# @raise [ArgumentError] If +path+ or +glob_patterns+ are +nil+ or if they
-
# don't have one of the expected types.
-
1
def initialize(path:, glob_patterns:, logger:)
-
13
validate_path(path)
-
11
validate_patterns(glob_patterns)
-
-
8
@path = path
-
8
@glob_patterns = *glob_patterns
-
8
@logger = logger
-
end
-
-
# Performs the search for MTR files and returns an array with the found
-
# files.
-
# @return [Array<Pathname>] The array of found MTR files.
-
# @raise [Dragnet::Errors::NoMTRFilesFoundError] If no MTR files are found.
-
1
def files
-
7
@files ||= find_files
-
end
-
-
1
private
-
-
# Raises an +ArgumentError+ with the appropriate message.
-
# @param [String] name The name of the missing parameter.
-
# @raise [ArgumentError] Is always raised with the appropriate message.
-
1
def missing_parameter(name)
-
2
raise ArgumentError, "Missing required parameter #{name}"
-
end
-
-
# Raises an +ArgumentError+ with the appropriate message.
-
# @param [String] name The name of the parameter with an incompatible type.
-
# @param [String, Class] expected The expected parameter type.
-
# @param [String, Class] given The given parameter type.
-
# @raise [ArgumentError] Is always raised with the appropriate message.
-
1
def incompatible_parameter(name, expected, given)
-
3
raise ArgumentError, "Incompatible parameter type #{name}. Expected: #{expected}, given: #{given}"
-
end
-
-
# Validates the given path
-
# @param [Object] path The path to validate
-
# @raise [ArgumentError] If the given path is nil or is not a +Pathname+.
-
1
def validate_path(path)
-
13
missing_parameter('path') unless path
-
12
return if path.is_a?(Pathname)
-
-
1
incompatible_parameter('path', Pathname, path.class)
-
end
-
-
# Validates the given glob patterns
-
# @param [String, Array<String>] glob_patterns The glob patterns
-
# @raise [ArgumentError] If +glob_patterns+ is +nil+ or it isn't an array
-
# of strings.
-
1
def validate_patterns(glob_patterns)
-
11
missing_parameter('glob_patterns') unless glob_patterns
-
-
10
return if glob_patterns.is_a?(String)
-
16
return if glob_patterns.is_a?(Array) && glob_patterns.all? { |value| value.is_a?(String) }
-
-
2
incompatible_parameter('glob_patterns', 'String or Array<String>', glob_patterns.class)
-
end
-
-
# Logs the MTR files that were found.
-
# @param [Array<Pathname>] files The found MTR files.
-
# @return [Array<Pathname>] The same array given in +files+.
-
1
def log_found_files(files)
-
27
files.each { |file| logger.info("Found MTR file: #{file}") }
-
end
-
-
# Searches the +path+ for MTR files using the +glob_patterns+
-
# @return [Array<Pathname>] The array of found MTR files.
-
# @raise [Dragnet::Errors::NoMTRFilesFoundError] If no MTR files are found.
-
1
def find_files
-
7
logger.info 'Searching for Manual Test Records...'
-
-
7
files = []
-
-
7
glob_patterns.each do |glob_pattern|
-
11
logger.info "Globbing #{path} with #{glob_pattern}..."
-
-
11
files += log_found_files(path.glob(glob_pattern))
-
end
-
-
7
return files if files.any?
-
-
1
raise Dragnet::Errors::NoMTRFilesFoundError,
-
"No MTR Files found in #{path} with the following glob patterns: #{glob_patterns.join(', ')}"
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'errors/unable_to_write_report_error'
-
1
require_relative 'errors/unknown_export_format_error'
-
1
require_relative 'exporters/html_exporter'
-
1
require_relative 'exporters/json_exporter'
-
-
1
module Dragnet
-
# The base exporter class, receives an array of test records, an array of
-
# errors and an array of file names and exports the results to the given
-
# files. (For each file the format is deduced from its file name).
-
1
class Exporter
-
KNOWN_FORMATS = {
-
1
'HTML' => { extensions: %w[.html .htm], exporter: Dragnet::Exporters::HTMLExporter },
-
'JSON' => { extensions: %w[.json], exporter: Dragnet::Exporters::JSONExporter }
-
}.freeze
-
-
1
attr_reader :test_records, :errors, :repository, :targets, :logger
-
-
# Creates a new instance of the class.
-
# @param [Array<Dragnet::TestRecord>] test_records The array of MTRs that
-
# should be included in the reports.
-
# @param [Array<Hash>] errors An array of Hashes with the data of the MTR
-
# files that did not pass the validation process.
-
# @param [Dragnet::Repository, Dragnet::MultiRepository] repository The
-
# repository where the MTR files and the source code are stored.
-
# @param [Array<String>] targets The array of target files. For each of them
-
# the format of the export will be deduced from the file's extension.
-
# @param [#info, #debug] logger A logger object to use for output.
-
1
def initialize(test_records:, errors:, repository:, targets:, logger:)
-
13
@test_records = test_records
-
13
@errors = errors
-
13
@repository = repository
-
13
@targets = targets
-
13
@logger = logger
-
end
-
-
# Starts the export process.
-
# @raise [Dragnet::Errors::UnableToWriteReportError] If one of the target
-
# files cannot be created, opened, or if the output cannot be written to
-
# it.
-
1
def export
-
13
logger.info 'Starting export process...'
-
13
log_target_files
-
-
12
formats.each do |format, targets|
-
20
exporter = KNOWN_FORMATS.dig(format, :exporter).new(
-
test_records: test_records, errors: errors, repository: repository, logger: logger
-
)
-
-
20
text = exporter.export
-
20
write_output(text, format, targets)
-
end
-
end
-
-
1
private
-
-
# Writes the given text output with the given format to the given targets.
-
# @param [String] text The text output to write.
-
# @param [String] format The format of the target file.
-
# @param [Array<String>] targets The paths of the target files the output
-
# should be written to.
-
# @raise [Dragnet::Errors::UnableToWriteReportError] If one of the target
-
# files cannot be created, opened, or if the output cannot be written to
-
# it.
-
1
def write_output(text, format, targets)
-
20
targets.each do |target|
-
22
logger.info "Writing #{format} output to #{target}..."
-
-
begin
-
22
start = Time.now
-
22
bytes = File.write(target, text)
-
21
elapsed = Time.now - start
-
-
21
logger.debug("Ok (#{bytes} bytes written in #{elapsed} seconds)")
-
rescue SystemCallError => e
-
1
raise Dragnet::Errors::UnableToWriteReportError,
-
"Unable to write report output to #{target}: #{e.message}"
-
end
-
end
-
end
-
-
# Writes a log entry with the files that will be written as a result of the
-
# export process (each with its corresponding format).
-
1
def log_target_files
-
13
files_with_formats = formats.flat_map do |format, targets|
-
42
targets.map { |target| "\t * #{target} as #{format}" }
-
end
-
-
12
logger.debug "Target files are:\n#{files_with_formats.join("\n")}"
-
end
-
-
# @return [Hash] A hash whose keys are known formats and whose values are
-
# arrays of target files.
-
1
def formats
-
25
@formats ||= deduce_formats
-
end
-
-
# Deduces the format of each target file (given its extension) and relates
-
# them to their corresponding formats.
-
# @return [Hash] A hash whose keys are known formats and whose values are
-
# arrays of target files.
-
1
def deduce_formats
-
13
formats = {}
-
-
13
targets.each do |target|
-
24
extension = File.extname(target).downcase
-
57
format, = KNOWN_FORMATS.find { |_name, config| config[:extensions].include?(extension) }
-
24
unknown_format_error(extension) unless format
-
-
23
formats[format] ||= []
-
23
formats[format] << target
-
end
-
-
12
formats
-
end
-
-
# Raises a +Dragnet::Errors::UnknownExportFormatError+ with the proper error
-
# message.
-
# @param [String] extension The extension of the given target file.
-
# @raise [Dragnet::Errors::UnknownExportFormatError] is always raised.
-
1
def unknown_format_error(extension)
-
3
allowed_extensions = KNOWN_FORMATS.flat_map { |_format, config| config[:extensions] }
-
-
1
raise Dragnet::Errors::UnknownExportFormatError,
-
"Unknown export format: '#{extension}'. Valid export formats are: "\
-
"#{allowed_extensions.join(', ')}"
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'exporters/exporter'
-
1
require_relative 'exporters/html_exporter'
-
1
require_relative 'exporters/id_generator'
-
1
require_relative 'exporters/json_exporter'
-
1
require_relative 'exporters/serializers'
-
-
1
module Dragnet
-
# Namespace for the exporters: classes that produce files, or reports out of
-
# the results of the Manual Test Record verification execution.
-
1
module Exporters; end
-
end
-
# frozen_string_literal: true
-
-
1
module Dragnet
-
1
module Exporters
-
# Base class for all exporter classes.
-
1
class Exporter
-
1
attr_reader :test_records, :errors, :repository, :logger
-
-
# @param [Array<Hash>] test_records The array of test records.
-
# @param [Array<Hash>] errors The array of errors.
-
# @param [Dragnet::Repository, Dragnet::MultiRepository] repository The
-
# repository where the MTR files and the source code are stored.
-
# @param [#info] logger A logger object to use for output.
-
1
def initialize(test_records:, errors:, repository:, logger:)
-
65
@test_records = test_records
-
65
@errors = errors
-
65
@repository = repository
-
65
@logger = logger
-
end
-
-
# @raise [NotImplementedError] Is always raised. Subclasses are expected
-
# to override this method.
-
1
def export
-
1
raise NotImplementedError,
-
"'export' method not implemented for class #{self.class}"
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require 'erb'
-
-
1
require_relative '../helpers/repository_helper'
-
1
require_relative 'exporter'
-
-
1
module Dragnet
-
1
module Exporters
-
# Creates an HTML report from the given Test Records and Errors data.
-
1
class HTMLExporter < Dragnet::Exporters::Exporter
-
1
include Dragnet::Helpers::RepositoryHelper
-
-
1
TEMPLATE = File.join(__dir__, 'templates', 'template.html.erb').freeze
-
-
# Generates the report and returns it as a string.
-
# @return [String] The generated HTML report.
-
1
def export
-
31
logger.info "Generating HTML report from template: #{TEMPLATE}..."
-
31
ERB.new(File.read(TEMPLATE)).result(binding)
-
end
-
-
1
private
-
-
# Returns the percentage that +num1+ represents with respect to +num2+
-
# @param [Integer, Float] num1 A number.
-
# @param [Integer, Float] num2 A number.
-
# @return [Integer, Float] The percentage that +num1+ represents with
-
# respect to +num2+ rounded to two decimal places.
-
1
def percentage(num1, num2)
-
34
return 0.0 if num1.zero? || num2.zero?
-
-
13
((num1.to_f / num2) * 100).round(2)
-
end
-
-
# @param [Dragnet::Repository] repository The repository whose branches
-
# should be retrieved.
-
# @return [Array<String>] An array with the names of the branches that
-
# "contain" the current head of the repository (may be empty).
-
1
def software_branches(repository)
-
# (uniq needed because of remote/local branches)
-
11
repository.branches_with_head.map(&:name).uniq
-
rescue Git::GitExecuteError => e
-
logger.warn "Failed to read branches information from the repository at #{repository.path}"
-
logger.warn e.message
-
[]
-
end
-
-
# Method used to memoize the output of the +group_by_requirement+ method.
-
# @see #group_by_requirement
-
# @return [Hash] A hash whose keys are the requirement IDs and whose
-
# values are arrays of MTRs
-
1
def test_records_by_requirement
-
10
@test_records_by_requirement ||= group_by_requirement
-
end
-
-
# Groups the MTRs by the requirement(s) they are covering, if a MTR covers
-
# more than one requirement it will be added to all of them, if a
-
# requirement is covered by more than one MTR the requirement will end up
-
# with more than one MTR, example:
-
#
-
# {
-
# 'ESR_REQ_9675' => [MTR1],
-
# 'ESR_REQ_1879' => [MTR2, MTR3]
-
# 'ESR_REQ_4714' => [MTR3]
-
# }
-
#
-
# @return [Hash] A hash whose keys are the requirement IDs and whose
-
# values are arrays of MTRs
-
1
def group_by_requirement
-
10
tests_by_requirement = {}
-
-
10
test_records.each do |test_record|
-
33
ids = *test_record.id
-
33
ids.each do |id|
-
8
tests_by_requirement[id] ||= []
-
8
tests_by_requirement[id] << test_record
-
end
-
end
-
-
10
tests_by_requirement
-
end
-
-
# Returns the HTML code needed to render the Review Status of a MTR as a
-
# badge.
-
# @param [Dragnet::TestRecord] test_record The Test Record.
-
# @return [String] The HTML code to display the Test Record's review
-
# status as a badge.
-
1
def review_status_badge(test_record)
-
57
if test_record.review_status
-
2
color = test_record.reviewed? ? 'green' : 'red'
-
2
review_status = test_record.review_status.capitalize
-
else
-
55
color = 'gray'
-
55
review_status = '(unknown)'
-
end
-
-
57
badge_html(color, review_status)
-
end
-
-
# Returns the HTML code needed to display the verification result of a MTR
-
# (the color and the text inside the badge are picked in accordance to the
-
# given result).
-
# @param [Dragnet::VerificationResult] verification_result The result of
-
# the verification for a given +TestRecord+
-
# @return [String] The HTML code needed to display the result as a badge.
-
1
def verification_result_badge(verification_result)
-
57
badge_html(
-
verification_result_color(verification_result),
-
verification_result.status.capitalize
-
)
-
end
-
-
# Returns a color that depends on the verification result for a Test
-
# Record. To be used on HTML elements.
-
# @param [Dragnet::VerificationResult] verification_result The
-
# +VerificationResult+ object.
-
# @return [String] The corresponding color (depends on the +status+ field
-
# of the +VerificationResult+ object).
-
1
def verification_result_color(verification_result)
-
87
case verification_result.status
-
when :passed
-
29
'green'
-
when :skipped
-
29
'yellow'
-
else
-
29
'red'
-
end
-
end
-
-
# Returns the color that should be used for the highlight line on the left
-
# of the card given the result of the MTR's verification.
-
# @param [Dragnet::VerificationResult] verification_result The
-
# +VerificationResult+ object associated with the +TestRecord+ being
-
# rendered on the card.
-
1
def card_color(verification_result)
-
30
verification_result_color(verification_result)
-
end
-
-
# Returns the HTML string to produce a Badge
-
# @param [String] color The color of the badge.
-
# @param [String] text The text that goes inside the badge.
-
# @return [String] The HTML code to produce a badge with the given color
-
# and text.
-
1
def badge_html(color, text)
-
114
"<span class=\"badge bg-#{color}\">#{text}</span>"
-
end
-
-
# Converts the ID (+String+) or IDs (+Array<String>+) of a +TestRecord+
-
# object into a string that can be safely rendered in the HTML report.
-
# @param [Dragnet::TestRecord] test_record The +TestRecord+ object.
-
# @return [String] A string with the ID or IDs of the +TestRecord+ object.
-
1
def test_record_id_to_string(test_record)
-
57
Array(test_record.id).join(', ')
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require 'digest'
-
-
1
require_relative '../helpers/repository_helper'
-
-
1
module Dragnet
-
1
module Exporters
-
# Generates unique IDs for the Manual Test Records by hashing some of their
-
# properties into a hexadecimal SHA1.
-
1
class IDGenerator
-
1
include Dragnet::Helpers::RepositoryHelper
-
-
1
attr_reader :repository
-
-
# @param [Dragnet::Repository] repository The repository where the MTR
-
# files are located. This allows the SHA1 to be calculated with relative
-
# paths to the MTRs' files.
-
1
def initialize(repository)
-
23
@repository = repository
-
end
-
-
# Calculates the ID of the given MTR
-
# @param [Dragnet::TestRecord] test_record The record for which the ID
-
# should be calculated.
-
# @return [String] The ID for the given +TestRecord+.
-
# :reek:FeatureEnvy (Cannot be done in the TestRecord itself because it needs the Repository)
-
1
def id_for(test_record)
-
65
string = "#{relative_to_repo(test_record.source_file)}#{test_record.id}"
-
# noinspection RubyMismatchedReturnType (This is never nil)
-
65
Digest::SHA1.hexdigest(string)[0...16]
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require 'json'
-
-
1
require_relative 'exporter'
-
1
require_relative 'id_generator'
-
1
require_relative 'serializers/test_record_serializer'
-
-
1
module Dragnet
-
1
module Exporters
-
# Exports the results for the Manual Test Record verification to a JSON
-
# string.
-
1
class JSONExporter < ::Dragnet::Exporters::Exporter
-
# @return [String] A JSON string containing an array of objects, one for
-
# each Test Record.
-
1
def export
-
33
logger.info 'Exporting data to JSON'
-
33
test_records.map do |test_record|
-
87
::Dragnet::Exporters::Serializers::TestRecordSerializer
-
.new(test_record, repository).serialize
-
.merge(id: id_generator.id_for(test_record))
-
end.to_json
-
end
-
-
1
private
-
-
# @return [Dragnet::Exporters::IDGenerator] An instance of the IDGenerator
-
# class that can be used to calculate the ID for the exported MTRs.
-
1
def id_generator
-
87
@id_generator ||= ::Dragnet::Exporters::IDGenerator.new(repository)
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'serializers/repo_serializer'
-
1
require_relative 'serializers/test_record_serializer'
-
1
require_relative 'serializers/verification_result_serializer'
-
-
1
module Dragnet
-
1
module Exporters
-
# Namespace for the serializer classes used by the exporters.
-
1
module Serializers; end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require 'active_support'
-
1
require 'active_support/core_ext/object/blank'
-
-
1
module Dragnet
-
1
module Exporters
-
1
module Serializers
-
# Serializes a +Repo+ object into a +Hash+
-
1
class RepoSerializer
-
1
attr_reader :repo
-
-
# @param [Dragnet::Repo] repo The +Repo+ object to serialize.
-
1
def initialize(repo)
-
27
@repo = repo
-
end
-
-
# Serializes the given +Repo+ object.
-
# @return [Hash] A +Hash+ representing the given +Repo+ object.
-
1
def serialize
-
{
-
27
path: repo.path,
-
sha1: repo.sha1
-
}.tap do |hash|
-
27
hash[:files] = serialize_files if repo.files.present?
-
end
-
end
-
-
1
private
-
-
# Serializes the array of files attached to the +Repo+
-
# @return [Array<String>] The array of file names (without the path to
-
# the repository).
-
1
def serialize_files
-
89
repo.files.map { |file| file.to_s.gsub("#{repo.path}/", '') }
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require 'active_support'
-
1
require 'active_support/core_ext/object/blank'
-
-
1
require_relative '../../helpers/repository_helper'
-
1
require_relative 'repo_serializer'
-
1
require_relative 'verification_result_serializer'
-
-
1
module Dragnet
-
1
module Exporters
-
1
module Serializers
-
# Serializes a +TestRecord+ object into a +Hash+.
-
1
class TestRecordSerializer
-
1
include ::Dragnet::Helpers::RepositoryHelper
-
-
1
attr_reader :test_record, :repository
-
-
# @param [Dragnet::TestRecord] test_record The +TestRecord+ object to
-
# serialize.
-
# @param [Dragnet::RepositoryBase] repository The +Repository+ object
-
# associated with the +TestRecord+. Used to render file paths relative
-
# to the repository instead of as absolute paths.
-
1
def initialize(test_record, repository)
-
96
@test_record = test_record
-
96
@repository = repository
-
end
-
-
# rubocop:disable Metrics/AbcSize (because of the Hash)
-
# rubocop:disable Metrics/CyclomaticComplexity (because of the conditionals)
-
# rubocop:disable Metrics/PerceivedComplexity (because of the conditionals)
-
# rubocop:disable Metrics/MethodLength (because of he Hash)
-
-
# @return [Hash] A +Hash+ representing the given +TestRecord+ object.
-
1
def serialize
-
{
-
96
refs: Array(test_record.id),
-
result: test_record.result,
-
review_status: render_review_status,
-
mtr_file: relative_to_repo(test_record.source_file).to_s,
-
verification_result: serialized_verification_result,
-
-
# TODO: Remove the started_at and finished_at attributes after solving
-
# https://esrlabs.atlassian.net/browse/JAY-493
-
started_at: serialized_verification_result[:started_at],
-
finished_at: serialized_verification_result[:finished_at]
-
}.tap do |hash|
-
96
hash[:sha1] = test_record.sha1 if test_record.sha1.present?
-
96
hash[:owner] = Array(test_record.name).join(', ') if test_record.name.present?
-
96
hash[:description] = test_record.description if test_record.description.present?
-
96
hash[:test_method] = Array(test_record.test_method) if test_record.test_method.present?
-
-
96
if test_record.tc_derivation_method.present?
-
2
hash[:tc_derivation_method] = Array(test_record.tc_derivation_method)
-
end
-
-
96
hash[:review_comments] = test_record.review_comments if test_record.review_comments.present?
-
96
hash[:findings] = test_record.findings if test_record.findings?
-
96
hash[:files] = serialize_files if test_record.files.present?
-
96
hash[:repos] = serialize_repos if test_record.repos.present?
-
end
-
end
-
-
# rubocop:enable Metrics/AbcSize
-
# rubocop:enable Metrics/CyclomaticComplexity
-
# rubocop:enable Metrics/PerceivedComplexity
-
# rubocop:enable Metrics/MethodLength
-
-
1
private
-
-
# Renders the +TestRecord+'s review status
-
# @return [String] The review status, either +'not_reviewed'+ or +'reviewed'+
-
1
def render_review_status
-
96
"#{test_record.reviewed? ? nil : 'not_'}reviewed"
-
end
-
-
# Serializes the files listed in the given +TestRecord+
-
# @return [Array<String>] An array of strings, one for each listed file.
-
1
def serialize_files
-
88
test_record.files.map { |file| relative_to_repo(file).to_s }
-
end
-
-
# Serializes the +Repo+ objects attached to the +TestRecord+
-
# @return [Array<Hash>] An array of +Hash+es representing each of the
-
# +Repo+ objects associated with the +TestRecord+
-
1
def serialize_repos
-
46
test_record.repos.map { |repo| ::Dragnet::Exporters::Serializers::RepoSerializer.new(repo).serialize }
-
end
-
-
# Serializes the +VerificationResult+ object attached to the given
-
# +TestRecord+
-
# @return [Hash] A +Hash+ representation of the +VerificationResult+
-
# object.
-
1
def serialized_verification_result
-
288
@serialized_verification_result ||= ::Dragnet::Exporters::Serializers::VerificationResultSerializer.new(
-
test_record.verification_result
-
).serialize
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
module Dragnet
-
1
module Exporters
-
1
module Serializers
-
# Serializes a +VerificationResult+ object into a +Hash+.
-
1
class VerificationResultSerializer
-
1
attr_reader :verification_result
-
-
# Format used to serialize the +VerificationResult+'s date/time attributes.
-
1
DATE_FORMAT = '%F %T %z'
-
-
# @param [Dragnet::VerificationResult] verification_result The
-
# +VerificationResult+ object to serialize.
-
1
def initialize(verification_result)
-
75
@verification_result = verification_result
-
end
-
-
# @return [Hash] The +Hash+ representation of the given
-
# +VerificationResult+ object.
-
1
def serialize
-
{
-
75
status: verification_result.status,
-
started_at: verification_result.started_at.strftime(DATE_FORMAT),
-
finished_at: verification_result.finished_at.strftime(DATE_FORMAT),
-
runtime: verification_result.runtime
-
}.tap do |hash|
-
75
hash[:reason] = verification_result.reason if verification_result.reason
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
module Dragnet
-
1
module Helpers
-
# Some helper methods to use when working with repositories.
-
1
module RepositoryHelper
-
# @return [String] The first 10 characters of the given string (normally
-
# used to shorten SHA1s when building messages).
-
1
def shorten_sha1(sha1)
-
53
sha1[0...10]
-
end
-
-
# @return [Pathname] The base path of the repository where the MTR and the
-
# source files are located. Used to present relative paths.
-
1
def repo_base
-
301
@repo_base ||= repository.path
-
end
-
-
# Transforms the given path into a path relative to the repository's root
-
# @param [Pathname] path The absolute path.
-
# @return [Pathname] A path relative to the repository's root.
-
1
def relative_to_repo(path)
-
301
path.relative_path_from(repo_base)
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'base_repository'
-
-
1
module Dragnet
-
# This is a dummy class that acts as a placeholder when Dragnet is executed
-
# on a multi-repo set-up. Since there is no Git repository in the directory
-
# where git-repo runs git commands cannot be executed there only in the inner
-
# repositories.
-
#
-
# This class's job is to raise a particular error when a git operation is
-
# attempted directly on this directory so that Dragnet can recognize the cause
-
# of the error and display it correctly.
-
#
-
# It also acts as a collection of repositories. It stores a collection of
-
# +Dragnet::Repository+ objects, which point to the actual repositories (this
-
# is just so that the same repository isn't initialized multiple times).
-
1
class MultiRepository < Dragnet::BaseRepository
-
1
attr_reader :repositories
-
-
# @param [Pathname] path Path to the directory where the inner repositories
-
# reside.
-
1
def initialize(path:)
-
37
super
-
37
@repositories = {}
-
end
-
-
# @return [TrueClass] It always returns true
-
1
def multi?
-
1
true
-
end
-
-
1
private
-
-
# @param [Symbol] method_name The name of the method that was invoked.
-
# @raise [Dragnet::Errors::IncompatibleRepositoryError] Is always raised
-
# with a description of the method that was invoked and a possible cause
-
# for the failure.
-
1
def incompatible_repository(method_name)
-
8
super(
-
"Failed to perform the action '#{method_name}' on '#{path}'."\
-
" There isn't a git repository there. If you are running with the"\
-
' --multi-repo command line switch make sure that all of your MTRs'\
-
" contain a valid 'repos' attribute."
-
)
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'validators/entities/repo_validator'
-
-
1
module Dragnet
-
# Represents a repository, (for MTRs which reference multiple repositories in
-
# a multi-repo project, often managed with git-repo)
-
1
class Repo
-
1
attr_accessor :path, :sha1, :files
-
-
# @param [Hash] args The data for the Repo
-
# @option args [String] :path The path where the repository is stored.
-
# @option args [String] :sha1 The SHA1 the repository had when the MTR was
-
# created.
-
# @option args [String, Array<String>, nil] :files The file or array of
-
# files covered by the MTR.
-
1
def initialize(args)
-
25
@path = args[:path]
-
25
@sha1 = args[:sha1]
-
25
@files = args[:files]
-
end
-
-
# Validates the +Repo+ instance (by checking each of its attributes).
-
# @raise [Dragnet::Errors::ValidationError] If any of the attributes of the
-
# +Repo+ object is invalid.
-
# @see Dragnet::Validators::Entities::RepoValidator#validate
-
1
def validate
-
2
Dragnet::Validators::Entities::RepoValidator.new(self).validate
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require 'forwardable'
-
1
require 'git/url'
-
-
1
require_relative 'base_repository'
-
-
1
module Dragnet
-
# A small wrapper around a Git Repository object. It provides some useful
-
# methods needed during the verify process as well as for reporting.
-
1
class Repository < Dragnet::BaseRepository
-
1
extend Forwardable
-
-
1
attr_reader :git
-
-
1
def_delegators :@git, :branch, :branches, :diff
-
-
# Creates a new instance of the class. Tries to open the given path as a Git
-
# repository.
-
# @param [Pathname] path The path where the root of the repository is located.
-
# @raise [ArgumentError] If the given path is not a valid git repository.
-
1
def initialize(path:)
-
35
super
-
35
@git = Git.open(path)
-
end
-
-
# @return [Git::Object::Commit] The +Commit+ object at the +HEAD+ of the
-
# repository.
-
1
def head
-
33
@head ||= git.object('HEAD')
-
end
-
-
# Returns the URI path of the repository (extracted from its first remote
-
# [assumed to be the origin]). Example:
-
#
-
# ssh://jenkins@gerrit.int.esrlabs.com:29418/tools/dragnet -> /tools/dragnet
-
#
-
# @return [String] The URI path of the repository
-
1
def remote_uri_path
-
16
Git::URL.parse(git.remotes.first.url).path
-
end
-
-
# @return [FalseClass] It always returns false
-
1
def multi?
-
19
false
-
end
-
-
# Returns an array of all the branches that include the given commit.
-
# @param [String] commit The SHA1 of the commit to look for.
-
# @return [Array<Git::Branch>] An array with all the branches that contain
-
# the given commit.
-
1
def branches_with(commit)
-
63
branches.select { |branch| branch.contains?(commit) }
-
end
-
-
# Returns an array of all the branches that include the current HEAD.
-
# @return [Array<Git::Branch>] An array with all the branches that contain
-
# the current HEAD.
-
1
def branches_with_head
-
14
@branches_with_head ||= branches_with(head.sha)
-
end
-
-
1
private
-
-
# @param [Symbol] method_name The name of the method that was invoked.
-
# @raise [Dragnet::Errors::IncompatibleRepositoryError] Is always raised
-
# with a description of the method that was invoked and a possible cause
-
# for the failure.
-
1
def incompatible_repository(method_name)
-
1
super(
-
"Failed to perform the action '#{method_name}' on '#{path}'."\
-
' The path was not set-up as a multi-repo path. If you are running'\
-
' without the --multi-repo command line switch make sure that none of'\
-
" your MTRs have a 'repos' attribute or run with the --multi-repo switch"
-
)
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'validators/entities/test_record_validator'
-
-
1
module Dragnet
-
# Represents a Manual Test Record loaded from a MTR file.
-
1
class TestRecord
-
1
PASSED_RESULT = 'passed'
-
1
REVIEWED_STATUS = 'reviewed'
-
1
NO_FINDINGS = 'no findings'
-
-
# :reek:Attribute (This is an entity class)
-
-
1
attr_accessor :id, :result, :sha1, :name, :description, :files, :repos,
-
:review_status, :review_comments, :findings, :test_method,
-
:tc_derivation_method, :source_file, :verification_result
-
-
# rubocop:disable Metrics/AbcSize (There isn't much that can be done here,
-
# those are the attributes an MTR has).
-
# :reek:FeatureEnvy (Refers to args as much as it refers to itself)
-
-
# Creates a new instance of the class.
-
# @param [Hash] args The data for the Manual Test Record
-
# @option args [String] :id The ID of the MTR
-
# @option args [String] :result The result of the Manual Test.
-
# @option args [String] :sha1 The SHA1 of the commit in which the Manual
-
# Test was performed.
-
# @option args [String, Array<String>, nil] :name The name of the person who
-
# performed the Manual Test.
-
# @option args [String, nil] :description The description of the Manual
-
# Test, normally which actions were performed and what it was mean to
-
# test.
-
# @option args [String, Array<String>, nil] :files The files involved in the
-
# MTR, these are the files which will be checked for changes when
-
# evaluating the validity of the MTR.
-
# @option args [Array<Hash>, nil] :repos An array of +Hash+es with the
-
# information about the repositories that are involved in the MTR, these
-
# repositories will be checked for changes during the evaluation of the
-
# MTR.
-
# @option args [String, nil] :review_status or :reviewstatus The review
-
# status of the MTR. (Normally changed when someone other than the tester
-
# verifies the result of the Manual Test)
-
# @option args [String, nil] :review_comments or :reviewcomments The
-
# comments left by the person who performed the review of the Manual Test.
-
# @option args [String, nil] :findings The findings that the reviewer
-
# collected during the review process (if any).
-
# @option args [String, Array<String>, nil] :test_method The method(s) used
-
# to carry out the test.
-
# @option args [String, Array<String>, nil] :tc_derivation_method: The
-
# method(s) used to derive the test case,
-
# @note Either +:files+ or +:repos+ should be present, not both.
-
1
def initialize(args)
-
144
@id = args[:id]
-
144
@result = args[:result]
-
144
@sha1 = args[:sha1]
-
144
@name = args[:name]
-
144
@description = args[:description]
-
144
@files = args[:files]
-
144
@repos = args[:repos]
-
144
@review_status = args[:review_status] || args[:reviewstatus]
-
144
@review_comments = args[:review_comments] || args[:reviewcomments]
-
144
@findings = args[:findings]
-
144
@test_method = args[:test_method]
-
144
@tc_derivation_method = args[:tc_derivation_method]
-
end
-
# rubocop:enable Metrics/AbcSize
-
-
# Validates the MTR's fields
-
# @raise [Dragnet::Errors::ValidationError] If the validation fails.
-
1
def validate
-
2
Dragnet::Validators::Entities::TestRecordValidator.new(self).validate
-
end
-
-
# @return [Boolean] True if the Manual Test passed, false otherwise.
-
1
def passed?
-
2
result == PASSED_RESULT
-
end
-
-
# @return [Boolean] True if the Manual Test Record has been reviewed, false
-
# otherwise.
-
1
def reviewed?
-
94
review_status == REVIEWED_STATUS
-
end
-
-
# @return [Boolean] True if the Manual Test Record has findings (problems
-
# annotated during the review), false otherwise.
-
1
def findings?
-
69
!(findings.nil? || findings.strip.empty? || findings.downcase == NO_FINDINGS)
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require 'colorize'
-
-
1
require_relative 'validators/data_validator'
-
1
require_relative 'validators/files_validator'
-
1
require_relative 'validators/repos_validator'
-
-
1
module Dragnet
-
# Validates a set of Manual Test Record files. That means, checking that they
-
# can be read, that they are valid YAML files, that they have the expected
-
# keys and that these keys have sensible values.
-
1
class Validator
-
1
attr_reader :files, :path, :logger, :errors, :valid_files
-
-
# Creates a new instance of the class.
-
# @param [Array<Pathname>] files An array with the MTR files to validate.
-
# @param [Pathname] path The path where the MTR files are located.
-
# @param [#info, #error] logger A logger object to use for output.
-
1
def initialize(files:, path:, logger:)
-
19
@files = files
-
19
@path = path
-
19
@logger = logger
-
end
-
-
# Validates the given files.
-
# @return [Array<Dragnet::TestRecord>] An array of +TestRecord+s, one for
-
# each valid MTR file (invalid files will be added to the +errors+ array).
-
# The returned hash has the following structure:
-
1
def validate
-
19
logger.info('Validating MTR Files...')
-
-
19
@errors = []
-
73
@valid_files = files.map { |file| validate_file(file) }.compact
-
end
-
-
1
private
-
-
# Validates the given file
-
# @param [Pathname] file The file to be validated.
-
# @return [Dragnet::TestRecord, nil] A +TestRecord+ object or +nil+ if the
-
# file is invalid.
-
# rubocop:disable Metrics/AbcSize (because of logging).
-
1
def validate_file(file)
-
54
logger.info "Validating #{file}..."
-
54
data = YAML.safe_load(File.read(file))
-
50
test_record = Dragnet::Validators::DataValidator.new(data, file).validate
-
44
Dragnet::Validators::FilesValidator.new(test_record, path).validate
-
38
Dragnet::Validators::ReposValidator.new(test_record, path).validate
-
-
20
logger.info "#{'✔ SUCCESS'.colorize(:light_green)} #{file} Successfully loaded"
-
20
test_record
-
rescue SystemCallError => e
-
2
push_error(file, 'IO Error: Cannot read the specified file', e)
-
rescue Psych::Exception => e
-
2
push_error(file, 'YAML Parsing Error', e)
-
rescue Dragnet::Errors::YAMLFormatError => e
-
6
push_error(file, 'YAML Formatting Error', e)
-
rescue Dragnet::Errors::FileNotFoundError => e
-
15
push_error(file, 'Referenced file not found in repository', e)
-
rescue Dragnet::Errors::RepoPathNotFoundError => e
-
9
push_error(file, 'Referenced repository not found', e)
-
end
-
# rubocop:enable Metrics/AbcSize
-
-
# Pushes an entry into the +errors+ array.
-
# @param [Pathname] file The file that contains the error.
-
# @param [String] message A general description of the message.
-
# @param [Exception] exception The raised exception (through which the file
-
# was branded invalid)
-
# @return [nil] Returns nil so that +validate_file+ can return nil for
-
# invalid files.
-
1
def push_error(file, message, exception)
-
34
errors << { file: file, message: message, exception: exception }
-
34
logger.error "#{'✘ FAILED'.colorize(:light_red)} #{file} Failed: #{message} - #{exception.message}"
-
nil
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'validators/data_validator'
-
1
require_relative 'validators/entities'
-
1
require_relative 'validators/fields'
-
1
require_relative 'validators/files_validator'
-
1
require_relative 'validators/validator'
-
-
1
module Dragnet
-
# Namespace for Validator classes.
-
1
module Validators; end
-
end
-
# frozen_string_literal: true
-
-
1
require 'active_support'
-
1
require 'active_support/core_ext/hash/keys'
-
-
1
require_relative '../test_record'
-
1
require_relative 'validator'
-
-
1
module Dragnet
-
1
module Validators
-
# Validates the data (key-value pairs) inside an MTR file. Verifies the
-
# structure, the required keys and their types.
-
1
class DataValidator < Dragnet::Validators::Validator
-
1
attr_reader :data, :source_file
-
-
# Creates a new instance of the class
-
# @param [Hash] data The data inside the YAML (after parsing)
-
# @param [Pathname] source_file The path to the file from which the MTR
-
# data was loaded.
-
1
def initialize(data, source_file)
-
8
@data = data
-
8
@source_file = source_file
-
end
-
-
# Validates the given data
-
# @return [Dragnet::TestRecord] A +TestRecord+ object created
-
# from the given data (if the data was valid).
-
# @raise [Dragnet::Errors::YAMLFormatError] If the data is invalid. The
-
# raised exceptions contains a message specifying why the data is
-
# invalid.
-
1
def validate
-
8
yaml_format_error("Incompatible data structure. Expecting a Hash, got a #{data.class}") unless data.is_a?(Hash)
-
7
data.deep_symbolize_keys!
-
-
# A call to chomp for strings is needed because the following YAML
-
# syntax:
-
#
-
# findings: >
-
# no findings
-
#
-
# causes the string values to end with a newline ("\n"):
-
35
data.transform_values! { |value| value.is_a?(String) ? value.chomp : value }
-
7
test_record = create_mtr(data)
-
7
validate_mtr(test_record)
-
end
-
-
1
private
-
-
# @param [Hash] data A hash with the data for the +TestRecord+
-
# @see Dragnet::TestRecord#initialize
-
1
def create_mtr(data)
-
7
Dragnet::TestRecord.new(data).tap do |test_record|
-
7
test_record.source_file = source_file
-
end
-
end
-
-
# Creates a +Dragnet::TestRecord+ with the given data and runs its
-
# validation. If the validation is successful the +TestRecord+
-
# object is returned, if the validation fails an error is raised.
-
# @param [Dragnet::TestRecord] test_record The +TestRecord+ object to
-
# validate.
-
# @return [Dragnet::TestRecord] The given +TestRecord+ object if the
-
# validation passed.
-
# @raise [Dragnet::Errors::YAMLFormatError] If the data is invalid. The
-
# raised exceptions contains a message specifying why the data is
-
# invalid.
-
1
def validate_mtr(test_record)
-
7
test_record.validate
-
6
test_record
-
rescue Dragnet::Errors::ValidationError => e
-
1
yaml_format_error(e.message)
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'entities/repo_validator'
-
1
require_relative 'entities/test_record_validator'
-
-
1
module Dragnet
-
1
module Validators
-
# Namespace for Dragnet's Entity Validators (validators that deal with data
-
# objects / models).
-
1
module Entities; end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative '../fields/sha1_validator'
-
1
require_relative '../fields/files_validator'
-
1
require_relative '../fields/path_validator'
-
-
1
module Dragnet
-
1
module Validators
-
1
module Entities
-
# Validates a +Dragnet::Repo+ object, by checking its attributes.
-
1
class RepoValidator
-
1
attr_reader :repo
-
-
# @param [Dragnet::Repo] repo An instance of +Dragnet::Repo+ to validate.
-
1
def initialize(repo)
-
7
@repo = repo
-
end
-
-
# Validates the instance of the +Dragnet::Repo+ object by checking each
-
# of its attributes.
-
# @raise [Dragnet::Errors::ValidationError] If any of the fields in the
-
# given +Dragnet::Repo+ object fails the validation.
-
1
def validate
-
7
Dragnet::Validators::Fields::SHA1Validator.new.validate('repos[sha1]', repo.sha1)
-
6
Dragnet::Validators::Fields::PathValidator.new.validate('repos[path]', repo.path)
-
5
repo.files = Dragnet::Validators::Fields::FilesValidator.new.validate('repos[files]', repo.files)
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative '../fields/description_validator'
-
1
require_relative '../fields/files_validator'
-
1
require_relative '../fields/id_validator'
-
1
require_relative '../fields/meta_data_field_validator'
-
1
require_relative '../fields/repos_validator'
-
1
require_relative '../fields/result_validator'
-
1
require_relative '../fields/sha1_validator'
-
-
1
require_relative '../../errors/validation_error'
-
-
1
module Dragnet
-
1
module Validators
-
1
module Entities
-
# Validates a MTR object
-
1
class TestRecordValidator
-
1
attr_reader :test_record
-
-
# Creates a new instance of the class.
-
# @param [Dragnet::TestRecord] test_record The test record to validate.
-
1
def initialize(test_record)
-
28
@test_record = test_record
-
end
-
-
# Validates the given test record
-
# @raise [Dragnet::Errors::ValidationError] If the validation fails.
-
1
def validate
-
28
repos_xor_files
-
27
repos_xor_sha1
-
-
24
Dragnet::Validators::Fields::IDValidator.new.validate('id', test_record.id)
-
23
Dragnet::Validators::Fields::DescriptionValidator.new.validate('description', test_record.description)
-
22
validate_meta_data_fields
-
-
21
test_record.files = Dragnet::Validators::Fields::FilesValidator.new.validate('files', test_record.files)
-
20
test_record.result = Dragnet::Validators::Fields::ResultValidator.new.validate('result', test_record.result)
-
end
-
-
1
private
-
-
# @raise [Dragnet::Errors::ValidationError] If the MTR has both a
-
# +files+ and a +repos+ attribute.
-
1
def repos_xor_files
-
28
return unless test_record.files && test_record.repos
-
-
1
raise Dragnet::Errors::ValidationError,
-
"Invalid MTR: #{test_record.id}. Either 'files' or 'repos' should be provided, not both"
-
end
-
-
# Executes the validation over the +repos+ attribute and then verifies
-
# if the +sha1+ attribute was also given. If it was, an error is
-
# raised. If +repos+ is not present, then the +sha1+ attribute is
-
# validated.
-
#
-
# This happens in this order to leverage the fact that the
-
# +ReposValidator+ returns +nil+ for empty +Array+s. So if +repos+ is
-
# given as en empty +Array+ the MTR will still be considered valid
-
# (provided it has a SHA1).
-
#
-
# @raise [Dragnet::Errors::ValidationError] If the validation of the
-
# +repos+ attribute fails, if both +repos+ and +sha1+ are present or
-
# if the validation of the +sha1+ attribute fails.
-
1
def repos_xor_sha1
-
27
test_record.repos = Dragnet::Validators::Fields::ReposValidator.new.validate('repos', test_record.repos)
-
-
26
unless test_record.repos
-
22
Dragnet::Validators::Fields::SHA1Validator.new.validate('sha1', test_record.sha1)
-
21
return
-
end
-
-
4
return unless test_record.sha1
-
-
1
raise Dragnet::Errors::ValidationError,
-
"Invalid MTR: #{test_record.id}. Either 'repos' or 'sha1' should be provided, not both"
-
end
-
-
# Validates the meta-data fields of the Test Record.
-
# @raise [Dragnet::Errors::ValidationError] If any of the meta-data
-
# fields fail the validation.
-
1
def validate_meta_data_fields
-
22
meta_data_validator = Dragnet::Validators::Fields::MetaDataFieldValidator.new
-
-
22
test_record.name = meta_data_validator.validate('name', test_record.name)
-
21
test_record.test_method = meta_data_validator.validate('test_method', test_record.test_method)
-
21
test_record.tc_derivation_method = meta_data_validator.validate(
-
'tc_derivation_method', test_record.tc_derivation_method
-
)
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'fields/description_validator'
-
1
require_relative 'fields/files_validator'
-
1
require_relative 'fields/id_validator'
-
1
require_relative 'fields/meta_data_field_validator'
-
1
require_relative 'fields/path_validator'
-
1
require_relative 'fields/repos_validator'
-
1
require_relative 'fields/result_validator'
-
1
require_relative 'fields/sha1_validator'
-
-
1
module Dragnet
-
1
module Validators
-
# Namespace module for entity fields validators.
-
1
module Fields; end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'field_validator'
-
-
1
module Dragnet
-
1
module Validators
-
1
module Fields
-
# Validates the +description+ field for a MTR.
-
1
class DescriptionValidator < Dragnet::Validators::Fields::FieldValidator
-
# Validates a MTR's description
-
# @param [String] key The name of the key
-
# @param [Object] value The value of the key
-
# @raise [Dragnet::Errors::ValidationError] If the description contains
-
# anything but a +String+ or +nil+.
-
# :reek:NilCheck (Only +nil+ is allowed, +false+ should be considered invalid).
-
1
def validate(key, value)
-
6
return if value.nil?
-
-
5
validate_type(key, value, String)
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative '../../errors/validation_error'
-
-
1
module Dragnet
-
1
module Validators
-
1
module Fields
-
# Base class for all the validators used to validate individual fields
-
# inside entities.
-
1
class FieldValidator
-
1
def validate(_key, _value)
-
1
raise NotImplementedError, "#validate method not implemented in #{self.class}"
-
end
-
-
1
private
-
-
# Validates the presence of a value
-
# @param [String, Symbol] key The key associated with the value.
-
# @param [Object] value The value to validate.
-
# @raise [Dragnet::Errors::ValidationError] If the given value is not
-
# present (i.e. is +nil+)
-
1
def validate_presence(key, value)
-
19
validation_error("Missing required key: #{key}") if value.nil?
-
end
-
-
# Validates the type of the given value.
-
# @param [String, Symbol] key The key associated with the value.
-
# @param [Object] value The value to validate.
-
# @param [Array<Class>] expected_types The allowed types for the given
-
# value.
-
# @raise [Dragnet::Errors::ValidationError] If the given value has a type
-
# which is not in the given array of expected types.
-
1
def validate_type(key, value, *expected_types)
-
43
return if expected_types.include?(value.class)
-
-
11
validation_error(
-
"Incompatible type for key #{key}: "\
-
"Expected #{expected_types.join(', ')} got #{value.class} instead"
-
)
-
end
-
-
# Raises a +Dragnet::Errors::ValidationError+ with the given message.
-
# @param [String] message The message for the error.
-
# @raise [Dragnet::Errors::ValidationError] Is always raised.
-
1
def validation_error(message)
-
24
raise Dragnet::Errors::ValidationError, message
-
end
-
-
# Validates that all elements inside the given array are of the
-
# expected type
-
# @param [String, Symbol] key The key associated with the array.
-
# @param [Array] array The array whose types should be checked.
-
# @param [Class] expected_type The type the elements inside the array
-
# should have.
-
# @raise [Dragnet::Errors::ValidationError] If any of the elements inside
-
# the given array is of a different type.
-
1
def validate_array_types(key, array, expected_type)
-
42
incompatible_value = array.find { |val| !val.is_a?(expected_type) }
-
12
return unless incompatible_value
-
-
4
validation_error(
-
"Incompatible type for key #{key}: Expected a Array<#{expected_type}>. "\
-
"Found a(n) #{incompatible_value.class} inside the array"
-
)
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'field_validator'
-
-
1
module Dragnet
-
1
module Validators
-
1
module Fields
-
# Validates the files field on a Manual Test Record
-
1
class FilesValidator < Dragnet::Validators::Fields::FieldValidator
-
# Validates the MTR's +files+ array.
-
# @param [String] key The name of the key
-
# @param [Object] value The value of the key
-
# @return [Array<String>, nil] If +files+ is an Array or a String then
-
# an array is returned, if +files+ is +nil+ then +nil+ is returned.
-
# @raise [Dragnet::Errors::ValidationError] If the +files+ key is not a
-
# +String+ or an +Array+ of +String+s.
-
1
def validate(key, value)
-
4
return unless value
-
-
4
validate_type(key, value, String, Array)
-
3
value = *value
-
3
validate_array_types(key, value, String)
-
-
2
value
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'field_validator'
-
-
1
module Dragnet
-
1
module Validators
-
1
module Fields
-
# Validates the ID Field for Manual Test Records
-
1
class IDValidator < Dragnet::Validators::Fields::FieldValidator
-
# Validates the Requirement ID(s) of the MTR
-
# @param [String] key The name of the key
-
# @param [Object] value The value of the key
-
# @raise [Dragnet::Errors::ValidationError] If the Requirement ID(s) are
-
# missing, they are not a String or an Array of Strings if they contain
-
# a disallowed character or (in the case of an Array) any of its
-
# elements is not a String.
-
1
def validate(key, value)
-
5
validate_presence(key, value)
-
4
validate_type(key, value, String, Array)
-
-
3
if value.is_a?(String)
-
2
match = value.match(/,|\s/)
-
2
return unless match
-
-
1
validation_error(
-
"Disallowed character '#{match}' found in the value for key #{key}. "\
-
'To use multiple requirement IDs please put them into an array'
-
)
-
else
-
1
validate_array_types(key, value, String)
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'field_validator'
-
1
require_relative '../../errors/validation_error'
-
-
1
module Dragnet
-
1
module Validators
-
1
module Fields
-
# Base class to validate the fields that are part of the meta-data group.
-
# This means: Either +String+ +Array<String>+ or +nil+ as value.
-
1
class MetaDataFieldValidator < Dragnet::Validators::Fields::FieldValidator
-
# Validates the specified attribute as a meta-data field.
-
# @param [String] key The name of the key
-
# @param [Object] value The value of the key
-
# @raise [Dragnet::Errors::ValidationError] If the attribute fails the
-
# validation.
-
# @return [nil] If +value+ is +nil+ or an empty array.
-
# @return [Array<String>] If +value+ is a +String+ or an +Arry<String>+
-
1
def validate(key, value)
-
10
return unless value
-
-
8
validate_type(key, value, String, Array)
-
-
7
if value.is_a?(Array)
-
5
return if value.empty?
-
-
3
validate_array_types(key, value, String)
-
2
value
-
else
-
2
[value]
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'field_validator'
-
-
1
module Dragnet
-
1
module Validators
-
1
module Fields
-
# Validates the +path+ attribute of a +Repo+ object.
-
1
class PathValidator < Dragnet::Validators::Fields::FieldValidator
-
# Validates the Path of the repository.
-
# @param [String] key The name of the key
-
# @param [Object] value The value of the key
-
# @raise [Dragnet::Errors::ValidationError] If the path is missing, or
-
# it isn't a String.
-
1
def validate(key, value)
-
3
validate_presence(key, value)
-
2
validate_type(key, value, String)
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative '../../repo'
-
1
require_relative 'field_validator'
-
-
1
module Dragnet
-
1
module Validators
-
1
module Fields
-
# Validates that the +repos+ attribute in an MTR is valid. This means:
-
# * It is either a +Hash+ or an +Array+ of +Hash+es.
-
# * The attributes inside each of the +Hash+es are also valid.
-
1
class ReposValidator < Dragnet::Validators::Fields::FieldValidator
-
# Validates the MTR's +repos+ field.
-
# @param [String] key The name of the key (usually +'repos'+)
-
# @param [Object] value The value associated to the attribute.
-
# @return [Array<Dragnet::Repo>, nil] If +value+ is a valid +Hash+ or a
-
# valid +Array+ of +Hash+es an +Array+ of +Dragnet::Repo+ objects is
-
# returned. If +value+ is +nil+, +nil+ is returned.
-
# @raise [Dragnet::Errors::ValidationError] If +value+ is not a +Hash+
-
# or an +Array+ of +Hash+es or the attributes inside the +Hash+es are
-
# invalid.
-
# @see Dragnet::Repo#validate
-
1
def validate(key, value)
-
12
return unless value
-
-
11
validate_type(key, value, Hash, Array)
-
-
10
if value.is_a?(Array)
-
6
return if value.empty?
-
-
5
validate_array_types(key, value, Hash)
-
else
-
# This is needed because trying to apply the splat operator over a
-
# Hash will result in an Array of Arrays (one for each of the Hash's
-
# key pairs).
-
4
value = [value]
-
end
-
-
8
create_repos(value)
-
end
-
-
1
private
-
-
# @param [Array<Hash>] hashes The array of +Hash+es from which the Repo
-
# objects shall be created.
-
# @return [Array<Dragnet::Repo>] The array of +Dragnet::Repo+ objects
-
# that result from using each of the given +Hash+es as parameters for
-
# the constructor.
-
# @raise [Dragnet::Errors::ValidationError] If the attributes inside the
-
# +Hash+es are invalid.
-
# @see Dragnet::Repo#validate
-
1
def create_repos(hashes)
-
8
hashes.map do |hash|
-
12
repo = Dragnet::Repo.new(**hash)
-
12
repo.validate
-
12
repo
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'field_validator'
-
-
1
module Dragnet
-
1
module Validators
-
1
module Fields
-
# Validates the result field of an MTR Record
-
1
class ResultValidator < Dragnet::Validators::Fields::FieldValidator
-
1
VALID_RESULTS = %w[passed failed].freeze
-
-
# Validates the MTR's result
-
# @param [String] key The name of the key
-
# @param [Object] value The value of the key
-
# @return [String] The downcase version of the result field.
-
# @raise [Dragnet::Errors::ValidationError] If the result is missing, if
-
# it isn't a String or is not one of the allowed values for the field.
-
1
def validate(key, value)
-
5
validate_presence(key, value)
-
4
validate_type(key, value, String)
-
-
3
value = value.downcase
-
3
return value if VALID_RESULTS.include?(value)
-
-
1
validation_error(
-
"Invalid value for key result: '#{value}'. "\
-
"Valid values are #{VALID_RESULTS.join(', ')}"
-
)
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'field_validator'
-
-
1
module Dragnet
-
1
module Validators
-
1
module Fields
-
# Validates the SHA1 field of a Manual Test Record
-
1
class SHA1Validator < Dragnet::Validators::Fields::FieldValidator
-
1
SHA1_MIN_LENGTH = 7
-
1
SHA1_MAX_LENGTH = 40
-
1
SHA1_REGEX = /\A[0-9a-f]+\Z/.freeze
-
-
# Validates the SHA1 of the MTR
-
# @param [String] key The name of the key
-
# @param [Object] value The value of the key
-
# @raise [Dragnet::Errors::ValidationError] If the SHA1 is missing, is not
-
# and string, is too short or too long or is not a valid hexadecimal
-
# string.
-
1
def validate(key, value)
-
6
validate_presence(key, value)
-
5
validate_type(key, value, String)
-
-
4
length = value.length
-
4
unless length >= SHA1_MIN_LENGTH && length <= SHA1_MAX_LENGTH
-
2
validation_error(
-
"Invalid value for key #{key}: '#{value}'. Expected a string between "\
-
"#{SHA1_MIN_LENGTH} and #{SHA1_MAX_LENGTH} characters"
-
)
-
end
-
-
2
return if value.match(SHA1_REGEX)
-
-
1
validation_error(
-
"Invalid value for key #{key}: '#{value}'. "\
-
"Doesn't seem to be a valid hexadecimal string"
-
)
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative '../errors/file_not_found_error'
-
1
require_relative 'validator'
-
-
1
module Dragnet
-
1
module Validators
-
# Validates the +files+ key in the given Manual Test Record object.
-
# Validates:
-
# - That the listed file(s) glob pattern(s) match at least one file in the
-
# repository.
-
1
class FilesValidator < Dragnet::Validators::Validator
-
1
attr_reader :test_record, :path
-
-
# Creates a new instance of the class.
-
# @param [Dragnet::TestRecord] test_record The +TestRecord+ object whose
-
# files should be validated.
-
# @param [Pathname] path The path to the repository where the files are
-
# supposed to be located.
-
1
def initialize(test_record, path)
-
6
@test_record = test_record
-
6
@files = test_record.files
-
6
@path = path
-
end
-
-
# Validates the +files+ key in the given data.
-
# Updates the +file+ key in the given +data+ to the actual files found in
-
# the repository.
-
# @raise [Dragnet::Errors::FileNotFoundError] If any of the listed files
-
# cannot be found in the given repository path or if a glob pattern
-
# doesn't match any files there.
-
1
def validate
-
6
return unless files
-
-
5
test_record.files = translate_paths && force_relative_paths && resolve_files
-
end
-
-
1
private
-
-
1
attr_reader :files
-
-
# Forces all the file paths to be relative by removing the +/+ at the
-
# start (if they have one). This is done to ensure that files are always
-
# considered relative to the path being checked.
-
1
def force_relative_paths
-
5
@files = files.map do |file|
-
13
file.sub(%r{^/}, '')
-
end
-
end
-
-
# Translate the file paths from windows style paths (with \ as path
-
# separator) to Unix style paths (with / as path separator).
-
# This is done so that the git commands work in all systems.
-
1
def translate_paths
-
5
@files = files.map do |file|
-
13
file.tr('\\', '/')
-
end
-
end
-
-
# Resolve the given files by checking for matches in the given repository.
-
# Glob patterns are resolved an translated into individual files.
-
# @return [Array<Pathname>] The resolved file paths.
-
# @raise [Dragnet::Errors::FileNotFoundError] If any of the listed files
-
# cannot be found in the given repository path or if a glob pattern
-
# doesn't match any files there.
-
1
def resolve_files
-
5
resolved_files = []
-
-
5
files.each do |file|
-
# Files can be defined as glob patterns
-
11
matched_files = path.glob(file)
-
-
11
if matched_files.empty?
-
1
raise Dragnet::Errors::FileNotFoundError,
-
"Could not find any files matching #{file} in #{path}"
-
end
-
-
10
resolved_files += matched_files
-
end
-
-
4
resolved_files
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative '../errors/repo_path_not_found_error'
-
1
require_relative 'files_validator'
-
-
1
module Dragnet
-
1
module Validators
-
# Validates the +Repo+ objects attached to the given +TestRecord+
-
1
class ReposValidator < Dragnet::Validators::Validator
-
1
attr_reader :test_record, :repos, :path
-
-
# @param [Dragnet::TestRecord] test_record The Test Record to validate.
-
# @param [Pathname] path The path where the repositories are supposed to
-
# be located.
-
1
def initialize(test_record, path)
-
11
@test_record = test_record
-
11
@path = path
-
11
@repos = test_record.repos
-
end
-
-
# Validates the +Repo+ objects inside the given +TestCase+
-
1
def validate
-
11
return unless repos
-
-
10
validate_paths
-
end
-
-
1
private
-
-
# Validates the +paths+ of the +Repo+ objects (makes sure the paths
-
# exist). Knowing that these paths exist, the +files+ attribute of the
-
# +Repo+ object can be validated as well.
-
# @raise [Dragnet::Errors::RepoPathNotFoundError] If one or more of the
-
# paths cannot be found.
-
# @raise [Dragnet::Errors::FileNotFoundError] If any of the files listed
-
# in the +files+ attribute do not exist inside the given +path+.
-
1
def validate_paths
-
10
repos.each do |repo|
-
27
repo_path = repo.path = repo.path.gsub('\\', '/')
-
27
repo_path = Pathname.new(repo_path)
-
-
27
complete_path = repo_path.absolute? ? repo_path : path / repo_path
-
-
27
if complete_path.exist?
-
25
validate_files(repo, complete_path)
-
24
next
-
end
-
-
2
repo_path_not_found(repo_path)
-
end
-
end
-
-
# Validates the existence of the files listed in the +files+ attributes
-
# inside the +Repo+ object inside the +Repo+'s +path+.
-
# @param [Dragnet::Repo] repo The +Repo+ whose files should be validated.
-
# @param [Pathname] complete_path The path to the repository.
-
1
def validate_files(repo, complete_path)
-
25
return unless repo.files
-
-
6
Dragnet::Validators::FilesValidator.new(repo, complete_path).validate
-
end
-
-
# Raises a Dragnet::Errors::RepoPathNotFoundError with the appropriate
-
# message (which depends on whether the path is absolute or relative).
-
# @param [Pathname] repo_path The path that couldn't be found.
-
# @raise [Dragnet::Errors::RepoPathNotFoundError] is always raised.
-
1
def repo_path_not_found(repo_path)
-
2
message = "Cannot find the repository path #{repo_path}"
-
2
message += " inside #{path}" if repo_path.relative?
-
2
raise Dragnet::Errors::RepoPathNotFoundError, message
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative '../errors/yaml_format_error'
-
-
1
module Dragnet
-
1
module Validators
-
# Base class for all validators.
-
1
class Validator
-
1
private
-
-
# Raises a +Dragnet::Errors::YAMLFormatError+ with the given message.
-
# @param [String] message The message for the exception.
-
# @raise [Dragnet::Errors::YAMLFormatError] Is always raised with the
-
# given message.
-
1
def yaml_format_error(message)
-
2
raise Dragnet::Errors::YAMLFormatError, message
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require 'colorize'
-
-
1
require_relative 'errors/missing_timestamp_attribute_error'
-
-
1
module Dragnet
-
# Holds the verification result of a Manual Test Record
-
1
class VerificationResult
-
1
VALID_STATUSES = %i[passed skipped failed].freeze
-
-
1
attr_reader :status, :reason, :started_at, :finished_at
-
-
# Creates a new instance of the class.
-
# @param [Symbol] status The status
-
# @param [String] reason
-
1
def initialize(status:, reason: nil)
-
147
self.status = status
-
146
@reason = reason
-
end
-
-
1
def passed?
-
33
status == :passed
-
end
-
-
1
def skipped?
-
5
status == :skipped
-
end
-
-
1
def failed?
-
2
status == :failed
-
end
-
-
# Assigns the given status
-
# @param [Symbol] status The status
-
# @raise [ArgumentError] If the given status is not one of the accepted
-
# valid statuses.
-
1
def status=(status)
-
149
unless VALID_STATUSES.include?(status)
-
2
raise ArgumentError, "Invalid status #{status}."\
-
" Valid statuses are: #{VALID_STATUSES.join(', ')}"
-
end
-
-
147
@status = status
-
end
-
-
# Sets the verification's start time.
-
# @param [Time] time The verification's start time.
-
# @raise [ArgumentError] If the given +time+ is not an instance of +Time+.
-
# @raise [ArgumentError] If +finished_at+ is set and the given +time+ is
-
# bigger than or equal to it.
-
1
def started_at=(time)
-
83
validate_time(time)
-
82
raise ArgumentError, 'started_at must be smaller than finished_at' if finished_at && time >= finished_at
-
-
81
@runtime = nil
-
81
@started_at = time
-
end
-
-
# Sets the verification's finish time
-
# @param [Time] time The verification's finish time.
-
# @raise [TypeError] Is an attempt is made to set +finished_at+ before
-
# setting +started_at+.
-
# @raise [ArgumentError] If the given +time+ is not an instance of +Time+.
-
# @raise [ArgumentError] If +started_at+ is set and the given +time+ is
-
# smaller than or equal to it.
-
1
def finished_at=(time)
-
83
validate_time(time)
-
82
raise ArgumentError, 'finished_at must be greater than started_at' if started_at && time <= started_at
-
-
81
@runtime = nil
-
81
@finished_at = time
-
end
-
-
# @return [Float, nil] The runtime calculated from the started_at and
-
# finished_at attributes, if any of them is missing +nil+ is returned
-
# instead.
-
1
def runtime
-
75
runtime!
-
rescue Dragnet::Errors::MissingTimestampAttributeError
-
3
nil
-
end
-
-
# @return [Float] The runtime calculated from the started_at and finished_at
-
# timestamp attributes.
-
# @raise [TypeError] If either of these attributes is +nil+
-
1
def runtime!
-
79
@runtime ||= calculate_runtime
-
end
-
-
# @return [String] A string representation of the receiver that can be used
-
# to log the result of a verification.
-
1
def log_message
-
4
if passed?
-
1
'✔ PASSED '.colorize(:light_green)
-
3
elsif skipped?
-
1
"#{'âš SKIPPED'.colorize(:light_yellow)} #{reason}"
-
else
-
2
"#{'✘ FAILED '.colorize(:light_red)} #{reason || 'Unknown reason'}"
-
end
-
end
-
-
1
private
-
-
# Checks if the given object is a +Time+ and raises an +ArgumentError+ if it
-
# isn't.
-
# @param [Object] time The object to check.
-
# @raise [ArgumentError] If the given object is not a +Time+ object.
-
1
def validate_time(time)
-
166
raise ArgumentError, "Expected a Time object, got #{time.class}" unless time.is_a?(Time)
-
end
-
-
# @return [Float] The runtime calculated from the started_at and finished_at
-
# timestamp attributes.
-
# @raise [TypeError] If either of these attributes is +nil+
-
1
def calculate_runtime
-
79
if started_at.nil? || finished_at.nil?
-
6
raise Dragnet::Errors::MissingTimestampAttributeError,
-
'Both started_at and finished_at must be set in order to calculate the runtime'
-
end
-
-
73
finished_at - started_at
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require 'git'
-
-
1
require_relative 'verifiers/test_record_verifier'
-
-
1
module Dragnet
-
# Executes the verification process on the given Test Records
-
1
class Verifier
-
1
attr_reader :test_records, :path, :logger
-
-
# Creates a new instance of the class.
-
# @param [Array<Hash>] test_records An array with the test records.
-
# @param [Dragnet::Repository] repository The repository where the MTR and
-
# the source files are stored.
-
# @param [#info] logger The logger object to use for output.
-
1
def initialize(test_records:, repository:, logger:)
-
25
@test_records = test_records
-
25
@repository = repository
-
25
@logger = logger
-
end
-
-
# Runs the verify process
-
# After the execution of this method each Test Record will get a +:result+
-
# key with the result of the verification process. This key contains a hash
-
# like the following:
-
#
-
# result: {
-
# status: :passed, # Either :passed, :failed or :skipped
-
# reason: 'String' # The reason for the failure (for :failed and :skipped)
-
# }
-
1
def verify
-
25
logger.info 'Verifying MTR files...'
-
25
test_records.each do |test_record|
-
75
logger.info "Verifying #{test_record.source_file}"
-
75
verify_mtr(test_record)
-
end
-
end
-
-
1
private
-
-
1
attr_reader :repository
-
-
# Verifies the given Manual Test Record
-
# Runs the given test record through all the verifiers. If no verifier adds
-
# a +result+ key to the Test Record then the method adds one with passed
-
# status.
-
# @param [Dragnet::TestRecord] test_record The Test Record to verify.
-
1
def verify_mtr(test_record)
-
75
started_at = Time.now.utc
-
-
75
verification_result = Dragnet::Verifiers::TestRecordVerifier.new(
-
test_record: test_record, repository: repository, test_records: test_records
-
).verify
-
-
75
finished_at = Time.now.utc
-
75
verification_result.started_at = started_at
-
75
verification_result.finished_at = finished_at
-
75
test_record.verification_result = verification_result
-
-
75
logger.info(verification_result.log_message)
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'verifiers/repos_verifier'
-
-
1
module Dragnet
-
# Namespace for the Verifier classes.
-
1
module Verifiers; end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative '../verification_result'
-
1
require_relative 'mixins/git_error_handling'
-
1
require_relative 'repository_verifier'
-
-
1
module Dragnet
-
1
module Verifiers
-
# Checks for changes in the repository since the creation of the MTR Record
-
1
class ChangesVerifier < Dragnet::Verifiers::RepositoryVerifier
-
1
include ::Dragnet::Verifiers::Mixins::GitErrorHandling
-
-
1
attr_reader :test_records
-
-
# @param [Dragnet::TestRecord] test_record The +TestRecord+ object to
-
# verify.
-
# @param [Dragnet::Repository] repository A +Dragnet::Repository+ object
-
# linked to the repository where the verification should be executed.
-
# @param [Array<Hash>] test_records The hash of all the test records. This
-
# is used to determine if the changes in the repository are only in the
-
# Test Record Files, in which case the Test Records will still be
-
# considered valid.
-
1
def initialize(test_record:, repository:, test_records:)
-
6
super(test_record: test_record, repository: repository)
-
6
@test_records = test_records
-
end
-
-
# Runs the verification process. Checks the changes on the repository
-
# between the Commit with the SHA1 registered in the MTR and the current
-
# HEAD.
-
# @return [Dragnet::VerificationResult, nil] A +VerificationResult+ with
-
# the details of the changes found in the repository or +nil+ if no
-
# changes were found.
-
1
def verify
-
6
diff = repository.diff(sha1, 'HEAD')
-
6
return unless diff.size.positive?
-
-
3
find_changes(diff)
-
rescue Git::FailedError => e
-
2
result_from_git_error(e)
-
end
-
-
1
private
-
-
# Scans the given diff for changes. If changes are detected then a
-
# +:result+ key will be added to the +test_record+. Changes to the MTR
-
# files themselves are ignored.
-
# @return [Dragnet::VerificationResult, nil] A +VerificationResult+ with
-
# the details of the changes found in the repository, or +nil+ if no
-
# changes were found.
-
1
def find_changes(diff)
-
3
diff.stats[:files].each_key do |file|
-
5
next if mtr_files.include?(file) # Changes to MTR files are ignored.
-
-
2
return Dragnet::VerificationResult.new(
-
status: :skipped,
-
reason: "Changes detected in the repository: #{shorten_sha1(sha1)}..#{shorten_sha1(repository.head.sha)}"\
-
" # -- #{file}"
-
)
-
end
-
-
nil
-
end
-
-
# @return [Array<Strings>] An array of strings with the paths to all the
-
# known MTR files. These will be excluded when checking from changes in
-
# the repository.
-
1
def mtr_files
-
5
@mtr_files ||= test_records.map do |test_record|
-
6
test_record.source_file.relative_path_from(path).to_s
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative '../verification_result'
-
1
require_relative 'mixins/git_error_handling'
-
1
require_relative 'repository_verifier'
-
-
1
module Dragnet
-
1
module Verifiers
-
# Checks if any of the files listed in the MTR have changed since the MTR
-
# was created.
-
1
class FilesVerifier < Dragnet::Verifiers::RepositoryVerifier
-
1
include ::Dragnet::Verifiers::Mixins::GitErrorHandling
-
-
# Executes the verification process.
-
# Checks the changes in the repository. If a change in one of the files
-
# is detected a +:result+ key is added to the MTR, including the detected
-
# change.
-
# @return [Dragnet::VerificationResult, nil] A +VerificationResult+ object
-
# with the detected changes to the listed files or +nil+ if no changes
-
# are found.
-
1
def verify
-
6
changes = []
-
-
6
files.each do |file|
-
11
diff = repository.diff(sha1, 'HEAD').path(file.to_s)
-
11
next unless diff.size.positive?
-
-
4
changes << file
-
end
-
-
4
result_from(changes) if changes.any?
-
rescue Git::FailedError => e
-
2
result_from_git_error(e)
-
end
-
-
1
private
-
-
# @return [Array<String>] The paths to the files listed in the MTR file.
-
1
def files
-
6
@files ||= test_record.files.map do |file|
-
15
file.relative_path_from(path)
-
end
-
end
-
-
# Stores the detected changes on the Test Record
-
# @param [Array<String>] changes The array of changed files.
-
1
def result_from(changes)
-
2
Dragnet::VerificationResult.new(
-
status: :skipped,
-
reason: "Changes detected in listed file(s): #{shorten_sha1(sha1)}..#{shorten_sha1(repository.head.sha)}"\
-
" -- #{changes.join(' ')}"
-
)
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
module Dragnet
-
1
module Verifiers
-
1
module Mixins
-
# A mixin that provides methods to gracefully handle errors during Git's
-
# execution.
-
1
module GitErrorHandling
-
1
private
-
-
# @param [Git::FailedError] error The error that occurred during the
-
# verification.
-
# @return [Dragnet::VerificationResult] A +VerificationResult+ object that
-
# encapsulates the occurred error so that it can be presented to the
-
# user.
-
1
def result_from_git_error(error)
-
4
Dragnet::VerificationResult.new(
-
status: :failed,
-
reason: "Unable to diff the revisions: #{shorten_sha1(sha1)}..#{shorten_sha1(repository.head.sha)}: " \
-
"#{error.result.stdout}"
-
)
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative '../errors/not_a_repository_error'
-
1
require_relative '../test_record'
-
1
require_relative 'changes_verifier'
-
1
require_relative 'files_verifier'
-
1
require_relative 'verifier'
-
-
1
module Dragnet
-
1
module Verifiers
-
# Verifies the +Repo+ objects attached to a +TestRecord+
-
1
class ReposVerifier < Dragnet::Verifiers::Verifier
-
1
attr_reader :multi_repository
-
-
# @param [Dragnet::TestRecord] test_record The +TestRecord+ object to
-
# verify.
-
# @param [Dragnet::MultiRepository] multi_repository The +MultiRepository+
-
# object that is supposed to contain the actual repositories inside.
-
1
def initialize(test_record:, multi_repository:)
-
17
super(test_record: test_record)
-
17
@multi_repository = multi_repository
-
end
-
-
# Carries out the verification of the +Repo+ objects.
-
# @return [Dragnet::VerificationResult, nil] A +VerificationResult+ object
-
# when the verification of any of the listed repositories fails and
-
# +nil+ when all of them pass the verification.
-
1
def verify
-
17
return unless test_record.repos&.any?
-
-
15
test_record.repos.each do |repo|
-
23
repository = fetch_repository(repo.path, multi_repository)
-
20
verification_result = verify_repo(repo, repository)
-
-
20
return verification_result if verification_result
-
end
-
-
nil
-
rescue Dragnet::Errors::NotARepositoryError => e
-
3
Dragnet::VerificationResult.new(status: :failed, reason: e.message)
-
end
-
-
1
private
-
-
# Verifies the given +Repo+ object using the given +Dragnet::Repository+
-
# object and a Proxy TestRecord object.
-
# @param [Dragnet::Repo] repo +Repo+ the +Repo+ object to verify.
-
# @param [Dragnet::Repository] repository The +Repository+ object that
-
# actually contains the source the +Repo+ object is referring to.
-
# @return [Dragnet::VerificationResult, nil] A +VerificationResult+ object
-
# when the verification fails or +nil+ when the verification passes.
-
1
def verify_repo(repo, repository)
-
# The Proxy TestRecord object allows the use of the +FilesVerifier+ and
-
# the +ChangesVerifier+ since they expect a +TestRecord+ and not a
-
# +Repo+ object.
-
20
proxy_test_record = Dragnet::TestRecord.new(files: repo.files, sha1: repo.sha1)
-
-
20
if repo.files
-
12
Dragnet::Verifiers::FilesVerifier
-
.new(test_record: proxy_test_record, repository: repository).verify
-
else
-
8
Dragnet::Verifiers::ChangesVerifier
-
.new(test_record: proxy_test_record, repository: repository, test_records: []).verify
-
end
-
end
-
-
# Fetches the +Repository+ object associated with the given path from the
-
# given +MultiRepository+ object.
-
# @param [Pathname] path The path of the repository.
-
# @param [Dragnet::MultiRepository] multi_repository The +MultiRepository+
-
# object that contains the repository to be fetched.
-
# @return [Dragnet::Repository] The +Repository+ object associated with
-
# the given path.
-
1
def fetch_repository(path, multi_repository)
-
23
multi_repository.repositories[path] ||= create_repository(multi_repository, path)
-
end
-
-
# Creates a new repository with the given path.
-
# @param [Dragnet::MultiRepository] multi_repository The +MultiRepository+
-
# object in which the individual repository should be created.
-
# @param [Pathname] path The path for the +Repository+ object.
-
# @return [Dragnet::Repository] The resulting +Repository+ object.
-
# @raise [Dragnet::Errors::NotARepositoryError] If the given path doesn't
-
# lead to a valid git repository or the repository cannot be opened.
-
1
def create_repository(multi_repository, path)
-
22
repository_path = multi_repository.path / path
-
22
Dragnet::Repository.new(path: repository_path)
-
rescue ArgumentError
-
3
raise Dragnet::Errors::NotARepositoryError,
-
"The path '#{path}' does not contain a valid git repository."
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'verifier'
-
-
1
module Dragnet
-
1
module Verifiers
-
# Base class for the Verifiers that need access to the repository to perform
-
# the validation.
-
1
class RepositoryVerifier < Dragnet::Verifiers::Verifier
-
1
attr_reader :repository
-
-
# @param [Dragnet::Repository] repository A +Dragnet::Repository+ object
-
# linked to the repository where the sources and MTR files are located
-
1
def initialize(test_record:, repository:)
-
12
super(test_record: test_record)
-
12
@repository = repository
-
end
-
-
1
private
-
-
# @return [String] The path to the repository.
-
1
def path
-
21
@path ||= repository.path
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'verifier'
-
1
require_relative '../verification_result'
-
-
1
module Dragnet
-
1
module Verifiers
-
# Verifies the +result+ field on the given MTR record.
-
1
class ResultVerifier < Dragnet::Verifiers::Verifier
-
# Performs the verification. If the +result+ field contains the "failed"
-
# text then a +result+ key will be added to the Test Record explaining
-
# the reason for the failure.
-
# @return [Dragnet::VerificationResult, nil] A +VerificationResult+ object
-
# when the verification fails and +nil+ when the verification passes.
-
1
def verify
-
5
return if test_record.passed?
-
-
4
Dragnet::VerificationResult.new(
-
status: :failed,
-
reason: "'result' field has the status '#{result}'"
-
)
-
end
-
-
1
private
-
-
# @return [String] The value for the +result+ key on the MTR file.
-
1
def result
-
4
@result ||= test_record.result
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative 'changes_verifier'
-
1
require_relative 'files_verifier'
-
1
require_relative 'repos_verifier'
-
1
require_relative 'result_verifier'
-
1
require_relative 'verifier'
-
-
1
module Dragnet
-
1
module Verifiers
-
# Performs the verification process over a single TestRecord object.
-
1
class TestRecordVerifier < Dragnet::Verifiers::Verifier
-
1
attr_reader :repository, :test_records
-
-
# @param [Dragnet::TestRecord] test_record The +TestRecord+ object to
-
# verify.
-
# @param [Dragnet::BaseRepository] repository An object representing the
-
# repository the test record is referring to.
-
# @param [Array<Dragnet::TestRecord>] test_records An array with all the
-
# +TestRecord+ objects found in the +Repository+. These are needed when
-
# changes to the repository are being verified. Changes targeting the
-
# MTRs only are ignored by the verifiers.
-
1
def initialize(test_record:, repository:, test_records:)
-
28
super(test_record: test_record)
-
28
@repository = repository
-
28
@test_records = test_records
-
end
-
-
# Performs the verification and attaches the corresponding
-
# +VerificationResult+ object to the +TestRecord+ object.
-
# @return [Dragnet::VerificationResult] The result of the verification
-
# process executed over the given +test_record+.
-
1
def verify
-
28
verification_result = verify_result
-
-
28
verification_result ||= if test_record.files
-
7
verify_files
-
17
elsif test_record.repos
-
7
verify_repos
-
else
-
10
verify_changes
-
end
-
-
28
verification_result || Dragnet::VerificationResult.new(status: :passed)
-
end
-
-
1
private
-
-
# Verifies the MTR's +result+ attribute.
-
# @return [Dragnet::VerificationResult, nil] A +VerificationResult+ object
-
# when the verification fails and +nil+ when the verification passes.
-
1
def verify_result
-
28
Dragnet::Verifiers::ResultVerifier.new(test_record: test_record).verify
-
end
-
-
# Verifies the files listed in the MTR, if any.
-
# @return [Dragnet::VerificationResult, nil] A +VerificationResult+ object
-
# with the detected changes to the listed files or +nil+ if no changes
-
# are found.
-
1
def verify_files
-
7
Dragnet::Verifiers::FilesVerifier
-
.new(test_record: test_record, repository: repository).verify
-
end
-
-
# Verifies the repositories listed in the MTR, only applies when working
-
# with a {Dragnet::MultiRepository} repository.
-
# @return [Dragnet::VerificationResult, nil] A +VerificationResult+ object
-
# when the verification of any of the listed repositories fails and
-
# +nil+ when all of them pass the verification.
-
1
def verify_repos
-
7
Dragnet::Verifiers::ReposVerifier.new(test_record: test_record, multi_repository: repository).verify
-
end
-
-
# Verifies the changes in the repository between the revision referenced
-
# in the MTR and the tip of the current branch.
-
# @return [Dragnet::VerificationResult, nil] A +VerificationResult+ with
-
# the details of the changes found in the repository or +nil+ if no
-
# changes were found.
-
1
def verify_changes
-
10
Dragnet::Verifiers::ChangesVerifier
-
.new(test_record: test_record, repository: repository, test_records: test_records).verify
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require_relative '../helpers/repository_helper'
-
-
1
module Dragnet
-
1
module Verifiers
-
# Base class for all validators.
-
1
class Verifier
-
1
include Dragnet::Helpers::RepositoryHelper
-
-
1
attr_reader :test_record
-
-
# Creates a new instance of the class.
-
# @param [Dragnet::TestRecord] test_record The +TestRecord+ object to
-
# verify.
-
1
def initialize(test_record:)
-
62
@test_record = test_record
-
end
-
-
# Needs to be implemented by the child classes. This method is called to
-
# perform the verification on the given +test_record+.
-
# @return [Dragnet::VerificationResult, nil] The method should return a
-
# +VerificationResult+ object if the verification fails or +nil+ if it
-
# passes.
-
1
def verify
-
raise NotImplementedError, "Please implement #{__method__} in #{self.class}"
-
end
-
-
1
private
-
-
# @return [String] The SHA1 stored in the MTR File.
-
1
def sha1
-
25
@sha1 ||= test_record.sha1
-
end
-
end
-
end
-
end