lib/covered/config.rb



# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2019-2023, by Samuel Williams.

require_relative 'policy'

module Covered
	class Config
		PATH = "config/covered.rb"
		
		def self.root
			ENV['COVERED_ROOT'] || Dir.pwd
		end
		
		def self.path(root)
			path = ::File.expand_path(PATH, root)
			
			if ::File.exist?(path)
				return path
			end
		end
		
		def self.coverage
			ENV['COVERAGE']
		end
		
		def self.load(root: self.root, coverage: self.coverage)
			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, coverage)
		end
		
		def initialize(root, coverage)
			@root = root
			@coverage = coverage
			@policy = nil
			
			@environment = nil
		end
		
		def report?
			!!@coverage
		end
		
		alias :record? :report?
		
		attr :coverage
		
		def policy
			@policy ||= Policy.new.tap{|policy| make_policy(policy)}.freeze
		end
		
		def output
			policy.output
		end
		
		def start
			# Save and setup the environment:
			@environment = ENV.to_h
			autostart!
			
			# Start coverage tracking:
			policy.start
		end
		
		def finish
			# Finish coverage tracking:
			policy.finish
			
			# Restore the environment:
			ENV.replace(@environment)
			@environment = nil
		end
		
		def call(output)
			policy.call(output)
		end
		
		def each(&block)
			policy.each(&block)
		end
		
		def ignore_paths
			['test/', 'fixtures/', 'spec/', 'vendor/', 'config/']
		end
		
		# 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:
			policy.include("lib/**/*.rb")
			
			policy.persist!
			
			policy.reports!(@coverage)
		end
		
		protected
		
		REQUIRE_COVERED_AUTOSTART = '-rcovered/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
	end
end