-
Notifications
You must be signed in to change notification settings - Fork 8
/
helper.cr
132 lines (112 loc) · 3.53 KB
/
helper.cr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
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
132
module Cli::Spec
# Extends modules for testing commands.
module Helper
macro included
extend ::Cli::Spec::Helper
end
# Returns a new Crystal Spec expectation that asserts how a command exits.
#
# ### Parameters
#
# * *output* : An expected output. If nil, the test is omitted.
# * *error* : An expected error output. If nil, the test is omitted.
# * *code* : An expected exit code. If nil, the test is omitted.
def exit_command(output : (String | Regex | Nil) = nil, error : (String | Regex | Nil) = nil, code : Int32? = nil)
Expectation.new(output, error, code)
end
# :nodoc:
class Expectation
alias Message = String | Regex
getter! output : Message?
getter! error : Message?
getter! code : Int32?
def initialize(@output, @error, @code)
end
def match(actual)
case actual
when Exit
match_output(actual) && match_error(actual) && match_code(actual)
else
raise "The actual value must be Cli::Exit but #{actual.class.name}."
end
end
def match_output(actual)
output?.nil? || match_string(actual_output(actual), output)
end
def match_error(actual)
error?.nil? || match_string(actual_error(actual), error)
end
def match_string(actual, expected)
case expected
when Regex
expected =~ actual
else
actual == expected
end
end
def match_code(actual)
code?.nil? || actual.exit_code == code
end
def failure_message(actual)
case actual
when Exit
return failure_message_for_output(actual) unless match_output(actual)
return failure_message_for_error(actual) unless match_error(actual)
return failure_message_for_code(actual) unless match_code(actual)
end
raise "Internal error."
end
def negative_failure_message(actual)
case actual
when Exit
return negative_failure_message_for_output(actual) if match_output(actual)
return negative_failure_message_for_error(actual) if match_error(actual)
return negative_failure_message_for_code(actual) if match_code(actual)
end
raise "Internal error."
end
def failure_message_for_output(actual)
<<-EOS
Unmatched output.
expected:#{indent(output, 4)}
got:#{indent(actual_output(actual), 4)}
EOS
end
def failure_message_for_error(actual)
<<-EOS
Unmatched error output.
expected:#{indent(error, 4)}
got:#{indent(actual_error(actual), 4)}
EOS
end
def failure_message_for_code(actual)
"Unmatched exit code: #{code} is expected but got #{actual.exit_code}."
end
def negative_failure_message_for_output(actual)
<<-EOS
Matched output.#{indent(output, 2)}
EOS
end
def negative_failure_message_for_error(actual)
<<-EOS
Matched error output.#{indent(error, 2)}
EOS
end
def negative_failure_message_for_code(actual)
"Matched exit code: #{code}"
end
def actual_output(actual)
actual.success? ? actual.message : nil
end
def actual_error(actual)
actual.error? ? actual.message : nil
end
def indent(s, spaces)
indent = " " * spaces
s = s.to_s
return "" if s.empty?
"\n" + s.split("\n").map{|i| "#{indent}#{i}"}.join("\n")
end
end
end
end