Module: Polytrix

Extended by:
Util::FileSystem
Includes:
DefaultLogger, Logging
Defined in:
lib/polytrix/util.rb,
lib/polytrix.rb,
lib/polytrix/cli.rb,
lib/polytrix/dash.rb,
lib/polytrix/color.rb,
lib/polytrix/error.rb,
lib/polytrix/spies.rb,
lib/polytrix/logger.rb,
lib/polytrix/result.rb,
lib/polytrix/command.rb,
lib/polytrix/logging.rb,
lib/polytrix/version.rb,
lib/polytrix/executor.rb,
lib/polytrix/manifest.rb,
lib/polytrix/challenge.rb,
lib/polytrix/reporters.rb,
lib/polytrix/validator.rb,
lib/polytrix/challenges.rb,
lib/polytrix/state_file.rb,
lib/polytrix/validation.rb,
lib/polytrix/implementor.rb,
lib/polytrix/command/list.rb,
lib/polytrix/command/show.rb,
lib/polytrix/command/test.rb,
lib/polytrix/configuration.rb,
lib/polytrix/command/action.rb,
lib/polytrix/challenge_result.rb,
lib/polytrix/challenge_runner.rb,
lib/polytrix/command/generate.rb,
lib/polytrix/validator_registry.rb,
lib/polytrix/spies/file_system_spy.rb,
lib/polytrix/documentation_generator.rb,
lib/polytrix/reporters/hash_reporter.rb,
lib/polytrix/reporters/json_reporter.rb,
lib/polytrix/reporters/yaml_reporter.rb,
lib/polytrix/command/generators/code2doc.rb,
lib/polytrix/reporters/markdown_reporter.rb,
lib/polytrix/command/generators/dashboard.rb,
lib/polytrix/documentation/code_segmenter.rb,
lib/polytrix/documentation/comment_styles.rb,
lib/polytrix/command/generators/documentation.rb,
lib/polytrix/executors/buff_shellout_executor.rb,
lib/polytrix/documentation/helpers/code_helper.rb,
lib/polytrix/executors/linux_challenge_executor.rb,
lib/polytrix/executors/mixlib_shellout_executor.rb,
lib/polytrix/executors/windows_challenge_executor.rb

Overview

Much of this code has been adapted from Fletcher Nichol (<[email protected]>) work on test-kitchen.

Defined Under Namespace

Modules: Color, Command, DefaultLogger, Documentation, Error, Executor, Executors, Logging, Reporters, Spies, Util Classes: ActionFailed, CLI, Challenge, ChallengeFailure, ChallengeResult, ChallengeRunner, Challenges, ClientError, Configuration, Dash, DocumentationGenerator, ExecutionError, FeatureNotImplementedError, Implementor, Logger, Manifest, ManifestSection, Result, Spy, StandardError, StateFile, StateFileLoadError, TransientFailure, UserError, Validation, ValidationFailure, Validator, ValidatorRegistry

Constant Summary collapse

SUPPORTED_EXTENSIONS =

File extensions that Polytrix can automatically detect/execute

%w(py rb js)
DEFAULT_MANIFEST_FILE =
'polytrix.yml'
DEFAULT_LOG_LEVEL =

Default log level verbosity

:info
VERSION =
'0.1.4'
RESOURCES_DIR =
File.expand_path '../../../resources', __FILE__

Class Attribute Summary collapse

Class Method Summary collapse

Methods included from Util::FileSystem

find_file, relativize

Methods included from Util::String

included

Methods included from Util::String::ClassMethods

#ansi2html, #escape_html, #highlight, #slugify

Methods included from DefaultLogger

included

Methods included from DefaultLogger::ClassMethods

#logger

Class Attribute Details

.loggerLogger

Returns the common Polytrix logger.

Returns:

  • (Logger)

    the common Polytrix logger



44
45
46
# File 'lib/polytrix.rb', line 44

def logger
  @logger
end

.mutexMutex

Returns a common mutex for global coordination.

