Class: CC::CLI::Test

Inherits:
Command show all
Defined in:
lib/cc/cli/test.rb

Constant Summary

Constants inherited from Command

Command::CODECLIMATE_YAML

Instance Method Summary collapse

Methods inherited from Command

command_name, #execute, #fatal, #initialize, #require_codeclimate_yml, #say, #success, #warn

Constructor Details

This class inherits a constructor from CC::CLI::Command

Instance Method Details

#announce_fail(marker, actual_issues) ⇒ Object



147
148
149
150
151
152
153
154
# File 'lib/cc/cli/test.rb', line 147

def announce_fail(marker, actual_issues)
  say colorize(format("FAIL %3d: %s", marker.line, marker.line_text), :red)
  say colorize("Expected:", :yellow)
  say colorize(JSON.pretty_generate(marker.issue), :yellow)
  say "\n"
  say colorize("Actual:", :yellow)
  say colorize(JSON.pretty_generate(actual_issues), :yellow)
end

#announce_pass(marker) ⇒ Object



143
144
145
# File 'lib/cc/cli/test.rb', line 143

def announce_pass(marker)
  say format("PASS %3d: %s", marker.line, marker.line_text)
end

#capture_stdoutObject

Stolen from ActiveSupport (where it was deprecated)



241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/cc/cli/test.rb', line 241

def capture_stdout
  captured_stream = Tempfile.new("stdout")
  origin_stream = $stdout.dup
  $stdout.reopen(captured_stream)

  yield

  $stdout.rewind
  return captured_stream.read
ensure
  captured_stream.close
  captured_stream.unlink
  $stdout.reopen(origin_stream)
end

#codeclimate_analyze(relative_path) ⇒ Object



180
181
182
183
184
185
186
187
188
189
# File 'lib/cc/cli/test.rb', line 180

def codeclimate_analyze(relative_path)
  codeclimate_path = File.expand_path(File.join(File.dirname(__FILE__), "../../../bin/codeclimate"))

  system(
    codeclimate_path, "analyze",
    "--engine", @engine_name,
    "-f", "json",
    relative_path
  )
end

#compare_issues(actual_issues, markers) ⇒ Object



115
116
117
118
119
120
121
# File 'lib/cc/cli/test.rb', line 115

def compare_issues(actual_issues, markers)
  markers.each do |marker|
    validate_issue(marker, actual_issues)
  end

  validate_unexpected_issues(actual_issues)
end

#engine_imageObject



236
237
238
# File 'lib/cc/cli/test.rb', line 236

def engine_image
  engine_registry[@engine_name]["channels"]["stable"]
end

#engine_specObject



232
233
234
# File 'lib/cc/cli/test.rb', line 232

def engine_spec
  @engine_spec ||= JSON.parse(`docker run --rm #{engine_image} cat /engine.json`)
end

#fuzzy_match(expected, actual) ⇒ Object



166
167
168
169
170
# File 'lib/cc/cli/test.rb', line 166

def fuzzy_match(expected, actual)
  expected.all? do |key, value|
    actual[key] == value
  end
end

#issues_in(test_directory) ⇒ Object



172
173
174
175
176
177
178
# File 'lib/cc/cli/test.rb', line 172

def issues_in(test_directory)
  issue_docs = capture_stdout do
    codeclimate_analyze(test_directory)
  end

  JSON.parse(issue_docs)
end

#locate_match(actual_issues, marker) ⇒ Object



133
134
135
136
137
138
139
140
141
# File 'lib/cc/cli/test.rb', line 133

def locate_match(actual_issues, marker)
  actual_issues.each_with_index do |actual, index|
    if fuzzy_match(marker.issue, actual)
      return index
    end
  end

  nil
end

#markers_in(test_directory) ⇒ Object



199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/cc/cli/test.rb', line 199

