cucumber 0.9.4 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. data/Gemfile +1 -1
  2. data/History.txt +16 -0
  3. data/bin/cucumber +7 -1
  4. data/cucumber.gemspec +5 -4
  5. data/cucumber.yml +1 -1
  6. data/examples/sinatra/features/support/env.rb +1 -4
  7. data/features/background.feature +284 -95
  8. data/features/custom_formatter.feature +3 -73
  9. data/features/execute_with_tag_filter.feature +63 -0
  10. data/features/{negative_tagged_hooks.feature → hooks.feature} +5 -6
  11. data/features/json_formatter.feature +161 -245
  12. data/features/stats_formatters.feature +70 -0
  13. data/features/step_definitions/cucumber_steps.rb +5 -163
  14. data/features/support/env.rb +23 -149
  15. data/features/{tag_logic.feature → tagged_hooks.feature} +11 -52
  16. data/gem_tasks/{features.rake → cucumber.rake} +6 -1
  17. data/{features → legacy_features}/announce.feature +0 -0
  18. data/{features → legacy_features}/api/list_step_defs_as_json.feature +0 -0
  19. data/{features → legacy_features}/api/run_cli_main_with_existing_runtime.feature +0 -0
  20. data/{features → legacy_features}/around_hooks.feature +0 -0
  21. data/{features → legacy_features}/bug_371.feature +0 -0
  22. data/{features → legacy_features}/bug_464.feature +0 -0
  23. data/{features → legacy_features}/bug_475.feature +0 -0
  24. data/{features → legacy_features}/bug_585_tab_indentation.feature +0 -0
  25. data/{features → legacy_features}/bug_600.feature +0 -0
  26. data/{features → legacy_features}/call_steps_from_stepdefs.feature +0 -0
  27. data/{features → legacy_features}/cucumber_cli.feature +9 -9
  28. data/{features → legacy_features}/cucumber_cli_outlines.feature +0 -0
  29. data/{features → legacy_features}/default_snippets.feature +0 -0
  30. data/{features → legacy_features}/diffing.feature +0 -0
  31. data/{features → legacy_features}/drb_server_integration.feature +0 -0
  32. data/{features → legacy_features}/exception_in_after_block.feature +0 -0
  33. data/{features → legacy_features}/exception_in_after_step_block.feature +0 -0
  34. data/{features → legacy_features}/exception_in_before_block.feature +0 -0
  35. data/{features → legacy_features}/exclude_files.feature +0 -0
  36. data/{features → legacy_features}/expand.feature +0 -0
  37. data/{features → legacy_features}/html_formatter.feature +0 -0
  38. data/{features → legacy_features}/html_formatter/a.html +0 -0
  39. data/{features → legacy_features}/junit_formatter.feature +0 -0
  40. data/{features → legacy_features}/language_from_header.feature +0 -0
  41. data/{features → legacy_features}/language_help.feature +0 -0
  42. data/{features → legacy_features}/listener_debugger_formatter.feature +0 -0
  43. data/legacy_features/multiline_names.feature +44 -0
  44. data/{features → legacy_features}/post_configuration_hook.feature +0 -0
  45. data/{features → legacy_features}/profiles.feature +0 -0
  46. data/{features → legacy_features}/rake_task.feature +0 -0
  47. data/{features → legacy_features}/report_called_undefined_steps.feature +0 -0
  48. data/{features → legacy_features}/rerun_formatter.feature +0 -0
  49. data/{features → legacy_features}/simplest.feature +0 -0
  50. data/{features → legacy_features}/snippet.feature +0 -0
  51. data/{features → legacy_features}/snippets_when_using_star_keyword.feature +0 -0
  52. data/legacy_features/step_definitions/cucumber_steps.rb +168 -0
  53. data/{features → legacy_features}/step_definitions/extra_steps.rb +0 -0
  54. data/{features → legacy_features}/step_definitions/simplest_steps.rb +0 -0
  55. data/{features → legacy_features}/step_definitions/wire_steps.rb +1 -0
  56. data/legacy_features/support/env.rb +157 -0
  57. data/{features → legacy_features}/support/env.rb.simplest +0 -0
  58. data/{features → legacy_features}/support/fake_wire_server.rb +0 -0
  59. data/{features → legacy_features}/table_diffing.feature +0 -0
  60. data/{features → legacy_features}/table_mapping.feature +0 -0
  61. data/{features → legacy_features}/transform.feature +0 -0
  62. data/{features → legacy_features}/unicode_table.feature +0 -0
  63. data/{features → legacy_features}/wire_protocol.feature +1 -1
  64. data/{features → legacy_features}/wire_protocol_table_diffing.feature +0 -0
  65. data/{features → legacy_features}/wire_protocol_tags.feature +0 -0
  66. data/{features → legacy_features}/wire_protocol_timeouts.feature +0 -0
  67. data/{features → legacy_features}/work_in_progress.feature +0 -0
  68. data/lib/cucumber/ast/examples.rb +5 -0
  69. data/lib/cucumber/ast/feature.rb +5 -0
  70. data/lib/cucumber/ast/feature_element.rb +5 -0
  71. data/lib/cucumber/ast/scenario_outline.rb +9 -4
  72. data/lib/cucumber/ast/step.rb +5 -0
  73. data/lib/cucumber/ast/step_invocation.rb +4 -0
  74. data/lib/cucumber/ast/table.rb +3 -3
  75. data/lib/cucumber/ast/tree_walker.rb +1 -39
  76. data/lib/cucumber/cli/main.rb +1 -6
  77. data/lib/cucumber/cli/options.rb +1 -2
  78. data/lib/cucumber/formatter/ansicolor.rb +2 -4
  79. data/lib/cucumber/formatter/gherkin_formatter_adapter.rb +84 -0
  80. data/lib/cucumber/formatter/gpretty.rb +24 -0
  81. data/lib/cucumber/formatter/html.rb +1 -1
  82. data/lib/cucumber/formatter/io.rb +2 -4
  83. data/lib/cucumber/formatter/json.rb +15 -152
  84. data/lib/cucumber/formatter/json_pretty.rb +5 -6
  85. data/lib/cucumber/formatter/unicode.rb +41 -20
  86. data/lib/cucumber/parser/gherkin_builder.rb +7 -1
  87. data/lib/cucumber/platform.rb +1 -1
  88. data/lib/cucumber/step_match.rb +5 -1
  89. data/spec/cucumber/ast/scenario_outline_spec.rb +11 -8
  90. data/spec/cucumber/ast/table_spec.rb +6 -1
  91. data/spec/cucumber/cli/main_spec.rb +4 -1
  92. metadata +105 -132
  93. data/features/multiline_names.feature +0 -44
  94. data/features/usage_and_stepdefs_formatter.feature +0 -169
  95. data/fixtures/json/features/pystring.feature +0 -8
  96. data/fixtures/self_test/features/background/background_tagged_before_on_outline.feature +0 -12
  97. data/fixtures/self_test/features/background/background_with_name.feature +0 -7
  98. data/fixtures/self_test/features/background/failing_background.feature +0 -12
  99. data/fixtures/self_test/features/background/failing_background_after_success.feature +0 -11
  100. data/fixtures/self_test/features/background/multiline_args_background.feature +0 -32
  101. data/fixtures/self_test/features/background/passing_background.feature +0 -10
  102. data/fixtures/self_test/features/background/pending_background.feature +0 -10
  103. data/fixtures/self_test/features/background/scenario_outline_failing_background.feature +0 -16
  104. data/fixtures/self_test/features/background/scenario_outline_passing_background.feature +0 -16
  105. data/lib/cucumber/formatter/color_io.rb +0 -23
  106. data/lib/cucumber/formatter/tag_cloud.rb +0 -35
  107. data/spec/cucumber/formatter/color_io_spec.rb +0 -29
