1use strict; 2use warnings; 3 4use Test2::IPC; 5use Test2::Tools::Tiny; 6use Test2::API qw/context test2_ipc_drivers/; 7use Test2::Util qw/CAN_FORK CAN_THREAD CAN_REALLY_FORK/; 8 9{ 10 package My::Formatter; 11 12 sub new { bless [], shift }; 13 14 my $check = 1; 15 sub write { 16 my $self = shift; 17 my ($e, $count) = @_; 18 push @$self => $e; 19 } 20} 21 22{ 23 package My::Event; 24 25 use base 'Test2::Event'; 26 use Test2::Util::HashBase qw{msg}; 27} 28 29tests basic => sub { 30 my $hub = Test2::Hub->new( 31 formatter => My::Formatter->new, 32 ); 33 34 my $send_event = sub { 35 my ($msg) = @_; 36 my $e = My::Event->new(msg => $msg, trace => Test2::EventFacet::Trace->new(frame => ['fake', 'fake.t', 1])); 37 $hub->send($e); 38 }; 39 40 ok(my $e1 = $send_event->('foo'), "Created event"); 41 ok(my $e2 = $send_event->('bar'), "Created event"); 42 ok(my $e3 = $send_event->('baz'), "Created event"); 43 44 my $old = $hub->format(My::Formatter->new); 45 46 ok($old->isa('My::Formatter'), "old formatter"); 47 is_deeply( 48 $old, 49 [$e1, $e2, $e3], 50 "Formatter got all events" 51 ); 52}; 53 54tests follow_ups => sub { 55 my $hub = Test2::Hub->new; 56 $hub->set_count(1); 57 58 my $trace = Test2::EventFacet::Trace->new( 59 frame => [__PACKAGE__, __FILE__, __LINE__], 60 ); 61 62 my $ran = 0; 63 $hub->follow_up(sub { 64 my ($d, $h) = @_; 65 is_deeply($d, $trace, "Got trace"); 66 is_deeply($h, $hub, "Got hub"); 67 ok(!$hub->ended, "Hub state has not ended yet"); 68 $ran++; 69 }); 70 71 like( 72 exception { $hub->follow_up('xxx') }, 73 qr/follow_up only takes coderefs for arguments, got 'xxx'/, 74 "follow_up takes a coderef" 75 ); 76 77 $hub->finalize($trace); 78 79 is($ran, 1, "ran once"); 80 81 is_deeply( 82 $hub->ended, 83 $trace->frame, 84 "Ended at the expected place." 85 ); 86 87 eval { $hub->finalize($trace) }; 88 89 is($ran, 1, "ran once"); 90 91 $hub = undef; 92}; 93 94tests IPC => sub { 95 my ($driver) = test2_ipc_drivers(); 96 is($driver, 'Test2::IPC::Driver::Files', "Default Driver"); 97 my $ipc = $driver->new; 98 my $hub = Test2::Hub->new( 99 formatter => My::Formatter->new, 100 ipc => $ipc, 101 ); 102 103 my $build_event = sub { 104 my ($msg) = @_; 105 return My::Event->new(msg => $msg, trace => Test2::EventFacet::Trace->new(frame => ['fake', 'fake.t', 1])); 106 }; 107 108 my $e1 = $build_event->('foo'); 109 my $e2 = $build_event->('bar'); 110 my $e3 = $build_event->('baz'); 111 112 my $do_send = sub { 113 $hub->send($e1); 114 $hub->send($e2); 115 $hub->send($e3); 116 }; 117 118 my $do_check = sub { 119 my $name = shift; 120 121 my $old = $hub->format(My::Formatter->new); 122 123 ok($old->isa('My::Formatter'), "old formatter"); 124 is(@$old, 3, "Formatter got all events ($name)"); 125 ok($_->{hubs}, "Set the hubs") for @$old; 126 }; 127 128 if (CAN_REALLY_FORK) { 129 my $pid = fork(); 130 die "Could not fork!" unless defined $pid; 131 132 if ($pid) { 133 is(waitpid($pid, 0), $pid, "waited properly"); 134 ok(!$?, "child exited with success"); 135 $hub->cull(); 136 $do_check->('Fork'); 137 } 138 else { 139 $do_send->(); 140 exit 0; 141 } 142 } 143 144 if (CAN_THREAD && $] ge '5.010') { 145 require threads; 146 my $thr = threads->new(sub { $do_send->() }); 147 $thr->join; 148 $hub->cull(); 149 $do_check->('Threads'); 150 } 151 152 $do_send->(); 153 $hub->cull(); 154 $do_check->('no IPC'); 155}; 156 157tests listen => sub { 158 my $hub = Test2::Hub->new(); 159 160 my @events; 161 my @counts; 162 my $it = $hub->listen(sub { 163 my ($h, $e, $count) = @_; 164 is_deeply($h, $hub, "got hub"); 165 push @events => $e; 166 push @counts => $count; 167 }); 168 169 my $second; 170 my $it2 = $hub->listen(sub { $second++ }); 171 172 my $ok1 = Test2::Event::Ok->new( 173 pass => 1, 174 name => 'foo', 175 trace => Test2::EventFacet::Trace->new( 176 frame => [ __PACKAGE__, __FILE__, __LINE__ ], 177 ), 178 ); 179 180 my $ok2 = Test2::Event::Ok->new( 181 pass => 0, 182 name => 'bar', 183 trace => Test2::EventFacet::Trace->new( 184 frame => [ __PACKAGE__, __FILE__, __LINE__ ], 185 ), 186 ); 187 188 my $ok3 = Test2::Event::Ok->new( 189 pass => 1, 190 name => 'baz', 191 trace => Test2::EventFacet::Trace->new( 192 frame => [ __PACKAGE__, __FILE__, __LINE__ ], 193 ), 194 ); 195 196 $hub->send($ok1); 197 $hub->send($ok2); 198 199 $hub->unlisten($it); 200 201 $hub->send($ok3); 202 203 is_deeply(\@counts, [1, 2], "Got counts"); 204 is_deeply(\@events, [$ok1, $ok2], "got events"); 205 is($second, 3, "got all events in listener that was not removed"); 206 207 like( 208 exception { $hub->listen('xxx') }, 209 qr/listen only takes coderefs for arguments, got 'xxx'/, 210 "listen takes a coderef" 211 ); 212}; 213 214tests metadata => sub { 215 my $hub = Test2::Hub->new(); 216 217 my $default = { foo => 1 }; 218 my $meta = $hub->meta('Foo', $default); 219 is_deeply($meta, $default, "Set Meta"); 220 221 $meta = $hub->meta('Foo', {}); 222 is_deeply($meta, $default, "Same Meta"); 223 224 $hub->delete_meta('Foo'); 225 is($hub->meta('Foo'), undef, "No Meta"); 226 227 $hub->meta('Foo', {})->{xxx} = 1; 228 is($hub->meta('Foo')->{xxx}, 1, "Vivified meta and set it"); 229 230 like( 231 exception { $hub->meta(undef) }, 232 qr/Invalid META key: undef, keys must be true, and may not be references/, 233 "Cannot use undef as a meta key" 234 ); 235 236 like( 237 exception { $hub->meta(0) }, 238 qr/Invalid META key: '0', keys must be true, and may not be references/, 239 "Cannot use 0 as a meta key" 240 ); 241 242 like( 243 exception { $hub->delete_meta(undef) }, 244 qr/Invalid META key: undef, keys must be true, and may not be references/, 245 "Cannot use undef as a meta key" 246 ); 247 248 like( 249 exception { $hub->delete_meta(0) }, 250 qr/Invalid META key: '0', keys must be true, and may not be references/, 251 "Cannot use 0 as a meta key" 252 ); 253}; 254 255tests filter => sub { 256 my $hub = Test2::Hub->new(); 257 258 my @events; 259 my $it = $hub->filter(sub { 260 my ($h, $e) = @_; 261 is($h, $hub, "got hub"); 262 push @events => $e; 263 return $e; 264 }); 265 266 my $count; 267 my $it2 = $hub->filter(sub { $count++; $_[1] }); 268 269 my $ok1 = Test2::Event::Ok->new( 270 pass => 1, 271 name => 'foo', 272 trace => Test2::EventFacet::Trace->new( 273 frame => [ __PACKAGE__, __FILE__, __LINE__ ], 274 ), 275 ); 276 277 my $ok2 = Test2::Event::Ok->new( 278 pass => 0, 279 name => 'bar', 280 trace => Test2::EventFacet::Trace->new( 281 frame => [ __PACKAGE__, __FILE__, __LINE__ ], 282 ), 283 ); 284 285 my $ok3 = Test2::Event::Ok->new( 286 pass => 1, 287 name => 'baz', 288 trace => Test2::EventFacet::Trace->new( 289 frame => [ __PACKAGE__, __FILE__, __LINE__ ], 290 ), 291 ); 292 293 $hub->send($ok1); 294 $hub->send($ok2); 295 296 $hub->unfilter($it); 297 298 $hub->send($ok3); 299 300 is_deeply(\@events, [$ok1, $ok2], "got events"); 301 is($count, 3, "got all events, even after other filter was removed"); 302 303 $hub = Test2::Hub->new(); 304 @events = (); 305 306 $hub->filter(sub { undef }); 307 $hub->listen(sub { 308 my ($hub, $e) = @_; 309 push @events => $e; 310 }); 311 312 $hub->send($ok1); 313 $hub->send($ok2); 314 $hub->send($ok3); 315 316 ok(!@events, "Blocked events"); 317 318 like( 319 exception { $hub->filter('xxx') }, 320 qr/filter only takes coderefs for arguments, got 'xxx'/, 321 "filter takes a coderef" 322 ); 323}; 324 325tests pre_filter => sub { 326 my $hub = Test2::Hub->new(); 327 328 my @events; 329 my $it = $hub->pre_filter(sub { 330 my ($h, $e) = @_; 331 is($h, $hub, "got hub"); 332 push @events => $e; 333 return $e; 334 }); 335 336 my $count; 337 my $it2 = $hub->pre_filter(sub { $count++; $_[1] }); 338 339 my $ok1 = Test2::Event::Ok->new( 340 pass => 1, 341 name => 'foo', 342 trace => Test2::EventFacet::Trace->new( 343 frame => [ __PACKAGE__, __FILE__, __LINE__ ], 344 ), 345 ); 346 347 my $ok2 = Test2::Event::Ok->new( 348 pass => 0, 349 name => 'bar', 350 trace => Test2::EventFacet::Trace->new( 351 frame => [ __PACKAGE__, __FILE__, __LINE__ ], 352 ), 353 ); 354 355 my $ok3 = Test2::Event::Ok->new( 356 pass => 1, 357 name => 'baz', 358 trace => Test2::EventFacet::Trace->new( 359 frame => [ __PACKAGE__, __FILE__, __LINE__ ], 360 ), 361 ); 362 363 $hub->send($ok1); 364 $hub->send($ok2); 365 366 $hub->pre_unfilter($it); 367 368 $hub->send($ok3); 369 370 is_deeply(\@events, [$ok1, $ok2], "got events"); 371 is($count, 3, "got all events, even after other pre_filter was removed"); 372 373 $hub = Test2::Hub->new(); 374 @events = (); 375 376 $hub->pre_filter(sub { undef }); 377 $hub->listen(sub { 378 my ($hub, $e) = @_; 379 push @events => $e; 380 }); 381 382 $hub->send($ok1); 383 $hub->send($ok2); 384 $hub->send($ok3); 385 386 ok(!@events, "Blocked events"); 387 388 like( 389 exception { $hub->pre_filter('xxx') }, 390 qr/pre_filter only takes coderefs for arguments, got 'xxx'/, 391 "pre_filter takes a coderef" 392 ); 393}; 394 395tests state => sub { 396 my $hub = Test2::Hub->new; 397 398 is($hub->count, 0, "count starts at 0"); 399 is($hub->failed, 0, "failed starts at 0"); 400 is($hub->is_passing, 1, "start off passing"); 401 is($hub->plan, undef, "no plan yet"); 402 403 $hub->is_passing(0); 404 is($hub->is_passing, 0, "Can Fail"); 405 406 $hub->is_passing(1); 407 is($hub->is_passing, 1, "Passes again"); 408 409 $hub->set_count(1); 410 is($hub->count, 1, "Added a passing result"); 411 is($hub->failed, 0, "still no fails"); 412 is($hub->is_passing, 1, "Still passing"); 413 414 $hub->set_count(2); 415 $hub->set_failed(1); 416 is($hub->count, 2, "Added a result"); 417 is($hub->failed, 1, "new failure"); 418 is($hub->is_passing, 0, "Not passing"); 419 420 $hub->is_passing(1); 421 is($hub->is_passing, 0, "is_passing always false after a failure"); 422 423 $hub->set_failed(0); 424 $hub->is_passing(1); 425 is($hub->is_passing, 1, "Passes again"); 426 427 $hub->set_failed(1); 428 is($hub->count, 2, "No new result"); 429 is($hub->failed, 1, "new failure"); 430 is($hub->is_passing, 0, "Not passing"); 431 432 ok(!eval { $hub->plan('foo'); 1 }, "Could not set plan to 'foo'"); 433 like($@, qr/'foo' is not a valid plan! Plan must be an integer greater than 0, 'NO PLAN', or 'SKIP'/, "Got expected error"); 434 435 ok($hub->plan(5), "Can set plan to integer"); 436 is($hub->plan, 5, "Set the plan to an integer"); 437 438 $hub->set__plan(undef); 439 ok($hub->plan('NO PLAN'), "Can set plan to 'NO PLAN'"); 440 is($hub->plan, 'NO PLAN', "Set the plan to 'NO PLAN'"); 441 442 $hub->set__plan(undef); 443 ok($hub->plan('SKIP'), "Can set plan to 'SKIP'"); 444 is($hub->plan, 'SKIP', "Set the plan to 'SKIP'"); 445 446 ok(!eval { $hub->plan(5); 1 }, "Cannot change plan"); 447 like($@, qr/You cannot change the plan/, "Got error"); 448 449 my $trace = Test2::EventFacet::Trace->new(frame => ['Foo::Bar', 'foo.t', 42, 'blah']); 450 $hub->finalize($trace); 451 my $ok = eval { $hub->finalize($trace) }; 452 my $err = $@; 453 ok(!$ok, "died"); 454 455 is($err, <<" EOT", "Got expected error"); 456Test already ended! 457First End: foo.t line 42 458Second End: foo.t line 42 459 EOT 460 461 $hub = Test2::Hub->new; 462 463 $hub->plan(5); 464 $hub->set_count(5); 465 $hub->set_failed(1); 466 $hub->set_ended($trace); 467 $hub->set_bailed_out("foo"); 468 $hub->set_skip_reason('xxx'); 469 ok(!$hub->is_passing, "not passing"); 470 471 $hub->reset_state; 472 473 ok(!$hub->plan, "no plan"); 474 is($hub->count, 0, "count reset to 0"); 475 is($hub->failed, 0, "reset failures"); 476 ok(!$hub->ended, "not ended"); 477 ok(!$hub->bailed_out, "did not bail out"); 478 ok(!$hub->skip_reason, "no skip reason"); 479}; 480 481done_testing; 482