| # please insert nothing before this line: -*- mode: cperl; cperl-indent-level: 4; cperl-continued-statement-offset: 4; indent-tabs-mode: nil -*- |
| package TestAPRlib::table; |
| |
| # testing APR::Table API |
| |
| use strict; |
| use warnings FATAL => 'all'; |
| |
| use Apache::Test; |
| use Apache::TestUtil; |
| |
| use APR::Table (); |
| use APR::Pool (); |
| |
| use APR::Const -compile => ':table'; |
| |
| use constant TABLE_SIZE => 20; |
| our $filter_count; |
| |
| sub num_of_tests { |
| my $tests = 56; |
| |
| # tied hash values() for a table w/ multiple values for the same |
| # key |
| $tests += 2 if $] >= 5.008; |
| |
| return $tests; |
| } |
| |
| sub test { |
| |
| $filter_count = 0; |
| my $pool = APR::Pool->new(); |
| my $table = APR::Table::make($pool, TABLE_SIZE); |
| |
| ok UNIVERSAL::isa($table, 'APR::Table'); |
| |
| # get on non-existing key |
| { |
| # in scalar context |
| my $val = $table->get('foo'); |
| ok t_cmp($val, undef, '$val = $table->get("no_such_key")'); |
| |
| # in list context |
| my @val = $table->get('foo'); |
| ok t_cmp(+@val, 0, '@val = $table->get("no_such_key")'); |
| } |
| |
| # set/add/get/copy normal values |
| { |
| $table->set(foo => 'bar'); |
| |
| # get scalar context |
| my $val = $table->get('foo'); |
| ok t_cmp($val, 'bar', '$val = $table->get("foo")'); |
| |
| # add + get list context |
| $table->add(foo => 'tar'); |
| $table->add(foo => 'kar'); |
| my @val = $table->get('foo'); |
| ok @val == 3 && |
| $val[0] eq 'bar' && |
| $val[1] eq 'tar' && |
| $val[2] eq 'kar'; |
| |
| # copy |
| $table->set(too => 'boo'); |
| my $table_copy = $table->copy($pool); |
| my $val_copy = $table->get('too'); |
| ok t_cmp($val_copy, 'boo', '$val = $table->get("too")'); |
| my @val_copy = $table_copy->get('foo'); |
| ok @val_copy == 3 && |
| $val_copy[0] eq 'bar' && |
| $val_copy[1] eq 'tar' && |
| $val_copy[2] eq 'kar'; |
| } |
| |
| # make sure 0 comes through as 0 and not undef |
| { |
| $table->set(foo => 0); |
| my $zero = $table->get('foo'); |
| ok t_cmp($zero, 0, 'table value 0 is not undef'); |
| } |
| |
| # unset |
| { |
| $table->set(foo => "bar"); |
| $table->unset('foo'); |
| ok t_cmp(+$table->get('foo'), undef, '$table->unset("foo")'); |
| } |
| |
| # merge |
| { |
| $table->set( merge => '1'); |
| $table->merge(merge => 'a'); |
| my $val = $table->get('merge'); |
| ok t_cmp($val, "1, a", 'one val $table->merge(...)'); |
| |
| # if there is more than one value for the same key, merge does |
| # the job only for the first value |
| $table->add( merge => '2'); |
| $table->merge(merge => 'b'); |
| my @val = $table->get('merge'); |
| ok t_cmp($val[0], "1, a, b", '$table->merge(...)'); |
| ok t_cmp($val[1], "2", 'two values $table->merge(...)'); |
| |
| # if the key is not found, works like set/add |
| $table->merge(miss => 'a'); |
| my $val_miss = $table->get('miss'); |
| ok t_cmp($val_miss, "a", 'no value $table->merge(...)'); |
| } |
| |
| # clear |
| { |
| $table->set(foo => 0); |
| $table->set(bar => 1); |
| $table->clear(); |
| # t_cmp forces scalar context on get |
| ok t_cmp($table->get('foo'), undef, '$table->clear'); |
| ok t_cmp($table->get('bar'), undef, '$table->clear'); |
| } |
| |
| # filtering |
| { |
| for (1..TABLE_SIZE) { |
| $table->set(chr($_+97), $_); |
| } |
| |
| # Simple filtering |
| $filter_count = 0; |
| $table->do("my_filter"); |
| ok t_cmp($filter_count, TABLE_SIZE); |
| |
| # Filtering aborting in the middle |
| $filter_count = 0; |
| $table->do("my_filter_stop"); |
| ok t_cmp($filter_count, int(TABLE_SIZE)/2) ; |
| |
| # Filtering with anon sub |
| $filter_count=0; |
| $table->do(sub { |
| my ($key,$value) = @_; |
| $filter_count++; |
| unless ($key eq chr($value+97)) { |
| die "arguments I recieved are bogus($key,$value)"; |
| } |
| return 1; |
| }); |
| |
| ok t_cmp($filter_count, TABLE_SIZE, "table size"); |
| |
| $filter_count = 0; |
| $table->do("my_filter", "c", "b", "e"); |
| ok t_cmp($filter_count, 3, "table size"); |
| } |
| |
| #Tied interface |
| { |
| my $table = APR::Table::make($pool, TABLE_SIZE); |
| |
| ok UNIVERSAL::isa($table, 'HASH'); |
| |
| ok UNIVERSAL::isa($table, 'HASH') && tied(%$table); |
| |
| ok $table->{'foo'} = 'bar'; |
| |
| # scalar context |
| ok $table->{'foo'} eq 'bar'; |
| |
| ok delete $table->{'foo'} || 1; |
| |
| ok not exists $table->{'foo'}; |
| |
| for (1..TABLE_SIZE) { |
| $table->{chr($_+97)} = $_; |
| } |
| |
| $filter_count = 0; |
| foreach my $key (sort keys %$table) { |
| my_filter($key, $table->{$key}); |
| } |
| ok $filter_count == TABLE_SIZE; |
| } |
| |
| |
| # each, values |
| { |
| my $table = APR::Table::make($pool, 2); |
| |
| $table->add("first" => 1); |
| $table->add("second" => 2); |
| $table->add("first" => 3); |
| |
| my $i = 0; |
| while (my ($a,$b) = each %$table) { |
| my $key = ("first", "second")[$i % 2]; |
| my $val = ++$i; |
| |
| ok t_cmp $a, $key, "table each: key test"; |
| ok t_cmp $b, $val, "table each: value test"; |
| ok t_cmp $table->{$a}, $val, "table each: get test"; |
| |
| ok t_cmp tied(%$table)->FETCH($a), $val, |
| "table each: tied get test"; |
| } |
| |
| # this doesn't work with Perl < 5.8 |
| if ($] >= 5.008) { |
| ok t_cmp "1,2,3", join(",", values %$table), |
| "table values"; |
| ok t_cmp "first,1,second,2,first,3", join(",", %$table), |
| "table entries"; |
| } |
| } |
| |
| # overlap and compress routines |
| { |
| my $base = APR::Table::make($pool, TABLE_SIZE); |
| my $add = APR::Table::make($pool, TABLE_SIZE); |
| |
| $base->set(foo => 'one'); |
| $base->add(foo => 'two'); |
| |
| $add->set(foo => 'three'); |
| $add->set(bar => 'beer'); |
| |
| my $overlay = $base->overlay($add, $pool); |
| |
| my @foo = $overlay->get('foo'); |
| my @bar = $overlay->get('bar'); |
| |
| ok t_cmp(+@foo, 3); |
| ok t_cmp($bar[0], 'beer'); |
| |
| my $overlay2 = $overlay->copy($pool); |
| |
| # compress/merge |
| $overlay->compress(APR::Const::OVERLAP_TABLES_MERGE); |
| # $add first, then $base |
| ok t_cmp($overlay->get('foo'), |
| 'three, one, two', |
| "\$overlay->compress/merge"); |
| ok t_cmp($overlay->get('bar'), |
| 'beer', |
| "\$overlay->compress/merge"); |
| |
| # compress/set |
| $overlay->compress(APR::Const::OVERLAP_TABLES_SET); |
| # $add first, then $base |
| ok t_cmp($overlay2->get('foo'), |
| 'three', |
| "\$overlay->compress/set"); |
| ok t_cmp($overlay2->get('bar'), |
| 'beer', |
| "\$overlay->compress/set"); |
| } |
| |
| # overlap set |
| { |
| my $base = APR::Table::make($pool, TABLE_SIZE); |
| my $add = APR::Table::make($pool, TABLE_SIZE); |
| |
| $base->set(bar => 'beer'); |
| $base->set(foo => 'one'); |
| $base->add(foo => 'two'); |
| |
| $add->set(foo => 'three'); |
| |
| $base->overlap($add, APR::Const::OVERLAP_TABLES_SET); |
| |
| my @foo = $base->get('foo'); |
| my @bar = $base->get('bar'); |
| |
| ok t_cmp(+@foo, 1, 'overlap/set'); |
| ok t_cmp($foo[0], 'three'); |
| ok t_cmp($bar[0], 'beer'); |
| } |
| |
| # overlap merge |
| { |
| my $base = APR::Table::make($pool, TABLE_SIZE); |
| my $add = APR::Table::make($pool, TABLE_SIZE); |
| |
| $base->set(foo => 'one'); |
| $base->add(foo => 'two'); |
| |
| $add->set(foo => 'three'); |
| $add->set(bar => 'beer'); |
| |
| $base->overlap($add, APR::Const::OVERLAP_TABLES_MERGE); |
| |
| my @foo = $base->get('foo'); |
| my @bar = $base->get('bar'); |
| |
| ok t_cmp(+@foo, 1, 'overlap/set'); |
| ok t_cmp($foo[0], 'one, two, three'); |
| ok t_cmp($bar[0], 'beer'); |
| } |
| |
| |
| # temp pool objects. |
| # testing here that the temp pool object doesn't go out of scope |
| # before the object based on it was freed. the following tests |
| # were previously segfaulting when using apr1/httpd2.1 built w/ |
| # --enable-pool-debug CPPFLAGS="-DAPR_BUCKET_DEBUG", |
| # the affected methods are: |
| # - make |
| # - copy |
| # - overlay |
| { |
| { |
| my $table = APR::Table::make(APR::Pool->new, 10); |
| $table->set($_ => $_) for 1..20; |
| ok t_cmp $table->get(20), 20, "no segfault"; |
| } |
| |
| my $pool = APR::Pool->new; |
| my $table = APR::Table::make($pool, 10); |
| $table->set($_ => $_) for 1..20; |
| my $table_copy = $table->copy($pool->new); |
| { |
| # verify that the temp pool used to create $table_copy was |
| # not freed, by allocating a new table to fill with a |
| # different data. if that former pool was freed |
| # $table_copy will now contain bogus data (and may |
| # segfault) |
| my $table = APR::Table::make(APR::Pool->new, 50); |
| $table->set($_ => $_) for 'a'..'z'; |
| ok t_cmp $table->get('z'), 'z', "helper test"; |
| |
| } |
| ok t_cmp $table_copy->get(20), 20, "no segfault/valid data"; |
| |
| my $table2 = APR::Table::make($pool, 1); |
| $table2->set($_**2 => $_**2) for 1..20; |
| my $table2_copy = APR::Table::make($pool, 1); |
| $table2_copy->set($_ => $_) for 1..20; |
| |
| my $overlay = $table2_copy->overlay($table2, $pool->new); |
| { |
| # see the comment for above's: |
| # $table_copy = $table->copy(APR::Pool->new); |
| my $table = APR::Table::make(APR::Pool->new, 50); |
| $table->set($_ => $_) for 'aa'..'za'; |
| ok t_cmp $table->get('za'), 'za', "helper test"; |
| |
| } |
| ok t_cmp $overlay->get(20), 20, "no segfault/valid data"; |
| } |
| { |
| { |
| my $p = APR::Pool->new; |
| $p->cleanup_register(sub { "whatever" }); |
| $table = APR::Table::make($p, 10) |
| }; |
| $table->set(a => 5); |
| ok t_cmp $table->get("a"), 5, "no segfault"; |
| } |
| |
| } |
| |
| sub my_filter { |
| my ($key, $value) = @_; |
| $filter_count++; |
| unless ($key eq chr($value+97)) { |
| die "arguments I received are bogus($key,$value)"; |
| } |
| return 1; |
| } |
| |
| sub my_filter_stop { |
| my ($key, $value) = @_; |
| $filter_count++; |
| unless ($key eq chr($value+97)) { |
| die "arguments I received are bogus($key,$value)"; |
| } |
| return $filter_count == int(TABLE_SIZE)/2 ? 0 : 1; |
| } |
| |
| 1; |