@@ -2,7 +2,10 @@ $:.unshift(File.dirname(__FILE__) + '/../lib')
2
2
  require 'cucumber/rake/task'
3
3
  require 'cucumber/platform'
4
4
 
5
- Cucumber::Rake::Task.new do |t|
5
+ Cucumber::Rake::Task.new(:features)
6
+
7
+ Cucumber::Rake::Task.new(:legacy_features) do |t|
8
+ t.cucumber_opts = %w{legacy_features}
6
9
  if(Cucumber::JRUBY)
7
10
  t.profile = Cucumber::WINDOWS ? 'jruby_win' : 'jruby'
8
11
  elsif(Cucumber::WINDOWS_MRI)
@@ -12,3 +15,5 @@ Cucumber::Rake::Task.new do |t|
12
15
  end
13
16
  t.rcov = ENV['RCOV']
14
17
  end
18
+
19
+ task :cucumber => [:features, :legacy_features]
@@ -90,7 +90,7 @@ Feature: Cucumber command line
90
90
  """
91
91
 
92
92
  Scenario: Require missing step definition from elsewhere
93
- When I run cucumber -q -r ../../features/step_definitions/extra_steps.rb features/sample.feature:5
93
+ When I run cucumber -q -r ../../legacy_features/step_definitions/extra_steps.rb features/sample.feature:5
94
94
  Then it should pass with
95
95
  """
