Module: RScout

Defined in:
lib/rscout.rb

Constant Summary collapse

DEFAULT_LOGGER =
Logger.new(STDOUT)
DEFAULT_OPTIONS =
{
  logger: DEFAULT_LOGGER,
  verbose: false,
  env: 'development',
  from_email: 'rscout@localhost'
}

Class Method Summary collapse

Class Method Details

.capture_stdout(&block) ⇒ Object

[View source]

156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/rscout.rb', line 156

def capture_stdout(&block)
  previous_stdout, $stdout = $stdout, StringIO.new
  yield
  if $stdout.respond_to?(:string)
    $stdout.string
  else
    logger.warn "Test suite hijacked our STDOUT capture."
    nil
  end
ensure
  $stdout = previous_stdout
end

.envObject

[View source]

37
38
39
# File 'lib/rscout.rb', line 37

def env
  options[:env]
end

.log(msg) ⇒ Object

[View source]

29
30
31
# File 'lib/rscout.rb', line 29

def log(msg)
  options[:logger].add(options[:severity]) { msg }
end

.loggerObject

[View source]

33
34
35
# File 'lib/rscout.rb', line 33

def logger
  options[:logger]
end

.optionsObject

[View source]

25
26
27
# File 'lib/rscout.rb', line 25

def options
  @@options ||= DEFAULT_OPTIONS.clone
end

.run_suite(dir) ⇒ Object

[View source]

41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/rscout.rb', line 41

def run_suite(dir)
  verbose = options[:verbose]
  gemfile = File.join(dir, 'Gemfile')
  configfile = File.join(dir, 'config', 'rscout.yml')

  Bundler.with_clean_env do
    Dir.chdir(dir) do
      yaml = Hashie::Mash.new(YAML.load_file configfile)
      config = yaml[env]

      output = Hashie::Mash.new({
        txt: StringIO.new,
        html: StringIO.new,
        json: StringIO.new,
        stdout: nil,
        results: nil,
        error: Hashie::Mash.new({backtrace:[], message:nil})
      })

      failed = false
      begin
        html_formatter = RSpec::Core::Formatters::HtmlFormatter.new output.html
        txt_formatter = RSpec::Core::Formatters::DocumentationFormatter.new output.txt
        json_formatter = RSpec::Core::Formatters::JsonFormatter.new output.json

        reporter = RSpec::Core::Reporter.new(json_formatter, txt_formatter, html_formatter)

        rspec = RSpec.configuration
        rspec.instance_variable_set(:@reporter, reporter)

        tests = Dir.glob File.join(dir, 'spec/**/*_spec.rb')

        rspec_task = lambda { RSpec::Core::Runner.run tests }

        if verbose
          rspec_task.call
        else
          output.stdout = capture_stdout &rspec_task
        end

        output.results = json_formatter.output_hash

        failed = output.results[:summary][:failure_count] > 0
        failure_count = output.results[:summary][:failure_count].to_s
      rescue => e
        failed = true
        logger.error "Exception encountered while running RSpec: #{e.message}"
        logger.error e.backtrace
        output.error = e
      ensure
        output.txt.close unless output.txt.closed?
        output.html.close unless output.html.closed?
        output.json.close unless output.json.closed?
      end

      if failed
        logger.info "Tests failed."
        send_failure_notifications config, env, output
      end

      failed
    end
  end
end

.send_failure_notifications(config, env, output) ⇒ Object

[View source]

106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/rscout.rb', line 106

def send_failure_notifications(config, env, output)
  email_body = [output.txt.string, output.error.backtrace.join("\n")].join("\n")
  if config.email_enabled && config.email
    logger.info "Sending emails alert to #{config.email}"
    begin
      mail = Mail.new do
        from     RScout.options[:from_email]
        to       config.email
        subject  "RScout Alert: Tests failing on #{config.name.to_s.humanize.titleize} (#{env})"
        add_file filename: 'results.html', content: output.html.string

        header["X-Priority"] = "1 (Highest)"
        header["X-MSMail-Priority"] = "High"
        header["Importance"] = "High"

        text_part do
          body email_body
        end
      end

      mail.deliver!
    rescue => e
      logger.error "Failed to send email alert!"
      logger.error e.message + "\n " + e.backtrace.join("\n ")
    end
  end

  if config.pagerduty_enabled && config.pagerduty_service_key
    logger.info "Triggering PagerDuty incident to #{config.pagerduty_service_key}"
    begin
      if config.pagerduty_service_key.match(/@(.*)pagerduty.com$/)
        mail = Mail.new do
           from    RScout.options[:from_email]
           to      config.pagerduty_service_key
           subject "DOWN alert: RScout tests failing on #{config.name.to_s.humanize.titleize} (#{env})"
           body    email_body
        end

        mail.deliver!
      else
        p = Pagerduty.new config.pagerduty_service_key, ['scout', env].join('_')
        incident = p.trigger 'RScout tests failing!', output.results
      end
    rescue => e
      logger.error "Failed to send PagerDuty alert!"
      logger.error e.message + "\n " + e.backtrace.join("\n ")
    end
  end
end