Class: Sus::Config

Inherits:
Object
  • Object
show all
Defined in:
lib/sus/config.rb

Overview

Represents the configuration for running tests.

Constant Summary collapse

PATH =

The default path to the configuration file.

"config/sus.rb"
DEBUG_ENVIRONMENT =

Maps CI environment variables to the values they take when the CI provider is running in debug/verbose mode. When any of these match, we enable verbose output automatically.

  • RUNNER_DEBUG is set by GitHub Actions when a workflow is re-run with "Enable debug logging".
  • CI_DEBUG_TRACE is set by GitLab CI when debug logging (tracing) is enabled.
  • BUILDKITE_AGENT_DEBUG is set by Buildkite when agent debug is enabled.
  • SYSTEM_DEBUG is set by Azure Pipelines when the system.debug variable is enabled.
  • SUS_VERBOSE can be set explicitly to enable verbose output regardless of the CI provider.
{
	"SUS_VERBOSE" => "true",
	"RUNNER_DEBUG" => "1",
	"CI_DEBUG_TRACE" => "true",
	"BUILDKITE_AGENT_DEBUG" => "true",
	"SYSTEM_DEBUG" => "true",
}
DEFAULT_TEST_PATTERN =

The default pattern for finding test files.

"test/**/*.rb"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(root, paths, verbose: false) ⇒ Config

Initialize a new Config instance.



80
81
82
83
84
85
86
87
88
# File 'lib/sus/config.rb', line 80

def initialize(root, paths, verbose: false)
	@root = root
	@paths = paths
	@verbose = verbose
	
	@clock = Clock.new
	
	self.add_default_load_paths
end

Instance Attribute Details

#Optional paths to specific test files.(pathstospecifictestfiles.) ⇒ Object (readonly)



110
# File 'lib/sus/config.rb', line 110

attr :paths

#pathsObject (readonly)

Returns the value of attribute paths.



110
111
112
# File 'lib/sus/config.rb', line 110

def paths
  @paths
end

#rootObject (readonly)

Returns the value of attribute root.



107
108
109
# File 'lib/sus/config.rb', line 107

def root
  @root
end

#The root directory for the project.(rootdirectory) ⇒ Object (readonly)



107
# File 'lib/sus/config.rb', line 107

attr :root

Class Method Details

.load(root: Dir.pwd, arguments: ARGV, env: ENV) ⇒ Object

Load configuration from the given root directory.



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/sus/config.rb', line 32

def self.load(root: Dir.pwd, arguments: ARGV, env: ENV)
	derived = Class.new(self)
	
	if path = self.path(root)
		config = Module.new
		config.module_eval(::File.read(path), path)
		derived.prepend(config)
	end
	
	options = {
		verbose: !!arguments.delete("--verbose") || self.verbose_from_environment?(env)
	}
	
	return derived.new(root, arguments, **options)
end

.path(root) ⇒ Object

Find the configuration file path for the given root directory.



19
20
21
22
23
24
25
# File 'lib/sus/config.rb', line 19

def self.path(root)
	path = ::File.join(root, PATH)
	
	if ::File.exist?(path)
		return path
	end
end

.verbose_from_environment?(env = ENV) ⇒ Boolean

Whether verbose output should be enabled based on the environment.

Detects CI environments that request debug logging, e.g. GitHub Actions sets RUNNER_DEBUG=1 when a workflow is re-run with "Enable debug logging".

Returns:

  • (Boolean)


70
71
72
73
74
# File 'lib/sus/config.rb', line 70

def self.verbose_from_environment?(env = ENV)
	DEBUG_ENVIRONMENT.any? do |key, value|
		env[key] == value
	end
end

Instance Method Details

#add_default_load_pathsObject

Add default load paths (lib and fixtures).



101
102
103
104
# File 'lib/sus/config.rb', line 101

def add_default_load_paths
	add_load_path("lib")
	add_load_path("fixtures")
end

#add_load_path(path) ⇒ Object

Add a directory to the load path.



92
93
94
95
96
97
98
# File 'lib/sus/config.rb', line 92

