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



48
49
50
51
52
# File 'lib/scarpe/components/unit_test_helpers.rb', line 48

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.



116
117
118
# File 'lib/scarpe/components/unit_test_helpers.rb', line 116

def extra_log_config=(additional_log_config)
  @additional_log_config = additional_log_config
end

#file_idObject



54
55
56
# File 'lib/scarpe/components/unit_test_helpers.rb', line 54

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



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

def log_config_for_test
  {
    "default" => ["debug", "logger/test_failure_#{file_id}.log"],
    "DisplayService" => ["debug", "logger/test_failure_display_service_#{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



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

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 Shoes::Errors::InvalidAttributeValueError, "Something is wrong! Could not figure out failure-log output path for #{filepath.inspect}!"
  end

  if File.exist?(out_loc)
    raise Scarpe::DuplicateFileError, "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.



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

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("LoggedTest").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.



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

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.



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

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.



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

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.



140
141
142
143
144
# File 'lib/scarpe/components/unit_test_helpers.rb', line 140

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.



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

def set_up_test_failures
  return if ALREADY_SET_UP_LOGGED_TEST_FAILURES[:setup]

  log_dir = self.class.logger_dir
  raise(Scarpe::MustOverrideMethod, "Must set logger directory!") unless log_dir
  raise(Scarpe::NoSuchFile, "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`.



82
83
84
# File 'lib/scarpe/components/unit_test_helpers.rb', line 82

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.



107
108
109
# File 'lib/scarpe/components/unit_test_helpers.rb', line 107

def teardown
  logged_test_teardown
end