tap 0.7.9

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 (146) hide show
  1. data/MIT-LICENSE +21 -0
  2. data/README +71 -0
  3. data/Rakefile +117 -0
  4. data/bin/tap +63 -0
  5. data/lib/tap.rb +15 -0
  6. data/lib/tap/app.rb +739 -0
  7. data/lib/tap/file_task.rb +354 -0
  8. data/lib/tap/generator.rb +29 -0
  9. data/lib/tap/generator/generators/config/USAGE +0 -0
  10. data/lib/tap/generator/generators/config/config_generator.rb +23 -0
  11. data/lib/tap/generator/generators/config/templates/config.erb +2 -0
  12. data/lib/tap/generator/generators/file_task/USAGE +0 -0
  13. data/lib/tap/generator/generators/file_task/file_task_generator.rb +21 -0
  14. data/lib/tap/generator/generators/file_task/templates/task.erb +27 -0
  15. data/lib/tap/generator/generators/file_task/templates/test.erb +12 -0
  16. data/lib/tap/generator/generators/root/USAGE +0 -0
  17. data/lib/tap/generator/generators/root/root_generator.rb +36 -0
  18. data/lib/tap/generator/generators/root/templates/Rakefile +48 -0
  19. data/lib/tap/generator/generators/root/templates/app.yml +19 -0
  20. data/lib/tap/generator/generators/root/templates/config/process_tap_request.yml +4 -0
  21. data/lib/tap/generator/generators/root/templates/lib/process_tap_request.rb +26 -0
  22. data/lib/tap/generator/generators/root/templates/public/images/nav.jpg +0 -0
  23. data/lib/tap/generator/generators/root/templates/public/stylesheets/color.css +57 -0
  24. data/lib/tap/generator/generators/root/templates/public/stylesheets/layout.css +108 -0
  25. data/lib/tap/generator/generators/root/templates/public/stylesheets/normalize.css +40 -0
  26. data/lib/tap/generator/generators/root/templates/public/stylesheets/typography.css +21 -0
  27. data/lib/tap/generator/generators/root/templates/server/config/environment.rb +60 -0
  28. data/lib/tap/generator/generators/root/templates/server/lib/tasks/clear_database_prerequisites.rake +5 -0
  29. data/lib/tap/generator/generators/root/templates/server/test/test_helper.rb +53 -0
  30. data/lib/tap/generator/generators/root/templates/test/tap_test_helper.rb +3 -0
  31. data/lib/tap/generator/generators/root/templates/test/tap_test_suite.rb +4 -0
  32. data/lib/tap/generator/generators/task/USAGE +0 -0
  33. data/lib/tap/generator/generators/task/task_generator.rb +21 -0
  34. data/lib/tap/generator/generators/task/templates/task.erb +21 -0
  35. data/lib/tap/generator/generators/task/templates/test.erb +29 -0
  36. data/lib/tap/generator/generators/workflow/USAGE +0 -0
  37. data/lib/tap/generator/generators/workflow/templates/task.erb +16 -0
  38. data/lib/tap/generator/generators/workflow/templates/test.erb +7 -0
  39. data/lib/tap/generator/generators/workflow/workflow_generator.rb +21 -0
  40. data/lib/tap/generator/options.rb +26 -0
  41. data/lib/tap/generator/usage.rb +26 -0
  42. data/lib/tap/root.rb +275 -0
  43. data/lib/tap/script/console.rb +7 -0
  44. data/lib/tap/script/destroy.rb +8 -0
  45. data/lib/tap/script/generate.rb +8 -0
  46. data/lib/tap/script/run.rb +111 -0
  47. data/lib/tap/script/server.rb +12 -0
  48. data/lib/tap/support/audit.rb +415 -0
  49. data/lib/tap/support/batch_queue.rb +165 -0
  50. data/lib/tap/support/combinator.rb +114 -0
  51. data/lib/tap/support/logger.rb +91 -0
  52. data/lib/tap/support/rap.rb +38 -0
  53. data/lib/tap/support/run_error.rb +20 -0
  54. data/lib/tap/support/template.rb +81 -0
  55. data/lib/tap/support/templater.rb +155 -0
  56. data/lib/tap/support/versions.rb +63 -0
  57. data/lib/tap/task.rb +448 -0
  58. data/lib/tap/test.rb +320 -0
  59. data/lib/tap/test/env_vars.rb +16 -0
  60. data/lib/tap/test/inference_methods.rb +298 -0
  61. data/lib/tap/test/subset_methods.rb +260 -0
  62. data/lib/tap/version.rb +3 -0
  63. data/lib/tap/workflow.rb +73 -0
  64. data/test/app/config/addition_template.yml +6 -0
  65. data/test/app/config/batch.yml +2 -0
  66. data/test/app/config/empty.yml +0 -0
  67. data/test/app/config/erb.yml +1 -0
  68. data/test/app/config/template.yml +6 -0
  69. data/test/app/config/version-0.1.yml +1 -0
  70. data/test/app/config/version.yml +1 -0
  71. data/test/app/lib/app_test_task.rb +2 -0
  72. data/test/app_class_test.rb +33 -0
  73. data/test/app_test.rb +1372 -0
  74. data/test/file_task/config/batch.yml +2 -0
  75. data/test/file_task/config/configured.yml +1 -0
  76. data/test/file_task/old_file_one.txt +0 -0
  77. data/test/file_task/old_file_two.txt +0 -0
  78. data/test/file_task_test.rb +1041 -0
  79. data/test/root/alt_lib/alt_module.rb +4 -0
  80. data/test/root/lib/absolute_alt_filepath.rb +2 -0
  81. data/test/root/lib/alternative_filepath.rb +2 -0
  82. data/test/root/lib/another_module.rb +2 -0
  83. data/test/root/lib/nested/some_module.rb +4 -0
  84. data/test/root/lib/no_module_included.rb +0 -0
  85. data/test/root/lib/some/module.rb +4 -0
  86. data/test/root/lib/some_class.rb +2 -0
  87. data/test/root/lib/some_module.rb +3 -0
  88. data/test/root/load_path/load_path_module.rb +2 -0
  89. data/test/root/load_path/skip_module.rb +2 -0
  90. data/test/root/mtime/older.txt +0 -0
  91. data/test/root/unload/full_path.rb +2 -0
  92. data/test/root/unload/loaded_by_nested.rb +2 -0
  93. data/test/root/unload/nested/nested_load.rb +6 -0
  94. data/test/root/unload/nested/nested_with_ext.rb +4 -0
  95. data/test/root/unload/nested/relative_path.rb +4 -0
  96. data/test/root/unload/older.rb +2 -0
  97. data/test/root/unload/unload_base.rb +9 -0
  98. data/test/root/versions/another.yml +0 -0
  99. data/test/root/versions/file-0.1.2.yml +0 -0
  100. data/test/root/versions/file-0.1.yml +0 -0
  101. data/test/root/versions/file.yml +0 -0
  102. data/test/root_test.rb +483 -0
  103. data/test/support/audit_test.rb +449 -0
  104. data/test/support/batch_queue_test.rb +320 -0
  105. data/test/support/combinator_test.rb +249 -0
  106. data/test/support/logger_test.rb +31 -0
  107. data/test/support/template_test.rb +122 -0
  108. data/test/support/templater/erb.txt +2 -0
  109. data/test/support/templater/erb.yml +2 -0
  110. data/test/support/templater/somefile.txt +2 -0
  111. data/test/support/templater_test.rb +192 -0
  112. data/test/support/versions_test.rb +71 -0
  113. data/test/tap_test_helper.rb +4 -0
  114. data/test/tap_test_suite.rb +4 -0
  115. data/test/task/config/batch.yml +2 -0
  116. data/test/task/config/batched.yml +2 -0
  117. data/test/task/config/configured.yml +1 -0
  118. data/test/task/config/example.yml +1 -0
  119. data/test/task/config/overriding.yml +2 -0
  120. data/test/task/config/task_with_config.yml +1 -0
  121. data/test/task/config/template.yml +4 -0
  122. data/test/task_class_test.rb +118 -0
  123. data/test/task_execute_test.rb +233 -0
  124. data/test/task_test.rb +424 -0
  125. data/test/test/inference_methods/test_assert_expected/expected/file.txt +1 -0
  126. data/test/test/inference_methods/test_assert_expected/expected/folder/file.txt +1 -0
  127. data/test/test/inference_methods/test_assert_expected/input/file.txt +1 -0
  128. data/test/test/inference_methods/test_assert_expected/input/folder/file.txt +1 -0
  129. data/test/test/inference_methods/test_assert_files_exist/input/input_1.txt +0 -0
  130. data/test/test/inference_methods/test_assert_files_exist/input/input_2.txt +0 -0
  131. data/test/test/inference_methods/test_file_compare/expected/output_1.txt +3 -0
  132. data/test/test/inference_methods/test_file_compare/expected/output_2.txt +1 -0
  133. data/test/test/inference_methods/test_file_compare/input/input_1.txt +3 -0
  134. data/test/test/inference_methods/test_file_compare/input/input_2.txt +3 -0
  135. data/test/test/inference_methods/test_infer_glob/expected/file.yml +0 -0
  136. data/test/test/inference_methods/test_infer_glob/expected/file_1.txt +0 -0
  137. data/test/test/inference_methods/test_infer_glob/expected/file_2.txt +0 -0
  138. data/test/test/inference_methods/test_yml_compare/expected/output_1.yml +6 -0
  139. data/test/test/inference_methods/test_yml_compare/expected/output_2.yml +6 -0
  140. data/test/test/inference_methods/test_yml_compare/input/input_1.yml +4 -0
  141. data/test/test/inference_methods/test_yml_compare/input/input_2.yml +4 -0
  142. data/test/test/inference_methods_test.rb +311 -0
  143. data/test/test/subset_methods_test.rb +115 -0
  144. data/test/test_test.rb +233 -0
  145. data/test/workflow_test.rb +108 -0
  146. metadata +274 -0
