Move find_co_root into DBICTest::Util
[dbsrgits/DBIx-Class.git] / t / 60core.t
CommitLineData
70350518 1use strict;
be83e9ec 2use warnings;
70350518 3
4use Test::More;
d6df786a 5use Test::Exception;
00f3b1c7 6use Test::Warn;
70350518 7use lib qw(t/lib);
a5a7bb73 8use DBICTest ':DiffSQL';
70350518 9
ae515736 10my $schema = DBICTest->init_schema();
0567538f 11
f9db5527 12my @art = $schema->resultset("Artist")->search({ }, { order_by => 'name DESC'});
0567538f 13
d2f21b37 14is(@art, 3, "Three artists returned");
0567538f 15
16my $art = $art[0];
17
18is($art->name, 'We Are Goth', "Correct order too");
19
20$art->name('We Are In Rehab');
21
22is($art->name, 'We Are In Rehab', "Accessor update ok");
23
6dbea98e 24my %dirty = $art->get_dirty_columns();
d2f21b37 25is(scalar(keys(%dirty)), 1, '1 dirty column');
6dbea98e 26ok(grep($_ eq 'name', keys(%dirty)), 'name is dirty');
27
0567538f 28is($art->get_column("name"), 'We Are In Rehab', 'And via get_column');
29
30ok($art->update, 'Update run');
31
6dbea98e 32my %not_dirty = $art->get_dirty_columns();
d2f21b37 33is(scalar(keys(%not_dirty)), 0, 'Nothing is dirty');
6dbea98e 34
00f3b1c7 35throws_ok ( sub {
6dbea98e 36 my $ret = $art->make_column_dirty('name2');
00f3b1c7 37}, qr/No such column 'name2'/, 'Failed to make non-existent column dirty');
38
6dbea98e 39$art->make_column_dirty('name');
40my %fake_dirty = $art->get_dirty_columns();
d2f21b37 41is(scalar(keys(%fake_dirty)), 1, '1 fake dirty column');
6dbea98e 42ok(grep($_ eq 'name', keys(%fake_dirty)), 'name is fake dirty');
43
de5ce481 44ok($art->update, 'Update run');
45
ae515736 46my $record_jp = $schema->resultset("Artist")->search(undef, { join => 'cds' })->search(undef, { prefetch => 'cds' })->next;
47
48ok($record_jp, "prefetch on same rel okay");
49
50my $record_fn = $schema->resultset("Artist")->search(undef, { join => 'cds' })->search({'cds.cdid' => '1'}, {join => 'artist_undirected_maps'})->next;
51
52ok($record_fn, "funny join is okay");
53
f9db5527 54@art = $schema->resultset("Artist")->search({ name => 'We Are In Rehab' });
0567538f 55
d2f21b37 56is(@art, 1, "Changed artist returned by search");
0567538f 57
d2f21b37 58is($art[0]->artistid, 3,'Correct artist too');
0567538f 59
d6df786a 60lives_ok (sub { $art->delete }, 'Cascading delete on Ordered has_many works' ); # real test in ordered.t
0567538f 61
f9db5527 62@art = $schema->resultset("Artist")->search({ });
0567538f 63
d2f21b37 64is(@art, 2, 'And then there were two');
0567538f 65
63bb9738 66is($art->in_storage, 0, "It knows it's dead");
0567538f 67
de5ce481 68lives_ok { $art->update } 'No changes so update should be OK';
69
d6df786a 70dies_ok ( sub { $art->delete }, "Can't delete twice");
0567538f 71
72is($art->name, 'We Are In Rehab', 'But the object is still live');
73
74$art->insert;
75
76ok($art->in_storage, "Re-created");
77
f9db5527 78@art = $schema->resultset("Artist")->search({ });
0567538f 79
d2f21b37 80is(@art, 3, 'And now there are three again');
0567538f 81
f9db5527 82my $new = $schema->resultset("Artist")->create({ artistid => 4 });
0567538f 83
d2f21b37 84is($new->artistid, 4, 'Create produced record ok');
0567538f 85
f9db5527 86@art = $schema->resultset("Artist")->search({ });
0567538f 87
d2f21b37 88is(@art, 4, "Oh my god! There's four of them!");
0567538f 89
90$new->set_column('name' => 'Man With A Fork');
91
92is($new->name, 'Man With A Fork', 'set_column ok');
93
94$new->discard_changes;
95
96ok(!defined $new->name, 'Discard ok');
97
98$new->name('Man With A Spoon');
99
100$new->update;
101
70350518 102my $new_again = $schema->resultset("Artist")->find(4);
0567538f 103
104is($new_again->name, 'Man With A Spoon', 'Retrieved correctly');
105
9bbd8963 106is($new_again->ID, 'DBICTest::Artist|artist|artistid=4', 'unique object id generated correctly');
1f6715ab 107
8273e845 108# test that store_column is called once for create() for non sequence columns
52c53388 109{
110 ok(my $artist = $schema->resultset('Artist')->create({name => 'store_column test'}));
111 is($artist->name, 'X store_column test'); # used to be 'X X store...'
b236052f 112
a22688ab 113 # call store_column even though the column doesn't seem to be dirty
b236052f 114 $artist->name($artist->name);
a22688ab 115 is($artist->name, 'X X store_column test');
b236052f 116 ok($artist->is_column_changed('name'), 'changed column marked as dirty');
117
52c53388 118 $artist->delete;
119}
120
450e6dbf 121# deprecation of rolled-out search
122warnings_exist {
123 $schema->resultset('Artist')->search_rs(id => 4)
124} qr/\Qsearch( %condition ) is deprecated/, 'Deprecation warning on ->search( %condition )';
125
49ca473e 126# this has been warning for 4 years, killing
127throws_ok {
128 $schema->resultset('Artist')->find(artistid => 4);
129} qr|expects either a column/value hashref, or a list of values corresponding to the columns of the specified unique constraint|;
a87eb971 130
f9db5527 131is($schema->resultset("Artist")->count, 4, 'count ok');
0567538f 132
76cc4546 133# test find on an unresolvable condition
134is(
135 $schema->resultset('Artist')->find({ artistid => [ -and => 1, 2 ]}),
136 undef
137);
138
139
b3e1f1f5 140# test find_or_new
141{
142 my $existing_obj = $schema->resultset('Artist')->find_or_new({
143 artistid => 4,
144 });
145
146 is($existing_obj->name, 'Man With A Spoon', 'find_or_new: found existing artist');
147 ok($existing_obj->in_storage, 'existing artist is in storage');
148
149 my $new_obj = $schema->resultset('Artist')->find_or_new({
150 artistid => 5,
151 name => 'find_or_new',
152 });
153
154 is($new_obj->name, 'find_or_new', 'find_or_new: instantiated a new artist');
63bb9738 155 is($new_obj->in_storage, 0, 'new artist is not in storage');
b3e1f1f5 156}
157
f9db5527 158my $cd = $schema->resultset("CD")->find(1);
076a6864 159my %cols = $cd->get_columns;
160
d2f21b37 161is(keys %cols, 6, 'get_columns number of columns ok');
076a6864 162
163is($cols{title}, 'Spoonful of bees', 'get_columns values ok');
164
165%cols = ( title => 'Forkful of bees', year => 2005);
166$cd->set_columns(\%cols);
167
168is($cd->title, 'Forkful of bees', 'set_columns ok');
169
170is($cd->year, 2005, 'set_columns ok');
171
172$cd->discard_changes;
173
20518cb4 174# check whether ResultSource->columns returns columns in order originally supplied
175my @cd = $schema->source("CD")->columns;
571dced3 176
a1cb5921 177is_deeply( \@cd, [qw/cdid artist title year genreid single_track/], 'column order');
571dced3 178
82a96700 179$cd = $schema->resultset("CD")->search({ title => 'Spoonful of bees' }, { columns => ['title'] })->next;
90f3f5ff 180is($cd->title, 'Spoonful of bees', 'subset of columns returned correctly');
181
02ddfe6e 182$cd = $schema->resultset("CD")->search(undef, { '+columns' => [ { name => 'artist.name' } ], join => [ 'artist' ] })->find(1);
5ac6a044 183
184is($cd->title, 'Spoonful of bees', 'Correct CD returned with include');
185is($cd->get_column('name'), 'Caterwauler McCrae', 'Additional column returned');
186
67ba6646 187# check if new syntax +columns also works for this
53998003 188$cd = $schema->resultset("CD")->search(undef, { '+columns' => [ { name => 'artist.name' } ], join => [ 'artist' ] })->find(1);
67ba6646 189
190is($cd->title, 'Spoonful of bees', 'Correct CD returned with include');
191is($cd->get_column('name'), 'Caterwauler McCrae', 'Additional column returned');
192
193# check if new syntax for +columns select specifiers works for this
194$cd = $schema->resultset("CD")->search(undef, { '+columns' => [ {artist_name => 'artist.name'} ], join => [ 'artist' ] })->find(1);
195
196is($cd->title, 'Spoonful of bees', 'Correct CD returned with include');
197is($cd->get_column('artist_name'), 'Caterwauler McCrae', 'Additional column returned');
5ac6a044 198
82a96700 199# update_or_insert
f9db5527 200$new = $schema->resultset("Track")->new( {
0567538f 201 trackid => 100,
202 cd => 1,
0567538f 203 title => 'Insert or Update',
43556c5d 204 last_updated_on => '1973-07-19 12:01:02'
0567538f 205} );
82a96700 206$new->update_or_insert;
207ok($new->in_storage, 'update_or_insert insert ok');
0567538f 208
00f3b1c7 209throws_ok (sub {
210 $schema->class("Track")->load_components('DoesNotExist');
211}, qr!Can't locate DBIx/Class/DoesNotExist.pm!, 'exception on nonexisting component');
0567538f 212
1edaf6fe 213is($schema->class("Artist")->field_name_for->{name}, 'artist name', 'mk_classdata usage ok');
90e6de6c 214
54540863 215my $search = [ { 'tags.tag' => 'Cheesy' }, { 'tags.tag' => 'Blue' } ];
216
fb88ca2c 217my $or_rs = $schema->resultset("CD")->search_rs($search, { join => 'tags',
6aeb9185 218 order_by => 'cdid' });
a258ee5d 219is($or_rs->all, 5, 'Joined search with OR returned correct number of rows');
220is($or_rs->count, 5, 'Search count with OR ok');
54540863 221
a258ee5d 222my $collapsed_or_rs = $or_rs->search ({}, { distinct => 1 }); # induce collapse
223is ($collapsed_or_rs->all, 4, 'Collapsed joined search with OR returned correct number of rows');
224is ($collapsed_or_rs->count, 4, 'Collapsed search count with OR ok');
6aeb9185 225
00f3b1c7 226# make sure sure distinct on a grouped rs is warned about
a2f22854 227{
228 my $cd_rs = $schema->resultset ('CD')
229 ->search ({}, { distinct => 1, group_by => 'title' });
230 warnings_exist (sub {
231 $cd_rs->next;
232 }, qr/Useless use of distinct/, 'UUoD warning');
233}
00f3b1c7 234
1cc3ce1e 235{
d2f21b37 236 my $tcount = $schema->resultset('Track')->search(
286f32b3 237 {},
d2f21b37 238 {
11d68671 239 select => [ qw/position title/ ],
240 distinct => 1,
286f32b3 241 }
242 );
d2f21b37 243 is($tcount->count, 13, 'multiple column COUNT DISTINCT ok');
244
11d68671 245 $tcount = $schema->resultset('Track')->search(
246 {},
247 {
248 columns => [ qw/position title/ ],
249 distinct => 1,
250 }
251 );
252 is($tcount->count, 13, 'multiple column COUNT DISTINCT ok');
253
254 $tcount = $schema->resultset('Track')->search(
255 {},
256 {
257 group_by => [ qw/position title/ ]
258 }
259 );
8273e845 260 is($tcount->count, 13, 'multiple column COUNT DISTINCT using column syntax ok');
f2de4889 261}
584e74ed 262
f9db5527 263my $tag_rs = $schema->resultset('Tag')->search(
6aeb9185 264 [ { 'me.tag' => 'Cheesy' }, { 'me.tag' => 'Blue' } ]);
265
fb88ca2c 266my $rel_rs = $tag_rs->search_related('cd', {}, { order_by => 'cd.cdid'} );
6aeb9185 267
a258ee5d 268is($rel_rs->count, 5, 'Related search ok');
6aeb9185 269
d2f21b37 270is($or_rs->next->cdid, $rel_rs->next->cdid, 'Related object ok');
a4731ae0 271$or_rs->reset;
272$rel_rs->reset;
a953d8d9 273
a2f22854 274# at this point there should be no active statements
275# (finish() was called everywhere, either explicitly via
276# reset() or on DESTROY)
277for (keys %{$schema->storage->dbh->{CachedKids}}) {
278 fail("Unreachable cached statement still active: $_")
279 if $schema->storage->dbh->{CachedKids}{$_}->FETCH('Active');
280}
281
4c4ccf29 282my $tag = $schema->resultset('Tag')->search(
02ddfe6e 283 [ { 'me.tag' => 'Blue' } ],
284 { columns => 'tagid' }
285)->next;
4c4ccf29 286
d2f21b37 287ok($tag->has_column_loaded('tagid'), 'Has tagid loaded');
288ok(!$tag->has_column_loaded('tag'), 'Has not tag loaded');
4c4ccf29 289
a953d8d9 290ok($schema->storage(), 'Storage available');
291
16b4fd26 292{
293 my $rs = $schema->resultset("Artist")->search({
294 -and => [
295 artistid => { '>=', 1 },
296 artistid => { '<', 3 }
297 ]
298 });
299
84f7e8a1 300 $rs->update({ rank => 6134 });
16b4fd26 301
302 my $art;
303
304 $art = $schema->resultset("Artist")->find(1);
84f7e8a1 305 is($art->rank, 6134, 'updated first artist rank');
16b4fd26 306
307 $art = $schema->resultset("Artist")->find(2);
84f7e8a1 308 is($art->rank, 6134, 'updated second artist rank');
16b4fd26 309}
310
825135d8 311# test source_name
312{
313 # source_name should be set for normal modules
314 is($schema->source('CD')->source_name, 'CD', 'source_name is set to moniker');
a4731ae0 315
825135d8 316 # test the result source that sets source_name explictly
317 ok($schema->source('SourceNameArtists'), 'SourceNameArtists result source exists');
0009fa49 318
825135d8 319 my @artsn = $schema->resultset('SourceNameArtists')->search({}, { order_by => 'name DESC' });
d2f21b37 320 is(@artsn, 4, "Four artists returned");
8273e845 321
b1fb2c94 322 # make sure subclasses that don't set source_name are ok
93405cf0 323 ok($schema->source('ArtistSubclass'), 'ArtistSubclass exists');
825135d8 324}
bab77431 325
9c2c91ea 326my $newbook = $schema->resultset( 'Bookmark' )->find(1);
327
d6df786a 328lives_ok (sub { my $newlink = $newbook->link}, "stringify to false value doesn't cause error");
9c2c91ea 329
825135d8 330# test cascade_delete through many_to_many relations
331{
332 my $art_del = $schema->resultset("Artist")->find({ artistid => 1 });
d6df786a 333 lives_ok (sub { $art_del->delete }, 'Cascading delete on Ordered has_many works' ); # real test in ordered.t
d2f21b37 334 is( $schema->resultset("CD")->search({artist => 1}), 0, 'Cascading through has_many top level.');
335 is( $schema->resultset("CD_to_Producer")->search({cd => 1}), 0, 'Cascading through has_many children.');
825135d8 336}
bab77431 337
825135d8 338# test column_info
339{
340 $schema->source("Artist")->{_columns}{'artistid'} = {};
d9916234 341 $schema->source("Artist")->column_info_from_storage(1);
bab77431 342
825135d8 343 my $typeinfo = $schema->source("Artist")->column_info('artistid');
344 is($typeinfo->{data_type}, 'INTEGER', 'column_info ok');
345 $schema->source("Artist")->column_info('artistid');
52416317 346 ok($schema->source("Artist")->{_columns_info_loaded} == 1, 'Columns info loaded flag set');
347}
348
349# test columns_info
350{
351 $schema->source("Artist")->{_columns}{'artistid'} = {};
352 $schema->source("Artist")->column_info_from_storage(1);
353 $schema->source("Artist")->{_columns_info_loaded} = 0;
354
f45dc928 355 my @undef_default = DBIx::Class::_ENV_::STRESSTEST_COLUMN_INFO_UNAWARE_STORAGE
356 ? ()
357 : ( default_value => undef )
358 ;
359
52416317 360 is_deeply (
361 $schema->source('Artist')->columns_info,
362 {
363 artistid => {
364 data_type => "INTEGER",
f45dc928 365 @undef_default,
52416317 366 is_nullable => 0,
367 size => undef
368 },
369 charfield => {
370 data_type => "char",
f45dc928 371 @undef_default,
52416317 372 is_nullable => 1,
373 size => 10
374 },
375 name => {
376 data_type => "varchar",
f45dc928 377 @undef_default,
52416317 378 is_nullable => 1,
379 is_numeric => 0,
380 size => 100
381 },
382 rank => {
383 data_type => "integer",
384 default_value => 13,
385 is_nullable => 0,
386 size => undef
387 },
388 },
389 'columns_info works',
390 );
391
392 ok($schema->source("Artist")->{_columns_info_loaded} == 1, 'Columns info loaded flag set');
393
394 is_deeply (
395 $schema->source('Artist')->columns_info([qw/artistid rank/]),
396 {
397 artistid => {
398 data_type => "INTEGER",
f45dc928 399 @undef_default,
52416317 400 is_nullable => 0,
401 size => undef
402 },
403 rank => {
404 data_type => "integer",
405 default_value => 13,
406 is_nullable => 0,
407 size => undef
408 },
409 },
410 'limited columns_info works',
411 );
825135d8 412}
bab77431 413
a48e92d7 414# test source_info
415{
416 my $expected = {
417 "source_info_key_A" => "source_info_value_A",
418 "source_info_key_B" => "source_info_value_B",
419 "source_info_key_C" => "source_info_value_C",
420 };
421
422 my $sinfo = $schema->source("Artist")->source_info;
423
424 is_deeply($sinfo, $expected, 'source_info data works');
425}
426
825135d8 427# test remove_columns
428{
4738027b 429 is_deeply(
430 [$schema->source('CD')->columns],
431 [qw/cdid artist title year genreid single_track/],
432 'initial columns',
433 );
434
435 $schema->source('CD')->remove_columns('coolyear'); #should not delete year
436 is_deeply(
437 [$schema->source('CD')->columns],
438 [qw/cdid artist title year genreid single_track/],
439 'nothing removed when removing a non-existent column',
440 );
441
442 $schema->source('CD')->remove_columns('genreid', 'year');
443 is_deeply(
444 [$schema->source('CD')->columns],
445 [qw/cdid artist title single_track/],
446 'removed two columns',
447 );
448
449 my $priv_columns = $schema->source('CD')->_columns;
450 ok(! exists $priv_columns->{'year'}, 'year purged from _columns');
451 ok(! exists $priv_columns->{'genreid'}, 'genreid purged from _columns');
825135d8 452}
bab77431 453
ade8df5b 454# test resultsource->table return value when setting
455{
456 my $class = $schema->class('Event');
ade8df5b 457 my $table = $class->table($class->table);
458 is($table, $class->table, '->table($table) returns $table');
459}
0e80c4ca 460
461#make sure insert doesn't use set_column
462{
463 my $en_row = $schema->resultset('Encoded')->new_result({encoded => 'wilma'});
464 is($en_row->encoded, 'amliw', 'new encodes');
465 $en_row->insert;
466 is($en_row->encoded, 'amliw', 'insert does not encode again');
467}
3bb4eb8f 468
68888c09 469#make sure multicreate encoding still works
470{
471 my $empl_rs = $schema->resultset('Employee');
472
473 my $empl = $empl_rs->create ({
474 name => 'Secret holder',
475 secretkey => {
476 encoded => 'CAN HAZ',
477 },
478 });
479 is($empl->secretkey->encoded, 'ZAH NAC', 'correctly encoding on multicreate');
480
481 my $empl2 = $empl_rs->create ({
482 name => 'Same secret holder',
483 secretkey => {
484 encoded => 'CAN HAZ',
485 },
486 });
487 is($empl2->secretkey->encoded, 'ZAH NAC', 'correctly encoding on preexisting multicreate');
488
489 $empl_rs->create ({
490 name => 'cat1',
491 secretkey => {
492 encoded => 'CHEEZBURGER',
493 keyholders => [
494 {
495 name => 'cat2',
496 },
497 {
498 name => 'cat3',
499 },
500 ],
501 },
502 });
503
504 is($empl_rs->find({name => 'cat1'})->secretkey->encoded, 'REGRUBZEEHC', 'correct secret in database for empl1');
505 is($empl_rs->find({name => 'cat2'})->secretkey->encoded, 'REGRUBZEEHC', 'correct secret in database for empl2');
506 is($empl_rs->find({name => 'cat3'})->secretkey->encoded, 'REGRUBZEEHC', 'correct secret in database for empl3');
507
508}
509
4376a157 510# make sure that obsolete handle-based source tracking continues to work for the time being
511{
512 my $handle = $schema->source('Artist')->handle;
513
51c9ead2 514 my $rowdata = { $schema->resultset('Artist')->next->get_columns };
4376a157 515
516 my $rs = DBIx::Class::ResultSet->new($handle);
517 my $rs_result = $rs->next;
518 isa_ok( $rs_result, 'DBICTest::Artist' );
519 is_deeply (
520 { $rs_result->get_columns },
521 $rowdata,
522 'Correct columns retrieved (rset/source link healthy)'
523 );
524
525 my $row = DBICTest::Artist->new({ -source_handle => $handle });
526 is_deeply(
527 { $row->get_columns },
528 {},
529 'No columns yet'
530 );
531
532 # store_column to fool the _orig_ident tracker
533 $row->store_column('artistid', $rowdata->{artistid});
534 $row->in_storage(1);
535
536 $row->discard_changes;
537 is_deeply(
538 { $row->get_columns },
539 $rowdata,
540 'Storage refetch successful'
541 );
542}
543
3f1d61d0 544# test to make sure that calling ->new() on a resultset object gives
545# us a row object
546{
547 my $new_artist = $schema->resultset('Artist')->new({});
548 isa_ok( $new_artist, 'DBIx::Class::Row', '$rs->new gives a row object' );
549}
550
551
3bb4eb8f 552# make sure we got rid of the compat shims
553SKIP: {
7f08eb01 554 my $remove_version = 0.083;
555 skip "Remove in $remove_version", 3 if $DBIx::Class::VERSION < $remove_version;
3bb4eb8f 556
1225a9e0 557 for (qw/compare_relationship_keys pk_depends_on resolve_condition/) {
7f08eb01 558 ok (! DBIx::Class::ResultSource->can ($_), "$_ no longer provided by DBIx::Class::ResultSource, removed before $remove_version");
3bb4eb8f 559 }
560}
42a87bbb 561
562#------------------------------
563# READ THIS BEFORE "FIXING"
564#------------------------------
565#
566# make sure we got rid of discard_changes mess - this is a mess and a source
567# of great confusion. Here I simply die if the methods are available, which
568# is wrong on its own (we *have* to provide some sort of back-compat, even
569# if with warnings). Here is how I envision things should actually be. Also
570# note that a lot of the deprecation can be started today (i.e. the switch
571# from get_from_storage to copy_from_storage). So:
572#
573# $row->discard_changes =>
574# warning, and delegation to reload_from_storage
575#
576# $row->reload_from_storage =>
577# does what discard changes did in 0.08 - issues a query to the db
578# and repopulates all column slots, regardless of dirty states etc.
579#
580# $row->revert_changes =>
581# does what discard_changes should have done initially (before it became
582# a dual-purpose call). In order to make this work we will have to
583# augment $row to carry its own initial-state, much like svn has a
584# copy of the current checkout in contrast to cvs.
585#
586# my $db_row = $row->get_from_storage =>
587# warns and delegates to an improved name copy_from_storage, with the
588# same semantics
589#
590# my $db_row = $row->copy_from_storage =>
591# a much better/descriptive name than get_from_storage
592#
593#------------------------------
594# READ THIS BEFORE "FIXING"
595#------------------------------
596#
597SKIP: {
598 skip "Something needs to be done before 0.09", 2 if $DBIx::Class::VERSION < 0.09;
599
600 my $row = $schema->resultset ('Artist')->next;
601
602 for (qw/discard_changes get_from_storage/) {
603 ok (! $row->can ($_), "$_ needs *some* sort of facelift before 0.09 ships - current state of affairs is unacceptable");
604 }
605}
606
73d47f9f 607throws_ok { $schema->resultset} qr/resultset\(\) expects a source name/, 'resultset with no argument throws exception';
608
4f52479b 609throws_ok { $schema->source('Artist')->result_class->new( 'bugger' ) } qr/must be a hashref/;
610
42a87bbb 611done_testing;