From b37371736ef3619891224281d5c46928b0c98f13 Mon Sep 17 00:00:00 2001 From: "Dmitry E. Oboukhov" <unera@debian.org> Date: Mon, 2 Jul 2012 15:56:35 +0400 Subject: [PATCH] rewrite box.pl as usual test --- connector/perl/lib/MR/Tarantool/Box.pm | 13 +- connector/perl/t/02-two-servers.t | 29 +- connector/perl/t/03-box.t | 1057 ++++++++++++++++++++++++ connector/perl/t/data/box.t.cfg | 61 ++ connector/perl/t/data/init.lua | 33 +- 5 files changed, 1165 insertions(+), 28 deletions(-) create mode 100644 connector/perl/t/03-box.t create mode 100644 connector/perl/t/data/box.t.cfg diff --git a/connector/perl/lib/MR/Tarantool/Box.pm b/connector/perl/lib/MR/Tarantool/Box.pm index 265355623c..0873c4779d 100644 --- a/connector/perl/lib/MR/Tarantool/Box.pm +++ b/connector/perl/lib/MR/Tarantool/Box.pm @@ -639,6 +639,7 @@ sub Call { $x; } @$tuple ]; + $self->_chat ( msg => 22, payload => pack("L w/a* L(w/a*)*", $flags, $sp_name, scalar(@$tuple), @$tuple), @@ -741,9 +742,10 @@ sub Insert { my $chk_divisor = $namespace->{long_tuple} ? @$long_fmt : @$fmt; confess "Wrong fields number in tuple" if 0 != (@tuple - @$fmt) % $chk_divisor; for (0..$#tuple) { - confess "$self->{name}: ref in tuple $_=`$tuple[$_]'" if ref $tuple[$_]; + confess "$self->{name}: ref in tuple $_=`$tuple[$_]'" + if ref $tuple[$_]; no warnings 'uninitialized'; - Encode::_utf8_off($_) if Encode::is_utf8($_,0); + Encode::_utf8_off($tuple[$_]) if Encode::is_utf8($tuple[$_],0); if(exists $chkkey->{$_}) { if($chkkey->{$_}) { confess "$self->{name}: undefined key $_" unless defined $tuple[$_]; @@ -1217,9 +1219,8 @@ my %update_ops = ( or => OP_OR, splice => sub { confess "value for operation splice must be an ARRAYREF of <int[, int[, string]]>" if ref $_[0] ne 'ARRAY' || @{$_[0]} < 1; - $_[0]->[0] = 0x7FFFFFFF unless defined $_[0]->[0]; - $_[0]->[0] = pack 'l', $_[0]->[0]; - $_[0]->[1] = defined $_[0]->[1] ? pack 'l', $_[0]->[1] : ''; + $_[0]->[0] = pack 'l', defined($_[0]->[0]) ? $_[0]->[0] : 0x7FFF_FFFF; + $_[0]->[1] = pack 'l', defined($_[0]->[1]) ? $_[0]->[1] : 0x7FFF_FFFF; $_[0]->[2] = '' unless defined $_[0]->[2]; return (OP_SPLICE, [ pack '(w/a*)*', @{$_[0]} ]); }, @@ -1565,7 +1566,7 @@ L</UpdateMulti> can be given a field number in several ways: $box->UpdateMulti(5, [ 5 => set => $val ]) #3: set 6 to $val -=item an arrayref of [$index_of_folded_subtuple_int, $long_field_name_str_or_index_int] +=item an arrayref of [$index_of_folded_subtuple_int, $long_field_name_str_or_index_int] $box->UpdateMulti(5, [ [1,0] => set => $val ]) #3: set 6 to $val $box->UpdateMulti(5, [ [1,'d1'] => set => $val ]) #3: set 6 to $val diff --git a/connector/perl/t/02-two-servers.t b/connector/perl/t/02-two-servers.t index 3383b1f469..da95c5d98f 100644 --- a/connector/perl/t/02-two-servers.t +++ b/connector/perl/t/02-two-servers.t @@ -6,7 +6,7 @@ use utf8; use open qw(:std :utf8); use lib qw(lib ../lib); -use Test::More tests => 17; +use Test::More tests => 16; use Encode qw(decode encode); @@ -103,28 +103,15 @@ ok $box_union, 'connector is created'; ok $box1, 'connector is created'; ok $box2, 'connector is created'; + my %resps; -for (1 .. 2) { - my $tuples = $box_union->Call(tst_sleep => [], { unpack_format => '$$' }); +for (1 .. 20) { + my $tuples = $box_union->Call( + tst_server_name => [], { unpack_format => '$' } + ); $resps{ $tuples->[0][0] }++; } -ok exists $resps{first}, 'first server was used'; -ok exists $resps{second}, 'second server was used'; - - -%resps = (); -for (1 .. 100) { - my $started = time; - my $tuples = $box_union->Call(tst_sleep => [], { unpack_format => '$$' }); - my $done = time - $started; - - if ($done <= .3) { - $resps{ok}++; - } else { - $resps{fail}++; - } -} - -ok $resps{fail} < $resps{ok} / 8, 'random fails'; +is 1, scalar keys %resps, 'one server was used for all requests'; +is $resps{first} || $resps{second}, 20, 'all requests were handled'; diff --git a/connector/perl/t/03-box.t b/connector/perl/t/03-box.t new file mode 100644 index 0000000000..eb51050fcf --- /dev/null +++ b/connector/perl/t/03-box.t @@ -0,0 +1,1057 @@ +#!/usr/bin/perl + +# Tarantool/Box config below + +use strict; +use warnings; +BEGIN { + sub mPOP::Config::GetValue ($) { + die; + } +} +use FindBin qw($Bin); +use lib "$Bin"; +use lib "$Bin/../lib"; +use Carp qw/confess/; + +use Test::More tests => 372; +use Test::Exception; + +BEGIN { + warn $Bin; + use_ok 'Test::Tarantool'; + use_ok 'POSIX'; +} + +use List::MoreUtils qw/zip/; + +local $SIG{__DIE__} = \&confess; + +our $CLASS; +BEGIN { + $CLASS = $ENV{BOXCLASS} || 'MR::Tarantool::Box'; + eval "require $CLASS" or die $@; +} + +POSIX::setlocale(LC_ALL, 'C'); + +use constant ILL_PARAM => qr/Error (00000202|00002A02)/; +use constant TUPLE_NOT_EXISTS => qr/Error 00003102/; +use constant TUPLE_EXISTS => qr/Error 00003702/; +use constant INDEX_VIOLATION => qr/Error 00003802/; + +use constant NO_SUCCESS => qr/no success after/; + +use constant TOO_BIG_FIELD => qr/too big field/; + + +our $box; + + +my $tarantool_config = "$Bin/data/box.t.cfg"; +ok -r $tarantool_config, "-r $tarantool_config"; +my $tnt_srv = Test::Tarantool->run(cfg => 't/data/box.t.cfg'); +ok $tnt_srv, 'server instance created'; +diag $tnt_srv->log unless ok $tnt_srv->started, 'server is started'; + + +our $server = shift || $ENV{BOX} || + sprintf '127.0.0.1:%d', $tnt_srv->primary_port; + +ok $server, 'server address was defined ' . $server || 'undef'; + +our %opts = ( + debug => $ENV{DEBUG}||0, + ipdebug => $ENV{IPDEBUG}||0, + raise => 1, +); + +sub cleanup ($) { + my ($id) = @_; + die unless defined $id; + ok defined $box->Delete($id), 'delete of possible existing record'; + ok $box->Delete($id) == 0, 'delete of non existing record'; +} + +sub def_param { + my $format = shift || 'l& SSLL'; + return { servers => $server, + name => $CLASS, + namespaces => [ { + indexes => [ { + index_name => 'primary_id', + keys => [0], + }, { + index_name => 'primary_email', + keys => [1], + }, ], + namespace => 0, + format => $format, + default_index => 'primary_id', + name => 'main', + } ], + default_space => "main", + %opts, + } +} + +$box = $CLASS->new(def_param('l&SSLL&')); +ok $box->isa($CLASS), 'connect'; +cleanup 13; + +ok $box->Insert(13, q/some_email@test.mail.ru/, 1, 2, 3, 4, '777',{space => 'main'}), 'insert'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '777'], 'select/insert'; + +ok $box->Insert(13, q/some_email@test.mail.ru/, 2, 2, 3, 4, '666',{namespace => 'main'}), 'replace'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 2, 2, 3, 4, '666'], 'select/replace'; + +ok $box->Update(13, 3 => 1) == 1, 'update of some field'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 2, 1, 3, 4, '666'], 'select/update'; + +ok $box->Append( 13, 6 => 'APPEND') , 'append op'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 2, 1, 3, 4, '666APPEND'], 'select/append'; + +ok $box->Prepend(13, 6 => 'PREPEND'), 'prepend op'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 2, 1, 3, 4, 'PREPEND666APPEND'], 'select/prepend'; + +ok $box->Cutbeg(13, 6 => 2), 'cutbeg op'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 2, 1, 3, 4, 'EPEND666APPEND'], 'select/cutbeg'; + +ok $box->Cutend(13, 6 => 2), 'cutend op'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 2, 1, 3, 4, 'EPEND666APPE'], 'select/cutend'; + +ok $box->Substr(13, 6 => [3,4,'12345']), 'substr op'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 2, 1, 3, 4, 'EPE123456APPE'], 'select/substr'; + + + +ok $box->UpdateMulti(13, [6 => splice => [0]]), 'generic splice (offset = 0)'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 2, 1, 3, 4, ''], 'select/splice (offset = 0)'; + +cleanup 13; + +ok $box->Insert(13, q/some_email@test.mail.ru/, 1, 2, 3, 4, '123456789'), 'insert'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '123456789'], 'select/insert'; + +throws_ok sub { $box->UpdateMulti(13, [6 => splice => [-10]]) }, ILL_PARAM, "splice/bad_params_1"; + +ok $box->UpdateMulti(13, [6 => splice => [100]]), "splice/big_offset"; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '123456789'], 'select'; + +ok $box->UpdateMulti(13, [6 => splice => [5]]), "splice/cut_tail_pos_offset"; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '12345'], 'select'; + +ok $box->UpdateMulti(13, [6 => splice => [-2]]), "splice/cut_tail_neg_offset"; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '123'], 'select'; + +ok $box->Insert(13, q/some_email@test.mail.ru/, 1, 2, 3, 4, '123456789'), 'replace'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '123456789'], 'select'; + +ok $box->UpdateMulti(13, [6 => splice => [8, 1000]]), "splice/big_pos_length"; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '12345678'], 'select'; + +ok $box->UpdateMulti(13, [6 => splice => [1, -1000]]), "splice/big_neg_length"; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '12345678'], 'select'; + +ok $box->Insert(13, q/some_email@test.mail.ru/, 1, 2, 3, 4, '123456789'), 'replace'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '123456789'], 'select'; + +ok $box->UpdateMulti(13, [6 => splice => [0x7fffffff]]), "splice/max_offset"; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '123456789'], 'select'; + +ok $box->UpdateMulti(13, [6 => splice => [1, 2]], [6 => splice => [-2, -1, 'qwe']]), "splice/multi"; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '14567qwe9'], 'select'; + + + +cleanup 13; +cleanup 14; +throws_ok sub { $box->Replace(13, q/some_email@test.mail.ru/, 5, 5, 5, 5, '555555555')}, TUPLE_NOT_EXISTS, 'replace'; + +ok $box->Add(13, q/some_email@test.mail.ru/, 1, 2, 3, 4, '123456789'), 'add'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '123456789'], 'select/add'; + +throws_ok sub { $box->Add(13, q/some_email@test.mail.ru/, 5, 5, 5, 5, '555555555')}, TUPLE_EXISTS, 'add2'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '123456789'], 'select/add2'; +is_deeply scalar $box->Select(q/some_email@test.mail.ru/, {use_index => "primary_email"}), [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '123456789'], 'select/add2'; + +throws_ok sub { $box->Add(14, q/some_email@test.mail.ru/, 5, 5, 5, 5, '555555555')}, INDEX_VIOLATION, 'add3'; + +ok $box->Add(14, q/some1email@test.mail.ru/, 1, 2, 3, 4, '123456789'), 'add4'; +is_deeply scalar $box->Select(14), [14, 'some1email@test.mail.ru', 1, 2, 3, 4, '123456789'], 'select/add4'; +is_deeply scalar $box->Select(q/some1email@test.mail.ru/, {use_index => "primary_email"}), [14, 'some1email@test.mail.ru', 1, 2, 3, 4, '123456789'], 'select/add4'; + +throws_ok sub { $box->Replace(13, q/some1email@test.mail.ru/, 6, 6, 6, 6, '666666666')}, INDEX_VIOLATION, 'replace'; +throws_ok sub { $box->Set(13, q/some1email@test.mail.ru/, 6, 6, 6, 6, '666666666')}, INDEX_VIOLATION, 'set'; + +ok $box->Set(13, q/some_email@test.mail.ru/, 5, 5, 5, 5, '555555555'), 'set'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 5, 5, 5, 5, '555555555'], 'select/set'; + +ok $box->Replace(13, q/some_email@test.mail.ru/, 1, 2, 3, 4, '123456789'), 'replace'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '123456789'], 'select/replace'; + + +is_deeply [$box->Select([13], {raise => 0, hash_by => 0, raw => 1})], [{13 => [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '123456789']}], 'select/rawhash1'; +is_deeply [$box->Select([13,14], {raise => 0, hash_by => 0, raw => 1})], [{13 => [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '123456789'], 14 => [14, 'some1email@test.mail.ru', 1, 2, 3, 4, '123456789']}], 'select/rawhash2'; + + +do { + my $continuation = $box->Select(13,{ return_fh => 1 }); + ok $continuation, "select/continuation"; + + my $rin = ''; + vec($rin,$continuation->{fh}->fileno,1) = 1; + my $ein = $rin; + ok 0 <= select($rin,undef,$ein,2), "select/continuation/select"; + + my $res = $continuation->{continue}->(); + use Data::Dumper; + is_deeply $res, [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '123456789'], "select/continuation/result"; +}; + +our $ANYEVENT = 1 && eval { require AnyEvent; 1 }; +SKIP:{ + skip "AnyEvent not found", 60 unless $ANYEVENT; + + local $opts{raise} = 0; + $box = $CLASS->new(def_param('l&SSLL&')); + + my $tt = [ [1, 'rtokarev@corp.mail.ru', 11, 111, 1111, 11111, "1111111111111"], + [2, 'vostrikov@corp.mail.ru', 22, 222, 2222, 22222, "2222222222222"], + [3, 'aleinikov@corp.mail.ru', 33, 333, 3333, 33333, "3333333333333"], + [4, 'roman.s.tokarev@gmail.com', 44, 444, 4444, 44444, "4444444444444"], + [5, 'vostrIIIkov@corp.mail.ru', 55, 555, 5555, 55555, "5555555555555"] ]; + + foreach my $tuple (@$tt) { + cleanup $tuple->[0]; + } + + AnyEvent->now_update; + my $cv = AnyEvent->condvar; + foreach my $tuple (@$tt) { + $cv->begin; + ok $box->Insert(@$tuple, {callback => sub { ok $_[0], "async/insert$tuple->[0]/result"; $cv->end; }}), "async/insert$tuple->[0]"; + } + $cv->recv; + + + AnyEvent->now_update; + $cv = AnyEvent->condvar; + $cv->begin; + ok $box->Select(1,2,3,{callback => sub { + my ($res) = @_; + $cv->end; + is_deeply $res, [@$tt[0,1,2]], "async/select1/result"; + }}), "async/select1"; + + $cv->begin; + ok $box->Select(4,5,{ callback => sub { + my ($res) = @_; + $cv->end; + is_deeply $res, [@$tt[3,4]], "async/select2/result"; + }}), "async/select2"; + + $cv->recv; + + + AnyEvent->now_update; + $cv = AnyEvent->condvar; + foreach my $tuple (@$tt) { + $tuple->[4] += 10000; + $cv->begin; + ok $box->UpdateMulti($tuple->[0], [ 4 => add => 10000 ], {callback => sub { ok $_[0], "async/update1-$tuple->[0]/result"; $cv->end; }}), "async/update1-$tuple->[0]"; + } + $cv->begin; + ok $box->Select((map{$_->[0]}@$tt),{ callback => sub { + my ($res) = @_; + $cv->end; + is_deeply $res, $tt, "async/update1-select/result"; + }}), "async/update1-select"; + $cv->recv; + + AnyEvent->now_update; + $cv = AnyEvent->condvar; + foreach my $tuple (@$tt) { + $tuple->[4] += 10000; + $cv->begin; + ok $box->UpdateMulti($tuple->[0], [ 4 => add => 10000 ], {want_result => 1, callback => sub { is_deeply $_[0], $tuple, "async/update2-$tuple->[0]/result"; $cv->end; }}), "async/update2-$tuple->[0]"; + } + $cv->begin; + ok $box->Select((map{$_->[0]}@$tt),{ callback => sub { + my ($res) = @_; + $cv->end; + is_deeply $res, $tt, "async/update2-select/result"; + }}), "async/update2-select"; + $cv->recv; + + AnyEvent->now_update; + $cv = AnyEvent->condvar; + foreach my $tuple (@$tt) { + $cv->begin; + ok $box->Delete($tuple->[0], {want_result => 1, callback => sub { is_deeply $_[0], $tuple, "async/delete-$tuple->[0]/result"; $cv->end; }}), "async/delete-$tuple->[0]"; + } + $cv->begin; + ok $box->Select((map{$_->[0]}@$tt),{ callback => sub { + my ($res) = @_; + $cv->end; + is_deeply $res, [], "async/delete-select/result"; + }}), "async/delete-select"; + $cv->recv; +} + +sub countwarn { + my ($qr, $counter) = @_; + return sub { + ++$$counter if $_[0] =~ $qr; +# note "caught warning: ", @_; + }; +}; + +do { + local $server = sprintf "127.0.0.1:%s", Test::Tarantool::_find_free_port; + local $opts{raise} = 0; + my $try = 3; + + my $counter = 0; + local $SIG{__WARN__} = countwarn(qr/refused/i, \$counter); + + my $box = $CLASS->new(def_param('l&SSLL&')); + + throws_ok sub{my$x=$box->Select(13,{ want => "arrayref", raise => 1 })}, NO_SUCCESS, "reject/select/raise/sync"; + is $counter, $try, "reject/select/raise/sync/counter"; + $counter = 0; + + ok !$box->Select(13,{ want => "arrayref", raise => 0 }), "reject/select/noraise/sync"; + ok $counter == $try, "reject/select/noraise/sync/counter"; + $counter = 0; + + my $continuation = $box->Select(13,{ return_fh => 1, raise => 0 }); + ok !$continuation, "reject/select/continuation"; + ok $counter == 1, "reject/select/continuation/counter"; + $counter = 0; + + + SKIP:{ + skip "AnyEvent not found", 5 unless $ANYEVENT; + + AnyEvent->now_update; + my $cv = AnyEvent->condvar; + $cv->begin; + ok $box->Select(4,5,{ callback => sub { + my ($res) = @_; + $cv->end; + ok !$res, "reject/select/async/noraise/cb"; + ok $box->Error, "reject/select/async/noraise/cb/error"; + ok $box->ErrorStr, "reject/select/async/noraise/cb/errorstr"; + }}), "reject/select/async/noraise"; + + $cv->recv; + ok $counter == $try, "reject/select/async/noraise/counter"; + $counter = 0; + } +}; + +do { + my $pid; + local $SIG{INT} = $SIG{TERM} = sub { kill 'TERM', $pid }; + + $pid = fork(); + die unless defined $pid; + unless($pid) { + $0 = "$0 <SERVER>"; + my $stop = 0; + my $h; + my $l = IO::Socket::INET->new( + LocalAddr => '127.0.0.1', + LocalPort => 1111, + Proto => 'tcp', + Listen => 10, + Blocking => 1, + ReuseAddr => 1, + ) or die $!; + $SIG{INT} = $SIG{TERM} = sub { ++$stop; close $l; close $h; exit; }; + while(!$stop) { + $h = $l->accept; + my $data; + while($h->read($data,1024) > 0) { 0; } + close $h; + } + exit; + } + + + local $server = "127.0.0.1:1111"; + local $opts{raise} = 0; + local $opts{timeout} = 0.1; + local $opts{select_timeout} = 0.1; + + my $try = 3; + + my $counter = 0; + local $SIG{__WARN__} = countwarn(qr/timed? ?out/i, \$counter); + + my $box = $CLASS->new(def_param('l&SSLL&')); + + sleep 1; + + throws_ok sub{my$x=$box->Select(13,{ want => "arrayref", raise => 1 })}, NO_SUCCESS, "timeout1/select/raise/sync"; + ok $counter == $try, "timeout1/select/raise/sync/counter"; + $counter = 0; + + ok !$box->Select(13,{ want => "arrayref", raise => 0 }), "timeout1/select/noraise/sync"; + ok $counter == $try, "/counter"; + $counter = 0; + + my $continuation = $box->Select(13,{ return_fh => 1, raise => 0 }); + ok $continuation, "timeout1/select/continuation"; + ok !$continuation->{continue}->(), "timeout1/select/continuation/result"; + ok $counter == 1, "timeout1/select/continuation/counter"; + $counter = 0; + + + SKIP:{ + skip "AnyEvent not found", 5 unless $ANYEVENT; + + AnyEvent->now_update; + my $cv = AnyEvent->condvar; + $cv->begin; + ok $box->Select(4,5,{ callback => sub { + my ($res) = @_; + $cv->end; + ok !$res, "timeout1/select/async/noraise/cb"; + ok $box->Error, "timeout1/select/async/noraise/cb/error"; + ok $box->ErrorStr, "timeout1/select/async/noraise/cb/errorstr"; + }}), "timeout1/select/async/noraise"; + + $cv->recv; + ok $counter == $try, "timeout1/select/async/noraise/counter"; + $counter = 0; + } + + kill 'TERM', $pid; +}; + +do { + my $pid; + local $SIG{INT} = $SIG{TERM} = sub { kill 'TERM', $pid }; + + $pid = fork(); + die unless defined $pid; + unless($pid) { + $0 = "$0 <SERVER>"; + my $stop = 0; + my $h; + my @ok = (0,0,1,0,0,1,1,0,0,1); + my $l = IO::Socket::INET->new( + LocalAddr => '127.0.0.1', + LocalPort => 1111, + Proto => 'tcp', + Listen => 10, + Blocking => 1, + ReuseAddr => 1, + ) or die $!; + my ($host, $port) = split /:/, $server; + my $box = IO::Socket::INET->new( + PeerAddr => $host, + PeerPort => $port, + Proto => 'tcp', + Blocking => 1, + ) or die; + $SIG{INT} = $SIG{TERM} = sub { ++$stop; close $l; close $h; close $box; exit; }; + + while(!$stop) { + $h = $l->accept; + $h->blocking(1); + my $data = ''; + if (shift @ok) { + while(!$stop) { + $h->blocking(0); + $h->read($data,1024,length$data); + if(length$data) { + $h->blocking(1); + $h->read($data,12-length$data,length$data) while length $data < 12; + my ($len) = unpack 'x4L', $data; + $h->read($data,12+$len-length$data,length$data) while length $data < 12+$len; + $box->write($data); + + $data = ''; + $box->read($data,12-length$data, length$data) while length $data < 12; + ($len) = unpack 'x4L', $data; + $box->read($data,12+$len-length$data,length$data) while length $data < 12+$len; + $h->write($data); + close $h; + last; + } + sleep 0.1; + } + } else { + while($h->read($data,1024) > 0) { 0; } + } + close $h; + } + close $l; + close $box; + exit; + } + + + local $server = "127.0.0.1:1111"; + local $opts{raise} = 0; + local $opts{timeout} = 0.1; + local $opts{select_timeout} = 0.1; + + my $try = 2; + + my $counter = 0; + local $SIG{__WARN__} = countwarn(qr/timed? ?out/i, \$counter); + + my $box = $CLASS->new(def_param('l&SSLL&')); + + sleep 1; + + is_deeply $box->Select(13,{ want => "arrayref", raise => 1 }), [[13, 'some_email@test.mail.ru', 1, 2, 3, 4, '123456789']], "timeout2/select/raise/sync"; + ok !$box->Error, "timeout2/select/raise/sync/error"; + ok !$box->ErrorStr, "timeout2/select/raise/sync/errorstr"; + ok $counter == $try, "timeout2/select/raise/sync/counter"; + $counter = 0; + + is_deeply $box->Select(13,{ want => "arrayref", raise => 0 }), [[13, 'some_email@test.mail.ru', 1, 2, 3, 4, '123456789']], "timeout2/select/noraise/sync"; + ok !$box->Error, "timeout2/select/noraise/sync/error"; + ok !$box->ErrorStr, "timeout2/select/noraise/sync/errorstr"; + ok $counter == $try, "timeout2/select/noraise/sync/counter"; + $counter = 0; + + my $continuation = $box->Select(13,{ return_fh => 1, raise => 0, want => 'arrayref' }); + ok $continuation, "timeout2/select/continuation"; + is_deeply $continuation->{continue}->(), [[13, 'some_email@test.mail.ru', 1, 2, 3, 4, '123456789']], "timeout2/select/continuation/result"; + ok !$box->Error, "timeout2/select/continuation/error"; + ok !$box->ErrorStr, "timeout2/select/continuation/errorstr"; + ok $counter == 0, "timeout2/select/continuation/counter"; + $counter = 0; + + + SKIP:{ + skip "AnyEvent not found", 5 unless $ANYEVENT; + + AnyEvent->now_update; + my $cv = AnyEvent->condvar; + $cv->begin; + ok $box->Select(13,{ callback => sub { + my ($res) = @_; + $cv->end; + is_deeply $res, [[13, 'some_email@test.mail.ru', 1, 2, 3, 4, '123456789']], "timeout2/select/async/noraise/cb"; + ok !$box->Error, "timeout2/select/async/noraise/cb/error"; + ok !$box->ErrorStr, "timeout2/select/async/noraise/cb/errorstr"; + }}), "timeout2/select/async/noraise"; + + $cv->recv; + ok $counter == $try, "timeout2/select/async/noraise/counter"; + $counter = 0; + } + + kill 'TERM', $pid; +}; + + +$box = $CLASS->new(def_param); +ok $box->isa($CLASS), 'connect'; +cleanup 13; + +ok $box->Insert(13, q/some_email@test.mail.ru/, 1, 2, 3, 4), 'insert'; +ok $box->Insert(13, q/some_email@test.mail.ru/, 2, 2, 3, 4), 'replace'; + +ok $box->Update(13, 3 => 1) == 1, 'update of some field'; +is_deeply scalar $box->Select(13), [13, 'some_email@test.mail.ru', 2, 1, 3, 4], 'select/update'; + +cleanup 14; +ok $box->Insert(14, 'aaa@test.mail.ru', 0, 0, 1, 1), 'insert'; + +is_deeply scalar $box->Select(14), [14, 'aaa@test.mail.ru', 0, 0, 1, 1], 'select'; +is_deeply scalar $box->Select('aaa@test.mail.ru', {use_index => 'primary_email'}), [14, 'aaa@test.mail.ru', 0, 0, 1, 1], 'select'; + + +ok $box->Update(14, 2 => 2), 'update of some field'; + + +is_deeply scalar $box->Select(14), [14, 'aaa@test.mail.ru', 2, 0, 1, 1], 'select'; +is_deeply scalar $box->Select('aaa@test.mail.ru', {use_index => 'primary_email'}), [14, 'aaa@test.mail.ru', 2, 0, 1, 1], 'select'; + + + +$box = $CLASS->new(def_param); +ok $box->isa($CLASS), 'connect'; + + + +for (1..3) { + %MR::IProto::sockets = (); + %MR::IProto::sockets = (); + $box = $CLASS->new(def_param); + ok $box->isa($CLASS), 'connect'; + + cleanup 14; + + ok $box->Insert(14, 'aaa@test.mail.ru', 0, 0, 1, 1), 'insert'; + + ok $box->Update(14, 2 => 2), 'update of field'; +} + +# interfaces +# 0 1 2 3 4 5 +my @tuple = (14, 'aaa@test.mail.ru', 0, 0, 1, 0); +my $id = $tuple[0]; +my $email = $tuple[1]; + +cleanup 14; +ok $box->Insert(@tuple), 'insert'; + +is_deeply scalar $box->Select($id), \@tuple, 'select'; + + +### Bit ops + +# zero namespace +$box->Bit($id, 5, bit_set => (1 << 15)); +$tuple[5] |= (1 << 15); +is_deeply scalar $box->Select($id), \@tuple, 'bit set'; + +$box->Bit($id, 5, bit_clear => (1 << 15)); +$tuple[5] &= ~(1 << 15); +is_deeply scalar $box->Select($id), \@tuple, 'bit clear'; + +$box->Bit($id, 5, bit_set => (1 << 15), bit_clear => (1 << 16)); +$tuple[5] |= (1 << 15); +$tuple[5] &= ~(1 << 16); +is_deeply scalar $box->Select($id), \@tuple, 'bit_set + bit_clear'; + +$box->Bit($id, 5, set => 4095, bit_set => (1 << 5), bit_clear => (1 << 6)); +$tuple[5] = 4095; +$tuple[5] |= (1 << 5); +$tuple[5] &= ~(1 << 6); +is_deeply scalar $box->Select($id), \@tuple, 'set + bit_set + bit_clear'; + +$box->Bit($id, 5, set => 123); +$tuple[5] = 123; +is_deeply scalar $box->Select($id), \@tuple, 'set via Bit'; + + + + +## Num ops + +# zero namespace +$box->Num($id, 5, num_add => 1); +$tuple[5] += 1; +is_deeply scalar $box->Select($id), \@tuple, 'num_add'; + +$box->Num($id, 5, num_sub => 1); +$tuple[5] -= 1; +is_deeply scalar $box->Select($id), \@tuple, 'num_sub'; + +$box->Num($id, 5, set => 123); +$tuple[5] = 123; +is_deeply scalar $box->Select($id), \@tuple, 'set via Num'; + +$box->Num($id, 5, set => 4095, num_add => 5, num_sub => 10); +$tuple[5] = 4095; +$tuple[5] += 5; +$tuple[5] -= 10; +is_deeply scalar $box->Select($id), \@tuple, 'set + num_add + num_sub'; + + +### Bit & Num opt parse +throws_ok sub { $box->Bit($id, 5, update => 123) }, qr/unknown op 'update'/, 'bad op for Bit'; +throws_ok sub { $box->Bit($id, 5, hxxxxx => 123) }, qr/unknown op 'hxxxxx'/, 'bad op for Bit'; +throws_ok sub { $box->Num($id, 5, update => 123) }, qr/unknown op 'update'/, 'bad op for Num'; +throws_ok sub { $box->Num($id, 5, hxxxxx => 123) }, qr/unknown op 'hxxxxx'/, 'bad op for Num'; + + +### AndXorAdd + +$box->AndXorAdd($id, 5, 4095, 5, 10); +$tuple[5] &= 4095; +$tuple[5] ^= 5; +$tuple[5] += 10; +is_deeply scalar $box->Select($id), \@tuple, 'AndXorAdd namespace=1'; + + +### key parser +throws_ok sub { my $a = $box->Select('some@test.mai;.ru') }, qr/not numeric key/, 'validation of $key'; +throws_ok sub { my $a = $box->Num('some@test.mai;.ru', 5, num_add => 1) }, qr/not numeric key/, 'validation of $key'; +throws_ok sub { my $a = $box->Delete('some@test.mai;.ru') }, qr/not numeric key/, 'validation of $key'; + +throws_ok sub { my $a = $box->Select('1.1') }, qr/not numeric key/, 'validation of $key'; +throws_ok sub { my $a = $box->Num('1.1', 5, num_add => 1) }, qr/not numeric key/, 'validation of $key'; +throws_ok sub { my $a = $box->Delete('1.1') }, qr/not numeric key/, 'validation of $key'; + +throws_ok sub { my $a = $box->Select('') }, qr/not numeric key/, 'validation of $key'; +throws_ok sub { my $a = $box->Num('', 5, num_add => 1) }, qr/not numeric key/, 'validation of $key'; +throws_ok sub { my $a = $box->Delete('') }, qr/not numeric key/, 'validation of $key'; + + +## Detete +cleanup $id; +ok $box->Insert(@tuple), 'insert'; +ok $box->Delete($id), 'delete by id'; +ok 0 == $box->Delete($id), 'delete by id'; + +## UpdateMulti +cleanup $id; +ok $box->Insert(@tuple), 'insert'; + +ok $box->UpdateMulti($id, ([5 => set => 1]) x 127), 'big update multi'; +# CANT TEST throws_ok sub { $box->UpdateMulti($id, ([5 => set => 1]) x 128) }, ILL_PARAM, 'too big update multi'; +throws_ok sub { $box->UpdateMulti($id, ([5 => set => 1]) x 129) }, qr/too many op/, 'too big update multi'; +{ + my $box = $CLASS->new(def_param(q/l& SSL&/)); + my @tuple = @tuple; + ok $box->isa($CLASS), 'connect'; + + ok $box->UpdateMulti($id, map { [5 => set => 'x' x 127] } (1..127)), 'big update multi'; + $tuple[5] = 'x' x 127; + is_deeply scalar $box->Select($id), \@tuple, 'result of update multi'; + + # DISABLED since no BER IN BOX yet + # ok $box->UpdateMulti($id, ([5 => set => 'x' x 6554])), 'very big update multi'; + # $tuple[5] = 'x' x 6554; + # is_deeply scalar $box->Select($id), \@tuple, 'result of update multi'; +} + +{ + my $box = $CLASS->new(def_param(q/l& &&&&/)); + my @tuple = @tuple; + my $id = $tuple[0]; + ok $box->isa($CLASS), 'connect'; + + ok $box->UpdateMulti($id, [2 => set => 'ab'], [5 => set => 'z' x 127]), 'update multi no teplate'; + $tuple[2] = 'ab'; + $tuple[5] = 'z' x 127; + my @r = @{$box->Select($id)}; + is_deeply scalar [@r[2,5]], [@tuple[2,5]] , 'result of update multi'; +} + +throws_ok sub { $box->UpdateMulti($id, [5 => and_xor_add => [1,2,3,4]]) }, qr/bad op <and_xor_add>/, 'bad and_xor_add'; +throws_ok sub { $box->UpdateMulti($id, [5 => and_xor_add => [1,2]]) }, qr/bad op <and_xor_add>/, 'bad and_xor_add'; +throws_ok sub { $box->UpdateMulti($id, [5 => and_xor_add => 1]) }, qr/bad op <and_xor_add>/, 'bad and_xor_add'; +throws_ok sub { $box->UpdateMulti($id, [1, 2, 3]) }, qr/bad op/, 'bad op'; +throws_ok sub { $box->UpdateMulti($id, [1, 2]) }, qr/bad op/, 'bad op'; +throws_ok sub { $box->UpdateMulti($id, '') }, qr/bad op/, 'bad op'; + +{ + my @tuple = (14, 'aaa@test.mail.ru', 0, 0, 1, 1); + + cleanup 14; + cleanup 15; + ok $box->Insert(@tuple), 'insert'; + + my @op = ([4 => num_add => 300], [5 => num_sub => 100], [5 => set => 1414], [5 => bit_set => 1 | 2], [5 => bit_clear => 2]); + ok $box->UpdateMulti($tuple[0], @op), 'update multi'; + my @tuple_new = @tuple; + $tuple_new[4] += 300; + $tuple_new[5] -= 100; + $tuple_new[5] = 1414; + $tuple_new[5] |= 1 | 2; + $tuple_new[5] &= ~2; + is_deeply scalar $box->Select(14), \@tuple_new, 'update multi'; + + cleanup 14; + ok $box->Insert(@tuple), 'insert'; + is_deeply scalar $box->UpdateMulti($tuple[0], @op, {want_result => 1}), \@tuple_new, 'update multi, want_result'; + + ok 0 == $box->UpdateMulti(15, [4 => num_add => 300], [5 => num_sub => 100], [5 => set => 1414]), 'update multi of nonexist'; +} + + +## Select +throws_ok sub { $box->Select(1) }, qr/void context/, 'select in void context'; +throws_ok sub { my $a = $box->Select(1,2,3) }, qr/too many keys in scalar context/, 'too many keys in scalar context'; + +cleanup $tuple[0]; +ok $box->Insert(@tuple); +is_deeply [$box->Select($tuple[0], $tuple[0])], [\@tuple, \@tuple], 'select in scalar context'; +is_deeply scalar $box->Select($tuple[0], $tuple[0], {want => 'arrayref'}), [\@tuple, \@tuple], 'select want => arrrayref'; + +{ + my $box = $CLASS->new( + { %{def_param()}, + hashify => sub { + my ($namespace, $row) = @_; + my $i = 1; + foreach (@$row) { + my @tuple = @{$_}; + $_ = {}; + foreach my $k (@tuple) { + $_->{$i++} = $k; + } + } + }, + } + ); + + my $hash = {}; + my $i = 1; + $hash->{$i++} = $_ foreach @tuple; + + is_deeply $box->Select($tuple[0]), $hash, 'select with hashify'; +} + +## Tree indexes +sub def_param1 { + my $format = 'l&l'; + return { servers => $server, + namespaces => [ { + indexes => [ { + index_name => 'primary_num1', + keys => [0], + }, { + index_name => 'secondary_str2', + keys => [1], + }, { + index_name => 'secondary_complex', + keys => [1, 2], + } ], + namespace => 26, + format => $format, + default_index => 'primary_num1', + } ], + %opts, + } +} + +$box = $CLASS->new(def_param1); +ok $box->isa($CLASS), 'connect'; + +my @tuple1 = (13, 'mail.ru', 123); +cleanup $tuple1[0]; + +my @tuple2 = (14, 'mail.ru', 456); +cleanup $tuple2[0]; + +ok $box->Insert(@tuple1); + +is_deeply [$box->Select([[$tuple1[0]]])], [\@tuple1], 'select by primary_num1 index'; +is_deeply [$box->Select([[$tuple1[1]]], { use_index => 'secondary_str2' })], [\@tuple1], 'select by secondary_str2 index'; +is_deeply [$box->Select([[$tuple1[1], $tuple1[2]]], { use_index => 'secondary_complex' })], [\@tuple1], 'select by secondary_complex index'; +is_deeply [$box->Select([[$tuple1[1]]], { use_index => 'secondary_complex' })], [\@tuple1], 'select by secondary_complex index, partial key'; + +ok $box->Insert(@tuple2); + +is_deeply [$box->Select([[$tuple2[0]]])], [\@tuple2], 'select by primary_num1 index'; +is_deeply [$box->Select([[$tuple1[1]]], { use_index => 'secondary_str2', limit => 2, offset => 0 })], [\@tuple1, \@tuple2], 'select by secondary_str2 index'; +is_deeply [$box->Select([[$tuple2[1], $tuple2[2]]], { use_index => 'secondary_complex' })], [\@tuple2], 'select by secondary_complex index'; + +## Check index constrains +sub def_param_bad { + my $format = 'l&&'; + return { servers => $server, + spaces => [ { + indexes => [ { + index_name => 'primary_num1', + keys => [0], + }, { + index_name => 'secondary_str2', + keys => [1], + }, { + index_name => 'secondary_complex', + keys => [1, 2], + } ], + namespace => 26, + format => $format, + default_index => 'primary_num1', + } ], + %opts, + } +} + +$box = $CLASS->new(def_param_bad); +ok $box->isa($CLASS), 'connect'; + +my @tuple_bad = (13, 'mail.ru', '123'); +cleanup $tuple_bad[0]; +throws_ok sub { $box->Insert(@tuple_bad) }, ILL_PARAM, "index_constains/bad_field_type"; + + +## Check unique tree index +sub def_param_unique { + my $format = 'l&&&'; + return { servers => $server, + namespaces => [ { + indexes => [ { + index_name => 'id', + keys => [0], + }, { + index_name => 'email', + keys => [1], + }, { + index_name => 'firstname', + keys => [2], + }, { + index_name => 'lastname', + keys => [3], + } , { + index_name => 'fullname', + keys => [2, 3] + } ], + space => 27, + format => $format, + default_index => 'id', + } ], + %opts, + } +} + +$box = $CLASS->new(def_param_unique); +ok $box->isa($CLASS), 'connect'; + +my $tuples = [ [1, 'rtokarev@corp.mail.ru', 'Roman', 'Tokarev'], + [2, 'vostrikov@corp.mail.ru', 'Yuri', 'Vostrikov'], + [3, 'aleinikov@corp.mail.ru', 'Roman', 'Aleinikov'], + [4, 'roman.s.tokarev@gmail.com', 'Roman', 'Tokarev'], + [5, 'vostrikov@corp.mail.ru', 'delamon', 'delamon'] ]; + +foreach my $tuple (@$tuples) { + cleanup $tuple->[0]; +} + +foreach my $tuple (@$tuples) { + if ($tuple == $tuples->[-1] || $tuple == $tuples->[-2]) { + throws_ok sub { $box->Insert(@$tuple) }, INDEX_VIOLATION, "unique_tree_index/insert \'$tuple->[0]\'"; + } else { + ok $box->Insert(@$tuple), "unique_tree_index/insert \'$tuple->[0]\'"; + } +} + + +my @res = $box->Select([map $_->[0], @$tuples], { limit => 100 }); +foreach my $r (@res) { + ok sub { return $r->[0] != $tuples->[-1] && $r->[0] != $tuples->[-2] }, + 'invalid insert into unique index'; +} + +my $flds; +my $lflds; +BEGIN{ $flds = [qw/ f1 f2 f3 f4 LL /] } +BEGIN{ $lflds = [qw/ l1 l2 l3 /] } + { + package TestBox; + use MR::Tarantool::Box::Singleton; + use base 'MR::Tarantool::Box::Singleton'; + + BEGIN { + __PACKAGE__->mkfields(@$flds); + __PACKAGE__->mklongfields(@$lflds); + } + + sub SERVER { $server } + sub REPLICAS { '' } + + sub SPACES {[{ + space => 27, + indexes => [ { + index_name => 'primary_id', + keys => [TUPLE_f1], + } ], + format => 'l&$&(&$&)*', + default_index => 'primary_id', + }]} + + } + + + +$box = 'TestBox'; +#$box = $CLASS->new(def_param_flds); +#ok $box->isa($CLASS), 'connect'; +do { + my $tuples = [ + [1, "asdasdasd1", "qqq\xD0\x8Eqqq1", "ww\xD0\x8Eww1", "la\xD0\x8Elalala11", "la\xD0\x8Elala11", "lala11"], + [2, "asdasdasd2", "qqq\xD0\x8Eqqq2", "ww\xD0\x8Eww2", "la\xD0\x8Elalala21", "la\xD0\x8Elala21", "lala21", "lalalala22", "lalala22", "lala22", "lalalala23", "lalala23", "lala23"], + [3, "asdasdasd3", "qqq\xD0\x8Eqqq3", "ww\xD0\x8Eww3", "la\xD0\x8Elalala31", "la\xD0\x8Elala31", "lala31", "lalalala32", "lalala32", "lala32"], + [4, "asdasdasd4", "qqq\xD0\x8Eqqq4", "ww\xD0\x8Eww4", "la\xD0\x8Elalala41", "la\xD0\x8Elala41", "lala41", "lalalala42", "lalala42", "lala42", "lalalala43", "lalala43", "lala43"], + [5, "asdasdasd5", "qqq\xD0\x8Eqqq5", "ww\xD0\x8Eww5", "la\xD0\x8Elalala51", "la\xD0\x8Elala51", "lala51"], + ]; + + my $check = []; + for my $tuple (@$tuples) { + my $i = 0; + Encode::_utf8_on($tuple->[2+$i*3]), ++$i while @$tuple > 1+$i*3; + + my $t = { zip @{[@$flds[0..($#$flds-1)]]}, @{[@$tuple[0..($#$flds-1)]]} }; + my $l = $t->{$flds->[-1]} = []; + + $i = 1; + push(@$l, { zip @$lflds, @{[@$tuple[(1+$i*3)..(1+$i*3+2)]]} }), ++$i while @$tuple > 1+$i*3; + + push @$check, $t; + } + + + foreach my $tuple (@$tuples) { + cleanup $tuple->[0]; + } + + foreach my $i (0 .. $#$tuples) { + is_deeply [$box->Insert(@{$tuples->[$i]}, {want_inserted_tuple => 1})], [$check->[$i]], "flds/insert \'$tuples->[$i]->[0]\'"; + } + + is_deeply [$box->Select([[$tuples->[0]->[0]]])], [$check->[0]], 'select by primary_num1 index'; + + my $res; + is_deeply [$res=$box->Select([map {$_->[0]} @$tuples],{want=>'arrayref'})], [$check], 'select all'; + # print $res->[0]->{f3}, "\n"; + # print $check->[0]->{f3}, "\n"; + ok $res->[$_]->{f3} eq $check->[$_]->{f3}, "utf8chk" for 0..$#$tuples; + ok $res->[$_]->{LL}->[0]->{l2} eq $check->[$_]->{LL}->[0]->{l2}, "utf8chklong" for 0..$#$tuples; + + is_deeply [$box->UpdateMulti($tuples->[2]->[0],[ $flds->[3] => set => $tuples->[2]->[3] ],{want_updated_tuple => 1})], [$check->[2]], 'update1'; + ok $box->UpdateMulti($tuples->[2]->[0],[ $flds->[3] => set => $tuples->[2]->[3] ]), 'update2'; + is_deeply [$box->UpdateMulti($tuples->[2]->[0],[ 3 => set => $tuples->[2]->[3] ],{want_updated_tuple => 1})], [$check->[2]], 'update3'; + ok $box->UpdateMulti($tuples->[2]->[0],[ 3 => set => $tuples->[2]->[3] ]), 'update4'; + is_deeply [$box->UpdateMulti($tuples->[2]->[0],[ 6 => set => $tuples->[2]->[6] ],{want_updated_tuple => 1})], [$check->[2]], 'update5'; + ok $box->UpdateMulti($tuples->[2]->[0],[ 6 => set => $tuples->[2]->[6] ]), 'update6'; + is_deeply [$box->UpdateMulti($tuples->[2]->[0],[ [0,2] => set => $tuples->[2]->[6] ],{want_updated_tuple => 1})], [$check->[2]], 'update7'; + ok $box->UpdateMulti($tuples->[2]->[0],[ [0,2] => set => $tuples->[2]->[6] ]), 'update8'; + is_deeply [$box->UpdateMulti($tuples->[2]->[0],[ [0,'l3'] => set => $tuples->[2]->[6] ],{want_updated_tuple => 1})], [$check->[2]], 'update9'; + ok $box->UpdateMulti($tuples->[2]->[0],[ [0,'l3'] => set => $tuples->[2]->[6] ]), 'update10'; + + is_deeply [$box->Delete($tuples->[$_]->[0],{want_deleted_tuple => 1})], [$check->[$_]], "delete$_" for 0..$#$tuples; +}; + + + +## Check u64 index +# note, that u64 keys are emulated via pack('ll') since default ubuntu perl doesn't support pack('q') +sub def_param_u64 { + my $format = '&&&&'; + return { servers => $server, + spaces => [ { + indexes => [ { + index_name => 'id', + keys => [0], + } ], + space => 20, + format => $format, + default_index => 'id', + } ], + %opts, + } +} + +$box = $CLASS->new(def_param_u64); +ok $box->isa($CLASS), 'connect'; + +$_->[0] = pack('ll', $_->[0], 0) foreach @$tuples; + +foreach my $tuple (@$tuples) { + cleanup $tuple->[0]; +} + +foreach my $tuple (@$tuples) { + ok $box->Insert(@$tuple), "unique_tree_index/insert \'$tuple->[0]\'"; +} + +is_deeply($tuples, [$box->Select([map $_->[0], @$tuples])], 'Select tuples'); + + +__END__ + diff --git a/connector/perl/t/data/box.t.cfg b/connector/perl/t/data/box.t.cfg new file mode 100644 index 0000000000..11ec7225e9 --- /dev/null +++ b/connector/perl/t/data/box.t.cfg @@ -0,0 +1,61 @@ +space[0].enabled = 1 +space[0].index[0].type = "HASH" +space[0].index[0].unique = 1 +space[0].index[0].key_field[0].fieldno = 0 +space[0].index[0].key_field[0].type = "NUM" +space[0].index[1].type = "HASH" +space[0].index[1].unique = 1 +space[0].index[1].key_field[0].fieldno = 1 +space[0].index[1].key_field[0].type = "STR" + +space[20].enabled = 1 +space[20].index[0].type = "HASH" +space[20].index[0].unique = 1 +space[20].index[0].key_field[0].fieldno = 0 +space[20].index[0].key_field[0].type = "NUM64" + + +space[26].enabled = 1 +space[26].index[0].type = "HASH" +space[26].index[0].unique = 1 +space[26].index[0].key_field[0].fieldno = 0 +space[26].index[0].key_field[0].type = "NUM" +space[26].index[1].type = "TREE" +space[26].index[1].unique = 0 +space[26].index[1].key_field[0].fieldno = 1 +space[26].index[1].key_field[0].type = "STR" +space[26].index[2].type = "TREE" +space[26].index[2].unique = 0 +space[26].index[2].key_field[0].fieldno = 1 +space[26].index[2].key_field[0].type = "STR" +space[26].index[2].key_field[1].fieldno = 2 +space[26].index[2].key_field[1].type = "NUM" + + + +space[27].enabled = 1 +space[27].index[0].type = "HASH" +space[27].index[0].unique = 1 +space[27].index[0].key_field[0].fieldno = 0 +space[27].index[0].key_field[0].type = "NUM" +space[27].index[1].type = "HASH" +space[27].index[1].unique = 1 +space[27].index[1].key_field[0].fieldno = 1 +space[27].index[1].key_field[0].type = "STR" + +space[27].index[2].type = "TREE" +space[27].index[2].unique = 1 +space[27].index[2].key_field[0].fieldno = 2 +space[27].index[2].key_field[0].type = "STR" + +space[27].index[2].type = "TREE" +space[27].index[2].unique = 1 +space[27].index[2].key_field[0].fieldno = 3 +space[27].index[2].key_field[0].type = "STR" + +space[27].index[3].type = "TREE" +space[27].index[3].unique = 1 +space[27].index[3].key_field[0].fieldno = 2 +space[27].index[3].key_field[0].type = "STR" +space[27].index[3].key_field[1].fieldno = 3 +space[27].index[3].key_field[1].type = "STR" diff --git a/connector/perl/t/data/init.lua b/connector/perl/t/data/init.lua index d5335b60df..91b47ad977 100644 --- a/connector/perl/t/data/init.lua +++ b/connector/perl/t/data/init.lua @@ -1,5 +1,14 @@ +function tst_server_name() + local tuple = box.select( 0, 0, box.pack('i', 1) ) + if tuple == nil then + return { 'unknown' } + else + return { tuple[1] } + end +end + function tst_sleep( ) - + local tuple = box.select( 0, 0, box.pack('i', 1) ) if tuple == nil then return { 'unknown', '0.0' } @@ -19,3 +28,25 @@ end function tst_rand_init() math.randomseed( os.time() ) end + + +function tst_sleep_force( first_delay, second_delay ) + + local tuple = box.select( 0, 0, box.pack('i', 1) ) + local name = tuple[1] + local delay = 10.0 + + if name == 'first' then + delay = first_delay + else + if name == 'second' then + delay = second_delay + else + return { 'unknown', '0.0', first_delay, second_delay } + end + end + + box.fiber.sleep(delay) + + return { name, string.format('%f', delay), first_delay, second_delay } +end -- GitLab