Returns:

  • (Mutex)

    a common mutex for global coordination



41
42
43
# File 'lib/polytrix.rb', line 41

def mutex
  @mutex
end

Class Method Details

.configurationObject

See Also:



158
159
160
161
# File 'lib/polytrix.rb', line 158

def configuration
  fail "configuration doesn't take a block, use configure" if block_given?
  @configuration ||= Configuration.new
end

.configure {|configuration| ... } ⇒ Object

Yields:

See Also:



164
165
166
# File 'lib/polytrix.rb', line 164

def configure
  yield(configuration)
end

.default_file_loggerLogger

Returns a default file logger which emits on standard output and to a log file.

Returns:



113
114
115
116
# File 'lib/polytrix.rb', line 113

def default_file_logger
  logfile = File.expand_path(File.join('.polytrix', 'logs', 'polytrix.log'))
  Logger.new(stdout: $stdout, logdev: logfile, level: env_log)
end

.env_logInteger?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Determine the default log level from an environment variable, if it is set.

Returns:

  • (Integer, nil)

    a log level or nil if not set



123
124
125
126
127
# File 'lib/polytrix.rb', line 123

def env_log
  level = ENV['POLYTRIX_LOG'] && ENV['POLYTRIX_LOG'].downcase.to_sym
  level = Util.to_logger_level(level) unless level.nil?
  level
end

.filter_scenarios(regexp, options = {}) ⇒ Object



101
102
103
104
105
106
107
# File 'lib/polytrix.rb', line 101

def filter_scenarios(regexp, options = {})
  select_scenarios(regexp).tap do |scenarios|
    scenarios.keep_if { |scenario| scenario.failed? == options[:failed] } unless options[:failed].nil?
    scenarios.keep_if { |scenario| scenario.skipped? == options[:skipped] } unless options[:skipped].nil?
    scenarios.keep_if { |scenario| scenario.sample? == options[:samples] } unless options[:samples].nil?
  end
end

.handle_challenge_failure(e) ⇒ Object Also known as: handle_validation_failure

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Handles an challenge failure exception.

Parameters:

See Also:



157
158
159
160
161
162
# File 'lib/polytrix/error.rb', line 157

def handle_challenge_failure(e)
  stderr_log(e.message.split(/\s{2,}/))
  stderr_log(Error.formatted_exception(e.original))
  file_log(:error, e.message.split(/\s{2,}/).first)
  debug_log(Error.formatted_trace(e))
end

.handle_error(e) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Handles an unexpected failure exception.

Parameters:

See Also:



171
172
173
174
175
176
# File 'lib/polytrix/error.rb', line 171

def handle_error(e)
  stderr_log(Error.formatted_exception(e))
  stderr_log('Please see .polytrix/logs/polytrix.log for more details')
  # stderr_log("Also try running `polytrix diagnose --all` for configuration\n")
  file_log(:error, Error.formatted_trace(e))
end

.implementorsObject

The set of Implementors registered with Polytrix.



143
144
145
# File 'lib/polytrix.rb', line 143

def implementors
  manifest.implementors.values
end

.manifestObject

The Manifest that describes the test scenarios known to Polytrix.



138
139
140
# File 'lib/polytrix.rb', line 138

def manifest
  configuration.manifest
end

.resetObject



132
133
134
135
# File 'lib/polytrix.rb', line 132

def reset
  @configuration = nil
  Polytrix::ValidatorRegistry.clear
end

.select_scenarios(regexp) ⇒ Object



87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/polytrix.rb', line 87