96
96
  # Feature comment
@@ -196,18 +196,18 @@ Feature: Cucumber command line
196
196
  Feature: multiline
197
197
 
198
198
  Background: I'm a multiline name
199
- which goes on and on and on for three lines
200
- yawn
199
+ which goes on and on and on for three lines
200
+ yawn
201
201
  Given passing without a table
202
202
 
203
203
  Scenario: I'm a multiline name
204
- which goes on and on and on for three lines
205
- yawn
204
+ which goes on and on and on for three lines
205
+ yawn
206
206
  Given passing without a table
207
207
 
208
208
  Scenario Outline: I'm a multiline name
209
- which goes on and on and on for three lines
210
- yawn
209
+ which goes on and on and on for three lines
210
+ yawn
211
211
  Given <state> without a table
212
212
 
213
213
  Examples:
@@ -218,8 +218,8 @@ Feature: Cucumber command line
218
218
  Given <state> without a table
219
219
 
220
220
  Examples: I'm a multiline name
221
- which goes on and on and on for three lines
222
- yawn
221
+ which goes on and on and on for three lines
222
+ yawn
223
223
  | state |
224
224
  | passing |
225
225
 
@@ -0,0 +1,44 @@
1
+ Feature: Multiline description names
2
+ In order to accurately document feature elements
3
+ As a cucumberist
4
+ I want to have multiline names
5
+
6
+ Scenario: multiline scenario
7
+ When I run cucumber features/multiline_name.feature --no-snippets
8
+ Then STDERR should be empty
9
+ Then it should pass with
10
+ """
11
+ Feature: multiline
12
+
13
+ Background: I'm a multiline name # features/multiline_name.feature:3
14
+ which goes on and on and on for three lines
15
+ yawn
16
+ Given passing without a table # features/step_definitions/sample_steps.rb:12
17
+
18
+ Scenario: I'm a multiline name # features/multiline_name.feature:8
19
+ which goes on and on and on for three lines
20
+ yawn
21
+ Given passing without a table # features/step_definitions/sample_steps.rb:12
22
+
23
+ Scenario Outline: I'm a multiline name # features/multiline_name.feature:13
24
+ which goes on and on and on for three lines
25
+ yawn
26
+ Given <state> without a table # features/step_definitions/sample_steps.rb:12
27
+
28
+ Examples:
29
+ | state |
30
+ | passing |
31
+
32
+ Scenario Outline: name # features/multiline_name.feature:21
33
+ Given <state> without a table # features/step_definitions/sample_steps.rb:12
34
+
35
+ Examples: I'm a multiline name
36
+ which goes on and on and on for three lines
37
+ yawn
38
+ | state |
39
+ | passing |
40
+
41
+ 3 scenarios (3 passed)
42
+ 6 steps (6 passed)
43
+
44
+ """
@@ -0,0 +1,168 @@
1
+ # encoding: utf-8
2
+ require 'tempfile'
3
+
4
+ Given /^I am in (.*)$/ do |example_dir_relative_path|
5
+ @current_dir = fixtures_dir(example_dir_relative_path)
6
+ end
7
+
8
+ Given /^a standard Cucumber project directory structure$/ do
9
+ @current_dir = working_dir
10
+ in_current_dir do
11
+ FileUtils.rm_rf 'features' if File.directory?('features')
12
+ FileUtils.mkdir_p 'features/support'
13
+ FileUtils.mkdir 'features/step_definitions'
14
+ end
15
+ end
16
+
17
+ Given /^the (.*) directory is empty$/ do |directory|
18
+ in_current_dir do
19
+ FileUtils.remove_dir(directory) rescue nil
20
+ FileUtils.mkdir 'tmp'
21
+ end
22
+ end
23
+
24
+ Given /^a file named "([^"]*)"$/ do |file_name|
25
+ create_file(file_name, '')
26
+ end
27
+
28
+ Given /^a file named "([^"]*)" with:$/ do |file_name, file_content|
29
+ create_file(file_name, file_content)
30
+ end
31
+
32
+ Given /^the following profiles? (?:are|is) defined:$/ do |profiles|
33
+ create_file('cucumber.yml', profiles)
34
+ end
35
+
36
+ Given /^I am running spork in the background$/ do
37
+ run_spork_in_background
38
+ end
39
+
40
+ Given /^I am running spork in the background on port (\d+)$/ do |port|
41
+ run_spork_in_background(port.to_i)
42
+ end
43
+
44
+ Given /^I am not running (?:.*) in the background$/ do
45
+ # no-op
46
+ end
47
+
48
+ Given /^I have environment variable (\w+) set to "([^"]*)"$/ do |variable, value|
49
+ set_env_var(variable, value)
50
+ end
51
+
52
+ When /^I run cucumber (.*)$/ do |cucumber_opts|
53
+ run "#{Cucumber::RUBY_BINARY} -I rubygems #{cucumber_bin} --no-color #{cucumber_opts} CUCUMBER_OUTPUT_ENCODING=UTF-8"
54
+ end
55
+
56
+ When /^I run rake (.*)$/ do |rake_opts|
57
+ run "rake #{rake_opts} --trace"
58
+ end
59
+
60
+ When /^I run the following Ruby code:$/ do |code|
61
+ run %{#{Cucumber::RUBY_BINARY} -r rubygems -I #{cucumber_lib_dir} -e "#{code}"}
62
+ end
63
+
64
+ Then /^it should (fail|pass)$/ do |success|
65
+ if success == 'fail'
66
+ last_exit_status.should_not == 0
67
+ else
68
+ if last_exit_status != 0
69
+ raise "Failed with exit status #{last_exit_status}\nSTDOUT:\n#{last_stdout}\nSTDERR:\n#{last_stderr}"
70
+ end
71
+ end
72
+ end
73
+
74
+ Then /^it should (fail|pass) with$/ do |success, output|
75
+ unless combined_output.index(output)
76
+ combined_output.should == output
77
+ end
78
+ Then("it should #{success}")
79
+ end
80
+
81
+ Then /^the output should contain:?$/ do |text|
82
+ last_stdout.should include(text)
83
+ end
84
+
85
+ Then /^the output should not contain$/ do |text|
86
+ last_stdout.should_not include(text)
87
+ end
88
+
89
+ Then /^the output should be$/ do |text|
90
+ last_stdout.should == text
91
+ end
92
+
93
+ Then /^it should (fail|pass) with JSON$/ do |success, text|
94
+ JSON.parse(last_stdout).should == JSON.parse(text)
95
+ Then("it should #{success}")
96
+ end
97
+
98
+ Then /^"([^"]*)" should contain$/ do |file, text|
99
+ strip_duration(IO.read(file)).should == text
100
+ end
101
+
102
+ Then /^"([^"]*)" with junit duration "([^"]*)" should contain$/ do |actual_file, duration_replacement, text|
103
+ actual = IO.read(actual_file)
104
+ actual = replace_junit_duration(actual, duration_replacement)
105
+ actual = strip_ruby186_extra_trace(actual)
106
+ actual.should == text
107
+ end
108
+
109
+ Then /^"([^"]*)" should match "(.+?)"$/ do |file, text|
110
+ File.open(file, Cucumber.file_mode('r')).read.should =~ Regexp.new(text)
111
+ end
112
+
113
+ Then /^"([^"]*)" should have the same contents as "([^"]*)"$/ do |actual_file, expected_file|
114
+ actual = IO.read(actual_file)
115
+ actual = replace_duration(actual, '0m30.005s')
116
+ # Comment out to replace expected file. Use with care!
117
+ # File.open(expected_file, "w") {|io| io.write(actual)}
118
+ actual.should == IO.read(expected_file)
119
+ end
120
+
121
+ Then /^STDERR should match$/ do |text|
122
+ last_stderr.should =~ /#{text}/
123
+ end
124
+
125
+ Then /^STDERR should not match$/ do |text|
126
+ last_stderr.should_not =~ /#{text}/
127
+ end
128
+
129
+ Then /^STDERR should be$/ do |text|
130
+ last_stderr.should == text
131
+ end
132
+
133
+ Then /^STDERR should be empty$/ do
134
+ last_stderr.should == ""
135
+ end
136
+
137
+ Then /^"([^"]*)" should exist$/ do |file|
138
+ File.exists?(file).should be_true
139
+ FileUtils.rm(file)
140
+ end
141
+
142
+ Then /^"([^"]*)" should not be required$/ do |file_name|
143
+ last_stdout.should_not include("* #{file_name}")
144
+ end
145
+
146
+ Then /^"([^"]*)" should be required$/ do |file_name|
147
+ last_stdout.should include("* #{file_name}")
148
+ end
149
+
150
+ Then /^exactly these files should be loaded:\s*(.*)$/ do |files|
151
+ last_stdout.scan(/^ \* (.*\.rb)$/).flatten.should == files.split(/,\s+/)
152
+ end
153
+
154
+ Then /^exactly these features should be ran:\s*(.*)$/ do |files|
155
+ last_stdout.scan(/^ \* (.*\.feature)$/).flatten.should == files.split(/,\s+/)
156
+ end
157
+
158
+ Then /^the (.*) profile should be used$/ do |profile|
159
+ last_stdout.should =~ /Using the #{profile} profile/
160
+ end
161
+
162
+ Then /^print output$/ do
163
+ puts last_stdout
164
+ end
165
+
166
+ Then /^the output should contain the following JSON:$/ do |json_string|
167
+ JSON.parse(last_stdout).should == JSON.parse(json_string)
168
+ end
@@ -1,4 +1,5 @@
1
1
  Given /^there is a wire server (running |)on port (\d+) which understands the following protocol:$/ do |running, port, table|
