Fixups for 5.17.6+ hash randomization
[dbsrgits/DBIx-Class.git] / t / resultset / rowparser_internals.t
1 use strict;
2 use warnings;
3
4 use Test::More;
5 use lib qw(t/lib);
6 use DBICTest;
7 use B::Deparse;
8
9 # globally set for the rest of test
10 # the rowparser maker does not order its hashes by default for the miniscule
11 # speed gain. But it does not disable sorting either - for this test
12 # everything will be ordered nicely, and the hash randomization of 5.18
13 # will not trip up anything
14 use Data::Dumper;
15 $Data::Dumper::Sortkeys = 1;
16
17 my $schema = DBICTest->init_schema(no_deploy => 1);
18 my $infmap = [qw/single_track.cd.artist.name year/];
19
20 is_same_src (
21   $schema->source ('CD')->_mk_row_parser({
22     inflate_map => $infmap,
23   }),
24   '$_ = [
25     { year => $_->[1] },
26     { single_track => [
27       undef,
28       { cd => [
29         undef,
30         { artist => [
31           { name  => $_->[0] },
32         ] },
33       ]},
34     ]},
35   ] for @{$_[0]}',
36   'Simple 1:1 descending non-collapsing parser',
37 );
38
39 $infmap = [qw/
40   single_track.cd.artist.artistid
41   year
42   single_track.cd.artist.cds.tracks.title
43   single_track.cd.artist.cds.cdid
44   title
45   artist
46 /];
47 is_same_src (
48   $schema->source ('CD')->_mk_row_parser({
49     inflate_map => $infmap,
50   }),
51   '$_ = [
52     { artist => $_->[5], title => $_->[4], year => $_->[1] },
53     { single_track => [
54       undef,
55       { cd => [
56         undef,
57         { artist => [
58           { artistid => $_->[0] },
59           { cds => [
60             { cdid => $_->[3] },
61             { tracks => [
62               { title => $_->[2] }
63             ] },
64           ] },
65         ] },
66       ] },
67     ] },
68   ] for @{$_[0]}',
69   '1:1 descending non-collapsing parser terminating with chained 1:M:M',
70 );
71
72 is_deeply (
73   $schema->source('CD')->_resolve_collapse({map { $infmap->[$_] => $_ } 0 .. $#$infmap}),
74   {
75     -node_index => 1,
76     -node_id => [ 4, 5 ],
77     -branch_id => [ 0, 2, 3, 4, 5 ],
78
79     single_track => {
80       -node_index => 2,
81       -node_id => [ 4, 5],
82       -branch_id => [ 0, 2, 3, 4, 5],
83       -is_optional => 1,
84       -is_single => 1,
85
86       cd => {
87         -node_index => 3,
88         -node_id => [ 4, 5 ],
89         -branch_id => [ 0, 2, 3, 4, 5 ],
90         -is_single => 1,
91
92         artist => {
93           -node_index => 4,
94           -node_id => [ 0 ],
95           -branch_id => [ 0, 2, 3 ],
96           -is_single => 1,
97
98           cds => {
99             -node_index => 5,
100             -node_id => [ 3 ],
101             -branch_id => [ 2, 3 ],
102             -is_optional => 1,
103
104             tracks => {
105               -node_index => 6,
106               -node_id => [ 2, 3 ],
107               -branch_id => [ 2, 3 ],
108               -is_optional => 1,
109             },
110           },
111         },
112       },
113     },
114   },
115   'Correct collapse map for 1:1 descending chain terminating with chained 1:M:M'
116 );
117
118 is_same_src (
119   $schema->source ('CD')->_mk_row_parser({
120     inflate_map => $infmap,
121     collapse => 1,
122   }),
123   ' my($rows_pos, $result_pos, $cur_row, @cur_row_ids, @collapse_idx, $is_new_res) = (0, 0);
124
125     while ($cur_row = (
126       ( $rows_pos >= 0 and $_[0][$rows_pos++] ) or do { $rows_pos = -1; undef } )
127         ||
128       ( $_[1] and $_[1]->() )
129     ) {
130
131       $cur_row_ids[$_] = defined $cur_row->[$_] ? $cur_row->[$_] : "\xFF\xFFN\xFFU\xFFL\xFFL\xFF\xFF"
132         for (0, 2, 3, 4, 5);
133
134       # a present cref implies lazy prefetch, implies a supplied stash in $_[2]
135       $_[1] and $result_pos and unshift(@{$_[2]}, $cur_row) and last
136         if $is_new_res = ! $collapse_idx[1]{$cur_row_ids[4]}{$cur_row_ids[5]};
137
138       $collapse_idx[1]{$cur_row_ids[4]}{$cur_row_ids[5]} ||= [{ artist => $cur_row->[5], title => $cur_row->[4], year => $cur_row->[1] }];
139       $collapse_idx[1]{$cur_row_ids[4]}{$cur_row_ids[5]}[1]{single_track} ||= $collapse_idx[2]{$cur_row_ids[4]}{$cur_row_ids[5]};
140       $collapse_idx[2]{$cur_row_ids[4]}{$cur_row_ids[5]}[1]{cd} ||= $collapse_idx[3]{$cur_row_ids[4]}{$cur_row_ids[5]};
141       $collapse_idx[3]{$cur_row_ids[4]}{$cur_row_ids[5]}[1]{artist} ||= $collapse_idx[4]{$cur_row_ids[0]} ||= [{ artistid => $cur_row->[0] }];
142
143       $collapse_idx[4]{$cur_row_ids[0]}[1]{cds} ||= [];
144       push @{$collapse_idx[4]{$cur_row_ids[0]}[1]{cds}}, $collapse_idx[5]{$cur_row_ids[3]} ||= [{ cdid => $cur_row->[3] }]
145         unless $collapse_idx[5]{$cur_row_ids[3]};
146
147       $collapse_idx[5]{$cur_row_ids[3]}[1]{tracks} ||= [];
148       push @{$collapse_idx[5]{$cur_row_ids[3]}[1]{tracks}}, $collapse_idx[6]{$cur_row_ids[2]}{$cur_row_ids[3]} ||= [{ title => $cur_row->[2] }]
149         unless $collapse_idx[6]{$cur_row_ids[2]}{$cur_row_ids[3]};
150
151       $_[0][$result_pos++] = $collapse_idx[1]{$cur_row_ids[4]}{$cur_row_ids[5]}
152         if $is_new_res;
153     }
154     splice @{$_[0]}, $result_pos;
155   ',
156   'Same 1:1 descending terminating with chained 1:M:M but with collapse',
157 );
158
159 $infmap = [qw/
160   tracks.lyrics.lyric_versions.text
161   existing_single_track.cd.artist.artistid
162   existing_single_track.cd.artist.cds.year
163   year
164   genreid
165   tracks.title
166   existing_single_track.cd.artist.cds.cdid
167   latest_cd
168   existing_single_track.cd.artist.cds.tracks.title
169   existing_single_track.cd.artist.cds.genreid
170 /];
171
172 is_deeply (
173   $schema->source('CD')->_resolve_collapse({map { $infmap->[$_] => $_ } 0 .. $#$infmap}),
174   {
175     -node_index => 1,
176     -node_id => [ 1 ], # existing_single_track.cd.artist.artistid
177     -branch_id => [ 0, 1, 5, 6, 8 ],
178
179     existing_single_track => {
180       -node_index => 2,
181       -node_id => [ 1 ], # existing_single_track.cd.artist.artistid
182       -branch_id => [ 1, 6, 8 ],
183       -is_single => 1,
184
185       cd => {
186         -node_index => 3,
187         -node_id => [ 1 ], # existing_single_track.cd.artist.artistid
188         -branch_id => [ 1, 6, 8 ],
189         -is_single => 1,
190
191         artist => {
192           -node_index => 4,
193           -node_id => [ 1 ], # existing_single_track.cd.artist.artistid
194           -branch_id => [ 1, 6, 8 ],
195           -is_single => 1,
196
197           cds => {
198             -node_index => 5,
199             -node_id => [ 6 ], # existing_single_track.cd.artist.cds.cdid
200             -branch_id => [ 6, 8 ],
201             -is_optional => 1,
202
203             tracks => {
204               -node_index => 6,
205               -node_id => [ 6, 8 ], # existing_single_track.cd.artist.cds.cdid, existing_single_track.cd.artist.cds.tracks.title
206               -branch_id => [ 6, 8 ],
207               -is_optional => 1,
208             }
209           }
210         }
211       }
212     },
213     tracks => {
214       -node_index => 7,
215       -node_id => [ 1, 5 ], # existing_single_track.cd.artist.artistid, tracks.title
216       -branch_id => [ 0, 1, 5 ],
217       -is_optional => 1,
218
219       lyrics => {
220         -node_index => 8,
221         -node_id => [ 1, 5 ], # existing_single_track.cd.artist.artistid, tracks.title
222         -branch_id => [ 0, 1, 5 ],
223         -is_single => 1,
224         -is_optional => 1,
225
226         lyric_versions => {
227           -node_index => 9,
228           -node_id => [ 0, 1, 5 ], # tracks.lyrics.lyric_versions.text, existing_single_track.cd.artist.artistid, tracks.title
229           -branch_id => [ 0, 1, 5 ],
230           -is_optional => 1,
231         },
232       },
233     }
234   },
235   'Correct collapse map constructed',
236 );
237
238 is_same_src (
239   $schema->source ('CD')->_mk_row_parser({
240     inflate_map => $infmap,
241     collapse => 1,
242   }),
243   ' my ($rows_pos, $result_pos, $cur_row, @cur_row_ids, @collapse_idx, $is_new_res) = (0,0);
244
245     while ($cur_row = (
246       ( $rows_pos >= 0 and $_[0][$rows_pos++] ) or do { $rows_pos = -1; undef } )
247         ||
248       ( $_[1] and $_[1]->() )
249     ) {
250
251       $cur_row_ids[$_] = defined $cur_row->[$_] ? $cur_row->[$_] : "\xFF\xFFN\xFFU\xFFL\xFFL\xFF\xFF"
252         for (0, 1, 5, 6, 8);
253
254       $is_new_res = ! $collapse_idx[1]{$cur_row_ids[1]} and (
255         $_[1] and $result_pos and (unshift @{$_[2]}, $cur_row) and last
256       );
257
258       $collapse_idx[1]{$cur_row_ids[1]} ||= [{ genreid => $cur_row->[4], latest_cd => $cur_row->[7], year => $cur_row->[3] }];
259
260       $collapse_idx[1]{$cur_row_ids[1]}[1]{existing_single_track} ||= $collapse_idx[2]{$cur_row_ids[1]};
261       $collapse_idx[2]{$cur_row_ids[1]}[1]{cd} ||= $collapse_idx[3]{$cur_row_ids[1]};
262       $collapse_idx[3]{$cur_row_ids[1]}[1]{artist} ||= $collapse_idx[4]{$cur_row_ids[1]} ||= [{ artistid => $cur_row->[1] }];
263
264       $collapse_idx[4]{$cur_row_ids[1]}[1]{cds} ||= [];
265       push @{ $collapse_idx[4]{$cur_row_ids[1]}[1]{cds} }, $collapse_idx[5]{$cur_row_ids[6]} ||= [{ cdid => $cur_row->[6], genreid => $cur_row->[9], year => $cur_row->[2] }]
266         unless $collapse_idx[5]{$cur_row_ids[6]};
267
268       $collapse_idx[5]{$cur_row_ids[6]}[1]{tracks} ||= [];
269       push @{ $collapse_idx[5]{$cur_row_ids[6]}[1]{tracks} }, $collapse_idx[6]{$cur_row_ids[6]}{$cur_row_ids[8]} ||= [{ title => $cur_row->[8] }]
270         unless $collapse_idx[6]{$cur_row_ids[6]}{$cur_row_ids[8]};
271
272       $collapse_idx[1]{$cur_row_ids[1]}[1]{tracks} ||= [];
273       push @{ $collapse_idx[1]{$cur_row_ids[1]}[1]{tracks} }, $collapse_idx[7]{$cur_row_ids[1]}{$cur_row_ids[5]} ||= [{ title => $cur_row->[5] }]
274         unless $collapse_idx[7]{$cur_row_ids[1]}{$cur_row_ids[5]};
275
276       $collapse_idx[7]{$cur_row_ids[1]}{$cur_row_ids[5]}[1]{lyrics} ||= $collapse_idx[8]{$cur_row_ids[1]}{$cur_row_ids[5] };
277
278       $collapse_idx[8]{$cur_row_ids[1]}{$cur_row_ids[5]}[1]{lyric_versions} ||= [];
279       push @{ $collapse_idx[8]{$cur_row_ids[1]}{$cur_row_ids[5]}[1]{lyric_versions} }, $collapse_idx[9]{$cur_row_ids[0]}{$cur_row_ids[1]}{$cur_row_ids[5]} ||= [{ text => $cur_row->[0] }]
280         unless $collapse_idx[9]{$cur_row_ids[0]}{$cur_row_ids[1]}{$cur_row_ids[5]};
281
282       $_[0][$result_pos++] = $collapse_idx[1]{$cur_row_ids[1]}
283         if $is_new_res;
284     }
285
286     splice @{$_[0]}, $result_pos;
287   ',
288   'Multiple has_many on multiple branches torture test',
289 );
290
291 done_testing;
292
293 my $deparser;
294 sub is_same_src {
295   $deparser ||= B::Deparse->new;
296   local $Test::Builder::Level = $Test::Builder::Level + 1;
297
298   my ($got, $expect) = map {
299     my $cref = eval "sub { $_ }" or do {
300       fail "Coderef does not compile!\n\n$@\n\n$_";
301       return undef;
302     };
303     $deparser->coderef2text($cref);
304   } @_[0,1];
305
306   is ($got, $expect, $_[2]||() )
307     or note ("Originals source:\n\n$_[0]\n\n$_[1]\n");
308 }
309