Module: Scarpe::Test::LoggedTest

Defined in:
lib/scarpe/components/unit_test_helpers.rb

Overview

This test will save extensive logs in case of test failure. Note that it defines setup/teardown methods. If you want multiple setup/teardowns from multiple places to happen you may need to explictly call (e.g. with logged_test_setup/teardown) to ensure everything you want happens.

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(includer) ⇒ Object



46
47
48
49
50
# File 'lib/scarpe/components/unit_test_helpers.rb', line 46

def self.included(includer)
  class << includer
    attr_accessor :logger_dir
  end
end

Instance Method Details

#extra_log_config=(additional_log_config) ⇒ Object

Set additional LoggedTest configuration for specific logs to separate or save. This is normally going to be display-service-specific log components. Note that this only really works with the modular logger or another logger that does something useful with the log config. The simple print logger doesn’t do a lot with it.



114
115
116
# File 'lib/scarpe/components/unit_test_helpers.rb', line 114

def extra_log_config=(additional_log_config)
  @additional_log_config = additional_log_config
end

#file_idObject



52
53
54
# File 'lib/scarpe/components/unit_test_helpers.rb', line 52

def file_id
  "#{self.class.name}_#{self.name}"
end

#log_config_for_testHash

This is the log config that LoggedTests use. It makes sure all components keep all logs, but also splits the logs into several different files for later ease of scanning.

TODO: this shouldn’t directly include any Webview entries like WebviewAPI or

CatsCradle. Those should be overridden in Webview.

Returns:

  • (Hash)

    the log config



125
126
127
128
129
130
# File 'lib/scarpe/components/unit_test_helpers.rb', line 125

def log_config_for_test
  {
    "default" => ["debug", "logger/test_failure_#{file_id}.log"],
    "DisplayService" => ["debug", "logger/test_failure_events_#{file_id}.log"],
  }.merge(@additional_log_config || {})
end

#logfail_out_loc(filepath) ⇒ String

Failure log output location for a given file path. This is normally used internally to this class, not externally.

Returns:

  • (String)

    the output path



179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/scarpe/components/unit_test_helpers.rb', line 179

def logfail_out_loc(filepath)
  # Add a .out prefix before final .log
  out_loc = filepath.gsub(%r{.log\Z}, ".out.log")

  if out_loc == filepath
    raise "Something is wrong! Could not figure out failure-log output path for #{filepath.inspect}!"
  end

  if File.exist?(out_loc)
    raise "Duplicate test file #{out_loc.inspect}? This file should *not* already exist!"
  end

  out_loc
end

#logged_test_setupvoid

This method returns an undefined value.

This should be called by the test during setup to make sure that failure logs will be saved if this test fails. It makes sure the log config will save all logs from all sources, but keeps a copy of the old log config to restore after the test is finished.



62
63
64
65
66
67
68
69
70
71
# File 'lib/scarpe/components/unit_test_helpers.rb', line 62

def logged_test_setup
  # Make sure test failures will be saved at the end of the run.
  # Delete stale test failures and logging only the *first* time this is called.
  set_up_test_failures

  @normal_log_config = Shoes::Log.current_log_config
  Shoes::Log.configure_logger(log_config_for_test)

  Shoes::Log.logger("LoggedScarpeTest").info("Test: #{self.class.name}##{self.name}")
end

#logged_test_teardownvoid

This method returns an undefined value.

After the test has finished, this will restore the old log configuration. It will also save the logfiles, but only if the test failed, not if it succeeded or was skipped.



89
90
91
92
93
94
95
96
97
98
# File 'lib/scarpe/components/unit_test_helpers.rb', line 89

def logged_test_teardown
  # Restore previous log config
  Shoes::Log.configure_logger(@normal_log_config)

  if self.failure
    save_failure_logs
  else
    remove_unsaved_logs
  end
end

#remove_unsaved_logsvoid

This method returns an undefined value.

Remove unsaved failure logs. This is normally used internally, not externally.



210
211
212
213
214
215
216
# File 'lib/scarpe/components/unit_test_helpers.rb', line 210

def remove_unsaved_logs
  Dir["#{self.class.logger_dir}/test_failure*.log"].each do |f|
    next if f.include?(".out.log") # Don't delete saved logs

    File.unlink(f)
  end
end

#save_failure_logsvoid

This method returns an undefined value.

Save the failure logs in the appropriate place(s). This is normally used internally, not externally.



197
198
199
200
201
202
203
204
205
# File 'lib/scarpe/components/unit_test_helpers.rb', line 197

def save_failure_logs
  saved_log_files.each do |log_file|
    full_loc = File.expand_path("#{self.class.logger_dir}/#{log_file}")
    # TODO: we'd like to skip 0-length logfiles. But also Logging doesn't flush. For now, ignore.
    next unless File.exist?(full_loc)

    FileUtils.mv full_loc, logfail_out_loc(full_loc)
  end
end

#saved_log_filesObject

The list of logfiles that should be saved. Normally this is called internally by the class, not externally from elsewhere.

This could be a lot simpler except I want to only update the file list in one place, log_config_for_test(). Having a single spot should (I hope) make it a lot friendlier to add more logfiles for different components, logged API objects, etc.



138
139
140
141
142
# File 'lib/scarpe/components/unit_test_helpers.rb', line 138

def saved_log_files
  lc = log_config_for_test
  log_outfiles = lc.values.map { |_level, loc| loc }
  log_outfiles.select { |s| s.start_with?("logger/") }.map { |s| s.delete_prefix("logger/") }
end

#set_up_test_failuresvoid

This method returns an undefined value.

Make sure that test failure logs will be noticed, and a message will be printed, if any logged tests fail. This needs to be called at least once in any Minitest-enabled process using logged tests.



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/scarpe/components/unit_test_helpers.rb', line 149

def set_up_test_failures
  return if ALREADY_SET_UP_LOGGED_TEST_FAILURES[:setup]

  log_dir = self.class.logger_dir
  raise("Must set logger directory!") unless log_dir
  raise("Can't find logger directory!") unless File.directory?(log_dir)

  ALREADY_SET_UP_LOGGED_TEST_FAILURES[:setup] = true
  # Delete stale test failures, if any, before starting the first failure-logged test
  Dir["#{log_dir}/test_failure*.log"].each { |fn| File.unlink(fn) }

  Minitest.after_run do
    # Print test failure notice to console
    unless Dir["#{log_dir}/test_failure*.out.log"].empty?
      puts "Some tests have failed! See #{log_dir}/test_failure*.out.log for test logs!"
    end

    # Remove un-saved test logs
    Dir["#{log_dir}/test_failure*.log"].each do |f|
      next if f.include?(".out.log")

      File.unlink(f) if File.exist?(f)
    end
  end
end

#setupvoid

This method returns an undefined value.

If you include this module and don’t override setup/teardown, everything will work fine. But if you need more setup/teardown steps, you can do that too.

The setup method guarantees that just including this module will do setup automatically. If you override it, be sure to call super or logged_test_setup.



80
81
82
# File 'lib/scarpe/components/unit_test_helpers.rb', line 80

def setup
  logged_test_setup
end

#teardownvoid

This method returns an undefined value.

Make sure that, by default, #logged_test_teardown will be called for teardown. If a class overrides teardown, it should also call super or logged_test_teardown to make sure this still happens.



105
106
107
# File 'lib/scarpe/components/unit_test_helpers.rb', line 105

def teardown
  logged_test_teardown
end