# Net::ZooKeeper - Perl extension for Apache ZooKeeper
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
use File::Spec;
use Test::More tests => 30;
BEGIN { use_ok('Net::ZooKeeper', qw(:all)) };
my $test_dir;
(undef, $test_dir, undef) = File::Spec->splitpath($0);
require File::Spec->catfile($test_dir, 'util.pl');
my($hosts, $root_path, $node_path) = zk_test_setup(0);
SKIP: {
my $zkh = Net::ZooKeeper->new($hosts);
my $path = $zkh->create($node_path, 'foo',
'acl' => ZOO_OPEN_ACL_UNSAFE) if (defined($zkh));
skip 'no connection to ZooKeeper', 20 unless
(defined($path) and $path eq $node_path);
## exists()
$zkh->{'watch_timeout'} = 100;
my $watch = $zkh->watch();
my $ret = $zkh->exists($node_path, 'watch' => $watch);
ok($ret,
'exists(): checked node existence with watch handle');
$ret = $watch->wait();
ok(!$ret,
'wait(): watch after checking node existence timed out');
$ret = $zkh->exists($node_path, 'watch' => $watch);
ok($ret,
'exists(): checked node existence with renewed watch handle');
$ret = $watch->wait();
ok(!$ret,
'wait(): watch after checking node existence timed out with ' .
'renewed watch handle');
undef $watch;
ok(!defined($watch),
'undef: released watch handle');
my $pending_watches = $zkh->{'pending_watches'};
is($pending_watches, 2,
'_zk_release_watches(): report pending watches');
## get_children()
$watch = $zkh->watch('timeout' => 50);
my $num_children = $zkh->get_children($node_path, 'watch' => $watch);
ok((defined($num_children) and $num_children == 0),
'get_children(): retrieved zero count of child nodes with ' .
'watch handle');
$ret = $watch->wait();
ok(!$ret,
'wait(): watch after retrieving child nodes timed out with ' .
'watch handle');
$watch->{'timeout'} = 100;
my @child_paths = $zkh->get_children($node_path, 'watch' => $watch);
ok((@child_paths == 0),
'get_children(): retrieved empty list of child nodes with ' .
'renewed watch handle');
$ret = $watch->wait();
ok(!$ret,
'wait(): watch after retrieving child nodes timed out with ' .
'renewed watch handle');
$pending_watches = $zkh->{'pending_watches'};
is($pending_watches, 4,
'_zk_release_watches(): report pending watches');
## get()
$watch = $zkh->watch();
my $node = $zkh->get($node_path, 'watch' => $watch);
is($node, 'foo',
'get(): retrieved node value with watch handle');
$ret = $watch->wait('timeout' => 0);
ok(!$ret,
'wait(): watch after retrieving node value timed out with ' .
'watch handle');
$node = $zkh->get($node_path, 'watch' => $watch);
is($node, 'foo',
'get(): retrieved node value with renewed watch handle');
$ret = $watch->wait();
ok(!$ret,
'wait(): watch after retrieving node value timed out with ' .
'renewed watch handle');
$pending_watches = $zkh->{'pending_watches'};
is($pending_watches, 6,
'_zk_release_watches(): all watches pending');
## _zk_release_watches()
$ret = $zkh->DESTROY();
ok($ret,
'DESTROY(): destroyed handle with pending watches');
my $event = $watch->{'event'};
is($event, 0,
'_zk_release_watches(): watch not destroyed when tied to watch handle');
$zkh = Net::ZooKeeper->new($hosts);
SKIP: {
my $ret = $zkh->exists($node_path, 'watch' => $watch);
skip 'no connection to ZooKeeper', 2 unless
(defined($ret) and $ret);
ok($ret,
'exists(): checked node existence with renewed watch handle ' .
'from prior connection');
$ret = $watch->wait();
ok(!$ret,
'wait(): watch after checking node existence timed out with ' .
'renewed watch handle from prior connection');
}
}
my $pid = fork();
SKIP: {
skip 'unable to fork', 4 unless (defined($pid));
my $zkh = Net::ZooKeeper->new($hosts);
my $ret = $zkh->exists($node_path) if (defined($zkh));
if ($pid == 0) {
## child process
my $code = 0;
if (defined($ret) and $ret) {
sleep(1);
my $ret = $zkh->set($node_path, 'foo');
diag(sprintf('set(): failed in child process: %d, %s',
$zkh->get_error(), $!)) unless ($ret);
$code = !$ret;
sleep(1);
my $path = $zkh->create("$node_path/c", 'foo',
'acl' => ZOO_OPEN_ACL_UNSAFE);
diag(sprintf('create(): failed in child process: %d, %s',
$zkh->get_error(), $!)) unless
(defined($path) and $path eq "$node_path/c");
$code &= !$ret;
sleep(1);
$ret = $zkh->delete("$node_path/c");
diag(sprintf('delete(): failed in child process: %d, %s',
$zkh->get_error(), $!)) unless ($ret);
$code &= !$ret;
sleep(1);
$ret = $zkh->set($node_path, 'foo');
diag(sprintf('set(): failed in child process: %d, %s',
$zkh->get_error(), $!)) unless ($ret);
$code &= !$ret;
}
exit($code);
}
else {
## parent process
SKIP: {
skip 'no connection to ZooKeeper', 9 unless
(defined($ret) and $ret);
my $watch = $zkh->watch('timeout' => 5000);
## wait()
my $ret = $zkh->exists($node_path, 'watch' => $watch);
ok($ret,
'exists(): checked node existence with watch handle ' .
'in parent');
$ret = $watch->wait();
ok(($ret and $watch->{'event'} == ZOO_CHANGED_EVENT and
$watch->{'state'} == ZOO_CONNECTED_STATE),
'wait(): waited for event after checking node existence');
my $num_children = $zkh->get_children($node_path,
'watch' => $watch);
ok((defined($num_children) and $num_children == 0),
'get_children(): retrieved zero count of child nodes with ' .
'watch handle in parent');
$ret = $watch->wait();
ok(($ret and $watch->{'event'} == ZOO_CHILD_EVENT and
$watch->{'state'} == ZOO_CONNECTED_STATE),
'wait(): waited for create child event after ' .
'retrieving child nodes');
my @child_paths = $zkh->get_children($node_path,
'watch' => $watch);
ok((@child_paths == 1 and $child_paths[0] eq 'c'),
'get_children(): retrieved list of child nodes with ' .
'watch handle in parent');
$ret = $watch->wait();
ok(($ret and $watch->{'event'} == ZOO_CHILD_EVENT and
$watch->{'state'} == ZOO_CONNECTED_STATE),
'wait(): waited for delete child event after ' .
'retrieving child nodes');
my $node = $zkh->get($node_path, 'watch' => $watch);
is($node, 'foo',
'get(): retrieved node value with watch handle in parent');
$ret = $watch->wait();
ok(($ret and $watch->{'event'} == ZOO_CHANGED_EVENT and
$watch->{'state'} == ZOO_CONNECTED_STATE),
'wait(): waited for event after retrieving node value');
undef $watch;
my $pending_watches = $zkh->{'pending_watches'};
is($pending_watches, 0,
'_zk_release_watches(): no watches pending');
}
my $reap = waitpid($pid, 0);
diag(sprintf('child process failed: exit %d, signal %d%s',
($? >> 8), ($? & 127),
(($? & 128) ? ', core dump' : ''))) if
($reap == $pid and $? != 0);
}
}
## cleanup
{
my $zkh = Net::ZooKeeper->new($hosts);
my $ret = $zkh->exists($node_path) if (defined($zkh));
if (defined($ret) and $ret) {
$ret = $zkh->delete($node_path);
diag(sprintf('unable to delete node %s: %d, %s',
$node_path, $zkh->get_error(), $!)) unless ($ret);
}
}