def select_scenarios(regexp)
  regexp ||= 'all'
  scenarios = if regexp == 'all'
                manifest.challenges.values
              else
                manifest.challenges.get(regexp) || manifest.challenges.get_all(/#{regexp}/)
              end
  if scenarios.is_a? Array
    scenarios
  else
    [scenarios]
  end
end

.setup(options, manifest_file = DEFAULT_MANIFEST_FILE) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/polytrix.rb', line 46

def setup(options, manifest_file = DEFAULT_MANIFEST_FILE)
  # manifest_file = File.expand_path manifest
  if File.exist? manifest_file
    logger.debug "Loading manifest file: #{manifest_file}"
    Polytrix.configuration.manifest = manifest_file
  elsif options[:solo]
    solo_setup(options)
  else
    fail StandardError, "No manifest found at #{manifest_file} and not using --solo mode"
  end

  manifest.build_challenges

  test_dir = options[:test_dir].nil? ? nil : File.expand_path(options[:test_dir])
  return nil unless test_dir && File.directory?(test_dir)

  autoload_polytrix_files(test_dir)
end

.solo_setup(options) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/polytrix.rb', line 65

def solo_setup(options)
  suites = {}
  solo_basedir = options[:solo]
  solo_glob = options.fetch('solo_glob', "**/*.{#{SUPPORTED_EXTENSIONS.join(',')}}")
  Dir[File.join(solo_basedir, solo_glob)].sort.each do | code_sample |
    code_sample = Pathname.new(code_sample)
    suite_name = relativize(code_sample.dirname, solo_basedir).to_s
    suite_name = solo_basedir if suite_name == '.'
    scenario_name = code_sample.basename(code_sample.extname).to_s
    suite = suites[suite_name] ||= Polytrix::Manifest::Suite.new(samples: [])
    suite.samples << scenario_name
  end
  @manifest = Polytrix.configuration.manifest = Polytrix::Manifest.new(
    implementors: {
      File.basename(solo_basedir) => {
        basedir: solo_basedir
      }
    },
    suites: suites
  )
end

.tty?true, false

Returns whether or not standard output is associated with a terminal device (tty).

Returns:

  • (true, false)

    is there a tty?



172
173
174
# File 'lib/polytrix.rb', line 172

def tty?
  $stdout.tty?
end

.validate(desc, scope = { suite: //, scenario: // }, &block) ⇒ Object

Registers a Validator that will be used during test execution on matching Challenges.



149
150
151
152
153
154
155
# File 'lib/polytrix.rb', line 149

def validate(desc, scope = { suite: //, scenario: // }, &block)
  fail ArgumentError 'You must pass block' unless block_given?
  validator = Polytrix::Validator.new(desc, scope, &block)

  Polytrix::ValidatorRegistry.register validator
  validator
end

.with_friendly_errorsObject

Yields to a code block in order to consistently emit a useful crash/error message and exit appropriately. There are two primary failure conditions: an expected challenge failure, and any other unexpected failures.

Note This method may call ‘Kernel.exit` so may not return if the yielded code block raises an exception.

## Challenge Failure

This is an expected failure scenario which could happen if an challenge couldn’t be created, a Chef run didn’t successfully converge, a post-convergence test suite failed, etc. In other words, you can count on encountering these failures all the time–this is Polytrix’s worldview: crash early and often. In this case a cleanly formatted exception is written to ‘STDERR` and the exception message is written to the common Polytrix file logger.

## Unexpected Failure

All other forms of ‘Polytrix::Error` exceptions are considered unexpected or unplanned exceptions, typically from user configuration errors, driver or provisioner coding issues or bugs, or internal code issues. Given a stable release of Polytrix and a solid set of drivers and provisioners, the most likely cause of this is user configuration error originating in the `.polytrix.yml` setup. For this reason, the exception is written to `STDERR`, a full formatted exception trace is written to the common Polytrix file logger, and a message is displayed on `STDERR` to the user informing them to check the log files and check their configuration with the `polytrix diagnose` subcommand.

Raises:

  • (SystemExit)

    if an exception is raised in the yielded block



138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/polytrix/error.rb', line 138

def with_friendly_errors
  yield
rescue Polytrix::ChallengeFailure => e
  Polytrix.mutex.synchronize do
    handle_challenge_failure(e)
  end
  exit 10
rescue Polytrix::Error => e
  Polytrix.mutex.synchronize do
    handle_error(e)
  end
  exit 20
end