2
+ table.map_column!('response') {|cell| cell.gsub(/\n/, '\n')}
2
3
  protocol = table.hashes
3
4
  @server = FakeWireServer.new(port.to_i, protocol)
4
5
  start_wire_server if running.strip == "running"
@@ -0,0 +1,157 @@
1
+ require 'rubygems'
2
+
3
+ require 'tempfile'
4
+ require 'rspec/expectations'
5
+ require 'fileutils'
6
+ require 'forwardable'
7
+ require 'cucumber/formatter/unicode'
8
+ # This is to force miniunit to be loaded on 1.9.2, and verify that we can still run with --profile. See:
9
+ # * disable_mini_test_autorun.rb and
10
+ # * http://groups.google.com/group/cukes/browse_thread/thread/5682d41436e235d7
11
+ # * https://rspec.lighthouseapp.com/projects/16211/tickets/677-cucumber-093-prevents-testunit-from-running
12
+ require 'test/unit'
13
+
14
+ class CucumberWorld
15
+ extend Forwardable
16
+ def_delegators CucumberWorld, :fixtures_dir, :self_test_dir, :working_dir, :cucumber_lib_dir
17
+
18
+ def self.fixtures_dir(subdir=nil)
19
+ @fixtures_dir ||= File.expand_path(File.join(File.dirname(__FILE__), '../../fixtures'))
20
+ subdir ? File.join(@fixtures_dir, subdir) : @fixtures_dir
21
+ end
22
+
23
+ def self.self_test_dir
24
+ @self_test_dir ||= fixtures_dir('self_test')
25
+ end
26
+
27
+ def self.working_dir
28
+ @working_dir ||= fixtures_dir('self_test/tmp')
29
+ end
30
+
31
+ def cucumber_lib_dir
32
+ @cucumber_lib_dir ||= File.expand_path(File.join(File.dirname(__FILE__), '../../lib'))
33
+ end
34
+
35
+ # Don't use Cucumber::BINARY (which is the binary used to start the "outer" cucumber)
36
+ # Instead we force the use of this codebase's cucumber bin script.
37
+ # This allows us to run cucumber's cukes with an older, stable cucumber.
38
+ def cucumber_bin
39
+ File.expand_path(File.dirname(__FILE__) + '/../../bin/cucumber')
40
+ end
41
+
42
+ def initialize
43
+ @current_dir = self_test_dir
44
+ end
45
+
46
+ private
47
+ attr_reader :last_exit_status, :last_stderr
48
+
49
+ # The last standard out, with the duration line taken out (unpredictable)
50
+ def last_stdout
51
+ strip_1_9_paths(strip_duration(@last_stdout))
52
+ end
53
+
54
+ def combined_output
55
+ last_stdout + "\n" + last_stderr
56
+ end
57
+
58
+ def strip_duration(s)
59
+ s.gsub(/^\d+m\d+\.\d+s\n/m, "")
60
+ end
61
+
62
+ def strip_1_9_paths(s)
63
+ s.gsub(/#{Dir.pwd}\/fixtures\/self_test\/tmp/m, ".").gsub(/#{Dir.pwd}\/fixtures\/self_test/m, ".")
64
+ end
65
+
66
+ def replace_duration(s, replacement)
67
+ s.gsub(/\d+m\d+\.\d+s/m, replacement)
68
+ end
69
+
70
+ def replace_junit_duration(s, replacement)
71
+ s.gsub(/\d+\.\d\d+/m, replacement)
72
+ end
73
+
74
+ def strip_ruby186_extra_trace(s)
75
+ s.gsub(/^.*\.\/features\/step_definitions(.*)\n/, "")
76
+ end
77
+
78
+ def create_file(file_name, file_content)
79
+ file_content.gsub!("CUCUMBER_LIB", "'#{cucumber_lib_dir}'") # Some files, such as Rakefiles need to use the lib dir
80
+ in_current_dir do
81
+ FileUtils.mkdir_p(File.dirname(file_name)) unless File.directory?(File.dirname(file_name))
82
+ File.open(file_name, 'w') { |f| f << file_content }
83
+ end
84
+ end
85
+
86
+ def set_env_var(variable, value)
87
+ @original_env_vars ||= {}
88
+ @original_env_vars[variable] = ENV[variable]
89
+ ENV[variable] = value
90
+ end
91
+
92
+ def background_jobs
93
+ @background_jobs ||= []
94
+ end
95
+
96
+ def in_current_dir(&block)
97
+ Dir.chdir(@current_dir, &block)
98
+ end
99
+
100
+ def run(command)
101
+ stderr_file = Tempfile.new('cucumber')
102
+ stderr_file.close
103
+ in_current_dir do
104
+ mode = Cucumber::RUBY_1_9 ? {:external_encoding=>"UTF-8"} : 'r'
105
+ IO.popen("#{command} 2> #{stderr_file.path}", mode) do |io|
106
+ @last_stdout = io.read
107
+ end
108
+
109
+ @last_exit_status = $?.exitstatus
110
+ end
111
+ @last_stderr = IO.read(stderr_file.path)
112
+ end
113
+
114
+ def run_spork_in_background(port = nil)
115
+ require 'spork'
116
+
117
+ pid = fork
118
+ in_current_dir do
119
+ if pid
120
+ background_jobs << pid
121
+ else
122
+ # STDOUT.close
123
+ # STDERR.close
124
+ port_arg = port ? "-p #{port}" : ''
125
+ cmd = "#{Cucumber::RUBY_BINARY} -I #{Cucumber::LIBDIR} #{Spork::BINARY} cuc #{port_arg}"
126
+ exec cmd
127
+ end
128
+ end
129
+ sleep 1.0
130
+ end
131
+
132
+ def terminate_background_jobs
133
+ background_jobs.each do |pid|
134
+ Process.kill(Signal.list['TERM'], pid)
135
+ end
136
+ end
137
+
138
+ def restore_original_env_vars
139
+ @original_env_vars.each { |variable, value| ENV[variable] = value } if @original_env_vars
140
+ end
141
+
142
+ end
143
+
144
+ World do
145
+ CucumberWorld.new
146
+ end
147
+
148
+ Before do
149
+ FileUtils.rm_rf CucumberWorld.working_dir
150
+ FileUtils.mkdir CucumberWorld.working_dir
151
+ end
152
+
153
+ After do
154
+ FileUtils.rm_rf CucumberWorld.working_dir unless ENV['KEEP_FILES']
155
+ terminate_background_jobs
156
+ restore_original_env_vars
157
+ end