cucumber_cpp_mappings.rb 8.25 KB
Newer Older
1
2
require 'json'

3
4
5
6
7
8
9
10
11
12
13
module CucumberCppMappings

  def features_dir
    FEATURES_DIR
  end

  def write_passing_mapping(step_name)
    append_step_definition(step_name, '// no-op, pass gently')
  end

  def write_failing_mapping(step_name)
14
    write_failing_mapping_with_message(step_name, "rotten cucumbers")
15
16
17
  end

  def write_failing_mapping_with_message(step_name, message)
18
    append_step_definition(step_name, "throw \"#{message}\";")
19
20
21
22
23
24
25
  end

  def write_pending_mapping(step_name)
    append_step_definition(step_name, "pending();")
  end

  def write_calculator_code
26
    include_file "../support/RpnCalculator.cpp"
27
28
29
  end

  def write_mappings_for_calculator
30
    include_file "RpnCalculatorSteps.cpp"
31
32
33
  end

  def write_mapping_receiving_data_table_as_headless_row_array(step_name)
34
    not_implemented
35
36
37
  end

  def write_mapping_receiving_data_table_as_raw(step_name)
38
    not_implemented
39
40
41
  end

  def write_mapping_receiving_data_table_as_hashes(step_name)
42
43
44
45
    append_step_definition step_name, <<-EOF
TABLE_PARAM(table);
logTableAsHashes(table);
EOF
46
47
48
  end

  def write_passing_hook options = {}
49
50
51
52
53
54
55
56
57
58
59
60
61
62
    log_string = options[:log_cycle_event_as]
    if options[:type]
      hook_type  = options[:type]
      log_string ||= hook_type
    else
      hook_type  = "before"
      log_string ||= "hook"
    end
    tags        = options[:tags] || []
    define_hook = hook_type.upcase
    params      = tags.any? ? '"'+tags.join('", "')+'"' : ""

    if hook_type == "around"
      # around scenario hooks not implemented
63
      not_implemented
64
65
66
67
68
69
70
    else
      append_support_code <<-EOF
#{define_hook}(#{params}) {
  logCycleEvent("#{log_string}");
}
EOF
    end
71
72
73
  end

  def write_scenario options = {}
74
75
76
77
78
79
80
81
82
83
84
    tags = options[:with_tags] || []

    @next_step_count ||= 0
    step_name = nth_step_name @next_step_count += 1
    tags_definition = tags.any? ? "\n  #{tags.join(' ')}" : ""
    append_logging_step_definition(step_name)
    append_to_feature <<-EOF
#{tags_definition}
  Scenario: scenario #{"tagged with " + tags.join(', ') if tags.any?}
    Given #{step_name}
EOF
85
86
87
  end

  def write_world_variable_with_numeric_value(value)
88
89
    append_support_code <<-EOF
struct World {
Paolo Ambrosio's avatar
Paolo Ambrosio committed
90
91
  int variable;
  World() : variable(#{value}) {}
92
93
};
EOF
94
95
96
  end

  def write_world_function
97
98
99
100
101
102
103
    append_support_code <<-EOF
struct World {
    void someFunction() {
        writeToFile("#{WORLD_FUNCTION_LOG_FILE}", "");
    }
};
EOF
104
105
106
  end

  def write_custom_world_constructor
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
    # it does not make any sense! the scenario should be changed
  end

  def write_mapping_incrementing_world_variable_by_value(step_name, increment_value)
    append_step_definition step_name, <<-EOF
ScenarioScope<World> world;

world->variable += #{increment_value};
EOF
  end

  def write_mapping_logging_world_variable_value(step_name, time = "1")
    append_step_definition step_name, <<-EOF
ScenarioScope<World> world;

writeToFile("#{WORLD_VARIABLE_LOG_FILE}.#{time}", world->variable);
EOF
  end

  def write_mapping_calling_world_function(step_name)
    append_step_definition step_name, <<-EOF
ScenarioScope<World> world;

world->someFunction();
EOF
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
  end

  def assert_passing_scenario
    assert_partial_output("1 scenario (1 passed)", all_output)
    assert_success true
  end

  def assert_failing_scenario
    assert_partial_output("1 scenario (1 failed)", all_output)
    assert_success false
  end

  def assert_pending_scenario
    assert_partial_output("1 scenario (1 pending)", all_output)
    assert_success true
  end

  def assert_undefined_scenario
    assert_partial_output("1 scenario (1 undefined)", all_output)
    assert_success true
  end

  def assert_scenario_reported_as_failing(scenario_name)
    assert_partial_output("# Scenario: #{scenario_name}", all_output)
    assert_success false
  end

  def assert_scenario_not_reported_as_failing(scenario_name)
    assert_no_partial_output("# Scenario: #{scenario_name}", all_output)
  end

  def assert_suggested_step_definition_snippet(stepdef_keyword, stepdef_pattern, parameter_count = 0, doc_string = false, data_table = false)
164
    not_implemented
165
166
  end

167
168
169
170
171
172
173
174
  def assert_executed_scenarios *scenario_offsets
    sequence = scenario_offsets.inject([]) do |sequence, scenario_offset|
      sequence << nth_step_name(scenario_offset)
    end
    assert_complete_cycle_sequence *sequence
  end

  def assert_world_variable_held_value_at_time(value, time)
175
    expect("#{WORLD_VARIABLE_LOG_FILE}.#{time}").to have_file_content(value)
176
177
178
  end

  def assert_world_function_called
179
    expect(WORLD_FUNCTION_LOG_FILE).to be_an_existing_file
180
181
182
183
  end