@@ -0,0 +1,3 @@
1
+ some
2
+ multiline file
3
+ content!
@@ -0,0 +1,6 @@
1
+ # this file has equal yml, but with comments and a reversed hash order.
2
+
3
+ array:
4
+ - one
5
+ - two
6
+ field: value
@@ -0,0 +1,6 @@
1
+ # this file has unequal yml
2
+
3
+ array:
4
+ - one
5
+ - two
6
+ field: different value
@@ -0,0 +1,4 @@
1
+ field: value
2
+ array:
3
+ - one
4
+ - two
@@ -0,0 +1,4 @@
1
+ field: value
2
+ array:
3
+ - one
4
+ - two
@@ -0,0 +1,311 @@
1
+ require File.join(File.dirname(__FILE__), '../tap_test_helper.rb')
2
+ require 'tap/test/inference_methods'
3
+
4
+ class InferenceMethodsTest < Test::Unit::TestCase
5
+ include Tap::Test::SubsetMethods
6
+ acts_as_inference_test
7
+
8
+ def test_method_name_returns_test_method_name1
9
+ assert_equal "test_method_name_returns_test_method_name1", method_name
10
+ end
11
+
12
+ def test_method_name_returns_test_method_name2
13
+ assert_equal "test_method_name_returns_test_method_name2", method_name
14
+ end
15
+
16
+ def test_make_directory_structure
17
+ root = File.join(File.dirname(__FILE__), "inference_methods")
18
+ begin
19
+ assert_equal root, ifs[:root]
20
+ assert_equal({
21
+ :input => 'input',
22
+ :output => 'output',
23
+ :expected => 'expected',
24
+ :fail => 'fail'}, ifs.directories)
25
+
26
+ ifs.directories.values.each do |dir|
27
+ assert !File.exists?(File.join(root, "test_make_directory_structure", dir.to_s)), dir
28
+ end
29
+
30
+ make_directory_structure # alias for make_directory_structure
31
+
32
+ ifs.directories.values.each do |dir|
33
+ assert File.exists?(File.join(root, "test_make_directory_structure", dir.to_s)), dir
34
+ end
35
+ ensure
36
+ dir = File.join(File.join(root, "test_make_directory_structure"))
37
+ FileUtils.rm_r dir if File.exists?(dir)
38
+ end
39
+ end
40
+
41
+ #
42
+ # infer filepath tests
43
+ #
44
+
45
+ def test_infer_dir_adds_method_to_path
46
+ assert_equal File.join(ifs.root, method_name, "input"), infer_dir(:input)
47
+ end
48
+
49
+ def test_infer_filepath_adds_method_to_path
50
+ input_root = File.join(ifs.root, method_name, "input")
51
+ assert_equal File.join(input_root, "file.txt"), infer_filepath(:input, 'file.txt')
52
+ assert_equal File.join(input_root, "folder/file.txt"), infer_filepath(:input, 'folder', 'file.txt')
53
+ end
54
+
55
+ #
56
+ # infer relative filepath tests
57
+ #
58
+
59
+ def test_infer_relative_filepath_removes_infer_dir
60
+ input_root = File.join(ifs.root, method_name, "input")
61
+ assert_equal 'file.txt', infer_relative_filepath(:input, File.join(input_root, "file.txt"))
62
+ assert_equal 'folder/file.txt', infer_relative_filepath(:input, File.join(input_root, "folder/file.txt"))
63
+ end
64
+
65
+ def test_infer_relative_filepath_expands_filepaths
66
+ input_root = File.join(ifs.root, method_name, "input")
67
+ assert_equal 'file.txt', infer_relative_filepath(:input, File.join(input_root, "folder/.././file.txt") )
68
+ end
69
+
70
+ def test_infer_relative_filepath_raises_error_unless_filepath_begins_with_infer_dir
71
+ assert_raise(RuntimeError) { infer_relative_filepath(:input, File.join('some/path', ifs[:input], 'file.txt') ) }
72
+ end
73
+
74
+ #
75
+ # infer translate tests
76
+ #
77
+
78
+ def test_infer_translate
79
+ acase_test('file.txt', 'folder/file.txt') do |path|
80
+ filepath = File.join(ifs.root, method_name, "input", path)
81
+ expected = File.join(ifs.root, method_name, "output", path)
82
+
83
+ assert_equal expected, infer_translate(filepath, :input, :output)
84
+ end
85
+ end
86
+
87
+ #
88
+ # infer glob tests
89
+ #
90
+
91
+ def test_infer_glob
92
+ case_test(
93
+ [:expected] => ["file.yml", "file_1.txt", "file_2.txt"],
94
+ [:expected, "*"] => ["file.yml", "file_1.txt", "file_2.txt"],
95
+ [:expected, "*.txt"] => ["file_1.txt", "file_2.txt"],
96
+ [:expected, "*.txt", "*.yml"] => ["file.yml", "file_1.txt", "file_2.txt"]
97
+ ) do |testcase, expected|
98
+ expected.collect! { |file| infer_filepath(:expected, file) }
99
+
100
+ assert_equal expected.sort, infer_glob(*testcase).sort
101
+ end
102
+ end
103
+
104
+ #
105
+ # tempfile test
106
+ #
107
+
108
+ def test_tempfile_returns_new_file_in_output_dir
109
+ output_root = File.join(ifs.root, method_name, "output")
110
+
111
+ filepath =File.join(output_root, "file#{$$}.0")
112
+ assert_equal filepath, tempfile('file')
113
+
114
+ filepath = File.join(output_root, "file#{$$}.0")
115
+ assert_equal filepath, tempfile('file')
116
+
117
+ FileUtils.touch filepath
118
+ filepath = File.join(output_root, "file#{$$}.1")
119
+ assert_equal filepath, tempfile('file')
120
+
121
+ FileUtils.touch filepath
122
+ filepath = File.join(output_root, "file#{$$}.2")
123
+ assert_equal filepath, tempfile('file')
124
+ end
125
+
126
+ #
127
+ # segregate file test
128
+ #
129
+
130
+ def segregate_file_setup(path)
131
+ filepath = File.join(ifs.root, method_name, "output", path)
132
+ segregated = File.join(ifs.root, method_name, "fail", path)
133
+
134
+ dirname = File.dirname(filepath)
135
+ FileUtils.mkdir_p(dirname) unless File.exists?(dirname)
136
+ FileUtils.touch(filepath)
137
+
138
+ [filepath, segregated]
139
+ end
140
+
141
+ def test_segregate_file
142
+ acase_test('file.txt', 'folder/file.txt') do |path|
143
+ filepath, segregated = segregate_file_setup(path)
144
+
145
+ assert File.exists?(filepath)
146
+ assert !File.exists?(segregated)
147
+
148
+ segregate_file(filepath)
149
+
150
+ assert File.exists?(filepath)
151
+ assert File.exists?(segregated)
152
+ end
153
+ end
154
+
155
+ def test_segregate_file_only_segregates_if_block_returns_true
156
+ acase_test('file.txt', 'folder/file.txt') do |path|
157
+ filepath, segregated = segregate_file_setup(path)
158
+
159
+ assert File.exists?(filepath)
160
+ assert !File.exists?(segregated)
161
+
162
+ segregate_file(filepath) do |file|
163
+ false
164
+ end
165
+
166
+ assert File.exists?(filepath)
167
+ assert !File.exists?(segregated)
168
+
169
+ segregate_file(filepath) do |file|
170
+ true
171
+ end
172
+
173
+ assert File.exists?(filepath)
174
+ assert File.exists?(segregated)
175
+ end
176
+ end
177
+
178
+ def test_segregate_file_segregates_if_block_raises_error
179
+ acase_test('file.txt', 'folder/file.txt') do |path|
180
+ filepath, segregated = segregate_file_setup(path)
181
+
182
+ assert File.exists?(filepath)
183
+ assert !File.exists?(segregated)
184
+
185
+ begin
186
+ segregate_file(filepath) do |file|
187
+ raise "error"
188
+ end
189
+
190
+ flunk("error was not re-raised")
191
+ rescue
192
+ assert File.exists?(filepath)
193
+ assert File.exists?(segregated)
194
+ end
195
+ end
196
+ end
197
+
198
+ #
199
+ # assert expected tests
200
+ #
201
+
202
+ def test_assert_expected
203
+ target = nil
204
+ infer_glob(:input).sort.each do |input_file|
205
+ target = ifs.translate(input_file, "#{method_name}/input", "#{method_name}/output")
206
+
207
+ if File.file?(input_file)
208
+ dirname = File.dirname(target)
209
+ FileUtils.mkdir_p(dirname) unless File.exists?(dirname)
210
+ FileUtils.cp(input_file, target)
211
+ else
212
+ FileUtils.mkdir_p(target)
213
+ end
214
+ end
215
+
216
+ # all files
217
+ assert_expected
218
+
219
+ # files globed
220
+ assert_expected(infer_filepath(:expected, 'file.txt'))
221
+
222
+ # with block
223
+ assert_expected do | expected, output|
224
+ true
225
+ end
226
+
227
+ # now with a block that fails
228
+ fail_file = ifs.translate(target, "#{method_name}/output", "#{method_name}/fail")
229
+ begin
230
+ assert_expected do |expected, output|
231
+ false
232
+ end
233
+ flunk
234
+ rescue
235
+ assert File.exists?(fail_file)
236
+ File.delete(fail_file)
237
+ end
238
+
239
+ # now with a file that is different
240
+ File.open(target, 'a') {|file| file << "additional contents" }
241
+ begin
242
+ assert_expected
243
+ flunk
244
+ rescue
245
+ assert File.exists?(fail_file)
246
+ File.delete(fail_file)
247
+ end
248
+
249
+ # now with a missing file -- note there is no file to segregate
250
+ File.delete(target)
251
+ begin
252
+ assert_expected
253
+ flunk
254
+ rescue
255
+ assert $!.message =~ Regexp.new("Expected output file does not exist: #{target}")
256
+ assert !File.exists?(fail_file)
257
+ end
258
+ end
259
+
260
+ #
261
+ # files equal test
262
+ #
263
+
264
+ def test_file_compare
265
+ count = 0
266
+ each_pair(infer_glob(:input, "*1*"), infer_glob(:expected, "*1*") ) do |a, b|
267
+ assert files_equal?(a, b)
268
+ count += 1
269
+ end
270
+ assert_equal 1, count
271
+
272
+ each_pair(infer_glob(:input, "*2*"), infer_glob(:expected, "*2*") ) do |a, b|
273
+ assert !files_equal?(a, b)
274
+ count += 1
275
+ end
276
+ assert_equal 2, count
277
+ end
278
+
279
+ #
280
+ # yml equal test
281
+ #
282
+
283
+ def test_yml_compare
284
+ count = 0
285
+ each_pair(infer_glob(:input, "*1*"), infer_glob(:expected, "*1*") ) do |a, b|
286
+ assert yml_equal?(a, b)
287
+ count += 1
288
+ end
289
+ assert_equal 1, count
290
+
291
+ each_pair(infer_glob(:input, "*2*"), infer_glob(:expected, "*2*") ) do |a, b|
292
+ assert !yml_equal?(a, b)
293
+ count += 1
294
+ end
295
+ assert_equal 2, count
296
+ end
297
+ end
298
+
299
+ class InferenceTestTestWithOptions < Test::Unit::TestCase
300
+ acts_as_inference_test :root => "some/root/dir"
301
+
302
+ def test_test_setup
303
+ assert_equal "some/root/dir", ifs[:root]
304
+ assert_equal({
305
+ :input => 'input',
306
+ :output => 'output',
307
+ :expected => 'expected',
308
+ :fail => 'fail'}, ifs.directories)
309
+ end
310
+ end
311
+
@@ -0,0 +1,115 @@
1
+ require File.join(File.dirname(__FILE__), '../tap_test_helper.rb')
2
+ require 'tap/test/subset_methods'
3
+
4
+ class SubsetMethodsTest < Test::Unit::TestCase
5
+ include Tap::Test::SubsetMethods
6
+
7
+ # TODO - replace ENV[] with env() throughout,
8
+ # and override env() so ENV is absolutely NOT
9
+ # modified
10
+
11
+ def setup
12
+ @env_hold = {}
13
+ ENV.each_pair do |key, value|
14
+ @env_hold[key] = value
15
+ end
16
+ ENV.clear
17
+ end
18
+
19
+ def teardown
20
+ ENV.clear
21
+ @env_hold.each_pair do |key, value|
22
+ ENV[key] = value
23
+ end
24
+ end
25
+
26
+ #
27
+ # env_true test
28
+ #
29
+
30
+ def test_env_true_is_true_if_var_is_true
31
+ assert !ENV['type']
32
+ assert !env_true?('type')
33
+
34
+ ENV['type'] = "false"
35
+ assert !env_true?('type')
36
+
37
+ ENV['type'] = "true"
38
+ assert env_true?('type')
39
+
40
+ ENV['type'] = "True"
41
+ assert env_true?('type')
42
+
43
+ ENV['type'] = "TRUE"
44
+ assert env_true?('type')
45
+ end
46
+
47
+ #
48
+ # run subset test
49
+ #
50
+
51
+ def test_run_subset_true_if_ENV_type_is_true
52
+ assert_equal false, run_subset?('type')
53
+
54
+ ENV['type'] = "true"
55
+ assert_equal true, run_subset?('type')
56
+ end
57
+
58
+ def test_run_subset_true_if_ENV_all_is_true
59
+ ENV['ALL'] = "true"
60
+ assert_equal true, run_subset?('type')
61
+ end
62
+
63
+ #
64
+ # match_regexp test
65
+ #
66
+
67
+ def test_match_regexp
68
+ [1, [1,2]].each do |input|
69
+ ENV['type'] = "1"
70
+ assert_equal true, match_regexp?('type', input)
71
+ end
72
+
73
+ ["one", ["one", "two"]].each do |input|
74
+ ENV['type'] = "one"
75
+ assert_equal true, match_regexp?('type', input)
76
+ end
77
+ end
78
+
79
+ def test_match_regexp_returns_true_if_ENV_all_true
80
+ ENV['ALL'] = "true"
81
+ assert_equal true, match_regexp?('type', 1, "default")
82
+ end
83
+
84
+ def test_match_regexp_returns_default_unless_ENV_type_is_set
85
+ assert_equal "default", match_regexp?('type', 1, "default")
86
+ end
87
+
88
+ #
89
+ # calling method tests
90
+ #
91
+
92
+ def nested_call_to_calling_method
93
+ assert_equal 'test_calling_method', calling_method
94
+ end
95
+
96
+ def nested_call_with_alternate_pattern
97
+ assert_equal 'nested_call_with_alternate_pattern', calling_method(/^nested/)
98
+ end
99
+
100
+ def test_calling_method
101
+ assert_equal 'test_calling_method', calling_method
102
+ nested_call_to_calling_method
103
+ nested_call_with_alternate_pattern
104
+ end
105
+
106
+ #
107
+ # singleline pp test
108
+ #
109
+
110
+ def test_format_pps_input
111
+ [1, "two", [:three, 4]].each do |input|
112
+ assert_equal PP.singleline_pp(input, ''), spp(input)
113
+ end
114
+ end
115
+ end