def markers_in(test_directory)
  [].tap do |markers|
    Dir[File.join(test_directory, "**/*")].each do |file|
      next unless File.file?(file)
      lines = File.readlines(file)

      lines.each_with_index do |line, index|
        if line =~ /\[issue\].*/
          markers << Marker.from_text(@engine_name, file, index + 1, line)
        end
      end
    end
  end
end

#null_container_idObject



218
219
220
221
222
# File 'lib/cc/cli/test.rb', line 218

def null_container_id
  # docker cp only works with containers, not images so
  # workaround it by creating a throwaway container
  @null_container_id = `docker run -d #{engine_image} false`.chomp
end

#prepare_working_dirObject



191
192
193
194
195
196
197
# File 'lib/cc/cli/test.rb', line 191

def prepare_working_dir
  `git init`

  File.open(".codeclimate.yml", "w") do |config|
    config.write("engines:\n  #{@engine_name}:\n    enabled: true")
  end
end

#process_directory(test_directory) ⇒ Object



108
109
110
111
112
113
# File 'lib/cc/cli/test.rb', line 108

def process_directory(test_directory)
  markers = markers_in(test_directory)

  actual_issues = issues_in(test_directory)
  compare_issues(actual_issues, markers)
end

#remove_null_containerObject



224
225
226
# File 'lib/cc/cli/test.rb', line 224

def remove_null_container
  `docker rm -f #{null_container_id}` if null_container_id
end

#runObject



71
72
73
74
75
76
77
78
79
# File 'lib/cc/cli/test.rb', line 71

def run
  @engine_name = @args.first

  if @engine_name.blank?
    fatal "Usage: codeclimate test #{rainbow.wrap("engine_name").underline}"
  end

  test_engine
end

#run_testsObject



101
102
103
104
105
106
# File 'lib/cc/cli/test.rb', line 101

def run_tests
  Dir["*"].each do |file|
    next unless File.directory?(file)
    process_directory(file)
  end
end

#test_engineObject



81
82
83
84
85
86
87
88
89
# File 'lib/cc/cli/test.rb', line 81

def test_engine
  within_tempdir do
    prepare_working_dir
    unpack_tests
    run_tests
  end
ensure
  remove_null_container
end

#test_pathsObject



228
229
230
# File 'lib/cc/cli/test.rb', line 228

def test_paths
  Array.wrap(engine_spec["test_paths"])
end

#unpack(path) ⇒ Object



214
215
216
# File 'lib/cc/cli/test.rb', line 214

def unpack(path)
  system("docker cp #{null_container_id}:#{path} .")
end

#unpack_testsObject



95
96
97
98
99
# File 'lib/cc/cli/test.rb', line 95

def unpack_tests
  test_paths.each do |test_path|
    unpack(test_path)
  end
end

#validate_issue(marker, actual_issues) ⇒ Object



123
124
125
126
127
128
129
130
131
# File 'lib/cc/cli/test.rb', line 123

def validate_issue(marker, actual_issues)
  if (index = locate_match(actual_issues, marker))
    announce_pass(marker)
    actual_issues.delete_at(index)
  else
    announce_fail(marker, actual_issues)
    fatal "Expected issue not found."
  end
end

#validate_unexpected_issues(actual_issues) ⇒ Object



156
157
158
159
160
161
162
163
164
# File 'lib/cc/cli/test.rb', line 156

def validate_unexpected_issues(actual_issues)
  if actual_issues.any?
    say colorize("Actuals not empty after matching.", :red)
    say "\n"
    say colorize("#{actual_issues.size} remaining:", :yellow)
    say colorize(JSON.pretty_generate(actual_issues), :yellow)
    fatal "Unexpected issue found."
  end
end

#within_tempdir(&block) ⇒ Object



91
92
93
# File 'lib/cc/cli/test.rb', line 91

def within_tempdir(&block)
  Dir.mktmpdir { |tmp| Dir.chdir(tmp, &block) }
end