  def assert_cycle_sequence *args
    expected_string = args.join CYCLE_SEQUENCE_SEPARATOR
184
    expect(CYCLE_LOG_FILE).to have_file_content(Regexp.new expected_string)
185
186
187
188
  end

  def assert_cycle_sequence_excluding *args
    args.each do |unexpected_string|
189
      expect(CYCLE_LOG_FILE).not_to have_file_content(unexpected_string)
190
191
192
193
194
    end
  end

  def assert_complete_cycle_sequence *args
    expected_string = "#{CYCLE_SEQUENCE_SEPARATOR}#{args.join(CYCLE_SEQUENCE_SEPARATOR)}"
195
    expect(CYCLE_LOG_FILE).to have_file_content(expected_string)
196
197
  end

198
  def assert_data_table_equals_json(json)
199
200
201
202
    log_file_contents = IO.read(DATA_TABLE_LOG_FILE)
    actual_array      = JSON.parse(log_file_contents)
    expected_array    = JSON.parse(json)
    expect(actual_array).to be == expected_array
203
204
  end

205
  def run_feature
206
207
208
209
210
211
212
213
214
    run_feature_with_params("")
  end

  def run_feature_with_tags *tag_groups
    params = ""
    tag_groups.each do |tag_group|
      params += " --tags #{tag_group}"
    end
    run_feature_with_params(params)
215
216
217
218
219
220
221
222
223
  end

  def failed_output
    "failed"
  end

# cpp steps

  def append_step_definition(step_name, code) # todo params parameter?
224
225
226
227
228
    append_support_code <<-EOF
CUKE_STEP_("^#{step_name}$") {
#{indent_code code}
}
EOF
229
230
231
  end

  def append_support_code(code)
232
    helper_functions = get_absolute_path('../support/HelperFunctions.hpp');
233
    @support_code ||= <<-EOF
234
#include <cucumber-cpp/generic.hpp>
235
#include "#{helper_functions}"
236
237
238

using cucumber::ScenarioScope;

239
240
void logCycleEvent(const char *name) {
    logCycleEvent(name, "#{CYCLE_LOG_FILE}", "#{CYCLE_SEQUENCE_SEPARATOR}");
241
242
}

243
244
void logTableAsHashes(const cucumber::internal::Table & table) {
    logTableAsHashes(table, "#{DATA_TABLE_LOG_FILE}");
245
246
}
EOF
247
248
249
    @support_code += code
  end

250
251
252
  TMP_DIR                      = ENV["TMP_DIR"]
  FEATURES_DIR                 = ENV["TEST_FEATURES_DIR"]
  STEP_DEFINITIONS_SRC         = ENV["DYNAMIC_CPP_STEPS_SRC"]
253
  STEP_DEFINITIONS_OBJ         = ENV["DYNAMIC_CPP_STEPS_OBJ"]
254
255
256
257
258
  STEP_DEFINITIONS_EXE         = ENV["DYNAMIC_CPP_STEPS_EXE"]
  COMPILE_STEP_DEFINITIONS_CMD = ENV["COMPILE_DYNAMIC_CPP_STEPS"]

  WORLD_VARIABLE_LOG_FILE      = "#{TMP_DIR}/world_variable.log"
  WORLD_FUNCTION_LOG_FILE      = "#{TMP_DIR}/world_function.log"
259
  DATA_TABLE_LOG_FILE          = "#{TMP_DIR}/data_table.log"
260
261
262
  CYCLE_LOG_FILE               = "#{TMP_DIR}/cycle.log"
  CYCLE_SEQUENCE_SEPARATOR     = " -> "

263
264
private

265
266
267
268
  def not_implemented
    pending "Not yet implemented!"
  end

269
270
271
272
  def append_logging_step_definition(step_name)
    append_step_definition step_name, "logCycleEvent(\"#{step_name}\");"
  end

273
274
275
276
277
278
279
280
281
282
283
  def include_file(relative_file_path)
    absolute_file_path = get_absolute_path(relative_file_path)
    append_support_code <<-EOF
#include "#{absolute_file_path}"
EOF
  end

  def get_absolute_path(relative_path)
    File.expand_path(relative_path, File.dirname(__FILE__))
  end

284
285
286
287
288
289
290
291
292
293
294
  def nth_step_name n
    "step #{n}"
  end

  def run_feature_with_params(params)
    write_main_step_definitions_file
    compile_step_definitions
    create_wire_file
    run_cucumber_cpp
    run_cucumber_test_feature params
  end
295
296

  def write_main_step_definitions_file
297
    write_file STEP_DEFINITIONS_SRC, @support_code
298
299
300
  end

  def compile_step_definitions
301
    remove_step_definition_obj
302
    compiler_output = %x[ #{COMPILE_STEP_DEFINITIONS_CMD} ]
303
    expect($?.success?).to be == true, "Compilation failed!\n#{compiler_output}"
304
305
  end

306
  def remove_step_definition_obj
307
    remove STEP_DEFINITIONS_OBJ
308
309
310
  rescue Errno::ENOENT
  end

311
  def create_wire_file
312
313
314
315
    write_file "#{FEATURES_DIR}/step_definitions/cucumber-cpp.wire", <<-EOF
host: localhost
port: 3902
EOF
316
317
318
  end

  def run_cucumber_cpp
319
    @steps_out = IO.popen STEP_DEFINITIONS_EXE
320
321
  end

322
323
  def run_cucumber_test_feature(params)
    run_simple "cucumber #{params} #{FEATURES_DIR}", false
324
325
326
327
  end
end

World(CucumberCppMappings)