aboutsummaryrefslogtreecommitdiffstats
path: root/spec/mspec/lib/mspec/runner/actions/constants_leak_checker.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/mspec/lib/mspec/runner/actions/constants_leak_checker.rb')
-rw-r--r--spec/mspec/lib/mspec/runner/actions/constants_leak_checker.rb79
1 files changed, 79 insertions, 0 deletions
diff --git a/spec/mspec/lib/mspec/runner/actions/constants_leak_checker.rb b/spec/mspec/lib/mspec/runner/actions/constants_leak_checker.rb
new file mode 100644
index 0000000000..fd0a3efe14
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/actions/constants_leak_checker.rb
@@ -0,0 +1,79 @@
+class ConstantsLockFile
+ LOCK_FILE_NAME = '.mspec.constants'
+
+ def self.load
+ if File.exist?(LOCK_FILE_NAME)
+ File.readlines(LOCK_FILE_NAME).map(&:chomp)
+ else
+ []
+ end
+ end
+
+ def self.dump(ary)
+ contents = ary.map(&:to_s).uniq.sort.join("\n") + "\n"
+ File.write(LOCK_FILE_NAME, contents)
+ end
+end
+
+class ConstantLeakError < StandardError
+end
+
+class ConstantsLeakCheckerAction
+ def initialize(save)
+ @save = save
+ @check = !save
+ @constants_locked = ConstantsLockFile.load
+ @exclude_patterns = MSpecScript.get(:toplevel_constants_excludes) || []
+ end
+
+ def register
+ MSpec.register :start, self
+ MSpec.register :before, self
+ MSpec.register :after, self
+ MSpec.register :finish, self
+ end
+
+ def start
+ @constants_start = constants_now
+ end
+
+ def before(state)
+ @constants_before = constants_now
+ end
+
+ def after(state)
+ constants = remove_excludes(constants_now - @constants_before - @constants_locked)
+
+ if @check && !constants.empty?
+ MSpec.protect 'Constants leak check' do
+ raise ConstantLeakError, "Top level constants leaked: #{constants.join(', ')}"
+ end
+ end
+ end
+
+ def finish
+ constants = remove_excludes(constants_now - @constants_start - @constants_locked)
+
+ if @save
+ ConstantsLockFile.dump(@constants_locked + constants)
+ end
+
+ if @check && !constants.empty?
+ MSpec.protect 'Global constants leak check' do
+ raise ConstantLeakError, "Top level constants leaked in the whole test suite: #{constants.join(', ')}"
+ end
+ end
+ end
+
+ private
+
+ def constants_now
+ Object.constants.map(&:to_s)
+ end
+
+ def remove_excludes(constants)
+ constants.reject { |name|
+ @exclude_patterns.any? { |pattern| pattern === name }
+ }
+ end
+end