Module: Testrbl

Defined in:
lib/testrbl.rb,
lib/testrbl/version.rb

Constant Summary collapse

PATTERNS =
[
  /^(\s+)(should|test|it)\s+['"](.*)['"]\s+do\s*(?:#.*)?$/,
  /^(\s+)(context|describe)\s+['"]?(.*?)['"]?\s+do\s*(?:#.*)?$/,
  /^(\s+)def\s+(test_)([a-z_\d]+)\s*(?:#.*)?$/
]
OPTION_WITH_ARGUMENT =
["-I", "-r", "-n", "-e"]
INTERPOLATION =
/\\\#\\\{.*?\\\}/
VERSION =
'0.3.2'

Class Method Summary collapse

Class Method Details

.changed_files ⇒ Object



100
101
102
103
104
# File 'lib/testrbl.rb', line 100

def self.changed_files
  changed_files = `git status -s`.split("\n").map { |l| l.strip.split(/\s+/, 2)[1] }
  raise "Failed: #{changed_files}" unless $?.success?
  changed_files.select { |f| f =~ /_(test|spec)\.rb$/ && File.exist?(f) }
end

.filter_duplicate_final(patterns) ⇒ Object

only keep 1 pattern that stops matching via $



53
54
55
56
# File 'lib/testrbl.rb', line 53

def self.filter_duplicate_final(patterns)
  found_final = 0
  patterns.reject { |p| p.end_with?("$") and (found_final += 1) > 1 }
end

.localize(file) ⇒ Object

fix 1.9 not being able to load local files



78
79
80
# File 'lib/testrbl.rb', line 78

def self.localize(file)
  file =~ /^[-a-z\d_]/ ? "./#{file}" : file
end

.partition_argv(argv) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/testrbl.rb', line 82

def self.partition_argv(argv)
  next_is_option = false
  argv.partition do |arg|
    if next_is_option
      next_is_option = false
    else
      if arg =~ /^-.$/ or  arg =~ /^--/ # single letter option followed by argument like -I test or long options like --verbose
        next_is_option = true if OPTION_WITH_ARGUMENT.include?(arg)
        false
      elsif arg =~ /^-/ # multi letter option like -Itest
        false
      else
        true
      end
    end
  end
end

.partition_options(options) ⇒ Object



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/testrbl.rb', line 60

def self.partition_options(options)
  next_is_before = false
  options.partition do |option|
    if next_is_before
      next_is_before = false
      true
    else
      if option =~ /^-(r|I)/
        next_is_before = (option.size == 2)
        true
      else
        false
      end
    end
  end
end

.pattern_from_file(lines, line) ⇒ Object

usable via external tools like zeus



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/testrbl.rb', line 36

def self.pattern_from_file(lines, line)
  possible_lines = lines[0..(line.to_i-1)].reverse

  last_spaces = " " * 100
  found = possible_lines.map { |line| test_pattern_from_line(line) }.compact

  # pattern and the groups it is nested under (like describe - describe - it)
  patterns = found.select do |spaces, name|
    last_spaces = spaces if spaces.size < last_spaces.size
  end.map(&:last)

  return filter_duplicate_final(patterns).reverse.join(".*") if found.size > 0

  raise "no test found before line #{line}"
end

.ruby ⇒ Object



106
107
108
109
110
111
112
# File 'lib/testrbl.rb', line 106

def self.ruby
  if File.file?("Gemfile")
    ["ruby", "-rbundler/setup"] # faster then bundle exec ruby
  else
    ["ruby"]
  end
end

.run(command) ⇒ Object



114
115
116
117
118
# File 'lib/testrbl.rb', line 114

def self.run(command)
  puts command.join(" ")
  STDOUT.flush # if exec fails horribly we at least see some output
  Kernel.exec *command
end

.run_from_cli(argv) ⇒ Object



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/testrbl.rb', line 13

def self.run_from_cli(argv)
  files, options = partition_argv(argv)
  files.concat(changed_files) if options.delete("--changed")
  files = files.map { |f| localize(f) }
  load_options, options = partition_options(options)

  if files.size == 1 and files.first =~ /^(\S+):(\d+)$/
    file = $1
    line = $2
    run(ruby + load_options + [file, "-n", "/#{pattern_from_file(File.readlines(file), line)}/"] + options)
  else
    if files.size == 1 and File.file?(files.first)
      run(ruby + load_options + files + options)
    elsif files.all? { |f| File.file?(f) } and options.none? { |arg| arg =~ /^-n/ }
      run(ruby + load_options + files.map { |f| "-r#{f}" } + options + ["-e", ""])
    else # pass though
      # no bundle exec: projects with mini and unit-test do not run well via bundle exec testrb
      run ["testrb"] + argv
    end
  end
end

.test_pattern_from_line(line) ⇒ Object



120
121
122
123
124
125
126
127
# File 'lib/testrbl.rb', line 120

def self.test_pattern_from_line(line)
  PATTERNS.each do |r|
    next unless line =~ r
    whitespace, method, test_name = $1, $2, $3
    return [whitespace, test_pattern_from_match(method, test_name)]
  end
  nil
end

.test_pattern_from_match(method, test_name) ⇒ Object



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/testrbl.rb', line 129

def self.test_pattern_from_match(method, test_name)
  regex = Regexp.escape(test_name).gsub("\\ "," ").gsub(INTERPOLATION, ".*")

  regex = case method
  when "should"
    optional_test_name = "(?:\(.*\))?"
    "#{method} #{regex}\. #{optional_test_name}$"
  when "describe"
    "#{test_name}(::)?"
  when "test"
    # test "xxx -_ yyy"
    # test-unit:     "test: xxx -_ yyy"
    # activesupport: "test_xxx_-__yyy"
    "^test(: |_)#{regex.gsub(" ", ".")}$"
  when "it"
    "#test_\\d+_#{test_name}$"
  else
    regex
  end

  regex.gsub("'", ".")
end