baf 0.13.0 → 0.15.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/lib/baf/cli.rb +11 -8
- data/lib/baf/env.rb +11 -3
- data/lib/baf/option.rb +14 -14
- data/lib/baf/options_registrant.rb +2 -2
- data/lib/baf/testing/cucumber/steps/execution.rb +21 -36
- data/lib/baf/testing/cucumber/steps/filesystem.rb +3 -0
- data/lib/baf/testing/cucumber/steps/input.rb +1 -1
- data/lib/baf/testing/cucumber/steps/output.rb +14 -23
- data/lib/baf/testing/cucumber/steps/output_wait.rb +5 -46
- data/lib/baf/testing/cucumber.rb +9 -3
- data/lib/baf/testing/process.rb +107 -0
- data/lib/baf/testing.rb +138 -0
- data/lib/baf.rb +1 -1
- metadata +90 -16
- data/lib/baf/version.rb +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c3dd2c36e21fb8a41d21f0d3ea90c5126274b9f68f19f4b2cb0f09c33aa38605
|
4
|
+
data.tar.gz: 9cf8f0b59d4b8cbb70f174912f2569f204fd27a82236a8e992c6729b366cc574
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bcc9f8fb5658bb47c75067a09912585878ee73ab227c09628eb4d2742e3f360a8c2f55b3effa1e2acc1329f130046f8ec76caefa66d6c468fbdce46596ba6370
|
7
|
+
data.tar.gz: 8aa8f06dee9cb55a12e560177c80a44f724c4fab30b5a5e9de9764f7fdd4eb12d0e9e76ebdedbad4fdd5fbb7cb58ea101fb3402d68d56fd191a0297b0dd198bb
|
data/lib/baf/cli.rb
CHANGED
@@ -10,7 +10,7 @@ module Baf
|
|
10
10
|
class CLI
|
11
11
|
ArgumentError = Class.new ::Baf::ArgumentError
|
12
12
|
|
13
|
-
EX_USAGE
|
13
|
+
EX_USAGE = 64
|
14
14
|
EX_SOFTWARE = 70
|
15
15
|
|
16
16
|
class << self
|
@@ -43,15 +43,17 @@ module Baf
|
|
43
43
|
parent = Object.const_get parent_name
|
44
44
|
parent.const_defined?(:Env) ? parent.const_get(:Env) : Env
|
45
45
|
end
|
46
|
+
|
47
|
+
def ruby2_keywords *; end unless Module.respond_to? :ruby2_keywords, true
|
46
48
|
end
|
47
49
|
|
48
50
|
attr_reader :arguments, :env, :parser
|
49
51
|
|
50
52
|
def initialize env, arguments, **opts
|
51
|
-
@env
|
52
|
-
@arguments
|
53
|
-
@parser
|
54
|
-
@registrant = opts
|
53
|
+
@env = env
|
54
|
+
@arguments = arguments
|
55
|
+
@parser = opts.fetch(:parser) { OptionParser.new }
|
56
|
+
@registrant = opts.fetch(:registrant) { OptionsRegistrant.new }
|
55
57
|
|
56
58
|
registrant.register(env, parser) { setup }
|
57
59
|
end
|
@@ -63,6 +65,7 @@ module Baf
|
|
63
65
|
registrant.banner = arg
|
64
66
|
end
|
65
67
|
|
68
|
+
ruby2_keywords \
|
66
69
|
def flag *args
|
67
70
|
registrant.flag *args
|
68
71
|
end
|
@@ -80,15 +83,15 @@ module Baf
|
|
80
83
|
tail: true
|
81
84
|
end
|
82
85
|
|
83
|
-
def option *args
|
84
|
-
args = [*args,
|
86
|
+
def option *args, &block
|
87
|
+
args = [*args, block] if block_given?
|
85
88
|
registrant.option *args
|
86
89
|
end
|
87
90
|
|
88
91
|
def parse_arguments!
|
89
92
|
parser.parse! arguments
|
90
93
|
rescue OptionParser::InvalidOption
|
91
|
-
|
94
|
+
fail ArgumentError, parser
|
92
95
|
end
|
93
96
|
|
94
97
|
def usage!
|
data/lib/baf/env.rb
CHANGED
@@ -7,10 +7,18 @@ module Baf
|
|
7
7
|
def_delegators :@output, :print, :puts
|
8
8
|
def_delegator :@output_error, :puts, :puts_error
|
9
9
|
|
10
|
-
def initialize input: $
|
11
|
-
@input
|
12
|
-
@output
|
10
|
+
def initialize input: $stdin, output: $stdout, output_error: $stderr
|
11
|
+
@input = input
|
12
|
+
@output = output
|
13
13
|
@output_error = output_error
|
14
14
|
end
|
15
|
+
|
16
|
+
def sync_output
|
17
|
+
output.sync = true
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :output
|
15
23
|
end
|
16
24
|
end
|
data/lib/baf/option.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
module Baf
|
2
2
|
class Option
|
3
|
-
LONG_PREFIX
|
4
|
-
LONG_NORMALIZE_SEARCH
|
5
|
-
LONG_NORMALIZE_REPLACE
|
6
|
-
LONG_WITH_ARG_GLUE
|
7
|
-
PARSER_MESSAGE
|
8
|
-
PARSER_MESSAGE_TAIL
|
3
|
+
LONG_PREFIX = '--'.freeze
|
4
|
+
LONG_NORMALIZE_SEARCH = ?_.freeze
|
5
|
+
LONG_NORMALIZE_REPLACE = ?-.freeze
|
6
|
+
LONG_WITH_ARG_GLUE = ' '.freeze
|
7
|
+
PARSER_MESSAGE = :on
|
8
|
+
PARSER_MESSAGE_TAIL = :on_tail
|
9
9
|
|
10
10
|
attr_accessor :short, :long, :arg, :desc, :block, :tail
|
11
11
|
|
@@ -41,20 +41,20 @@ module Baf
|
|
41
41
|
|
42
42
|
def build_attrs short, long, arg_or_desc = nil, desc_or_block = nil, block = nil
|
43
43
|
{
|
44
|
-
short:
|
45
|
-
long:
|
46
|
-
desc:
|
44
|
+
short: short,
|
45
|
+
long: long,
|
46
|
+
desc: arg_or_desc
|
47
47
|
}.merge case desc_or_block
|
48
48
|
when Proc
|
49
49
|
{
|
50
|
-
desc:
|
51
|
-
block:
|
50
|
+
desc: arg_or_desc,
|
51
|
+
block: desc_or_block
|
52
52
|
}
|
53
53
|
when String
|
54
54
|
{
|
55
|
-
arg:
|
56
|
-
desc:
|
57
|
-
block:
|
55
|
+
arg: arg_or_desc,
|
56
|
+
desc: desc_or_block,
|
57
|
+
block: block
|
58
58
|
}
|
59
59
|
else
|
60
60
|
{}
|
@@ -1,50 +1,35 @@
|
|
1
|
-
def
|
2
|
-
cmd
|
3
|
-
cmd
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
else
|
8
|
-
run cmd.join ' '
|
9
|
-
end
|
10
|
-
program_run_check if check
|
11
|
-
end
|
12
|
-
|
13
|
-
def program_run_check status: 0
|
14
|
-
expect(last_command_started).to have_exit_status status
|
15
|
-
rescue RSpec::Expectations::ExpectationNotMetError => e
|
16
|
-
if ENV.key? 'BAF_TEST_DEBUG'
|
17
|
-
fail RSpec::Expectations::ExpectationNotMetError, <<-eoh
|
18
|
-
#{e.message} Output was:
|
19
|
-
```\n#{last_command_started.output.lines.map { |l| " #{l}" }.join} ```
|
20
|
-
eoh
|
21
|
-
else
|
22
|
-
raise
|
23
|
-
end
|
1
|
+
def run state, cmd: nil, wait: true, args: []
|
2
|
+
cmd ||= state[:program]
|
3
|
+
Baf::Testing.run cmd + args,
|
4
|
+
wait: wait,
|
5
|
+
env_allow: state.fetch(:env_allow) { [] },
|
6
|
+
timeout: state[:exec_timeout]
|
24
7
|
end
|
25
8
|
|
26
9
|
|
27
10
|
When /^I( successfully)? (run|\w+) the program$/ do |check, run|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
When /^I( successfully)? (run|\w+) the program with arguments? (.+)$/ do |check, run, args|
|
32
|
-
program_run check: !!check, args: args, wait: run == 'run'
|
11
|
+
$_baf[:process] = run $_baf, wait: run == 'run'
|
12
|
+
Baf::Testing.expect_ex $_baf[:process], 0 if check
|
33
13
|
end
|
34
14
|
|
35
|
-
When
|
36
|
-
|
15
|
+
When(
|
16
|
+
/^I( successfully)? (run|\w+) the program with (?:argument|command|option)s? (.+)$/
|
17
|
+
) do |check, run, args|
|
18
|
+
$_baf[:process] = run $_baf, wait: run == 'run', args: args.split(' ')
|
19
|
+
Baf::Testing.expect_ex $_baf[:process], 0 if check
|
37
20
|
end
|
38
21
|
|
39
|
-
When /^I( successfully)? (run|\w+)
|
40
|
-
|
22
|
+
When /^I( successfully)? (run|\w+) `([^`]+)`$/ do |check, run, command|
|
23
|
+
$_baf[:process] = run $_baf, cmd: command.split(' '), wait: run == 'run'
|
24
|
+
Baf::Testing.expect_ex $_baf[:process], 0 if check
|
41
25
|
end
|
42
26
|
|
43
27
|
|
44
|
-
Then /^the
|
45
|
-
|
28
|
+
Then /^the program must terminate successfully$/ do
|
29
|
+
Baf::Testing.wait $_baf[:process]
|
30
|
+
Baf::Testing.expect_ex $_baf[:process], 0
|
46
31
|
end
|
47
32
|
|
48
|
-
Then /^the
|
49
|
-
|
33
|
+
Then /^the exit status must be (\d+)$/ do |exit_status|
|
34
|
+
Baf::Testing.expect_ex $_baf[:process], Integer(exit_status)
|
50
35
|
end
|
@@ -1,44 +1,35 @@
|
|
1
|
-
def build_regexp pattern, options
|
2
|
-
Regexp.new(pattern, options.each_char.inject(0) do |m, e|
|
3
|
-
m | case e
|
4
|
-
when ?i then Regexp::IGNORECASE
|
5
|
-
when ?m then Regexp::MULTILINE
|
6
|
-
when ?x then Regexp::EXTENDED
|
7
|
-
end
|
8
|
-
end)
|
9
|
-
end
|
10
|
-
|
11
|
-
def expect_output content, stream: :output
|
12
|
-
stream = :stderr if stream == :error
|
13
|
-
expect(last_command_started.send stream).to eq unescape_text content
|
14
|
-
end
|
15
|
-
|
16
|
-
|
17
1
|
Then /^the output must be empty$/ do
|
18
|
-
expect(
|
2
|
+
expect($_baf[:process].output).to be_empty
|
19
3
|
end
|
20
4
|
|
21
5
|
Then /^the output must contain:$/ do |content|
|
22
|
-
expect(
|
6
|
+
expect($_baf[:process].output).to include content
|
23
7
|
end
|
24
8
|
|
25
9
|
Then /^the output must contain "([^"]+)"$/ do |content|
|
26
|
-
expect(
|
10
|
+
expect($_baf[:process].output).to include content
|
27
11
|
end
|
28
12
|
|
29
13
|
Then /^the output must not contain "([^"]+)"$/ do |content|
|
30
|
-
expect(
|
14
|
+
expect($_baf[:process].output).not_to include content
|
31
15
|
end
|
32
16
|
|
33
17
|
Then /^the output must contain exactly:$/ do |content|
|
34
|
-
|
18
|
+
expect($_baf[:process].output).to eq content + $/
|
35
19
|
end
|
36
20
|
|
37
21
|
Then /^the( error)? output must contain exactly "([^"]+)"$/ do |stream, content|
|
38
22
|
stream = stream ? :error : :output
|
39
|
-
|
23
|
+
expect($_baf[:process].output stream)
|
24
|
+
.to eq Baf::Testing::unescape_step_arg content
|
25
|
+
end
|
26
|
+
|
27
|
+
Then /^the output must match:/ do |pattern|
|
28
|
+
expect($_baf[:process].output)
|
29
|
+
.to match Baf::Testing.build_regexp(pattern, 'mx')
|
40
30
|
end
|
41
31
|
|
42
32
|
Then /^the output must match \/([^\/]+)\/([a-z]*)$/ do |pattern, options|
|
43
|
-
expect(
|
33
|
+
expect($_baf[:process].output)
|
34
|
+
.to match Baf::Testing.build_regexp(pattern, options)
|
44
35
|
end
|
@@ -1,57 +1,16 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
module Baf
|
4
|
-
module Testing
|
5
|
-
class WaitError < ::StandardError
|
6
|
-
attr_reader :timeout
|
7
|
-
|
8
|
-
def initialize message, timeout
|
9
|
-
super message
|
10
|
-
@timeout = timeout
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def wait_output! pattern, times: 1, results: nil
|
17
|
-
output = -> { last_command_started.output }
|
18
|
-
wait_until do
|
19
|
-
case pattern
|
20
|
-
when Regexp then (results = output.call.scan(pattern)).size >= times
|
21
|
-
when String then output.call.include? pattern
|
22
|
-
end
|
23
|
-
end
|
24
|
-
results
|
25
|
-
rescue Baf::Testing::WaitError => e
|
26
|
-
fail <<-eoh
|
27
|
-
expected `#{pattern}' not seen after #{e.timeout} seconds in:
|
28
|
-
```\n#{output.call.lines.map { |l| " #{l}" }.join} ```
|
29
|
-
eoh
|
30
|
-
end
|
31
|
-
|
32
|
-
def wait_until message: 'condition not met after %d seconds'
|
33
|
-
timeout = ENV.key?('BAF_TEST_TIMEOUT') ?
|
34
|
-
ENV['BAF_TEST_TIMEOUT'].to_i :
|
35
|
-
2
|
36
|
-
Timeout.timeout timeout do
|
37
|
-
loop do
|
38
|
-
break if yield
|
39
|
-
sleep 0.05
|
40
|
-
end
|
41
|
-
end
|
42
|
-
rescue Timeout::Error
|
43
|
-
raise Baf::Testing::WaitError.new(message % timeout, timeout)
|
1
|
+
def wait_output pattern, output: -> { $_baf[:process].output }, times: 1
|
2
|
+
Baf::Testing.wait_output pattern, stream: output, times: times
|
44
3
|
end
|
45
4
|
|
46
5
|
|
47
6
|
Then /^the output will match \/([^\/]+)\/([a-z]*)$/ do |pattern, options|
|
48
|
-
wait_output
|
7
|
+
wait_output Baf::Testing.build_regexp pattern, options
|
49
8
|
end
|
50
9
|
|
51
10
|
Then /^the output will contain:$/ do |content|
|
52
|
-
wait_output
|
11
|
+
wait_output content + $/
|
53
12
|
end
|
54
13
|
|
55
14
|
Then /^the output will contain "([^"]+)"$/ do |content|
|
56
|
-
wait_output
|
15
|
+
wait_output content
|
57
16
|
end
|
data/lib/baf/testing/cucumber.rb
CHANGED
@@ -1,5 +1,11 @@
|
|
1
|
-
require '
|
2
|
-
require 'aruba/cucumber/hooks'
|
3
|
-
|
1
|
+
require 'baf/testing'
|
4
2
|
require 'baf/testing/cucumber/steps/execution'
|
3
|
+
require 'baf/testing/cucumber/steps/filesystem'
|
5
4
|
require 'baf/testing/cucumber/steps/output'
|
5
|
+
|
6
|
+
$_baf = {}
|
7
|
+
|
8
|
+
Around do |_, block|
|
9
|
+
Baf::Testing.exercise_scenario &block
|
10
|
+
$_baf.delete :process
|
11
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'baf'
|
2
|
+
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
module Baf
|
6
|
+
module Testing
|
7
|
+
class Process
|
8
|
+
ExecutionFailure = Class.new Error
|
9
|
+
|
10
|
+
TIMEOUT = 4
|
11
|
+
TMP_FILE_PREFIX = 'baf_test_'.freeze
|
12
|
+
WAIT_POLL_DELAY = 0.01
|
13
|
+
|
14
|
+
attr_reader :pid, :exit_status, :timeout
|
15
|
+
|
16
|
+
def initialize command, env_allow: [], timeout: TIMEOUT
|
17
|
+
@command = command
|
18
|
+
@env_allow = env_allow
|
19
|
+
@timeout = timeout
|
20
|
+
@stdout = Tempfile.new TMP_FILE_PREFIX
|
21
|
+
@stderr = Tempfile.new TMP_FILE_PREFIX
|
22
|
+
end
|
23
|
+
|
24
|
+
def start
|
25
|
+
reader, writer = IO.pipe
|
26
|
+
|
27
|
+
@pid = spawn env,
|
28
|
+
*@command,
|
29
|
+
unsetenv_others: true,
|
30
|
+
in: reader,
|
31
|
+
out: @stdout,
|
32
|
+
err: @stderr
|
33
|
+
reader.close
|
34
|
+
@stdin = writer
|
35
|
+
rescue Errno::ENOENT => e
|
36
|
+
fail ExecutionFailure, e.message
|
37
|
+
end
|
38
|
+
|
39
|
+
def wait timeout: @timeout
|
40
|
+
deadline = Time.now + timeout
|
41
|
+
wait_poll
|
42
|
+
until stopped? || Time.now >= deadline
|
43
|
+
sleep WAIT_POLL_DELAY
|
44
|
+
wait_poll
|
45
|
+
end
|
46
|
+
yield unless stopped? if block_given?
|
47
|
+
end
|
48
|
+
|
49
|
+
def stop wait_timeout: 1
|
50
|
+
::Process.kill :TERM, @pid
|
51
|
+
wait timeout: wait_timeout
|
52
|
+
return if stopped?
|
53
|
+
::Process.kill :KILL, @pid
|
54
|
+
::Process.wait2 @pid
|
55
|
+
rescue Errno::ECHILD, Errno::ESRCH
|
56
|
+
end
|
57
|
+
|
58
|
+
def running?
|
59
|
+
started? && !stopped?
|
60
|
+
end
|
61
|
+
|
62
|
+
def input str
|
63
|
+
@stdin.write str
|
64
|
+
end
|
65
|
+
|
66
|
+
def output stream = nil
|
67
|
+
case stream
|
68
|
+
when :output then [@stdout]
|
69
|
+
when :error then [@stderr]
|
70
|
+
else [@stdout, @stderr]
|
71
|
+
end.inject '' do |memo, stream|
|
72
|
+
memo + IO.read(stream.path)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def env
|
79
|
+
ENV.inject({}) do |acc, (k, v)|
|
80
|
+
if env_allow? k then acc.merge k => v else acc end
|
81
|
+
end.merge 'HOME' => File.realpath(?.)
|
82
|
+
end
|
83
|
+
|
84
|
+
def env_allow? var
|
85
|
+
@env_allow.any? do |e|
|
86
|
+
case e
|
87
|
+
when String then var == e
|
88
|
+
when Regexp then var =~ e
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def started?
|
94
|
+
!!@pid
|
95
|
+
end
|
96
|
+
|
97
|
+
def stopped?
|
98
|
+
!!@exit_status
|
99
|
+
end
|
100
|
+
|
101
|
+
def wait_poll
|
102
|
+
pid, status = ::Process.wait2 @pid, ::Process::WNOHANG
|
103
|
+
@exit_status = status.exitstatus if pid
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
data/lib/baf/testing.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
require 'baf'
|
4
|
+
require 'baf/testing/process'
|
5
|
+
|
6
|
+
module Baf
|
7
|
+
module Testing
|
8
|
+
ExecutionTimeout = Class.new Error
|
9
|
+
ExitStatusMismatch = Class.new Error
|
10
|
+
|
11
|
+
class WaitError < Error
|
12
|
+
attr_reader :timeout
|
13
|
+
|
14
|
+
def initialize message, timeout
|
15
|
+
super message
|
16
|
+
@timeout = timeout
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
ENV_WHITELIST = [
|
21
|
+
/\ACHRUBY_/,
|
22
|
+
/\AGEM_/,
|
23
|
+
'PATH',
|
24
|
+
/\ARB_/,
|
25
|
+
'RUBYOPT'
|
26
|
+
].freeze
|
27
|
+
|
28
|
+
EXEC_TIMEOUT_ERROR_FMT = 'process did not exit after %.03f seconds'.freeze
|
29
|
+
|
30
|
+
EXIT_STATUS_MISMATCH_FMT = <<~eoh.freeze
|
31
|
+
expected %<expected>d exit status got %<actual>d; output was:
|
32
|
+
%{separator}
|
33
|
+
%{output}
|
34
|
+
%{separator}
|
35
|
+
eoh
|
36
|
+
|
37
|
+
OUTPUT_SEPARATOR = (?- * 70).freeze
|
38
|
+
|
39
|
+
WAIT_MESSAGE_FMT = 'condition not met after %.03f seconds'.freeze
|
40
|
+
WAIT_OUTPUT_MESSAGE_FMT = <<~eoh.freeze
|
41
|
+
expected `%{pattern}' not seen after %<timeout>.03f seconds in:
|
42
|
+
%{separator}
|
43
|
+
%{output}
|
44
|
+
%{separator}
|
45
|
+
eoh
|
46
|
+
WAIT_TIMEOUT = ENV.key?('BAF_TEST_TIMEOUT') ?
|
47
|
+
ENV['BAF_TEST_TIMEOUT'].to_i :
|
48
|
+
2
|
49
|
+
|
50
|
+
WORKING_DIR = 'tmp/uat'.freeze
|
51
|
+
|
52
|
+
class << self
|
53
|
+
def build_regexp pattern, options = ''
|
54
|
+
Regexp.new(pattern, options.each_char.inject(0) do |m, e|
|
55
|
+
m | case e
|
56
|
+
when ?i then Regexp::IGNORECASE
|
57
|
+
when ?m then Regexp::MULTILINE
|
58
|
+
when ?x then Regexp::EXTENDED
|
59
|
+
end
|
60
|
+
end)
|
61
|
+
end
|
62
|
+
|
63
|
+
def exercise_scenario dir: WORKING_DIR
|
64
|
+
FileUtils.remove_entry_secure dir, true
|
65
|
+
FileUtils.mkdir_p dir
|
66
|
+
Dir.chdir dir do
|
67
|
+
yield
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def expect_ex process, exit_status
|
72
|
+
return if process.exit_status == exit_status
|
73
|
+
|
74
|
+
fail ExitStatusMismatch, EXIT_STATUS_MISMATCH_FMT % {
|
75
|
+
expected: exit_status,
|
76
|
+
actual: process.exit_status,
|
77
|
+
separator: OUTPUT_SEPARATOR,
|
78
|
+
output: process.output.chomp
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
def run command, wait: true, env_allow: [], timeout: nil
|
83
|
+
Process.new(
|
84
|
+
command,
|
85
|
+
env_allow: ENV_WHITELIST + env_allow,
|
86
|
+
timeout: timeout || Process::TIMEOUT
|
87
|
+
).tap do |process|
|
88
|
+
process.start
|
89
|
+
wait process if wait
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def wait process
|
94
|
+
process.wait do
|
95
|
+
process.stop
|
96
|
+
fail ExecutionTimeout, EXEC_TIMEOUT_ERROR_FMT % process.timeout
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def unescape_step_arg str
|
101
|
+
str.gsub '\n', "\n"
|
102
|
+
end
|
103
|
+
|
104
|
+
def wait_until message: WAIT_MESSAGE_FMT, timeout: WAIT_TIMEOUT
|
105
|
+
return if yield
|
106
|
+
deadline = Time.now + timeout
|
107
|
+
until Time.now >= deadline
|
108
|
+
return if yield
|
109
|
+
sleep 0.05
|
110
|
+
end
|
111
|
+
fail WaitError.new message % timeout, timeout
|
112
|
+
end
|
113
|
+
|
114
|
+
def wait_output pattern, stream:, times: 1, timeout: WAIT_TIMEOUT
|
115
|
+
results = nil
|
116
|
+
wait_until timeout: timeout do
|
117
|
+
case pattern
|
118
|
+
when Regexp then (results = stream.call.scan(pattern)).size >= times
|
119
|
+
when String then stream.call.include? pattern
|
120
|
+
end
|
121
|
+
end
|
122
|
+
results
|
123
|
+
rescue Baf::Testing::WaitError => e
|
124
|
+
fail Baf::Testing::WaitError.new(WAIT_OUTPUT_MESSAGE_FMT % {
|
125
|
+
pattern: pattern,
|
126
|
+
timeout: timeout,
|
127
|
+
separator: OUTPUT_SEPARATOR,
|
128
|
+
output: stream.call.chomp
|
129
|
+
}, timeout)
|
130
|
+
end
|
131
|
+
|
132
|
+
def write_file path, content
|
133
|
+
FileUtils.mkdir_p File.dirname path
|
134
|
+
IO.write path, content
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
data/lib/baf.rb
CHANGED
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: baf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.15.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thibault Jouan
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-06-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: rake
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
@@ -25,20 +25,93 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: cucumber
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '3.2'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
41
|
-
|
40
|
+
version: '3.2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.11'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.11'
|
55
|
+
description: |
|
56
|
+
baf helps writing an user acceptance test suite with a dedicated library
|
57
|
+
and cucumber steps. It can run and wait for programs in a modified
|
58
|
+
environment, verify the exit status, the output streams and other side
|
59
|
+
effects. It also supports interactive programs and writing to their
|
60
|
+
standard input.
|
61
|
+
|
62
|
+
Then, it provides a DSL to write the CLI:
|
63
|
+
|
64
|
+
require 'baf/cli'
|
65
|
+
|
66
|
+
module MyProgram
|
67
|
+
class CLI < Baf::CLI
|
68
|
+
def setup
|
69
|
+
flag_version '0.1.2'.freeze
|
70
|
+
|
71
|
+
option :c, :config, 'config', 'specify config file' do |path|
|
72
|
+
@config_path = path
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def run
|
77
|
+
usage! unless arguments.any?
|
78
|
+
|
79
|
+
puts 'arguments: %s' % arguments
|
80
|
+
puts 'config: %s' % @config_path if @config_path
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
MyProgram::CLI.run ARGV
|
86
|
+
|
87
|
+
Which behaves this way:
|
88
|
+
|
89
|
+
% ./my_program
|
90
|
+
Usage: my_program [options]
|
91
|
+
|
92
|
+
options:
|
93
|
+
-c, --config config specify config file
|
94
|
+
|
95
|
+
-h, --help print this message
|
96
|
+
-V, --version print version
|
97
|
+
zsh: exit 64 ./my_program
|
98
|
+
|
99
|
+
% ./my_program --wrong-arg
|
100
|
+
Usage: my_program [options]
|
101
|
+
|
102
|
+
options:
|
103
|
+
-c, --config config specify config file
|
104
|
+
|
105
|
+
-h, --help print this message
|
106
|
+
-V, --version print version
|
107
|
+
zsh: exit 64 ./my_program --wrong-arg
|
108
|
+
|
109
|
+
% ./my_program foo
|
110
|
+
arguments ["foo"]
|
111
|
+
|
112
|
+
% ./my_program -c some_file foo
|
113
|
+
arguments ["foo"]
|
114
|
+
config path some_file
|
42
115
|
email: tj@a13.fr
|
43
116
|
executables: []
|
44
117
|
extensions: []
|
@@ -50,17 +123,19 @@ files:
|
|
50
123
|
- lib/baf/flag.rb
|
51
124
|
- lib/baf/option.rb
|
52
125
|
- lib/baf/options_registrant.rb
|
126
|
+
- lib/baf/testing.rb
|
53
127
|
- lib/baf/testing/cucumber.rb
|
54
128
|
- lib/baf/testing/cucumber/steps/execution.rb
|
129
|
+
- lib/baf/testing/cucumber/steps/filesystem.rb
|
55
130
|
- lib/baf/testing/cucumber/steps/input.rb
|
56
131
|
- lib/baf/testing/cucumber/steps/output.rb
|
57
132
|
- lib/baf/testing/cucumber/steps/output_wait.rb
|
58
|
-
- lib/baf/
|
133
|
+
- lib/baf/testing/process.rb
|
59
134
|
homepage: https://rubygems.org/gems/baf
|
60
135
|
licenses:
|
61
136
|
- BSD-3-Clause
|
62
137
|
metadata: {}
|
63
|
-
post_install_message:
|
138
|
+
post_install_message:
|
64
139
|
rdoc_options: []
|
65
140
|
require_paths:
|
66
141
|
- lib
|
@@ -75,9 +150,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
75
150
|
- !ruby/object:Gem::Version
|
76
151
|
version: '0'
|
77
152
|
requirements: []
|
78
|
-
|
79
|
-
|
80
|
-
signing_key:
|
153
|
+
rubygems_version: 3.3.14
|
154
|
+
signing_key:
|
81
155
|
specification_version: 4
|
82
|
-
summary:
|
156
|
+
summary: Toolkit for testing and writing CLI programs
|
83
157
|
test_files: []
|
data/lib/baf/version.rb
DELETED