1# vim: filetype=perl
2use strict;
3use warnings;
4
5use Test::More;
6use lib qw(t/lib);
7use DBICTest;
8
9use POSIX qw(ceil);
10
11my $schema = DBICTest->init_schema();
12
13my $employees = $schema->resultset('Employee');
14$employees->delete();
15
16foreach (1..5) {
17    $employees->create({ name=>'temp' });
18}
19$employees = $employees->search(undef,{order_by=>'position'});
20ok( check_rs($employees), "intial positions" );
21
22hammer_rs( $employees );
23
24DBICTest::Employee->grouping_column('group_id');
25$employees->delete();
26foreach my $group_id (1..4) {
27    foreach (1..6) {
28        $employees->create({ name=>'temp', group_id=>$group_id });
29    }
30}
31$employees = $employees->search(undef,{order_by=>'group_id,position'});
32
33foreach my $group_id (1..4) {
34    my $group_employees = $employees->search({group_id=>$group_id});
35    $group_employees->all();
36    ok( check_rs($group_employees), "group intial positions" );
37    hammer_rs( $group_employees );
38}
39
40my $group_3 = $employees->search({group_id=>3});
41my $to_group = 1;
42my $to_pos = undef;
43{
44  my @empl = $group_3->all;
45  while (my $employee = shift @empl) {
46    $employee->move_to_group($to_group, $to_pos);
47    $to_pos++;
48    $to_group = $to_group==1 ? 2 : 1;
49  }
50}
51foreach my $group_id (1..4) {
52    my $group_employees = $employees->search({group_id=>$group_id});
53    ok( check_rs($group_employees), "group positions after move_to_group" );
54}
55
56my $employee = $employees->search({group_id=>4})->first;
57$employee->position(2);
58$employee->update;
59ok( check_rs($employees->search_rs({group_id=>4})), "overloaded update 1" );
60$employee = $employees->search({group_id=>4})->first;
61$employee->update({position=>3});
62ok( check_rs($employees->search_rs({group_id=>4})), "overloaded update 2" );
63$employee = $employees->search({group_id=>4})->first;
64$employee->group_id(1);
65$employee->update;
66ok(
67  check_rs($employees->search_rs({group_id=>1})) && check_rs($employees->search_rs({group_id=>4})),
68  "overloaded update 3"
69);
70$employee = $employees->search({group_id=>4})->first;
71$employee->update({group_id=>2});
72ok(
73  check_rs($employees->search_rs({group_id=>2})) && check_rs($employees->search_rs({group_id=>4})),
74  "overloaded update 4"
75);
76$employee = $employees->search({group_id=>4})->first;
77$employee->group_id(1);
78$employee->position(3);
79$employee->update;
80ok(
81  check_rs($employees->search_rs({group_id=>1})) && check_rs($employees->search_rs({group_id=>4})),
82  "overloaded update 5"
83);
84$employee = $employees->search({group_id=>4})->first;
85$employee->group_id(2);
86$employee->position(undef);
87$employee->update;
88ok(
89  check_rs($employees->search_rs({group_id=>2})) && check_rs($employees->search_rs({group_id=>4})),
90  "overloaded update 6"
91);
92$employee = $employees->search({group_id=>4})->first;
93$employee->update({group_id=>1,position=>undef});
94ok(
95  check_rs($employees->search_rs({group_id=>1})) && check_rs($employees->search_rs({group_id=>4})),
96  "overloaded update 7"
97);
98
99$employee->group_id(2);
100$employee->name('E of the month');
101$employee->update({ employee_id => 666, position => 2 });
102is_deeply(
103  { $employee->get_columns },
104  {
105    employee_id => 666,
106    encoded => undef,
107    group_id => 2,
108    group_id_2 => undef,
109    group_id_3 => undef,
110    name => "E of the month",
111    position => 2
112  },
113  'combined update() worked correctly'
114);
115is_deeply(
116  { $employee->get_columns },
117  { $employee->get_from_storage->get_columns },
118  'object matches database state',
119);
120
121#####
122# multicol tests begin here
123#####
124
125DBICTest::Employee->grouping_column(['group_id_2', 'group_id_3']);
126$employees->delete();
127foreach my $group_id_2 (1..4) {
128    foreach my $group_id_3 (1..4) {
129        foreach (1..4) {
130            $employees->create({ name=>'temp', group_id_2=>$group_id_2, group_id_3=>$group_id_3 });
131        }
132    }
133}
134$employees = $employees->search(undef,{order_by=>[qw/group_id_2 group_id_3 position/]});
135
136foreach my $group_id_2 (1..3) {
137    foreach my $group_id_3 (1..3) {
138        my $group_employees = $employees->search({group_id_2=>$group_id_2, group_id_3=>$group_id_3});
139        $group_employees->all();
140        ok( check_rs($group_employees), "group intial positions" );
141        hammer_rs( $group_employees );
142    }
143}
144
145# move_to_group, specifying group by hash
146my $group_4 = $employees->search({group_id_2=>4});
147$to_group = 1;
148my $to_group_2_base = 7;
149my $to_group_2 = 1;
150$to_pos = undef;
151
152{
153  my @empl = $group_3->all;
154  while (my $employee = shift @empl) {
155    $employee->move_to_group({group_id_2=>$to_group, group_id_3=>$to_group_2}, $to_pos);
156    $to_pos++;
157    $to_group = ($to_group % 3) + 1;
158    $to_group_2_base++;
159    $to_group_2 = (ceil($to_group_2_base/3.0) %3) +1
160  }
161}
162foreach my $group_id_2 (1..4) {
163    foreach my $group_id_3 (1..4) {
164        my $group_employees = $employees->search({group_id_2=>$group_id_2,group_id_3=>$group_id_3});
165        ok( check_rs($group_employees), "group positions after move_to_group" );
166    }
167}
168
169$employees->delete();
170foreach my $group_id_2 (1..4) {
171    foreach my $group_id_3 (1..4) {
172        foreach (1..4) {
173            $employees->create({ name=>'temp', group_id_2=>$group_id_2, group_id_3=>$group_id_3 });
174        }
175    }
176}
177$employees = $employees->search(undef,{order_by=>[qw/group_id_2 group_id_3 position/]});
178
179$employee = $employees->search({group_id_2=>4, group_id_3=>1})->first;
180$employee->group_id_2(1);
181$employee->update;
182ok(
183    check_rs($employees->search_rs({group_id_2=>4, group_id_3=>1}))
184    && check_rs($employees->search_rs({group_id_2=>1, group_id_3=>1})),
185    "overloaded multicol update 1"
186);
187
188$employee = $employees->search({group_id_2=>4, group_id_3=>1})->first;
189$employee->update({group_id_2=>2});
190ok( check_rs($employees->search_rs({group_id_2=>4, group_id_3=>1}))
191    && check_rs($employees->search_rs({group_id_2=>2, group_id_3=>1})),
192   "overloaded multicol update 2"
193);
194
195$employee = $employees->search({group_id_2=>3, group_id_3=>1})->first;
196$employee->group_id_2(1);
197$employee->group_id_3(3);
198$employee->update();
199ok( check_rs($employees->search_rs({group_id_2=>3, group_id_3=>1}))
200    && check_rs($employees->search_rs({group_id_2=>1, group_id_3=>3})),
201    "overloaded multicol update 3"
202);
203
204$employee = $employees->search({group_id_2=>3, group_id_3=>1})->first;
205$employee->update({group_id_2=>2, group_id_3=>3});
206ok( check_rs($employees->search_rs({group_id_2=>3, group_id_3=>1}))
207    && check_rs($employees->search_rs({group_id_2=>2, group_id_3=>3})),
208    "overloaded multicol update 4"
209);
210
211$employee = $employees->search({group_id_2=>3, group_id_3=>2})->first;
212$employee->update({group_id_2=>2, group_id_3=>4, position=>2});
213ok( check_rs($employees->search_rs({group_id_2=>3, group_id_3=>2}))
214    && check_rs($employees->search_rs({group_id_2=>2, group_id_3=>4})),
215    "overloaded multicol update 5"
216);
217
218sub hammer_rs {
219    my $rs = shift;
220    my $employee;
221    my $count = $rs->count();
222    my $position_column = $rs->result_class->position_column();
223    my $row;
224
225    foreach my $position (1..$count) {
226
227        ($row) = $rs->search({ $position_column=>$position })->all();
228        $row->move_previous();
229        ok( check_rs($rs), "move_previous( $position )" );
230
231        ($row) = $rs->search({ $position_column=>$position })->all();
232        $row->move_next();
233        ok( check_rs($rs), "move_next( $position )" );
234
235        ($row) = $rs->search({ $position_column=>$position })->all();
236        $row->move_first();
237        ok( check_rs($rs), "move_first( $position )" );
238
239        ($row) = $rs->search({ $position_column=>$position })->all();
240        $row->move_last();
241        ok( check_rs($rs), "move_last( $position )" );
242
243        foreach my $to_position (1..$count) {
244            ($row) = $rs->search({ $position_column=>$position })->all();
245            $row->move_to($to_position);
246            ok( check_rs($rs), "move_to( $position => $to_position )" );
247        }
248
249        $row = $rs->find({ position => $position });
250        if ($position==1) {
251            ok( !$row->previous_sibling(), 'no previous sibling' );
252            ok( !$row->first_sibling(), 'no first sibling' );
253            ok( $row->next_sibling->position > $position, 'next sibling position > than us');
254            is( $row->next_sibling->previous_sibling->position, $position, 'next-prev sibling is us');
255            ok( $row->last_sibling->position > $position, 'last sibling position > than us');
256        }
257        else {
258            ok( $row->previous_sibling(), 'previous sibling' );
259            ok( $row->first_sibling(), 'first sibling' );
260            ok( $row->previous_sibling->position < $position, 'prev sibling position < than us');
261            is( $row->previous_sibling->next_sibling->position, $position, 'prev-next sibling is us');
262            ok( $row->first_sibling->position < $position, 'first sibling position < than us');
263        }
264        if ($position==$count) {
265            ok( !$row->next_sibling(), 'no next sibling' );
266            ok( !$row->last_sibling(), 'no last sibling' );
267            ok( $row->previous_sibling->position < $position, 'prev sibling position < than us');
268            is( $row->previous_sibling->next_sibling->position, $position, 'prev-next sibling is us');
269            ok( $row->first_sibling->position < $position, 'first sibling position < than us');
270        }
271        else {
272            ok( $row->next_sibling(), 'next sibling' );
273            ok( $row->last_sibling(), 'last sibling' );
274            ok( $row->next_sibling->position > $row->position, 'next sibling position > than us');
275            is( $row->next_sibling->previous_sibling->position, $position, 'next-prev sibling is us');
276            ok( $row->last_sibling->position > $row->position, 'last sibling position > than us');
277        }
278
279    }
280}
281
282sub check_rs {
283    my( $rs ) = @_;
284    $rs->reset();
285    my $position_column = $rs->result_class->position_column();
286    my $expected_position = 0;
287    while (my $row = $rs->next()) {
288        $expected_position ++;
289        if ($row->get_column($position_column)!=$expected_position) {
290            return 0;
291        }
292    }
293    return 1;
294}
295
296done_testing;
297