Bugzilla – Attachment 164504 Details for
Bug 35826
Optimize building of holds queue based on transport cost matrix
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 35826: Add unit tests.
Bug-35826-Add-unit-tests.patch (text/plain), 11.79 KB, created by
Martin Renvoize (ashimema)
on 2024-04-08 13:42:36 UTC
(
hide
)
Description:
Bug 35826: Add unit tests.
Filename:
MIME Type:
Creator:
Martin Renvoize (ashimema)
Created:
2024-04-08 13:42:36 UTC
Size:
11.79 KB
patch
obsolete
>From ce1b6307d59f9daf013a79c7509307c2069aacd2 Mon Sep 17 00:00:00 2001 >From: Andreas Jonsson <andreas.jonsson@kreablo.se> >Date: Sun, 10 Mar 2024 19:50:13 +0100 >Subject: [PATCH] Bug 35826: Add unit tests. > >Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com> >Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com> >--- > .../HoldsQueue/TransportCostOptimizations.t | 406 ++++++++++++++++++ > 1 file changed, 406 insertions(+) > create mode 100755 t/db_dependent/HoldsQueue/TransportCostOptimizations.t > >diff --git a/t/db_dependent/HoldsQueue/TransportCostOptimizations.t b/t/db_dependent/HoldsQueue/TransportCostOptimizations.t >new file mode 100755 >index 00000000000..b11b3ba0739 >--- /dev/null >+++ b/t/db_dependent/HoldsQueue/TransportCostOptimizations.t >@@ -0,0 +1,406 @@ >+#!/usr/bin/perl >+ >+# Test C4::HoldsQueue::CreateQueue() for optimal allocation using the >+# transport cost matrix. >+# >+# Wraps tests in transaction that's rolled back, so no data is destroyed >+# MySQL WARNING: This makes sense only if your tables are InnoDB, otherwise >+# transactions are not supported and mess is left behind >+ >+use Modern::Perl; >+ >+use Test::More tests => 117; >+use Data::Dumper; >+ >+use C4::Context; >+use Koha::Database; >+use C4::Reserves qw( AddReserve ); >+use C4::HoldsQueue; >+ >+use t::lib::TestBuilder; >+use t::lib::Mocks; >+ >+sub mock_cost_matrix { >+ my $matrix = shift; >+ my $builder = shift; >+ my $schema = shift; >+ >+ my $rows = scalar(@$matrix); >+ my $cols = $rows > 0 ? scalar( @{ $matrix->[0] } ) : 0; >+ >+ my $max = $rows >= $cols ? $rows : $cols; >+ >+ my @libraries = (); >+ >+ for ( my $i = 0 ; $i < $max ; $i++ ) { >+ push @libraries, $builder->build( >+ { >+ source => 'Branch', >+ } >+ ); >+ } >+ >+ for ( my $i = 0 ; $i < scalar(@$matrix) ; $i++ ) { >+ for ( my $j = 0 ; $j < scalar( @{ $matrix->[$i] } ) ; $j++ ) { >+ if ( $i != $j ) { >+ my $tc_rs = $schema->resultset('TransportCost'); >+ my $cost = $matrix->[$i]->[$j]; >+ $tc_rs->create( >+ { >+ frombranch => $libraries[$i]->{branchcode}, >+ tobranch => $libraries[$j]->{branchcode}, >+ cost => $cost, >+ disable_transfer => ( $cost < 0 ? 1 : 0 ) >+ } >+ ); >+ } >+ } >+ } >+ >+ return \@libraries; >+} >+ >+sub mock_scenario { >+ my $libraries = shift; >+ my $items_at_library = shift; >+ my $reserves_at_library = shift; >+ my $builder = shift; >+ >+ my @borrowers = (); >+ >+ for ( my $i = 0 ; $i < scalar(@$libraries) ; $i++ ) { >+ my @at_library = (); >+ for ( my $j = 0 ; $j < $reserves_at_library->[$i] ; $j++ ) { >+ push @at_library, $builder->build( >+ { >+ source => 'Borrower', >+ value => { >+ branchcode => $libraries->[$i]->{branchcode}, >+ } >+ } >+ ); >+ } >+ push @borrowers, \@at_library; >+ } >+ >+ my $biblio = $builder->build_sample_biblio(); >+ >+ my @items = (); >+ >+ for ( my $i = 0 ; $i < scalar(@$libraries) ; $i++ ) { >+ for ( my $j = 0 ; $j < $items_at_library->[$i] ; $j++ ) { >+ push @items, $builder->build_sample_item( >+ { >+ biblionumber => $biblio->biblionumber, >+ holdingbranch => $libraries->[$i]->{branchcode}, >+ homebranch => $libraries->[$i]->{branchcode}, >+ } >+ ); >+ } >+ } >+ >+ for ( my $i = 0 ; $i < scalar(@$libraries) ; $i++ ) { >+ for ( my $j = 0 ; $j < $reserves_at_library->[$i] ; $j++ ) { >+ my $borrower = $borrowers[$i]->[$j]; >+ AddReserve( >+ { >+ branchcode => $borrower->{branchcode}, >+ borrowernumber => $borrower->{borrowernumber}, >+ biblionumber => $biblio->biblionumber, >+ priority => 1 + $j + $i * scalar(@$libraries), >+ } >+ ); >+ } >+ } >+} >+ >+sub library_index { >+ my ( $branchcode, $libraries ) = @_; >+ >+ for ( my $i = 0 ; $i < scalar(@$libraries) ; $i++ ) { >+ if ( $libraries->[$i]->{branchcode} eq $branchcode ) { >+ return $i; >+ } >+ } >+ >+ return -1; >+} >+ >+sub allocation_indices { >+ my ( $libraries, $allocations ) = @_; >+ >+ my @allocations = (); >+ >+ for ( my $i = 0 ; $i < scalar(@$allocations) ; $i++ ) { >+ my $pick = library_index( $allocations->[$i]->{pickbranch}, $libraries ); >+ my $holding = library_index( $allocations->[$i]->{holdingbranch}, $libraries ); >+ push @allocations, [ $holding, $pick ]; >+ } >+ >+ return @allocations; >+} >+ >+sub total_cost { >+ my ( $matrix, $libraries, $allocations ) = @_; >+ >+ my @allocations = allocation_indices( $libraries, $allocations ); >+ my $total = 0; >+ >+ for my $allocation (@allocations) { >+ my $cost = $matrix->[ $allocation->[0] ]->[ $allocation->[1] ]; >+ $total += $cost; >+ } >+ >+ return $total; >+} >+ >+# Mock a scenario for reservations on a biblio. >+# >+# Parameters: >+# $label - Label for scenario >+# $matrix - Transport cost matrix (array of arrays of numbers). >+# $items_at_library - Array specifying the number of items for the biblio at each library. >+# Indices correspond to the indices in the transport cost matrix. >+# $reserves_at_library - Array specifying the number of holds on the biblio at each library. >+# Indices correspond to the indices in the transport cost matrix. >+# $expected_allocation - Array of tuples specifying expected allocation on the form >+# [<index of holding library>, <index of of pick library>]. Specify >+# undefined if optimal allocation is ambiguous >+# $expected_total_cost - The expected total cost of the allocation. >+sub test_allocation { >+ my ( $label, $matrix, $items_at_library, $reserves_at_library, $expected_allocation, $expected_total_cost ) = @_; >+ >+ my $schema = Koha::Database->schema; >+ $schema->storage->txn_begin; >+ my $dbh = C4::Context->dbh; >+ >+ my $builder = t::lib::TestBuilder->new; >+ >+ t::lib::Mocks::mock_preference( 'UseBranchTransferLimits', '0' ); >+ t::lib::Mocks::mock_preference( 'BranchTransferLimitsType', 'itemtype' ); >+ t::lib::Mocks::mock_preference( 'UseTransportCostMatrix', '1' ); >+ >+ my $libraries = mock_cost_matrix( $matrix, $builder, $schema ); >+ >+ mock_scenario( $libraries, $items_at_library, $reserves_at_library, $builder ); >+ >+ C4::HoldsQueue::CreateQueue(); >+ >+ my $holds_queue = $dbh->selectall_arrayref( "SELECT * FROM tmp_holdsqueue", { Slice => {} } ); >+ >+ if ($expected_allocation) { >+ my @indices = allocation_indices( $libraries, $holds_queue ); >+ >+ is( scalar(@indices), scalar(@$expected_allocation), "$label correct number of allocations" ); >+ >+ # print STDERR Dumper(\@indices); >+ >+ while ( my $expected = shift @$expected_allocation ) { >+ my $found = 0; >+ for ( my $i = 0 ; $i < scalar(@indices) ; $i++ ) { >+ if ( $expected->[0] == $indices[$i]->[0] >+ && $expected->[1] == $indices[$i]->[1] ) >+ { >+ $found = 1; >+ $indices[$i] = [ -1, -1 ]; >+ last; >+ } >+ } >+ ok( $found, "$label - allocation contained [" . $expected->[0] . ", " . $expected->[1] . "]" ); >+ } >+ } >+ >+ is( total_cost( $matrix, $libraries, $holds_queue ), $expected_total_cost, "$label the total cost is as expected" ); >+ >+ $schema->txn_rollback; >+} >+ >+test_allocation( >+ "trivial case", >+ [], >+ [], >+ [], >+ [], >+ 0 >+); >+ >+test_allocation( >+ "unit case", >+ [ [0] ], >+ [1], >+ [1], >+ [ [ 0, 0 ] ], >+ 0 >+); >+ >+test_allocation( >+ "all local allocations", >+ [ >+ [ 0, 1, 1 ], >+ [ 1, 0, 1 ], >+ [ 1, 1, 0 ] >+ ], >+ [ 1, 1, 1 ], >+ [ 1, 1, 1 ], >+ undef, >+ 0 >+); >+ >+test_allocation( >+ "some non-local allocations", >+ [ >+ [ 0, 1, 1 ], >+ [ 1, 0, 1 ], >+ [ 1, 1, 0 ] >+ ], >+ [ 3, 0, 0 ], >+ [ 1, 1, 1 ], >+ [ [ 0, 0 ], [ 0, 1 ], [ 0, 2 ] ], >+ 2 >+); >+ >+test_allocation( >+ "different costs 1", >+ [ >+ [ 0, 2, 2 ], >+ [ 1, 0, 1 ], >+ [ -1, -1, 0 ] >+ ], >+ [ 3, 3, 0 ], >+ [ 0, 0, 3 ], >+ [ [ 1, 2 ], [ 1, 2 ], [ 1, 2 ] ], >+ 3 >+); >+ >+test_allocation( >+ "different costs 2", >+ [ >+ [ 0, 2, 2 ], >+ [ 1, 0, 1 ], >+ [ -1, -1, 0 ] >+ ], >+ [ 2, 2, 0 ], >+ [ 0, 0, 3 ], >+ [ [ 1, 2 ], [ 1, 2 ], [ 0, 2 ] ], >+ 4 >+); >+ >+test_allocation( >+ "allocation prohibited", >+ [ >+ [ 0, -1 ], >+ [ -1, 0 ], >+ ], >+ [ 2, 0 ], >+ [ 0, 2 ], >+ [], >+ 0 >+); >+ >+test_allocation( >+ "some allocation prohibited", >+ [ >+ [ 0, -1, -1 ], >+ [ -1, 0, 1 ], >+ [ -1, -1, 0 ] >+ ], >+ [ 2, 2, 0 ], >+ [ 0, 0, 4 ], >+ [ [ 1, 2 ], [ 1, 2 ] ], >+ 2 >+); >+ >+test_allocation( >+ "local allocation suboptimal", >+ [ >+ [ 0, 1, 1 ], >+ [ 1, 0, 10 ], >+ [ 1, 1, 0 ] >+ ], >+ [ 1, 2, 0 ], >+ [ 1, 0, 1 ], >+ [ [ 0, 2 ], [ 1, 0 ] ], >+ 2 >+); >+ >+test_allocation( >+ "prefer fewer fails", >+ [ >+ [ 0, 1, 1, 1, 1 ], >+ [ -1, 0, -1, -1, 1 ], >+ [ -1, -1, 0, -1, -1 ], >+ [ -1, -1, -1, 0, -1 ], >+ [ -1, -1, -1, -1, 0 ] >+ ], >+ [ 1, 1, 0, 0, 0 ], >+ [ 0, 0, 1, 1, 1 ], >+ [ [ 0, 2 ], [ 1, 4 ] ], >+ 2 >+); >+ >+test_allocation( >+ "large volume", >+ [ >+ [ (0) x 38, 23, 8 ], >+ [ (0) x 38, 31, 29 ], >+ [ (0) x 38, 10, 21 ], >+ [ (0) x 38, 98, 58 ], >+ [ (0) x 38, 24, 93 ], >+ [ (0) x 38, 62, 71 ], >+ [ (0) x 38, 75, 3 ], >+ [ (0) x 38, 86, 32 ], >+ [ (0) x 38, 45, 38 ], >+ [ (0) x 38, 83, 75 ], >+ [ (0) x 38, 45, 16 ], >+ [ (0) x 38, 60, 7 ], >+ [ (0) x 38, 83, 79 ], >+ [ (0) x 38, 54, 59 ], >+ [ (0) x 38, 78, 44 ], >+ [ (0) x 38, 77, 49 ], >+ [ (0) x 38, 9, 8 ], >+ [ (0) x 38, 13, 63 ], >+ [ (0) x 38, 82, 6 ], >+ [ (0) x 38, 62, 9 ], >+ [ (0) x 38, 22, 94 ], >+ [ (0) x 38, 58, 8 ], >+ [ (0) x 38, 39, 1 ], >+ [ (0) x 38, 69, 71 ], >+ [ (0) x 38, 13, 26 ], >+ [ (0) x 38, 3, 67 ], >+ [ (0) x 38, 39, 33 ], >+ [ (0) x 38, 60, 91 ], >+ [ (0) x 38, 46, 44 ], >+ [ (0) x 38, 71, 59 ], >+ [ (0) x 38, 66, 93 ], >+ [ (0) x 38, 38, 92 ], >+ [ (0) x 38, 72, 71 ], >+ [ (0) x 38, 0, 7 ], >+ [ (0) x 38, 44, 87 ], >+ [ (0) x 38, 74, 41 ], >+ [ (0) x 38, 21, 78 ], >+ [ (0) x 38, 90, 68 ], >+ [ (0) x 38, 96, 18 ], >+ [ (0) x 38, 47, 68 ], >+ ], >+ [ (5) x 38, (0) x 2 ], >+ [ (0) x 38, (40) x 2 ], >+ [ >+ ( [ 33, 38 ] ) x 5, # cost 0 >+ ( [ 22, 39 ] ) x 5, # cost 1 >+ ( [ 6, 39 ] ) x 5, # cost 3 >+ ( [ 25, 38 ] ) x 5, # cost 3 >+ ( [ 18, 39 ] ) x 5, # cost 6 >+ ( [ 11, 39 ] ) x 5, # cost 7 >+ ( [ 0, 39 ] ) x 5, # cost 8 >+ ( [ 21, 39 ] ) x 5, # cost 8 >+ ( [ 16, 38 ] ) x 5, # cost 9 >+ ( [ 19, 39 ] ) x 5, # cost 9 >+ ( [ 2, 38 ] ) x 5, # cost 10 >+ ( [ 17, 38 ] ) x 5, # cost 13 >+ ( [ 24, 38 ] ) x 5, # cost 13 >+ ( [ 10, 39 ] ) x 5, # cost 16 >+ ( [ 36, 38 ] ) x 5, # cost 21 >+ ( [ 20, 38 ] ) x 5, # cost 22 >+ ], >+ 745 >+); >-- >2.44.0
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 35826
:
161373
|
161956
|
163017
|
163018
|
163107
|
163641
|
163642
|
163643
|
163644
|
163645
|
164501
|
164502
|
164503
|
164504
|
164505
|
164964
|
164965
|
164966
|
164967
|
164968