Module: FailureRateModule

Included in:
CucumberFormatter
Defined in:
lib/failure_rate_module.rb

Overview

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Instance Method Summary collapse

Instance Method Details

#check_past_failures(scenario) ⇒ Object

calculate and save the failure rate for scenarios



32
33
34
35
36
37
38
39
40
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
105
106
107
108
109
110
111
# File 'lib/failure_rate_module.rb', line 32

def check_past_failures(scenario)
  # load the failure rate config settings
  config = OpenStruct.new(YAML.load_file(METRICS_CONFIG_FILE)).failure_rate
  machines = Array.new

  # only useful if the regex is in the config file
  unless config == nil || config['machines_regex'] == nil || !config['machines_regex'].is_a?(Array)
    config['machines_regex'].each do |r|
      machines << r.gsub('/', '')
    end
  end
  machines.uniq!

  scenario = scenario.strip[0..255].gsub('\'', '')
  period = parse_time_period(config['time_period'])

  sql = "SELECT s.id, COUNT(*) as count FROM scenarios s
           LEFT JOIN scenario_test_runs str ON str.scenario_id = s.id
         WHERE s.scenario_name like \'#{scenario}\'
           AND test_run_at > DATE_SUB(NOW(), INTERVAL #{period.upcase})"

  # find the machines used to calculate failure rate
  # if not configured, will not add to query
  machines.each_with_index do |reg, i|
    if i == 0
      sql += " AND (machine_name RLIKE '#{reg}' "
    else
      sql += "OR machine_name RLIKE '#{reg}' "
    end
    sql += ") " if i == machines.size - 1
  end

  scenario_id = 0
  all_tests = 0
  failed_tests = 0

  Database.query(sql).each do |r|
    all_tests = r["count"]
    scenario_id = r["id"]
  end

  # this is required if the query above returns a count of 0; we still want
  # to update the failure rate, but we need the scenario ID to do so
  if scenario_id == 0 || scenario_id == nil
    scenario_sql = "SELECT id FROM scenarios WHERE scenario_name like '#{scenario}'"
    Database.query(scenario_sql).each do |r|
      scenario_id = r["id"]
    end
  end

  sql = sql + " AND (passed is NULL OR passed = 0)"

  Database.query(sql).each do |r|
    failed_tests = r["count"]
  end

  # prevents divide by zero errors when no test runs in db
  ratio = all_tests > 0 ? failed_tests.fdiv(all_tests) : 0
  # get threshold from config settings and make sure it's less than 1
  threshold = config['threshold']
  if threshold == nil || !threshold.is_a?(Float) || threshold >= 1
    threshold = 0.05
  end

  readable_period = period.split(' ')
  if readable_period[0].to_i != 1
    readable_period[1] += "s"
  end

  body_text = "\n#{(ratio * 100).round}% of the test runs for \"#{scenario}\"
  have failed in the past #{readable_period.join(' ').downcase}. You'd better get some eyes on it.\n\n"

  if ratio > threshold
    puts body_text
  end

  # update the scenarios table with the failure rate
  insert_sql = "UPDATE scenarios SET failure_rate = #{ratio * 100} WHERE id = #{scenario_id}"
  Database.query(insert_sql)
end

#default_time_periodObject



137
138
139
# File 'lib/failure_rate_module.rb', line 137

def default_time_period
  "1 WEEK"
end

#parse_time_period(period) ⇒ Object

basically, we need a format MySQL will recognize. That means an integer followed by a specific interval type. It is currently restricted to days, weeks, months, quarters, and years. See the MySQL developers site: dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_date-add Override valid_times and default_time_period to change the defaults



118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/failure_rate_module.rb', line 118

def parse_time_period(period)
  if period == nil || !period.is_a?(String)
    default_time_period
  else
    period = period.gsub('.', '').split(' ')
    if period.size != 2 || period[0] == nil || period[1] == nil
      default_time_period
    elsif valid_times.include?(period[1].upcase) && period[0].strip =~ /\d+/
      period.join(' ')
    else
      default_time_period
    end
  end
end

#valid_timesObject



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

def valid_times
  %w(DAY WEEK MONTH QUARTER YEAR)
end