def add_load_path(path)
	path = ::File.expand_path(path, @root)
	
	if ::File.directory?(path)
		$LOAD_PATH.unshift(path)
	end
end

#after_tests(assertions, output: self.output) ⇒ Object

Called after tests are run.



184
185
186
187
188
# File 'lib/sus/config.rb', line 184

def after_tests(assertions, output: self.output)
	@clock.stop!
	
	self.print_summary(output, assertions)
end

#before_tests(assertions, output: self.output) ⇒ Object

Called before tests are run.



174
175
176
177
178
179
# File 'lib/sus/config.rb', line 174

def before_tests(assertions, output: self.output)
	@clock.reset!
	@clock.start!
	
	prepare_warnings!
end

#load_registry(paths = @paths) ⇒ Object

Load the test registry, optionally filtering by paths.



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/sus/config.rb', line 144

def load_registry(paths = @paths)
	registry = make_registry
	
	if paths&.any?
		registry = Sus::Filter.new(registry)
		paths.each do |path|
			registry.load(path)
		end
	else
		test_paths.each do |path|
			registry.load(path)
		end
	end
	
	return registry
end

#make_registryObject

Create a new registry instance.



137
138
139
# File 'lib/sus/config.rb', line 137

def make_registry
	Sus::Registry.new(root: @root)
end

#outputObject



123
124
125
# File 'lib/sus/config.rb', line 123

def output
	@output ||= Sus::Output.default
end

#partial?Boolean

Returns:

  • (Boolean)


118
119
120
# File 'lib/sus/config.rb', line 118

def partial?
	@paths.any?
end

#prepare_warnings!Object

Prepare Ruby warnings for deprecated features.



167
168
169
# File 'lib/sus/config.rb', line 167

def prepare_warnings!
	Warning[:deprecated] = true
end

Print feedback about the test suite.



232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/sus/config.rb', line 232

def print_test_feedback(output, assertions = nil,
	duration: @clock.duration,
	count: assertions.count,
	total: assertions.total
)
	rate = count / duration
	
	if total < 10 or count < 10
		output.puts "😭 You should write more tests and assertions!"
		
		# Statistics will be less meaningful with such a small amount of data, so give up:
		return
	end
	
	# Check whether there is at least, on average, one assertion (or more) per test:
	assertions_per_test = count / total
	if assertions_per_test < 1.0
		output.puts "😩 Your tests don't have enough assertions (#{assertions_per_test.round(1)} < 1.0)!"
	end
	
	# Give some feedback about the number of tests:
	if total < 20
		output.puts "🥲 You should write more tests (#{total}/20)!"
	elsif total < 50
		output.puts "🙂 Your test suite is starting to shape up, keep on at it (#{total}/50)!"
	elsif total < 100
		output.puts "😀 Your test suite is maturing, keep on at it (#{total}/100)!"
	else
		output.puts "🤩 Your test suite is amazing!"
	end
	
	# Give some feedback about the performance of the tests:
	if rate < 10.0
		output.puts "💔 Ouch! Your test suite performance is painful (#{rate.round(1)} < 10)!"
	elsif rate < 100.0
		output.puts "💩 Oops! Your test suite performance could be better (#{rate.round(1)} < 100)!"
	elsif rate < 1_000.0
		output.puts "💪 Good job! Your test suite has good performance (#{rate.round(1)} < 1000)!"
	elsif rate < 10_000.0
		output.puts "🎉 Great job! Your test suite has excellent performance (#{rate.round(1)} < 10000)!"
	else
		output.puts "🔥 Wow! Your test suite has outstanding performance (#{rate.round(1)} >= 10000.0)!"
	end
end

#registryObject



162
163
164
# File 'lib/sus/config.rb', line 162

def registry
	@registry ||= self.load_registry
end

#test_pathsObject



131
132
133
# File 'lib/sus/config.rb', line 131

def test_paths
	return Dir.glob(DEFAULT_TEST_PATTERN, base: @root)
end

#verbose?Boolean

Returns:

  • (Boolean)


113
114
115
# File 'lib/sus/config.rb', line 113

def verbose?
	@verbose
end