class Covered::Config

Loads project coverage configuration and controls a configured policy.

def self.load(root: self.root, reports: self.reports)

@returns [Covered::Config] The loaded configuration instance.
@parameter reports [String | Boolean | Array | Object | Nil] The report configuration.
@parameter root [String] The project root.
Load the project coverage configuration for the given root.
def self.load(root: self.root, reports: self.reports)
	derived = Class.new(self)
	
	if path = self.path(root)
		config = Module.new
		config.module_eval(::File.read(path), path)
		derived.prepend(config)
	end
	
	return derived.new(root, reports)
end

def self.path(root)

@returns [String | Nil] The expanded configuration path if it exists.
@parameter root [String] The project root.
The coverage configuration path under the given root.
def self.path(root)
	path = ::File.expand_path(PATH, root)
	
	if ::File.exist?(path)
		return path
	end
end

def self.reports

@returns [String | Nil] The `COVERAGE` environment value.
The report names requested by the environment.
def self.reports
	ENV["COVERAGE"]
end

def self.root

@returns [String] The value of `COVERED_ROOT`, or the current working directory.
The root directory used for coverage configuration.
def self.root
	ENV["COVERED_ROOT"] || Dir.pwd
end

def autostart!

def autostart!
	if rubyopt = ENV["RUBYOPT"] and !rubyopt.empty?
		rubyopt = [rubyopt.strip, REQUIRE_COVERED_AUTOSTART].join(" ")
	else
		rubyopt = REQUIRE_COVERED_AUTOSTART
	end
	
	ENV["RUBYOPT"] = rubyopt
	
	unless ENV["COVERED_ROOT"]
		ENV["COVERED_ROOT"] = @root
	end
	
	# Don't report coverage in child processes:
	ENV.delete("COVERAGE")
end

def call(output)

@parameter output [IO] The output stream to write the coverage report to.
Generate coverage reports to the given output.
def call(output)
	policy.call(output)
end

def each(&block)

@parameter coverage [Covered::Coverage] The current coverage object.
@yields {|coverage| ...} Each coverage object from the policy.
Enumerate the coverage data from the configured policy.
def each(&block)
	policy.each(&block)
end

def finish

Stops the policy capture pipeline and restores the environment saved by {start}.
Finish coverage tracking.
def finish
	# Finish coverage tracking:
	policy.finish
	
	# Restore the environment:
	ENV.replace(@environment)
	@environment = nil
end

def ignore_paths

@returns [Array(String)] An array of relative paths to ignore.
Which paths to ignore when computing coverage for a given project.
def ignore_paths
	["test/", "fixtures/", "spec/", "vendor/", "config/"]
end

def include_patterns

@returns [Array(String)] An array of relative patterns to include, e.g. `"lib/**/*.rb"`.
Which paths to include when computing coverage for a given project.
def include_patterns
	["lib/**/*.rb"]
end

def initialize(root, reports)

@parameter reports [String | Boolean | Array | Object | Nil] The report configuration.
@parameter root [String] The project root.
Initialize the configuration for a project root and reports.
def initialize(root, reports)
	@root = root
	@reports = reports
	@policy = nil
	
	@environment = nil
end

def make_policy(policy)

@parameter policy [Covered::Policy] The policy to configure.
Override this method to implement your own policy.
def make_policy(policy)
	# Only files in the root would be tracked:
	policy.root(@root)
	
	patterns = ignore_paths.map do |path|
		File.join(@root, path)
	end
	
	# We will ignore any files in the test or spec directory:
	policy.skip(Regexp.union(patterns))
	
	# We will include all files under lib, even if they aren't loaded:
	include_patterns.each do |pattern|
		policy.include(pattern)
	end
	
	policy.persist!
	
	policy.reports!(@reports)
end

def output

@returns [Covered::Base] The output wrapper at the end of the policy pipeline.
The configured policy output wrapper.
def output
	policy.output
end

def policy

@returns [Covered::Policy] The memoized, frozen policy.
The configured coverage policy.
def policy
	@policy ||= Policy.new.tap{|policy| make_policy(policy)}.freeze
end

def policy_for(paths = nil, ignore_mtime: true)

@returns [Covered::Policy] The configured policy with loaded coverage data.
@parameter ignore_mtime [Boolean] Whether to ignore source file modification times.
@parameter paths [Array(String)] The coverage database paths.

Build a configured policy using coverage data from persistent storage.
def policy_for(paths = nil, ignore_mtime: true)
	paths ||= Dir.glob(Persist::DEFAULT_PATH, base: @root)
	paths = Array(paths)
	
	if paths.empty?
		raise ArgumentError, "No coverage paths specified!"
	end
	
	paths.each do |path|
		# It would be nice to have a better algorithm here than just ignoring mtime - perhaps using checksums:
		Persist.new(policy.output, path).load!(ignore_mtime: ignore_mtime)
	end
	
	return policy
end

def report?

@returns [Boolean] Whether reporting is enabled.
Whether reports should be generated.
def report?
	!!@reports
end

def start

Stores the current environment, configures child process autostart, and starts the policy capture pipeline.
Start coverage tracking.
def start
	# Save and setup the environment:
	@environment = ENV.to_h
	autostart!
	
	# Start coverage tracking:
	policy.start
end