not and action infrastructure
[scpubgit/DKit.git] / t / basic.t
CommitLineData
60cda014 1use strictures 1;
2use Test::More;
3use aliased 'DX::Op::FromCode';
4use aliased 'DX::ArrayStream';
94565614 5use DX::ResultStream;
60cda014 6use DX::Var;
7use DX::State;
94565614 8use Test::Exception;
60cda014 9
10my @servers = qw(
11 kitty.scsys.co.uk
12 jim.example.com
13 joe.example.com
14 pryde.scsys.co.uk
15 bob.example.com
16);
17
18my @shells = qw(csh bash);
19
20my %shells = (
21 bash => { map +($_ => 1),
46894e63 22 qw(joe.example.com kitty.scsys.co.uk) },
60cda014 23 csh => { map +($_ => 1),
24 qw(jim.example.com joe.example.com bob.example.com) },
25);
26
27sub bind_array {
28 my ($var, $array) = @_;
29 sub {
30 my ($self, $state) = @_;
31 $state->bind_stream_then(
32 $state->scope_var($var),
33 ArrayStream->from_array(@$array),
34 $self->next
35 )
36 }
37}
38
5622b4df 39sub test_values {
40 my ($vars, $test) = @_;
41 sub {
42 my ($self, $state) = @_;
43 my @values = map $state->scope_var($_)->bound_value, @$vars;
44 if ($test->(@values)) {
45 return $state->then($self->next);
46 }
47 return $state->backtrack;
48 }
49}
50
54817920 51sub make_op {
52 my ($inner) = @_;
53 FromCode->new(
54 code => bind_array(S => \@servers),
55 next => FromCode->new(
56 code => test_values([ 'S' ], sub { $_[0] =~ /\.example\.com$/ }),
57 next => $inner,
58 )
59 );
60}
61
62my $op = make_op;
60cda014 63
5622b4df 64sub make_state {
65 my ($vars, $op) = @_;
60cda014 66
5622b4df 67 my %scope = map +($_ => $_), @{$vars};
68 my %by_id = map +($_ => DX::Var->new(id => $_)), @{$vars};
69
70 DX::State->new(
71 next_op => $op,
72 return_stack => [],
73 by_id => \%by_id,
74 scope => \%scope,
75 last_choice => []
76 );
77}
60cda014 78
5622b4df 79my $stream = DX::ResultStream->new(for_state => make_state([ 'S' ], $op));
94565614 80
71217e42 81is($stream->next->{'S'}->bound_value, $_)
94565614 82 for qw(jim.example.com joe.example.com bob.example.com);
60cda014 83
5622b4df 84is($stream->next, undef, 'No more');
85
5622b4df 86my $complex_op = FromCode->new(
87 code => bind_array(S => \@servers),
88 next => FromCode->new(
89 code => test_values([ 'S' ], sub { $_[0] =~ /\.example\.com$/ }),
90 next => FromCode->new(
91 code => bind_array(P => \@shells),
92 next => FromCode->new(
93 code => test_values([ qw(S P) ], sub { $shells{$_[1]}{$_[0]} }),
94 )
95 )
96 )
97);
98
71217e42 99sub bound_values {
100 map {
101 my $v = $_;
102 +{
103 map +($_ => $v->{$_}->bound_value), keys %$v
104 }
105 } @_
106}
107
5622b4df 108my $cstream = DX::ResultStream->new(
109 for_state => make_state([ qw(S P) ], $complex_op)
110);
111
26300a7d 112is_deeply(
71217e42 113 [ bound_values $cstream->results ],
26300a7d 114 [
115 { P => 'csh', S => 'jim.example.com' },
116 { P => 'csh', S => 'joe.example.com' },
117 { P => 'bash', S => 'joe.example.com' },
118 { P => 'csh', S => 'bob.example.com' },
119 ],
120 'Complex stream'
121);
60cda014 122
46894e63 123my $pop_stack = FromCode->new(
b40d5c51 124 code => sub { $_[1]->pop_return_stack }
46894e63 125);
126
127my $inner_op = make_op($pop_stack);
54817920 128
71d26209 129my $call_op = FromCode->new(
130 code => sub {
131 my ($self, $state) = @_;
71d26209 132 my $save_scope = $state->scope;
133 my %scope = (S => $save_scope->{S});
134 my $ret_op = FromCode->new(
03079510 135 code => sub { $_[1]->but(scope => $save_scope, next_op => $_[0]->next) },
71d26209 136 next => $self->next,
137 );
12face77 138 $state->but(scope => \%scope)->push_return_then($ret_op, $inner_op);
71d26209 139 },
140 next => FromCode->new(
141 code => bind_array(P => \@shells),
142 next => FromCode->new(
143 code => test_values([ qw(S P) ], sub { $shells{$_[1]}{$_[0]} }),
144 )
145 )
146);
147
148my $callstream = DX::ResultStream->new(
149 for_state => make_state([ qw(S P) ], $call_op)
150);
151
152is_deeply(
71217e42 153 [ bound_values $callstream->results ],
71d26209 154 [
155 { P => 'csh', S => 'jim.example.com' },
156 { P => 'csh', S => 'joe.example.com' },
157 { P => 'bash', S => 'joe.example.com' },
158 { P => 'csh', S => 'bob.example.com' },
159 ],
160 'Call stream'
161);
162
46894e63 163my $has_csh = FromCode->new(
164 code => test_values([ 'S' ], sub { $shells{csh}{$_[0]} }),
165 next => $pop_stack
166);
167my $has_bash = FromCode->new(
168 code => test_values([ 'S' ], sub { $shells{bash}{$_[0]} }),
169 next => $pop_stack
170);
171
172my $or_code = sub {
173 my ($self, $state) = @_;
174 my $var = DX::Var->new(id => 'OR')->with_stream(
175 my $stream = ArrayStream->from_array($has_csh, $has_bash)
176 );
177 my $inner_or = FromCode->new(
178 code => sub { $_[1]->then($var->bound_value) }
179 );
12face77 180 $state->push_return_then($self->next, $inner_or)
181 ->mark_choice($var);
46894e63 182};
183
184my $top_or = FromCode->new(
185 code => bind_array(S => \@servers),
186 next => FromCode->new(code => $or_code),
187);
188
189my $orstream = DX::ResultStream->new(
190 for_state => make_state([ qw(S) ], $top_or)
191);
192
193is_deeply(
71217e42 194 [ bound_values $orstream->results ],
46894e63 195 [
196 {
197 S => "kitty.scsys.co.uk"
198 },
199 {
200 S => "jim.example.com"
201 },
202 {
203 S => "joe.example.com"
204 },
205 {
206 S => "joe.example.com"
207 },
208 {
209 S => "bob.example.com"
210 }
211 ],
212 'Or stream'
213);
214
215my $top_or_2 = FromCode->new(
216 code => bind_array(S => \@servers),
217 next => FromCode->new(
218 code => $or_code,
219 next => FromCode->new(
220 code => test_values([ 'S' ], sub { $_[0] =~ /\.example\.com$/ }),
221 ),
222 ),
223);
224
225my $orstream_2 = DX::ResultStream->new(
226 for_state => make_state([ qw(S) ], $top_or_2)
227);
228
229is_deeply(
71217e42 230 [ bound_values $orstream_2->results ],
46894e63 231 [
232 {
233 S => "jim.example.com"
234 },
235 {
236 S => "joe.example.com"
237 },
238 {
239 S => "joe.example.com"
240 },
241 {
242 S => "bob.example.com"
243 }
244 ],
245 'Or stream'
246);
247
60cda014 248done_testing;