You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zookeeper.apache.org by ha...@apache.org on 2018/07/16 04:30:39 UTC

[40/51] [partial] zookeeper git commit: Website update for release 3.4.13.

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/c9914857/content/build/contrib/zkperl/t/10_invalid.t
----------------------------------------------------------------------
diff --git a/content/build/contrib/zkperl/t/10_invalid.t b/content/build/contrib/zkperl/t/10_invalid.t
new file mode 100644
index 0000000..5e080b6
--- /dev/null
+++ b/content/build/contrib/zkperl/t/10_invalid.t
@@ -0,0 +1,773 @@
+# 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 => 107;
+
+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(1);
+
+
+## new()
+
+eval {
+    Net::ZooKeeper->new();
+};
+like($@, qr/Usage: Net::ZooKeeper::new\(package, hosts, \.\.\.\)/,
+     'new(): no hostname specified');
+
+eval {
+    Net::ZooKeeper->new($hosts, 'bar');
+};
+like($@, qr/invalid number of arguments/,
+     'new(): invalid number of arguments');
+
+eval {
+    Net::ZooKeeper->new($hosts, 'session_timeout' => -3);
+};
+like($@, qr/invalid session timeout/,
+     'new(): invalid session timeout');
+
+eval {
+    Net::ZooKeeper->new($hosts, 'session_timeout' => 0x4000_0000);
+};
+like($@, qr/invalid session timeout/,
+     'new(): invalid session timeout');
+
+eval {
+    Net::ZooKeeper->new($hosts, 'session_id' => 'abcdef');
+};
+like($@, qr/invalid session ID/,
+     'new(): invalid session ID');
+
+my $zkh = Net::ZooKeeper->new($hosts);
+isa_ok($zkh, 'Net::ZooKeeper',
+       'new(): created handle');
+
+
+## DESTROY()
+
+eval {
+    $zkh->DESTROY('foo');
+};
+like($@, qr/Usage: Net::ZooKeeper::DESTROY\(zkh\)/,
+     'DESTROY(): too many arguments');
+
+my $bad_zkh = {};
+$bad_zkh = bless($bad_zkh, 'Net::ZooKeeper');
+
+my $ret = $bad_zkh->DESTROY();
+ok(!$ret,
+   'DESTROY(): no action on invalid handle');
+
+
+## add_auth()
+
+eval {
+    $zkh->add_auth();
+};
+like($@, qr/Usage: Net::ZooKeeper::add_auth\(zkh, scheme, cert\)/,
+     'add_auth(): no scheme specified');
+
+eval {
+    $zkh->add_auth('foo');
+};
+like($@, qr/Usage: Net::ZooKeeper::add_auth\(zkh, scheme, cert\)/,
+     'add_auth(): no certificate specified');
+
+eval {
+    $zkh->add_auth('foo', 'foo', 'bar');
+};
+like($@, qr/Usage: Net::ZooKeeper::add_auth\(zkh, scheme, cert\)/,
+     'add_auth(): too many arguments');
+
+eval {
+    $bad_zkh->add_auth('foo', 'foo');
+};
+like($@, qr/invalid handle/,
+     'add_auth(): invalid handle');
+
+eval {
+    Net::ZooKeeper::add_auth(1, 'foo', 'foo');
+};
+like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/,
+     'add_auth(): invalid hash reference');
+
+
+## create()
+
+eval {
+    $zkh->create();
+};
+like($@, qr/Usage: Net::ZooKeeper::create\(zkh, path, buf, \.\.\.\)/,
+     'create(): no path specified');
+
+eval {
+    $zkh->create($node_path);
+};
+like($@, qr/Usage: Net::ZooKeeper::create\(zkh, path, buf, \.\.\.\)/,
+     'create(): no data buffer specified');
+
+eval {
+    $zkh->create($node_path, 'foo', 'bar');
+};
+like($@, qr/invalid number of arguments/,
+     'create(): invalid number of arguments');
+
+eval {
+    $zkh->create($node_path, 'foo', 'path_read_len' => -3);
+};
+like($@, qr/invalid path read length/,
+     'create(): invalid path read length');
+
+eval {
+    $zkh->create($node_path, 'foo', 'path_read_len' => 1);
+};
+like($@, qr/invalid path read length/,
+     'create(): invalid path read length');
+
+eval {
+    $zkh->create($node_path, 'foo', 'flags' => 15);
+};
+like($@, qr/invalid create flags/,
+     'create(): invalid create flags');
+
+eval {
+    $zkh->create($node_path, 'foo', 'flags' => ZOO_EPHEMERAL, 'acl', 'foo');
+};
+like($@, qr/invalid ACL array reference/,
+     'create(): invalid ACL array reference');
+
+eval {
+    $zkh->create($node_path, 'foo', 'acl', {});
+};
+like($@, qr/invalid ACL array reference/,
+     'create(): invalid ACL array reference to hash');
+
+eval {
+    my @acl = ('foo', 'bar');
+    $zkh->create($node_path, 'foo', 'acl', \@acl);
+};
+like($@, qr/invalid ACL entry hash reference/,
+     'create(): invalid ACL entry hash reference');
+
+eval {
+    my @acl = ({ 'foo' => 'bar' });
+    $zkh->create($node_path, 'foo', 'acl', \@acl);
+};
+like($@, qr/no ACL entry perms element/,
+     'create(): no ACL entry perms element');
+
+eval {
+    my @acl = (
+        {
+            'perms'  => -1
+        }
+    );
+    $zkh->create($node_path, 'foo', 'acl', \@acl);
+};
+like($@, qr/invalid ACL entry perms/,
+     'create(): invalid ACL entry perms');
+
+eval {
+    my @acl = (
+        {
+            'perms'  => ZOO_PERM_ALL
+        }
+    );
+    $zkh->create($node_path, 'foo', 'acl', \@acl);
+};
+like($@, qr/no ACL entry scheme element/,
+     'create(): no ACL entry scheme element');
+
+eval {
+    my @acl = (
+        {
+            'perms'  => ZOO_PERM_ALL,
+            'scheme' => 'foo'
+        }
+    );
+    $zkh->create($node_path, 'foo', 'acl', \@acl);
+};
+like($@, qr/no ACL entry id element/,
+     'create(): no ACL entry id element');
+
+eval {
+    my @acl = (
+        {
+            'perms'  => ZOO_PERM_ALL,
+            'scheme' => 'foo',
+            'id'     => 'bar'
+        },
+        'bar'
+    );
+    $zkh->create($node_path, 'foo', 'acl', \@acl);
+};
+like($@, qr/invalid ACL entry hash reference/,
+     'create(): invalid second ACL entry hash reference');
+
+eval {
+    $bad_zkh->create($node_path, 'foo');
+};
+like($@, qr/invalid handle/,
+     'create(): invalid handle');
+
+eval {
+    Net::ZooKeeper::create(1, $node_path, 'foo');
+};
+like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/,
+     'create(): invalid hash reference');
+
+
+## delete()
+
+eval {
+    $zkh->delete();
+};
+like($@, qr/Usage: Net::ZooKeeper::delete\(zkh, path, \.\.\.\)/,
+     'delete(): no path specified');
+
+eval {
+    $zkh->delete($node_path, 'bar');
+};
+like($@, qr/invalid number of arguments/,
+     'delete(): invalid number of arguments');
+
+eval {
+    $zkh->delete($node_path, 'version' => -3);
+};
+like($@, qr/invalid version requirement/,
+     'delete(): invalid version requirement');
+
+eval {
+    $bad_zkh->delete($node_path);
+};
+like($@, qr/invalid handle/,
+     'delete(): invalid handle');
+
+eval {
+    Net::ZooKeeper::delete(1, $node_path);
+};
+like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/,
+     'delete(): invalid hash reference');
+
+
+## exists()
+
+eval {
+    $zkh->exists();
+};
+like($@, qr/Usage: Net::ZooKeeper::exists\(zkh, path, \.\.\.\)/,
+     'exists(): no path specified');
+
+eval {
+    $zkh->exists($node_path, 'bar');
+};
+like($@, qr/invalid number of arguments/,
+     'exists(): invalid number of arguments');
+
+eval {
+    $zkh->exists($node_path, 'watch', 'bar');
+};
+like($@, qr/watch is not a hash reference of type Net::ZooKeeper::Watch/,
+     'exists(): invalid watch hash reference');
+
+eval {
+    $zkh->exists($node_path, 'watch', []);
+};
+like($@, qr/watch is not a hash reference of type Net::ZooKeeper::Watch/,
+     'exists(): invalid watch hash reference to array');
+
+eval {
+    $zkh->exists($node_path, 'stat', 'bar');
+};
+like($@, qr/stat is not a hash reference of type Net::ZooKeeper::Stat/,
+     'exists(): invalid stat hash reference');
+
+eval {
+    $zkh->exists($node_path, 'stat', []);
+};
+like($@, qr/stat is not a hash reference of type Net::ZooKeeper::Stat/,
+     'exists(): invalid stat hash reference');
+
+eval {
+    $bad_zkh->exists($node_path);
+};
+like($@, qr/invalid handle/,
+     'exists(): invalid handle');
+
+eval {
+    Net::ZooKeeper::exists(1, $node_path);
+};
+like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/,
+     'exists(): invalid hash reference');
+
+
+## get_children()
+
+eval {
+    $zkh->get_children();
+};
+like($@, qr/Usage: Net::ZooKeeper::get_children\(zkh, path, \.\.\.\)/,
+     'get_children(): no path specified');
+
+eval {
+    $zkh->get_children($node_path, 'bar');
+};
+like($@, qr/invalid number of arguments/,
+     'get_children(): invalid number of arguments');
+
+eval {
+    $zkh->get_children($node_path, 'watch', 'bar');
+};
+like($@, qr/watch is not a hash reference of type Net::ZooKeeper::Watch/,
+     'get_children(): invalid watch hash reference');
+
+eval {
+    $zkh->get_children($node_path, 'watch', []);
+};
+like($@, qr/watch is not a hash reference of type Net::ZooKeeper::Watch/,
+     'get_children(): invalid watch ash reference to array');
+
+eval {
+    $bad_zkh->get_children($node_path);
+};
+like($@, qr/invalid handle/,
+     'get_children(): invalid handle');
+
+eval {
+    Net::ZooKeeper::get_children(1, $node_path);
+};
+like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/,
+     'get_children(): invalid hash reference');
+
+
+## get()
+
+eval {
+    $zkh->get();
+};
+like($@, qr/Usage: Net::ZooKeeper::get\(zkh, path, \.\.\.\)/,
+     'get(): no path specified');
+
+eval {
+    $zkh->get($node_path, 'bar');
+};
+like($@, qr/invalid number of arguments/,
+     'get(): invalid number of arguments');
+
+eval {
+    $zkh->get($node_path, 'data_read_len' => -3);
+};
+like($@, qr/invalid data read length/,
+     'get(): invalid data read length');
+
+eval {
+    $zkh->get($node_path, 'data_read_len' => 10, 'watch', 'bar');
+};
+like($@, qr/watch is not a hash reference of type Net::ZooKeeper::Watch/,
+     'get(): invalid watch hash reference');
+
+eval {
+    $zkh->get($node_path, 'watch', []);
+};
+like($@, qr/watch is not a hash reference of type Net::ZooKeeper::Watch/,
+     'get(): invalid watch hash reference to array');
+
+eval {
+    $zkh->get($node_path, 'stat', 'bar');
+};
+like($@, qr/stat is not a hash reference of type Net::ZooKeeper::Stat/,
+     'get(): invalid stat hash reference');
+
+eval {
+    $zkh->get($node_path, 'stat', []);
+};
+like($@, qr/stat is not a hash reference of type Net::ZooKeeper::Stat/,
+     'get(): invalid stat hash reference');
+
+eval {
+    $bad_zkh->get($node_path);
+};
+like($@, qr/invalid handle/,
+     'get(): invalid handle');
+
+eval {
+    Net::ZooKeeper::get(1, $node_path);
+};
+like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/,
+     'get(): invalid hash reference');
+
+
+## set()
+
+eval {
+    $zkh->set();
+};
+like($@, qr/Usage: Net::ZooKeeper::set\(zkh, path, buf, \.\.\.\)/,
+     'set(): no path specified');
+
+eval {
+    $zkh->set($node_path);
+};
+like($@, qr/Usage: Net::ZooKeeper::set\(zkh, path, buf, \.\.\.\)/,
+     'set(): no data buffer specified');
+
+eval {
+    $zkh->set($node_path, 'foo', 'bar');
+};
+like($@, qr/invalid number of arguments/,
+     'set(): invalid number of arguments');
+
+eval {
+    $zkh->set($node_path, 'foo', 'version' => -3);
+};
+like($@, qr/invalid version requirement/,
+     'set(): invalid version requirement');
+
+eval {
+    $zkh->set($node_path, 'foo', 'version', 0, 'stat', 'bar');
+};
+like($@, qr/stat is not a hash reference of type Net::ZooKeeper::Stat/,
+     'set(): invalid stat hash reference');
+
+eval {
+    $zkh->set($node_path, 'foo', 'stat', []);
+};
+like($@, qr/stat is not a hash reference of type Net::ZooKeeper::Stat/,
+     'set(): invalid stat hash reference');
+
+eval {
+    $bad_zkh->set($node_path, 'foo');
+};
+like($@, qr/invalid handle/,
+     'set(): invalid handle');
+
+eval {
+    Net::ZooKeeper::set(1, $node_path, 'foo');
+};
+like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/,
+     'set(): invalid hash reference');
+
+
+## get_acl()
+
+eval {
+    $zkh->get_acl();
+};
+like($@, qr/Usage: Net::ZooKeeper::get_acl\(zkh, path, \.\.\.\)/,
+     'get_acl(): no path specified');
+
+eval {
+    $zkh->get_acl($node_path, 'bar');
+};
+like($@, qr/invalid number of arguments/,
+     'get_acl(): invalid number of arguments');
+
+eval {
+    $zkh->get_acl($node_path, 'stat', 'bar');
+};
+like($@, qr/stat is not a hash reference of type Net::ZooKeeper::Stat/,
+     'get_acl(): invalid stat hash reference');
+
+eval {
+    $zkh->get_acl($node_path, 'stat', []);
+};
+like($@, qr/stat is not a hash reference of type Net::ZooKeeper::Stat/,
+     'get_acl(): invalid stat hash reference');
+
+eval {
+    $bad_zkh->get_acl($node_path);
+};
+like($@, qr/invalid handle/,
+     'get_acl(): invalid handle');
+
+eval {
+    Net::ZooKeeper::get_acl(1, $node_path);
+};
+like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/,
+     'get_acl(): invalid hash reference');
+
+
+## set_acl()
+
+eval {
+    $zkh->set_acl();
+};
+like($@, qr/Usage: Net::ZooKeeper::set_acl\(zkh, path, acl_arr, \.\.\.\)/,
+     'set_acl(): no path specified');
+
+eval {
+    $zkh->set_acl($node_path);
+};
+like($@, qr/Usage: Net::ZooKeeper::set_acl\(zkh, path, acl_arr, \.\.\.\)/,
+     'set_acl(): no data buffer specified');
+
+eval {
+    $zkh->set_acl($node_path, 'foo');
+};
+like($@, qr/acl_arr is not an array reference/,
+     'set_acl(): invalid ACL array reference');
+
+eval {
+    $zkh->set_acl($node_path, {});
+};
+like($@, qr/acl_arr is not an array reference/,
+     'set_acl(): invalid ACL array reference to hash');
+
+eval {
+    my @acl = ('foo', 'bar');
+    $zkh->set_acl($node_path, \@acl);
+};
+like($@, qr/invalid ACL entry hash reference/,
+     'set_acl(): invalid ACL entry hash reference');
+
+eval {
+    my @acl = ({ 'foo' => 'bar' });
+    $zkh->set_acl($node_path, \@acl);
+};
+like($@, qr/no ACL entry perms element/,
+     'set_acl(): no ACL entry perms element');
+
+eval {
+    my @acl = (
+        {
+            'perms'  => -1
+        }
+    );
+    $zkh->set_acl($node_path, \@acl);
+};
+like($@, qr/invalid ACL entry perms/,
+     'set_acl(): invalid ACL entry perms');
+
+eval {
+    my @acl = (
+        {
+            'perms'  => ZOO_PERM_ALL
+        }
+    );
+    $zkh->set_acl($node_path, \@acl);
+};
+like($@, qr/no ACL entry scheme element/,
+     'set_acl(): no ACL entry scheme element');
+
+eval {
+    my @acl = (
+        {
+            'perms'  => ZOO_PERM_ALL,
+            'scheme' => 'foo'
+        }
+    );
+    $zkh->set_acl($node_path, \@acl);
+};
+like($@, qr/no ACL entry id element/,
+     'set_acl(): no ACL entry id element');
+
+eval {
+    my @acl = (
+        {
+            'perms'  => ZOO_PERM_ALL,
+            'scheme' => 'foo',
+            'id'     => 'bar'
+        },
+        'bar'
+    );
+    $zkh->set_acl($node_path, \@acl);
+};
+like($@, qr/invalid ACL entry hash reference/,
+     'set_acl(): invalid second ACL entry hash reference');
+
+eval {
+    $zkh->set_acl($node_path, [], 'bar');
+};
+like($@, qr/invalid number of arguments/,
+     'set_acl(): invalid number of arguments');
+
+eval {
+    $zkh->set_acl($node_path, [], 'version' => -3);
+};
+like($@, qr/invalid version requirement/,
+     'set_acl(): invalid version requirement');
+
+eval {
+    $bad_zkh->set_acl($node_path, []);
+};
+like($@, qr/invalid handle/,
+     'set_acl(): invalid handle');
+
+eval {
+    Net::ZooKeeper::set_acl(1, $node_path, []);
+};
+like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/,
+     'set_acl(): invalid hash reference');
+
+
+## stat()
+
+eval {
+    $zkh->stat('bar');
+};
+like($@, qr/Usage: Net::ZooKeeper::stat\(zkh\)/,
+     'stat(): too many arguments');
+
+eval {
+    $bad_zkh->stat();
+};
+like($@, qr/invalid handle/,
+     'stat(): invalid handle');
+
+eval {
+    Net::ZooKeeper::stat(1);
+};
+like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/,
+     'stat(): invalid hash reference');
+
+my $stat = $zkh->stat();
+isa_ok($stat, 'Net::ZooKeeper::Stat',
+       'stat(): created stat handle');
+
+
+## stat DESTROY()
+
+eval {
+    $stat->DESTROY('foo');
+};
+like($@, qr/Usage: Net::ZooKeeper::Stat::DESTROY\(zksh\)/,
+     'stat DESTROY(): too many arguments');
+
+my $bad_stat = {};
+$bad_stat = bless($bad_stat, 'Net::ZooKeeper::Stat');
+
+$ret = $bad_stat->DESTROY();
+ok(!$ret,
+   'stat DESTROY(): no action on invalid handle');
+
+
+## watch()
+
+eval {
+    $zkh->watch('bar');
+};
+like($@, qr/invalid number of arguments/,
+     'watch(): invalid number of arguments');
+
+eval {
+    $bad_zkh->watch();
+};
+like($@, qr/invalid handle/,
+     'watch(): invalid handle');
+
+eval {
+    Net::ZooKeeper::watch(1);
+};
+like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/,
+     'watch(): invalid hash reference');
+
+my $watch = $zkh->watch();
+isa_ok($watch, 'Net::ZooKeeper::Watch',
+       'watch(): created watch handle');
+
+
+## watch DESTROY()
+
+eval {
+    $watch->DESTROY('foo');
+};
+like($@, qr/Usage: Net::ZooKeeper::Watch::DESTROY\(zkwh\)/,
+     'watch DESTROY(): too many arguments');
+
+my $bad_watch = {};
+$bad_watch = bless($bad_watch, 'Net::ZooKeeper::Watch');
+
+$ret = $bad_watch->DESTROY();
+ok(!$ret,
+   'watch DESTROY(): no action on invalid handle');
+
+
+## wait()
+
+eval {
+    $watch->wait('bar');
+};
+like($@, qr/invalid number of arguments/,
+     'wait(): invalid number of arguments');
+
+eval {
+    $bad_watch->wait();
+};
+like($@, qr/invalid handle/,
+     'wait(): invalid watch handle');
+
+eval {
+    Net::ZooKeeper::Watch::wait(1);
+};
+like($@, qr/zkwh is not a hash reference of type Net::ZooKeeper::Watch/,
+     'wait(): invalid watch hash reference');
+
+
+## set_log_level()
+
+eval {
+    my $f = \&Net::ZooKeeper::set_log_level;
+    &$f();
+};
+like($@, qr/Usage: Net::ZooKeeper::set_log_level\(level\)/,
+     'set_log_level(): no level specified');
+
+eval {
+    my $f = \&Net::ZooKeeper::set_log_level;
+    &$f(ZOO_LOG_LEVEL_OFF, 'foo');
+};
+like($@, qr/Usage: Net::ZooKeeper::set_log_level\(level\)/,
+     'set_log_level(): too many arguments');
+
+eval {
+    Net::ZooKeeper::set_log_level((ZOO_LOG_LEVEL_OFF) - 1);
+};
+like($@, qr/invalid log level/,
+     'set_log_level(): invalid low log level');
+
+eval {
+    Net::ZooKeeper::set_log_level((ZOO_LOG_LEVEL_DEBUG) + 1);
+};
+like($@, qr/invalid log level/,
+     'set_log_level(): invalid high log level');
+
+
+## set_deterministic_conn_order()
+
+eval {
+    my $f = \&Net::ZooKeeper::set_deterministic_conn_order;
+    &$f();
+};
+like($@, qr/Usage: Net::ZooKeeper::set_deterministic_conn_order\(flag\)/,
+     'set_deterministic_conn_order(): no flag specified');
+
+eval {
+    my $f = \&Net::ZooKeeper::set_deterministic_conn_order;
+    &$f(1, 'foo');
+};
+like($@, qr/Usage: Net::ZooKeeper::set_deterministic_conn_order\(flag\)/,
+     'set_deterministic_conn_order(): too many arguments');
+

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/c9914857/content/build/contrib/zkperl/t/15_thread.t
----------------------------------------------------------------------
diff --git a/content/build/contrib/zkperl/t/15_thread.t b/content/build/contrib/zkperl/t/15_thread.t
new file mode 100644
index 0000000..1ef56d0
--- /dev/null
+++ b/content/build/contrib/zkperl/t/15_thread.t
@@ -0,0 +1,121 @@
+# 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 Config;
+use File::Spec;
+use Test::More;
+
+BEGIN {
+    if ($Config{'useithreads'}) {
+        plan tests => 10;
+    }
+    else {
+        plan skip_all => 'no thread support';
+    }
+}
+
+use threads;
+
+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);
+
+
+my $zkh = Net::ZooKeeper->new($hosts);
+
+SKIP: {
+    skip 'no valid handle', 9 unless (defined($zkh));
+
+    my($thread) = threads->new(\&thread_test, $zkh);
+
+    SKIP: {
+        skip 'no valid thread', 3 unless (defined($thread));
+
+        my(@ret) = $thread->join;
+
+        ok((@ret == 3 and $ret[0]),
+           'CLONE_SKIP(): handle reference after spawning thread');
+
+        ok((@ret == 3 and $ret[1]),
+           'CLONE_SKIP(): scalar handle reference after spawning thread');
+
+        ok((@ret == 3 and $ret[2]),
+           'CLONE_SKIP(): undef handle reference after spawning thread');
+    }
+
+    my $stat = $zkh->stat();
+
+    ($thread) = threads->new(\&thread_test, $stat);
+
+    SKIP: {
+        skip 'no valid thread', 3 unless (defined($thread));
+
+        my(@ret) = $thread->join;
+
+        ok((@ret == 3 and $ret[0]),
+           'stat CLONE_SKIP(): stat handle reference after spawning thread');
+
+        ok((@ret == 3 and $ret[1]),
+           'stat CLONE_SKIP(): scalar stat handle reference after ' .
+           'spawning thread');
+
+        ok((@ret == 3 and $ret[2]),
+           'stat CLONE_SKIP(): undef stat handle reference after ' .
+           'spawning thread');
+    }
+
+    my $watch = $zkh->watch();
+
+    ($thread) = threads->new(\&thread_test, $watch);
+
+    SKIP: {
+        skip 'no valid thread', 3 unless (defined($thread));
+
+        my(@ret) = $thread->join;
+
+        ok((@ret == 3 and $ret[0]),
+           'watch CLONE_SKIP(): watch handle reference after spawning thread');
+
+        ok((@ret == 3 and $ret[1]),
+           'watch CLONE_SKIP(): scalar watch handle reference after ' .
+           'spawning thread');
+
+        ok((@ret == 3 and $ret[2]),
+           'watch CLONE_SKIP(): undef watch handle reference after ' .
+           'spawning thread');
+    }
+}
+
+sub thread_test
+{
+    my $zkh = shift;
+
+    my @ret;
+
+    $ret[0] = ref($zkh) ? 1 : 0;
+    $ret[1] = ($ret[0] and ref($zkh) eq 'SCALAR') ? 1 : 0;
+    $ret[2] = ($ret[1] and !defined(${$zkh})) ? 1 : 0;
+
+    return @ret;
+}
+

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/c9914857/content/build/contrib/zkperl/t/20_tie.t
----------------------------------------------------------------------
diff --git a/content/build/contrib/zkperl/t/20_tie.t b/content/build/contrib/zkperl/t/20_tie.t
new file mode 100644
index 0000000..37e9a4f
--- /dev/null
+++ b/content/build/contrib/zkperl/t/20_tie.t
@@ -0,0 +1,353 @@
+# 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 => 54;
+
+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);
+
+    skip 'no valid handle', 4 unless (defined($zkh));
+
+
+    ## DESTROY()
+
+    my $attr = tied(%{$zkh});
+
+    my $ret = $attr->DESTROY();
+    ok($ret,
+       'DESTROY(): destroyed inner hash');
+
+    $ret = $attr->DESTROY();
+    ok(!$ret,
+       'DESTROY(): no action on destroyed inner hash');
+
+    $ret = $zkh->DESTROY();
+    ok(!$ret,
+       'DESTROY(): no action on handle with destroyed inner hash');
+
+    undef $zkh;
+    ok(!defined($zkh),
+       'undef: released handle with destroyed inner hash');
+}
+
+SKIP: {
+    my $zkh = Net::ZooKeeper->new($hosts);
+
+    skip 'no valid handle', 49 unless (defined($zkh));
+
+
+    ## TIEHASH(), UNTIE()
+
+    eval {
+        tie(%{$zkh}, 'Net::ZooKeeper');
+    };
+    like($@, qr/tying hashes of class Net::ZooKeeper not supported/,
+         'tie(): tying hashes not supported');
+
+    eval {
+        Net::ZooKeeper::TIEHASH('Net::ZooKeeper');
+    };
+    like($@, qr/tying hashes of class Net::ZooKeeper not supported/,
+         'TIEHASH(): tying hashes not supported');
+
+    eval {
+        untie(%{$zkh});
+    };
+    like($@, qr/untying hashes of class Net::ZooKeeper not supported/,
+         'untie(): untying hashes not supported');
+
+    my $attr = tied(%{$zkh});
+
+    eval {
+        $attr->UNTIE(0);
+    };
+    like($@, qr/untying hashes of class Net::ZooKeeper not supported/,
+         'UNTIE(): untying hashes not supported');
+
+
+    ## FIRSTKEY(), NEXTKEY(), SCALAR()
+
+    my $copy_zkh;
+    {
+        my %copy_zkh = %{$zkh};
+        $copy_zkh = \%copy_zkh;
+    }
+    bless($copy_zkh, 'Net::ZooKeeper');
+    is(ref($copy_zkh), 'Net::ZooKeeper',
+       'FIRSTKEY(), NEXTKEY(): copied dereferenced handle');
+
+    eval {
+        my $val = $copy_zkh->FIRSTKEY();
+    };
+    like($@, qr/invalid handle/,
+         'FETCHKEY(): invalid handle');
+
+    eval {
+        my $val = $copy_zkh->NEXTKEY('data_read_len');
+    };
+    like($@, qr/invalid handle/,
+         'NEXTKEY(): invalid handle');
+
+    my @keys = keys(%{$zkh});
+    is(scalar(@keys), 7,
+       'keys(): count of keys from handle');
+
+    @keys = keys(%{$copy_zkh});
+    is(scalar(@keys), 7,
+       'keys(): count of keys from copied dereferenced handle');
+
+    is($attr->FIRSTKEY(), 'data_read_len',
+       'FIRSTKEY(): retrieved first key using inner hash');
+
+    is($attr->NEXTKEY('session_id'), 'pending_watches',
+       'NEXTKEY(): retrieved last key using inner hash');
+
+    is($attr->NEXTKEY('pending_watches'), undef,
+       'NEXTKEY(): undef returned after last key using inner hash');
+
+    ok(scalar(%{$zkh}),
+       'scalar(): true value returned for dereferenced handle');
+
+    ok($zkh->SCALAR(),
+       'SCALAR(): true value returned');
+
+
+    ## FETCH()
+
+    eval {
+        my $val = $copy_zkh->FETCH('data_read_len');
+    };
+    like($@, qr/invalid handle/,
+         'FETCH(): invalid handle');
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        my $val = $zkh->{'foo'};
+        ok(!defined($val),
+           'FETCH(): undef returned for invalid element');
+
+        like($msg, qr/invalid element/,
+             'FETCH(): invalid element');
+    }
+
+    is($zkh->{'data_read_len'}, 1023,
+       'FETCH(): default data read length');
+
+    is($zkh->{'path_read_len'}, 1023,
+       'FETCH(): default path read length');
+
+    is($zkh->{'hosts'}, $hosts,
+       'FETCH(): server hosts');
+
+    is($zkh->{'session_timeout'}, 10000,
+       'FETCH(): default session timeout');
+
+    ok(defined($zkh->{'session_id'}),
+       'FETCH(): session ID');
+
+    SKIP: {
+        my $zkh = Net::ZooKeeper->new('0.0.0.0:0');
+
+        skip 'no valid handle with invalid host', 1 unless (defined($zkh));
+
+        is($zkh->{'session_id'}, '',
+           'FETCH(): empty session ID with invalid host');
+    }
+
+    is($zkh->{'pending_watches'}, 0,
+       'FETCH(): default pending watch list length');
+
+    is($attr->FETCH('data_read_len'), 1023,
+       'FETCH(): default data read length using inner hash');
+
+
+    ## STORE()
+
+    eval {
+        my $val = $copy_zkh->STORE('data_read_len', 'foo');
+    };
+    like($@, qr/invalid handle/,
+         'STORE(): invalid handle');
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        $zkh->{'foo'} = 'foo';
+        like($msg, qr/invalid element/,
+             'STORE(): invalid element');
+    }
+
+    eval {
+        $zkh->{'data_read_len'} = -3;
+    };
+    like($@, qr/invalid data read length/,
+         'STORE(): invalid data read length');
+
+    eval {
+        $zkh->{'path_read_len'} = -3;
+    };
+    like($@, qr/invalid path read length/,
+         'STORE(): invalid path read length');
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        $zkh->{'hosts'} = 'foo';
+        like($msg, qr/read-only element: hosts/,
+             'STORE(): read-only server hosts element');
+    }
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        $zkh->{'session_timeout'} = 0;
+        like($msg, qr/read-only element: session_timeout/,
+             'STORE(): read-only session timeout element');
+    }
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        $zkh->{'session_id'} = 'foo';
+        like($msg, qr/read-only element: session_id/,
+             'STORE(): read-only session ID element');
+    }
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        $zkh->{'pending_watches'} = 0;
+        like($msg, qr/read-only element: pending_watches/,
+             'STORE(): read-only pending watch list length element');
+    }
+
+    $zkh->{'data_read_len'} = 200;
+    is($zkh->{'data_read_len'}, 200,
+       'STORE(): updated data read length');
+
+    $zkh->{'path_read_len'} = 100;
+    is($zkh->{'path_read_len'}, 100,
+       'STORE(): updated path read length');
+
+    $attr->STORE('data_read_len', 100);
+    is($zkh->{'data_read_len'}, 100,
+       'STORE(): updated data read length using inner hash');
+
+
+    ## EXISTS()
+
+    eval {
+        my $val = $copy_zkh->EXISTS('data_read_len');
+    };
+    like($@, qr/invalid handle/,
+         'EXISTS(): invalid handle');
+
+    ok(!exists($zkh->{'foo'}),
+       'exists(): invalid element of handle');
+
+    ok(exists($zkh->{'data_read_len'}),
+       'exists(): data read length');
+
+    ok(exists($zkh->{'path_read_len'}),
+       'exists(): path read length');
+
+    ok(exists($zkh->{'hosts'}),
+       'exists(): server hosts');
+
+    ok(exists($zkh->{'session_timeout'}),
+       'exists(): session timeout');
+
+    ok(exists($zkh->{'session_id'}),
+       'exists(): session ID');
+
+    ok(exists($zkh->{'pending_watches'}),
+       'exists(): pending watch list length');
+
+    ok($attr->EXISTS('data_read_len'),
+       'EXISTS(): data read length using inner hash');
+
+
+    ## DELETE(), CLEAR()
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        delete($zkh->{'data_read_len'});
+        like($msg,
+             qr/deleting elements from hashes of class Net::ZooKeeper not supported/,
+             'delete(): deleting hash elements not supported');
+    }
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        $zkh->DELETE({'data_read_len'});
+        like($msg,
+             qr/deleting elements from hashes of class Net::ZooKeeper not supported/,
+             'DELETE(): deleting hash elements not supported');
+    }
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        %{$zkh} = ();
+        like($msg, qr/clearing hashes of class Net::ZooKeeper not supported/,
+             'assign: clearing hashes not supported');
+    }
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        $zkh->CLEAR();
+        like($msg, qr/clearing hashes of class Net::ZooKeeper not supported/,
+             'CLEAR(): clearing hashes not supported');
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/c9914857/content/build/contrib/zkperl/t/22_stat_tie.t
----------------------------------------------------------------------
diff --git a/content/build/contrib/zkperl/t/22_stat_tie.t b/content/build/contrib/zkperl/t/22_stat_tie.t
new file mode 100644
index 0000000..02e7913
--- /dev/null
+++ b/content/build/contrib/zkperl/t/22_stat_tie.t
@@ -0,0 +1,438 @@
+# 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 => 66;
+
+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 $stat = $zkh->stat() if (defined($zkh));
+
+    skip 'no valid stat handle', 4 unless (defined($stat));
+
+
+    ## DESTROY()
+
+    my $attr = tied(%{$stat});
+
+    my $ret = $attr->DESTROY();
+    ok($ret,
+       'stat DESTROY(): destroyed inner stat hash');
+
+    $ret = $attr->DESTROY();
+    ok(!$ret,
+       'stat DESTROY(): no action on destroyed inner stat hash');
+
+    $ret = $stat->DESTROY();
+    ok(!$ret,
+       'stat DESTROY(): no action on stat handle with destroyed inner hash');
+
+    undef $stat;
+    ok(!defined($stat),
+       'undef: released stat handle with destroyed inner hash');
+}
+
+SKIP: {
+    my $zkh = Net::ZooKeeper->new($hosts);
+    my $stat = $zkh->stat() if (defined($zkh));
+
+    skip 'no valid stat handle', 61 unless (defined($stat));
+
+
+    ## TIEHASH(), UNTIE()
+
+    eval {
+        tie(%{$stat}, 'Net::ZooKeeper::Stat');
+    };
+    like($@, qr/tying hashes of class Net::ZooKeeper::Stat not supported/,
+         'tie(): tying stat hashes not supported');
+
+    eval {
+        Net::ZooKeeper::Stat::TIEHASH('Net::ZooKeeper::Stat');
+    };
+    like($@, qr/tying hashes of class Net::ZooKeeper::Stat not supported/,
+         'stat TIEHASH(): tying stat hashes not supported');
+
+    eval {
+        untie(%{$stat});
+    };
+    like($@, qr/untying hashes of class Net::ZooKeeper::Stat not supported/,
+         'untie(): untying stat hashes not supported');
+
+    my $attr = tied(%{$stat});
+
+    eval {
+        $attr->UNTIE(0);
+    };
+    like($@, qr/untying hashes of class Net::ZooKeeper::Stat not supported/,
+         'stat UNTIE(): untying stat hashes not supported');
+
+
+    ## FIRSTKEY(), NEXTKEY(), SCALAR()
+
+    my $copy_stat;
+    {
+        my %copy_stat = %{$stat};
+        $copy_stat = \%copy_stat;
+    }
+    bless($copy_stat, 'Net::ZooKeeper::Stat');
+    is(ref($copy_stat), 'Net::ZooKeeper::Stat',
+       'stat FIRSTKEY(), NEXTKEY(): copied dereferenced stat handle');
+
+    eval {
+        my $val = $copy_stat->FIRSTKEY();
+    };
+    like($@, qr/invalid handle/,
+         'stat FETCHKEY(): invalid stat handle');
+
+    eval {
+        my $val = $copy_stat->NEXTKEY('czxid');
+    };
+    like($@, qr/invalid handle/,
+         'stat NEXTKEY(): invalid stat handle');
+
+    my @keys = keys(%{$stat});
+    is(scalar(@keys), 11,
+       'keys(): count of keys from stat handle');
+
+    @keys = keys(%{$copy_stat});
+    is(scalar(@keys), 11,
+       'keys(): count of keys from copied dereferenced stat handle');
+
+    is($attr->FIRSTKEY(), 'czxid',
+       'stat FIRSTKEY(): retrieved first key using inner stat hash');
+
+    is($attr->NEXTKEY('num_children'), 'children_zxid',
+       'stat NEXTKEY(): retrieved last key using inner stat hash');
+
+    is($attr->NEXTKEY('children_zxid'), undef,
+       'NEXTKEY(): undef returned after last key using inner stat hash');
+
+    ok(scalar(%{$stat}),
+       'scalar(): true value returned for dereferenced stat handle');
+
+    ok($stat->SCALAR(),
+       'stat SCALAR(): true value returned');
+
+
+    ## FETCH()
+
+    eval {
+        my $val = $copy_stat->FETCH('version');
+    };
+    like($@, qr/invalid handle/,
+         'stat FETCH(): invalid stat handle');
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        my $val = $stat->{'foo'};
+        ok(!defined($val),
+           'stat FETCH(): undef returned for invalid element');
+
+        like($msg, qr/invalid element/,
+             'stat FETCH(): invalid element');
+    }
+
+    is($stat->{'czxid'}, 0,
+       'stat FETCH(): default node creation ZooKeeper transaction ID');
+
+    is($stat->{'mzxid'}, 0,
+       'stat FETCH(): default data last-modified ZooKeeper transaction ID');
+
+    is($stat->{'ctime'}, 0,
+       'stat FETCH(): default node creation time');
+
+    is($stat->{'mtime'}, 0,
+       'stat FETCH(): default data last-modified time');
+
+    is($stat->{'version'}, 0,
+       'stat FETCH(): default data version');
+
+    is($stat->{'children_version'}, 0,
+       'stat FETCH(): default child node list version');
+
+    is($stat->{'acl_version'}, 0,
+       'stat FETCH(): default ACL version');
+
+    is($stat->{'ephemeral_owner'}, 0,
+       'stat FETCH(): ephemeral node owner session ID');
+
+    is($stat->{'data_len'}, 0,
+       'stat FETCH(): default data length');
+
+    is($stat->{'num_children'}, 0,
+       'stat FETCH(): default child node list length');
+
+    is($stat->{'children_zxid'}, 0,
+       'stat FETCH(): default child node list last-modified ' .
+       'ZooKeeper transaction ID');
+
+    is($attr->FETCH('version'), 0,
+       'stat FETCH(): default data version using inner stat hash');
+
+
+    ## STORE()
+
+    eval {
+        my $val = $copy_stat->STORE('version', 'foo');
+    };
+    like($@, qr/invalid handle/,
+         'stat STORE(): invalid stat handle');
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        $stat->{'foo'} = 'foo';
+        like($msg, qr/invalid element/,
+             'stat STORE(): invalid element');
+    }
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        $stat->{'czxid'} = 'foo';
+        like($msg, qr/read-only element: czxid/,
+             'stat STORE(): read-only node creation ' .
+             'ZooKeeper transaction ID element');
+    }
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        $stat->{'mzxid'} = 'foo';
+        like($msg, qr/read-only element: mzxid/,
+             'stat STORE(): read-only data last-modified ' .
+             'ZooKeeper transaction ID element');
+    }
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        $stat->{'ctime'} = 'foo';
+        like($msg, qr/read-only element: ctime/,
+             'stat STORE(): read-only node creation time element');
+    }
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        $stat->{'mtime'} = 'foo';
+        like($msg, qr/read-only element: mtime/,
+             'stat STORE(): read-only data last-modified time element');
+    }
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        $stat->{'version'} = 'foo';
+        like($msg, qr/read-only element: version/,
+             'stat STORE(): read-only data version element');
+    }
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        $stat->{'children_version'} = 'foo';
+        like($msg, qr/read-only element: children_version/,
+             'stat STORE(): read-only child node list version element');
+    }
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        $stat->{'acl_version'} = 'foo';
+        like($msg, qr/read-only element: acl_version/,
+             'stat STORE(): read-only ACL version element');
+    }
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        $stat->{'ephemeral_owner'} = 'foo';
+        like($msg, qr/read-only element: ephemeral_owner/,
+             'stat STORE(): read-only ephemeral node owner ' .
+             'session ID element');
+    }
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        $stat->{'data_len'} = 'foo';
+        like($msg, qr/read-only element: data_len/,
+             'stat STORE(): read-only data length element');
+    }
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        $stat->{'num_children'} = 'foo';
+        like($msg, qr/read-only element: num_children/,
+             'stat STORE(): read-only child node list length element');
+    }
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        $stat->{'children_zxid'} = 'foo';
+        like($msg, qr/read-only element: children_zxid/,
+             'stat STORE(): read-only child node list last-modified ' .
+             'ZooKeeper transaction ID element');
+    }
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        $attr->STORE('version', 'foo');
+        like($msg, qr/read-only element: version/,
+             'stat STORE(): read-only data version element using ' .
+             'inner stat hash');
+    }
+
+
+    ## EXISTS()
+
+    eval {
+        my $val = $copy_stat->EXISTS('version');
+    };
+    like($@, qr/invalid handle/,
+         'stat EXISTS(): invalid stat handle');
+
+    ok(!exists($stat->{'foo'}),
+       'exists(): invalid element of stat handle');
+
+    ok(exists($stat->{'czxid'}),
+       'exists(): node creation ZooKeeper transaction ID');
+
+    ok(exists($stat->{'mzxid'}),
+       'exists(): data last-modified ZooKeeper transaction ID');
+
+    ok(exists($stat->{'ctime'}),
+       'exists(): node creation time');
+
+    ok(exists($stat->{'mtime'}),
+       'exists(): data last-modified time');
+
+    ok(exists($stat->{'version'}),
+       'exists(): data version');
+
+    ok(exists($stat->{'children_version'}),
+       'exists(): child node list version');
+
+    ok(exists($stat->{'acl_version'}),
+       'exists(): ACL version');
+
+    ok(exists($stat->{'ephemeral_owner'}),
+       'exists(): ephemeral node owner session ID');
+
+    ok(exists($stat->{'data_len'}),
+       'exists(): data length');
+
+    ok(exists($stat->{'num_children'}),
+       'exists(): child node list length');
+
+    ok(exists($stat->{'children_zxid'}),
+       'exists(): child node list last-modified ZooKeeper transaction ID');
+
+    ok($attr->EXISTS('version'),
+       'stat EXISTS(): data version using inner stat hash');
+
+
+    ## DELETE(), CLEAR()
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        delete($stat->{'version'});
+        like($msg,
+             qr/deleting elements from hashes of class Net::ZooKeeper::Stat not supported/,
+             'delete(): deleting stat hash elements not supported');
+    }
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        $stat->DELETE({'version'});
+        like($msg,
+             qr/deleting elements from hashes of class Net::ZooKeeper::Stat not supported/,
+             'stat DELETE(): deleting stat hash elements not supported');
+    }
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        %{$stat} = ();
+        like($msg, qr/clearing hashes of class Net::ZooKeeper::Stat not supported/,
+             'assign: clearing stat hashes not supported');
+    }
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        $stat->CLEAR();
+        like($msg, qr/clearing hashes of class Net::ZooKeeper::Stat not supported/,
+             'stat CLEAR(): clearing stat hashes not supported');
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/c9914857/content/build/contrib/zkperl/t/24_watch_tie.t
----------------------------------------------------------------------
diff --git a/content/build/contrib/zkperl/t/24_watch_tie.t b/content/build/contrib/zkperl/t/24_watch_tie.t
new file mode 100644
index 0000000..e77879e
--- /dev/null
+++ b/content/build/contrib/zkperl/t/24_watch_tie.t
@@ -0,0 +1,292 @@
+# 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 => 42;
+
+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 $watch = $zkh->watch() if (defined($zkh));
+
+    skip 'no valid watch handle', 4 unless (defined($watch));
+
+
+    ## DESTROY()
+
+    my $attr = tied(%{$watch});
+
+    my $ret = $attr->DESTROY();
+    ok($ret,
+       'watch DESTROY(): destroyed inner watch hash');
+
+    $ret = $attr->DESTROY();
+    ok(!$ret,
+       'watch DESTROY(): no action on destroyed inner watch hash');
+
+    $ret = $watch->DESTROY();
+    ok(!$ret,
+       'watch DESTROY(): no action on watch handle with destroyed inner hash');
+
+    undef $watch;
+    ok(!defined($watch),
+       'undef: released watch handle with destroyed inner hash');
+}
+
+SKIP: {
+    my $zkh = Net::ZooKeeper->new($hosts);
+    my $watch = $zkh->watch() if (defined($zkh));
+
+    skip 'no valid watch handle', 37 unless (defined($watch));
+
+
+    ## TIEHASH(), UNTIE()
+
+    eval {
+        tie(%{$watch}, 'Net::ZooKeeper::Watch');
+    };
+    like($@, qr/tying hashes of class Net::ZooKeeper::Watch not supported/,
+         'tie(): tying watch hashes not supported');
+
+    eval {
+        Net::ZooKeeper::Watch::TIEHASH('Net::ZooKeeper::Watch');
+    };
+    like($@, qr/tying hashes of class Net::ZooKeeper::Watch not supported/,
+         'watch TIEHASH(): tying watch hashes not supported');
+
+    eval {
+        untie(%{$watch});
+    };
+    like($@, qr/untying hashes of class Net::ZooKeeper::Watch not supported/,
+         'untie(): untying watch hashes not supported');
+
+    my $attr = tied(%{$watch});
+
+    eval {
+        $attr->UNTIE(0);
+    };
+    like($@, qr/untying hashes of class Net::ZooKeeper::Watch not supported/,
+         'watch UNTIE(): untying watch hashes not supported');
+
+
+    ## FIRSTKEY(), NEXTKEY(), SCALAR()
+
+    my $copy_watch;
+    {
+        my %copy_watch = %{$watch};
+        $copy_watch = \%copy_watch;
+    }
+    bless($copy_watch, 'Net::ZooKeeper::Watch');
+    is(ref($copy_watch), 'Net::ZooKeeper::Watch',
+       'watch FIRSTKEY(), NEXTKEY(): copied dereferenced watch handle');
+
+    eval {
+        my $val = $copy_watch->FIRSTKEY();
+    };
+    like($@, qr/invalid handle/,
+         'watch FETCHKEY(): invalid watch handle');
+
+    eval {
+        my $val = $copy_watch->NEXTKEY('czxid');
+    };
+    like($@, qr/invalid handle/,
+         'watch NEXTKEY(): invalid watch handle');
+
+    my @keys = keys(%{$watch});
+    is(scalar(@keys), 3,
+       'keys(): count of keys from watch handle');
+
+    @keys = keys(%{$copy_watch});
+    is(scalar(@keys), 3,
+       'keys(): count of keys from copied dereferenced watch handle');
+
+    is($attr->FIRSTKEY(), 'timeout',
+       'watch FIRSTKEY(): retrieved first key using inner watch hash');
+
+    is($attr->NEXTKEY('event'), 'state',
+       'watch NEXTKEY(): retrieved last key using inner watch hash');
+
+    is($attr->NEXTKEY('state'), undef,
+       'NEXTKEY(): undef returned after last key using inner watch hash');
+
+    ok(scalar(%{$watch}),
+       'scalar(): true value returned for dereferenced watch handle');
+
+    ok($watch->SCALAR(),
+       'watch SCALAR(): true value returned');
+
+
+    ## FETCH()
+
+    eval {
+        my $val = $copy_watch->FETCH('version');
+    };
+    like($@, qr/invalid handle/,
+         'watch FETCH(): invalid watch handle');
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        my $val = $watch->{'foo'};
+        ok(!defined($val),
+           'watch FETCH(): undef returned for invalid element');
+
+        like($msg, qr/invalid element/,
+             'watch FETCH(): invalid element');
+    }
+
+    is($watch->{'timeout'}, 60000,
+       'watch FETCH(): default timeout');
+
+    is($watch->{'event'}, 0,
+       'watch FETCH(): default event');
+
+    is($watch->{'state'}, 0,
+       'watch FETCH(): default state');
+
+    is($attr->FETCH('timeout'), 60000,
+       'watch FETCH(): default timeout using inner watch hash');
+
+
+    ## STORE()
+
+    eval {
+        my $val = $copy_watch->STORE('version', 'foo');
+    };
+    like($@, qr/invalid handle/,
+         'watch STORE(): invalid watch handle');
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        $watch->{'foo'} = 'foo';
+        like($msg, qr/invalid element/,
+             'watch STORE(): invalid element');
+    }
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        $watch->{'event'} = 'foo';
+        like($msg, qr/read-only element: event/,
+             'watch STORE(): read-only event element');
+    }
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        $watch->{'state'} = 'foo';
+        like($msg, qr/read-only element: state/,
+             'watch STORE(): read-only state element');
+    }
+
+    $watch->{'timeout'} = 100;
+    is($watch->{'timeout'}, 100,
+       'watch STORE(): updated timeout');
+
+    $attr->STORE('timeout', 200);
+    is($watch->{'timeout'}, 200,
+       'watch STORE(): updated timeout using inner hash');
+
+
+    ## EXISTS()
+
+    eval {
+        my $val = $copy_watch->EXISTS('version');
+    };
+    like($@, qr/invalid handle/,
+         'watch EXISTS(): invalid watch handle');
+
+    ok(!exists($watch->{'foo'}),
+       'exists(): invalid element of watch handle');
+
+    ok(exists($watch->{'timeout'}),
+       'exists(): timeout');
+
+    ok(exists($watch->{'event'}),
+       'exists(): event');
+
+    ok(exists($watch->{'state'}),
+       'exists(): state');
+
+    ok($attr->EXISTS('timeout'),
+       'watch EXISTS(): timeout using inner watch hash');
+
+
+    ## DELETE(), CLEAR()
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        delete($watch->{'version'});
+        like($msg,
+             qr/deleting elements from hashes of class Net::ZooKeeper::Watch not supported/,
+             'delete(): deleting watch hash elements not supported');
+    }
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        $watch->DELETE({'version'});
+        like($msg,
+             qr/deleting elements from hashes of class Net::ZooKeeper::Watch not supported/,
+             'watch DELETE(): deleting watch hash elements not supported');
+    }
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        %{$watch} = ();
+        like($msg, qr/clearing hashes of class Net::ZooKeeper::Watch not supported/,
+             'assign: clearing watch hashes not supported');
+    }
+
+    {
+        my $msg;
+
+        $SIG{'__WARN__'} = sub { $msg = $_[0]; };
+
+        $watch->CLEAR();
+        like($msg, qr/clearing hashes of class Net::ZooKeeper::Watch not supported/,
+             'watch CLEAR(): clearing watch hashes not supported');
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/c9914857/content/build/contrib/zkperl/t/30_connect.t
----------------------------------------------------------------------
diff --git a/content/build/contrib/zkperl/t/30_connect.t b/content/build/contrib/zkperl/t/30_connect.t
new file mode 100644
index 0000000..c2b68bb
--- /dev/null
+++ b/content/build/contrib/zkperl/t/30_connect.t
@@ -0,0 +1,202 @@
+# 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 => 29;
+
+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);
+
+
+## new(), DESTROY()
+
+Net::ZooKeeper::set_deterministic_conn_order(1);
+
+my $zkh = Net::ZooKeeper->new($hosts);
+isa_ok($zkh, 'Net::ZooKeeper',
+       'new(): created handle');
+
+SKIP: {
+    skip 'no valid handle', 3 unless (defined($zkh));
+
+    my $ret = $zkh->DESTROY();
+    ok($ret,
+       'DESTROY(): destroyed handle');
+
+    $ret = $zkh->DESTROY();
+    ok(!$ret,
+       'DESTROY(): no action on destroyed handle');
+
+    undef $zkh;
+    ok(!defined($zkh),
+       'undef: released handle');
+}
+
+Net::ZooKeeper::set_deterministic_conn_order(0);
+
+SKIP: {
+    my $zkh = Net::ZooKeeper->new($hosts);
+
+    skip 'no valid handle', 10 unless (defined($zkh));
+
+    my $copy_zkh = $zkh;
+    isa_ok($copy_zkh, 'Net::ZooKeeper',
+           'assign: copied handle');
+
+    my $ret = $zkh->exists($root_path);
+    ok(defined($ret),
+       'exists(): no error from original handle');
+
+    undef $zkh;
+    ok(!defined($zkh),
+       'undef: released original handle');
+
+    $ret = $copy_zkh->exists($root_path);
+    ok(defined($ret),
+       'exists(): no error from first copy of handle');
+
+    $zkh = $copy_zkh;
+    isa_ok($zkh, 'Net::ZooKeeper',
+           'assign: re-copied handle');
+
+    $ret = $copy_zkh->DESTROY();
+    ok($ret,
+       'DESTROY(): destroyed first copy of handle');
+
+    eval {
+        $zkh->exists($root_path);
+    };
+    like($@, qr/invalid handle/,
+         'exists(): invalid second copy of handle');
+
+    undef $copy_zkh;
+    ok(!defined($copy_zkh),
+       'undef: released first copy of handle');
+
+    $ret = $zkh->DESTROY();
+    ok(!$ret,
+       'DESTROY(): no action on second copy of destroyed handle');
+
+    undef $zkh;
+    ok(!defined($zkh),
+       'undef: released second copy of handle');
+}
+
+SKIP: {
+    my $zkh = Net::ZooKeeper->new($hosts);
+
+    skip 'no valid handle', 6 unless (defined($zkh));
+
+    my $copy_zkh;
+    {
+        my %copy_zkh = %{$zkh};
+        $copy_zkh = \%copy_zkh;
+    }
+    bless($copy_zkh, 'Net::ZooKeeper');
+    isa_ok($copy_zkh, 'Net::ZooKeeper',
+           'FIRSTKEY(), NEXTKEY(): copied dereferenced handle');
+
+    eval {
+        $copy_zkh->exists($root_path);
+    };
+    like($@, qr/invalid handle/,
+         'exists(): invalid copy of dereferenced handle');
+
+    $ret = $copy_zkh->DESTROY();
+    ok(!$ret,
+       'DESTROY(): no action on copy of dereferenced handle');
+
+    undef $copy_zkh;
+    ok(!defined($copy_zkh),
+       'undef: released copy of dereferenced handle');
+
+    my $ret = $zkh->exists($root_path);
+    ok(defined($ret),
+       'exists(): no error from original handle');
+
+    undef $zkh;
+    ok(!defined($zkh),
+       'undef: released original handle');
+}
+
+Net::ZooKeeper::set_deterministic_conn_order(1);
+
+my $zkh1 = Net::ZooKeeper->new($hosts, 'session_timeout' => 0x3FFF_FFFF);
+isa_ok($zkh1, 'Net::ZooKeeper',
+       'new(): created handle with maximum session timeout');
+
+SKIP: {
+    my $ret = $zkh1->exists($root_path) if (defined($zkh1));
+
+    skip 'no connection to ZooKeeper', 7 unless
+        (defined($ret) and $ret);
+
+
+    ## FETCH() of read-only attributes
+
+    ok(($zkh1->{'session_timeout'} > 0 and
+        $zkh1->{'session_timeout'} <= 0x3FFF_FFFF),
+       'FETCH(): session timeout reset after connection');
+
+    my $session_id1 = $zkh1->{'session_id'};
+    ok((length($session_id1) > 0),
+       'FETCH(): non-empty session ID after connection');
+
+    SKIP: {
+        skip 'no session ID after connection', 1 unless
+            (length($session_id1) > 0);
+
+        my @nonzero_bytes = grep($_ != 0, unpack('c' x length($session_id1),
+                                                 $session_id1));
+        ok((@nonzero_bytes > 0),
+           'FETCH(): non-zero session ID after connection');
+    }
+
+    ## NOTE: to test re-connections with saved session IDs we create a second
+    ## connection with the same ID while the first is still active;
+    ## this is bad practice in normal usage
+
+    my $zkh2 = Net::ZooKeeper->new($hosts,
+                                  'session_id' => $session_id1,
+                                  'session_timeout' => 20000);
+    isa_ok($zkh2, 'Net::ZooKeeper',
+           'new(): created handle with session ID and valid session timeout');
+
+    $ret = $zkh2->exists($root_path);
+    ok($ret,
+       'new(): reconnection with session ID');
+
+    SKIP: {
+        skip 'no connection to ZooKeeper', 2 unless ($ret);
+
+        is($zkh2->{'session_timeout'}, 20000,
+           'FETCH(): session timeout unchanged after connection');
+
+        my $session_id2 = $zkh2->{'session_id'};
+        ok((length($session_id2) == length($session_id1)
+            and $session_id2 eq $session_id1),
+           'FETCH(): reconnect with session ID');
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/c9914857/content/build/contrib/zkperl/t/35_log.t
----------------------------------------------------------------------
diff --git a/content/build/contrib/zkperl/t/35_log.t b/content/build/contrib/zkperl/t/35_log.t
new file mode 100644
index 0000000..92821af
--- /dev/null
+++ b/content/build/contrib/zkperl/t/35_log.t
@@ -0,0 +1,88 @@
+# 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 => 3;
+
+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);
+
+
+my $zkh = Net::ZooKeeper->new($hosts);
+
+Net::ZooKeeper::set_log_level(ZOO_LOG_LEVEL_INFO);
+
+SKIP: {
+    skip 'no valid handle', 2 unless (defined($zkh));
+
+    SKIP: {
+        my $dup = 0;
+
+        if (open(OLDERR, '>&', fileno(STDERR))) {
+            if (close(STDERR) and open(STDERR, '+>', undef)) {
+                $dup = 1;
+
+                my $old_select = select(STDERR);
+                $| = 1;
+                select($old_select);
+            }
+            else {
+                open(STDERR, '>&', fileno(OLDERR));
+                close(OLDERR);
+            }
+        }
+
+        skip 'no duplicated stderr', 2 unless ($dup);
+
+        SKIP: {
+            $zkh->exists($root_path);
+
+            sleep(1);
+
+            skip 'no seek on stderr', 1 unless (seek(STDERR, 0, 0));
+
+            my $log = <STDERR>;
+            like($log, qr/ZOO_/,
+                 'exists(): generated log message');
+        }
+
+        SKIP: {
+            $zkh->DESTROY();
+
+            sleep(1);
+
+            skip 'no seek on stderr', 1 unless (seek(STDERR, 0, 0));
+
+            my $log = <STDERR>;
+            like($log, qr/ZOO_/,
+                 'DESTROY(): generated log message');
+        }
+
+        open(STDERR, '>&', fileno(OLDERR));
+        close(OLDERR);
+    }
+}
+
+Net::ZooKeeper::set_log_level(ZOO_LOG_LEVEL_OFF);
+

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/c9914857/content/build/contrib/zkperl/t/40_basic.t
----------------------------------------------------------------------
diff --git a/content/build/contrib/zkperl/t/40_basic.t b/content/build/contrib/zkperl/t/40_basic.t
new file mode 100644
index 0000000..38a8a21
--- /dev/null
+++ b/content/build/contrib/zkperl/t/40_basic.t
@@ -0,0 +1,277 @@
+# 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 => 35;
+
+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);
+
+
+my $zkh = Net::ZooKeeper->new($hosts);
+my $path;
+
+SKIP: {
+    my $ret = $zkh->exists($root_path) if (defined($zkh));
+
+    skip 'no connection to ZooKeeper', 1 unless
+        (defined($ret) and $ret);
+
+    $path = $zkh->create($node_path, 'foo', 'acl' => ZOO_OPEN_ACL_UNSAFE);
+    is($path, $node_path,
+       'create(): created node');
+}
+
+SKIP: {
+    skip 'no connection to ZooKeeper', 21 unless
+        (defined($path) and $path eq $node_path);
+
+
+    ## exists()
+
+    my $ret = $zkh->exists($node_path);
+    ok($ret,
+       'exists(): checked node existence');
+
+    $ret = $zkh->exists($node_path . '/NONE');
+    ok((!$ret and $zkh->get_error() == ZNONODE and $! eq ''),
+       'exists(): checked node non-existence');
+
+    my $stat = $zkh->stat();
+
+    $ret = $zkh->exists($node_path, 'stat' => $stat);
+    ok(($ret and $stat->{'data_len'} == 3),
+       'exists(): checked node existence with stat handle');
+
+
+    ## get()
+
+    my $node = $zkh->get($node_path);
+    is($node, 'foo',
+       'get(): retrieved node value');
+
+    $node = $zkh->get($node_path . '/NONE');
+    ok((!defined($node) and $zkh->get_error() == ZNONODE and $! eq ''),
+       'get(): undef returned for non-extant node');
+
+    $node = $zkh->get($node_path, 'data_read_len', 2);
+    is($node, 'fo',
+       'get(): retrieved truncated node value');
+
+    $node = $zkh->get($node_path, 'data_read_len' => 0);
+    is($node, '',
+       'get(): retrieved zero-length node value');
+
+    $node = $zkh->get($node_path, 'stat' => $stat);
+    ok(($node eq 'foo' and $stat->{'data_len'} == 3),
+       'get(): retrieved node value with stat handle');
+
+
+    ## set()
+
+    $ret = $zkh->set($node_path, 'foo');
+    ok($ret,
+       'set(): set node value');
+
+    SKIP: {
+        my $ret = $zkh->exists($node_path, 'stat' => $stat);
+
+        skip 'invalid node data', 2 unless
+            ($ret and $stat->{'version'} == 1);
+
+        $ret = $zkh->set($node_path, 'foo', 'version' => $stat->{'version'});
+        ok($ret,
+           'set(): set node value with matching version');
+
+        $ret = $zkh->set($node_path, 'foo', 'version' => $stat->{'version'});
+        ok((!$ret and $zkh->get_error() == ZBADVERSION and $! eq ''),
+           'set(): node value unchanged if non-matching version');
+    }
+
+    $ret = $zkh->set($node_path, 'foobaz', 'stat' => $stat);
+    ok(($ret and $stat->{'data_len'} == 6),
+       'set(): retrieved node value with stat handle');
+
+
+    ## create(), delete()
+
+    $path = $zkh->create($node_path, 'foo', 'acl' => ZOO_OPEN_ACL_UNSAFE);
+    ok((!defined($path) and $zkh->get_error() == ZNODEEXISTS and $! eq ''),
+       'create(): undef when attempting to create extant node');
+
+    $ret = $zkh->delete($node_path . '/NONE');
+    ok((!$ret and $zkh->get_error() == ZNONODE and $! eq ''),
+       'delete(): no deletion of non-extant node');
+
+    $ret = $zkh->delete($node_path);
+    ok($ret,
+       'delete(): deleted node');
+
+    my $path_read_len = length($node_path) - 2;
+
+    $path = $zkh->create($node_path, 'foo',
+                         'path_read_len' => $path_read_len,
+                         'acl' => ZOO_OPEN_ACL_UNSAFE);
+    is($path, substr($node_path, 0, -2),
+       'create(): created node with small return path buffer');
+
+    $path = $zkh->create("$node_path/s", 'foo',
+                         'flags' => ZOO_SEQUENCE,
+                         'acl' => ZOO_OPEN_ACL_UNSAFE);
+    like($path, qr/^$node_path\/s[0-9]+$/,
+       'create(): created sequential node');
+
+    SKIP: {
+        my $ret = $zkh->exists($path, 'stat' => $stat);
+
+        unless ($ret and $stat->{'version'} == 0) {
+            my $ret = $zkh->delete($path);
+            diag(sprintf('unable to delete node %s: %d, %s',
+                         $path, $zkh->get_error(), $!)) unless ($ret);
+
+            skip 'invalid node data', 2;
+        }
+
+        $ret = $zkh->delete($path, 'version' => ($stat->{'version'} + 1));
+        ok((!$ret and $zkh->get_error() == ZBADVERSION and $! eq ''),
+           'delete(): node not deleted if non-matching version');
+
+        $ret = $zkh->delete($path, 'version' => $stat->{'version'});
+        ok($ret,
+           'delete(): deleted sequential node with matching version');
+    }
+
+    $path = $zkh->create("$node_path/e", 'foo',
+                         'flags' => ZOO_EPHEMERAL,
+                         'acl' => ZOO_OPEN_ACL_UNSAFE);
+    is($path, "$node_path/e",
+       'create(): created ephemeral node');
+
+    $path = $zkh->create("$node_path/es", 'foo',
+                         'flags' => (ZOO_SEQUENCE | ZOO_EPHEMERAL),
+                         'acl' => ZOO_OPEN_ACL_UNSAFE);
+    like($path, qr/^$node_path\/es[0-9]+$/,
+       'create(): created ephemeral sequential node');
+
+    undef $zkh;
+}
+
+$zkh = Net::ZooKeeper->new($hosts);
+
+SKIP: {
+    my $ret = $zkh->exists($node_path) if (defined($zkh));
+
+    skip 'no connection to ZooKeeper', 12 unless
+        (defined($ret) and $ret);
+
+    $ret = $zkh->exists("$node_path/e");
+    ok((!$ret and $zkh->get_error() == ZNONODE and $! eq ''),
+       'exists(): checked ephemeral node non-extant after reconnection');
+
+    $ret = $zkh->exists($path);
+    ok((!$ret and $zkh->get_error() == ZNONODE and $! eq ''),
+       'exists(): checked ephemeral sequential node non-extant ' .
+       'after reconnection');
+
+
+    ## get_children()
+
+    my @child_paths = ('abc');
+    @child_paths = $zkh->get_children($node_path);
+    ok((@child_paths == 0 and $zkh->get_error() == ZOK),
+       'get_children(): retrieved empty list of child nodes');
+
+    my $num_children = $zkh->get_children($node_path);
+    ok((defined($num_children) and $num_children == 0),
+       'get_children(): retrieved zero count of child nodes');
+
+    @child_paths = $zkh->get_children($node_path . '/NONE');
+    ok((@child_paths == 0 and $zkh->get_error() == ZNONODE and $! eq ''),
+       'get_children(): empty list returned for non-extant node');
+
+    $num_children = $zkh->get_children($node_path . '/NONE');
+    ok((!defined($num_children) and $zkh->get_error() == ZNONODE and $! eq ''),
+       'get_children(): undef returned for non-extant node');
+
+    SKIP: {
+        my $path = $zkh->create("$node_path/c1", 'foo',
+                                'acl' => ZOO_OPEN_ACL_UNSAFE);
+
+        skip 'no connection to ZooKeeper', 6 unless
+            (defined($path) and $path eq "$node_path/c1");
+
+        my @child_paths = ('abc');
+        @child_paths = $zkh->get_children($node_path);
+        ok((@child_paths == 1 and $child_paths[0] eq 'c1'),
+           'get_children(): retrieved list of single child node');
+
+        my $num_children = $zkh->get_children($node_path);
+        ok((defined($num_children) and $num_children == 1),
+           'get_children(): retrieved count of single child node');
+
+        SKIP: {
+            my $path = $zkh->create("$node_path/c2", 'foo',
+                                    'acl' => ZOO_OPEN_ACL_UNSAFE);
+
+            skip 'no connection to ZooKeeper', 2 unless
+                (defined($path) and $path eq "$node_path/c2");
+
+            my @child_paths = ('abc');
+            @child_paths = $zkh->get_children($node_path);
+            ok((@child_paths == 2 and $child_paths[0] eq 'c1' and
+                $child_paths[1] eq 'c2'),
+               'get_children(): retrieved list of two child nodes');
+
+            my $num_children = $zkh->get_children($node_path);
+            ok((defined($num_children) and $num_children == 2),
+               'get_children(): retrieved count of two child nodes');
+
+            my $ret = $zkh->delete("$node_path/c2");
+            diag(sprintf('unable to delete node %s: %d, %s',
+                         "$node_path/c2", $zkh->get_error(), $!)) unless
+                ($ret);
+        }
+
+        @child_paths = ('abc');
+        @child_paths = $zkh->get_children($node_path);
+        ok((@child_paths == 1 and $child_paths[0] eq 'c1'),
+           'get_children(): retrieved list of single child node');
+
+        $num_children = $zkh->get_children($node_path);
+        ok((defined($num_children) and $num_children == 1),
+           'get_children(): retrieved count of single child node');
+
+        my $ret = $zkh->delete("$node_path/c1");
+        diag(sprintf('unable to delete node %s: %d, %s',
+                     "$node_path/c1", $zkh->get_error(), $!)) unless ($ret);
+    }
+
+
+    ## cleanup
+
+    $ret = $zkh->delete($node_path);
+    diag(sprintf('unable to delete node %s: %d, %s',
+                 $node_path, $zkh->get_error(), $!)) unless ($ret);
+}
+

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/c9914857/content/build/contrib/zkperl/t/45_class.t
----------------------------------------------------------------------
diff --git a/content/build/contrib/zkperl/t/45_class.t b/content/build/contrib/zkperl/t/45_class.t
new file mode 100644
index 0000000..4aa1a57
--- /dev/null
+++ b/content/build/contrib/zkperl/t/45_class.t
@@ -0,0 +1,408 @@
+# 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 => 47;
+
+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);
+
+    skip 'no valid handle', 15 unless (defined($zkh));
+
+    my $stat = $zkh->stat();
+    my $watch = $zkh->watch();
+
+
+    ## DESTROY() on reblessed handle
+
+    bless($zkh, 'My::ZooKeeper');
+    is(ref($zkh), 'My::ZooKeeper',
+       'bless(): reblessed handle');
+
+    eval {
+        $zkh->EXISTS();
+    };
+    like($@, qr/Can't locate object method "EXISTS" via package "My::ZooKeeper"/,
+         'EXISTS(): not defined on reblessed handle');
+
+    my $attr = tied(%{$zkh});
+
+    my $ret = $attr->DESTROY();
+    ok($ret,
+       'DESTROY(): destroyed inner hash of reblessed handle');
+
+    $ret = $attr->DESTROY();
+    ok(!$ret,
+       'DESTROY(): no action on destroyed inner hash of reblessed handle');
+
+    undef $zkh;
+    ok(!defined($zkh),
+       'undef: released reblessed handle');
+
+
+    ## DESTROY() on reblessed stat handle
+
+    bless($stat, 'My::ZooKeeper::Stat');
+    is(ref($stat), 'My::ZooKeeper::Stat',
+       'bless(): reblessed stat handle');
+
+    eval {
+        $stat->EXISTS(1);
+    };
+    like($@, qr/Can't locate object method "EXISTS" via package "My::ZooKeeper::Stat"/,
+         'stat EXISTS(): not defined on reblessed stat handle');
+
+    $attr = tied(%{$stat});
+
+    $ret = $attr->DESTROY();
+    ok($ret,
+       'stat DESTROY(): destroyed inner hash of reblessed stat handle');
+
+    $ret = $attr->DESTROY();
+    ok(!$ret,
+       'stat DESTROY(): no action on destroyed inner hash of ' .
+       'reblessed stat handle');
+
+    undef $stat;
+    ok(!defined($stat),
+       'undef: released reblessed stat handle');
+
+
+    ## DESTROY() on reblessed watch handle
+
+    bless($watch, 'My::ZooKeeper::Watch');
+    is(ref($watch), 'My::ZooKeeper::Watch',
+       'bless(): reblessed watch handle');
+
+    eval {
+        $watch->EXISTS(1);
+    };
+    like($@, qr/Can't locate object method "EXISTS" via package "My::ZooKeeper::Watch"/,
+         'watch EXISTS(): not defined on reblessed watch handle');
+
+    $attr = tied(%{$watch});
+
+    $ret = $attr->DESTROY();
+    ok($ret,
+       'watch DESTROY(): destroyed inner hash of reblessed watch handle');
+
+    $ret = $attr->DESTROY();
+    ok(!$ret,
+       'watch DESTROY(): no action on destroyed inner hash of ' .
+       'reblessed watch handle');
+
+    undef $watch;
+    ok(!defined($watch),
+       'undef: released reblessed watch handle');
+}
+
+SKIP: {
+    my $zkh = Net::ZooKeeper->new($hosts);
+
+    skip 'no valid handle', 9 unless (defined($zkh));
+
+    my $stat = $zkh->stat();
+    my $watch = $zkh->watch();
+
+
+    ## UNTIE() on reblessed handle
+
+    bless($zkh, 'My::ZooKeeper');
+    is(ref($zkh), 'My::ZooKeeper',
+       'bless(): reblessed handle');
+
+    eval {
+        untie(%{$zkh});
+    };
+    like($@, qr/untying hashes of class Net::ZooKeeper not supported/,
+         'untie(): untying hashes from reblessed handle not supported');
+
+    my $attr = tied(%{$zkh});
+
+    eval {
+        $attr->UNTIE(0);
+    };
+    like($@, qr/untying hashes of class Net::ZooKeeper not supported/,
+         'UNTIE(): untying hashes from reblessed handle not supported');
+
+
+    ## UNTIE() on reblessed stat handle
+
+    bless($stat, 'My::ZooKeeper::Stat');
+    is(ref($stat), 'My::ZooKeeper::Stat',
+       'bless(): reblessed stat handle');
+
+    eval {
+        untie(%{$stat});
+    };
+    like($@, qr/untying hashes of class Net::ZooKeeper::Stat not supported/,
+         'untie(): untying hashes from reblessed stat handle not supported');
+
+    $attr = tied(%{$stat});
+
+    eval {
+        $attr->UNTIE(0);
+    };
+    like($@, qr/untying hashes of class Net::ZooKeeper::Stat not supported/,
+         'stat UNTIE(): untying hashes from reblessed stat handle ' .
+         'not supported');
+
+
+    ## UNTIE() on reblessed watch handle
+
+    bless($watch, 'My::ZooKeeper::Watch');
+    is(ref($watch), 'My::ZooKeeper::Watch',
+       'bless(): reblessed watch handle');
+
+    eval {
+        untie(%{$watch});
+    };
+    like($@, qr/untying hashes of class Net::ZooKeeper::Watch not supported/,
+         'untie(): untying hashes from reblessed watch handle not supported');
+
+    $attr = tied(%{$watch});
+
+    eval {
+        $attr->UNTIE(0);
+    };
+    like($@, qr/untying hashes of class Net::ZooKeeper::Watch not supported/,
+         'watch UNTIE(): untying hashes from reblessed watch handle ' .
+         'not supported');
+}
+
+
+package Net::ZooKeeper::Test;
+
+use Net::ZooKeeper qw(:acls);
+
+our @ISA = qw(Net::ZooKeeper);
+
+sub create
+{
+    my($self, $path, $buf) = @_;
+
+    return $self->SUPER::create($path, $buf,
+                                'path_read_len' => length($path),
+                                'acl' => ZOO_OPEN_ACL_UNSAFE);
+}
+
+sub get_first_child
+{
+    my($self, $path) = @_;
+
+    my @child_paths = $self->get_children($path);
+
+    if (@child_paths > 0) {
+        return $path . (($path =~ /\/$/) ? '' : '/') . $child_paths[0];
+    }
+
+    return undef;
+}
+
+sub stat
+{
+    my $self = shift;
+
+    my $stat = $self->SUPER::stat();
+
+    return bless($stat, 'Net::ZooKeeper::Test::Stat');
+}
+
+
+sub watch
+{
+    my $self = shift;
+
+    my $watch = $self->SUPER::watch();
+
+    return bless($watch, 'Net::ZooKeeper::Test::Watch');
+}
+
+
+package Net::ZooKeeper::Test::Stat;
+
+our @ISA = qw(Net::ZooKeeper::Stat);
+
+sub get_ctime
+{
+    my $self = shift;
+
+    return $self->{'ctime'};
+}
+
+
+package Net::ZooKeeper::Test::Watch;
+
+our @ISA = qw(Net::ZooKeeper::Watch);
+
+sub get_timeout
+{
+    my $self = shift;
+
+    return $self->{'timeout'};
+}
+
+
+package main;
+
+my $sub_zkh = Net::ZooKeeper::Test->new($hosts);
+isa_ok($sub_zkh, 'Net::ZooKeeper::Test',
+       'new(): created subclassed handle');
+
+SKIP: {
+    skip 'no valid subclassed handle', 21 unless (defined($sub_zkh));
+
+    is($sub_zkh->{'data_read_len'}, 1023,
+       'FETCH(): default data read length using subclassed handle');
+
+    my $path;
+
+    SKIP: {
+        my $ret = $sub_zkh->exists($root_path);
+
+        skip 'no connection to ZooKeeper', 1 unless
+            (defined($ret) and $ret);
+
+        $path = $sub_zkh->create($node_path, 'foo',
+                                 'acl' => ZOO_OPEN_ACL_UNSAFE);
+        is($path, $node_path,
+           'create(): created node with subclassed handle');
+    }
+
+    SKIP: {
+        skip 'no connection to ZooKeeper', 1 unless
+            (defined($path) and $path eq $node_path);
+
+        my $child_path = $sub_zkh->get_first_child($root_path);
+        is($child_path, $node_path,
+           'get_first_child(): retrieved first child with subclassed handle');
+    }
+
+    my $sub_stat = $sub_zkh->stat();
+    isa_ok($sub_stat, 'Net::ZooKeeper::Test::Stat',
+           'stat(): created subclassed stat handle');
+
+    SKIP: {
+        skip 'no valid subclassed stat handle', 6 unless
+            (defined($sub_stat));
+
+        is($sub_stat->{'ctime'}, 0,
+           'stat FETCH(): default ctime using subclassed stat handle');
+
+        SKIP: {
+            my $ret = $sub_zkh->exists($node_path, 'stat' => $sub_stat) if
+                (defined($path) and $path eq $node_path);
+
+            skip 'no connection to ZooKeeper', 2 unless
+                (defined($ret) and $ret);
+
+            my $ctime = $sub_stat->get_ctime();
+            ok($ctime > 0,
+               'get_ctime(): retrieved ctime with subclassed stat handle');
+
+            is($sub_stat->{'ctime'}, $ctime,
+               'stat FETCH(): ctime using subclassed stat handle');
+        }
+
+        my $ret = $sub_stat->DESTROY();
+        ok($ret,
+           'stat DESTROY(): destroyed subclassed stat handle');
+
+        $ret = $sub_stat->DESTROY();
+        ok(!$ret,
+           'stat DESTROY(): no action on destroyed subclassed stat handle');
+
+        undef $sub_stat;
+        ok(!defined($sub_stat),
+           'undef: released subclassed stat handle');
+    }
+
+    my $sub_watch = $sub_zkh->watch();
+    isa_ok($sub_watch, 'Net::ZooKeeper::Test::Watch',
+           'watch(): created subclassed watch handle');
+
+    SKIP: {
+        skip 'no valid subclassed watch handle', 6 unless
+            (defined($sub_watch));
+
+        SKIP: {
+            my $ret = $sub_zkh->exists($root_path, 'watch' => $sub_watch);
+
+            skip 'no connection to ZooKeeper', 3 unless
+                (defined($ret) and $ret);
+
+            $sub_watch->{'timeout'} = 50;
+
+            is($sub_watch->get_timeout(), 50,
+               'get_timeout(): retrieved timeout with subclassed ' .
+               'watch handle');
+
+            is($sub_watch->{'timeout'}, 50,
+               'watch FETCH(): timeout using subclassed stat handle');
+
+            $ret = $sub_watch->wait();
+            ok(!$ret,
+               'wait(): watch after checking node existence timed out with ' .
+               'subclassed watch handle');
+        }
+
+        my $ret = $sub_watch->DESTROY();
+        ok($ret,
+           'watch DESTROY(): destroyed subclassed watch handle');
+
+        $ret = $sub_watch->DESTROY();
+        ok(!$ret,
+           'watch DESTROY(): no action on destroyed subclassed watch handle');
+
+        undef $sub_watch;
+        ok(!defined($sub_watch),
+           'undef: released subclassed watch handle');
+    }
+
+    SKIP: {
+        skip 'no connection to ZooKeeper', 1 unless
+            (defined($path) and $path eq $node_path);
+
+        my $ret = $sub_zkh->delete($node_path);
+        ok($ret,
+           'delete(): deleted node with subclassed handle');
+    }
+
+    my $ret = $sub_zkh->DESTROY();
+    ok($ret,
+       'DESTROY(): destroyed subclassed handle');
+
+    $ret = $sub_zkh->DESTROY();
+    ok(!$ret,
+       'DESTROY(): no action on destroyed subclassed handle');
+
+    undef $sub_zkh;
+    ok(!defined($sub_zkh),
+       'undef: released subclassed handle');
+}
+