diff --git a/.gitignore b/.gitignore index 3c0b580f4..1a45338eb 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ .project .settings .DS_Store +.vscode/ */.DS_Store gcovr-output/ gcov-data/ @@ -74,6 +75,7 @@ libtool doxy.conf doxygen_warnings.txt main_page.doxygen +dmr_memory_cache ## libdap specific files gl/*.h @@ -201,6 +203,14 @@ unit-tests/GridTest unit-tests/IsDap4ProjectedTest unit-tests/util_mitTest unit-tests/MarshallerFutureTest +unit-tests/BaseTypeTest +unit-tests/ConstraintEvaluatorTest +unit-tests/D4StreamRoundTripTest +unit-tests/DAPCache3Test +unit-tests/ErrorTest +unit-tests/MarshallerThreadTest +unit-tests/dmr_memory_cache +unit-tests/dapcache3test.* d4_ce/unit-tests/D4ConstraintEvaluatorTest d4_ce/unit-tests/test_config.h diff --git a/DAPCache3.h b/DAPCache3.h index 45295d321..4d78b7fbf 100644 --- a/DAPCache3.h +++ b/DAPCache3.h @@ -29,27 +29,18 @@ #ifndef DAPCache3_h_ #define DAPCache3_h_ 1 -// #include #include #include #include -// #include #include "DapObj.h" -#if 0 -#include "BESDebug.h" -#include "BESObj.h" - -class BESKeys; -#endif - namespace libdap { // These typedefs are used to record information about the files in the cache. // See DAPCache3.cc and look at the purge() method. typedef struct { - string name; + std::string name; unsigned long long size; time_t time; } cache_entry; @@ -80,25 +71,19 @@ typedef std::list CacheFiles; * on the cache info file must only be called when the lock has been obtained. */ class DAPCache3 : public libdap::DapObj { - -private: static DAPCache3 *d_instance; static const char DAP_CACHE_CHAR = '#'; - string d_cache_dir; /// pathname of the cache directory - string d_prefix; /// tack this on the front of cache file name + std::string d_cache_dir; /// pathname of the cache directory + std::string d_prefix; /// tack this on the front of cache file name /// How many megabytes can the cache hold before we have to purge unsigned long long d_max_cache_size_in_bytes; // When we purge, how much should we throw away. Set in the ctor to 80% of the max size. unsigned long long d_target_size; -#if 0 - // This class implements a singleton, so the constructor is hidden. - BESCache3(BESKeys *keys, const string &cache_dir_key, const string &prefix_key, const string &size_key); -#endif // Testing - DAPCache3(const string &cache_dir, const string &prefix, unsigned long long size); + DAPCache3(const std::string &cache_dir, const std::string &prefix, unsigned long long size); // Suppress the assignment operator and default copy ctor, ... DAPCache3(); @@ -111,47 +96,43 @@ class DAPCache3 : public libdap::DapObj { unsigned long long m_collect_cache_dir_info(CacheFiles &contents); /// Name of the file that tracks the size of the cache - string d_cache_info; + std::string d_cache_info; int d_cache_info_fd; - void m_record_descriptor(const string &file, int fd); - int m_get_descriptor(const string &file); + void m_record_descriptor(const std::string &file, int fd); + int m_get_descriptor(const std::string &file); // map that relates files to the descriptor used to obtain a lock - typedef std::map FilesAndLockDescriptors; + typedef std::map FilesAndLockDescriptors; FilesAndLockDescriptors d_locks; // Life-cycle control - virtual ~DAPCache3() {} + ~DAPCache3() override {} static void delete_instance(); public: - static DAPCache3 *get_instance(const string &cache_dir, const string &prefix, unsigned long long size); + static DAPCache3 *get_instance(const std::string &cache_dir, const std::string &prefix, unsigned long long size); static DAPCache3 *get_instance(); - string get_cache_file_name(const string &src, bool mangle = true); + std::string get_cache_file_name(const std::string &src, bool mangle = true); - virtual bool create_and_lock(const string &target, int &fd); - virtual bool get_read_lock(const string &target, int &fd); + virtual bool create_and_lock(const std::string &target, int &fd); + virtual bool get_read_lock(const std::string &target, int &fd); virtual void exclusive_to_shared_lock(int fd); - virtual void unlock_and_close(const string &target); + virtual void unlock_and_close(const std::string &target); virtual void unlock_and_close(int fd); virtual void lock_cache_write(); virtual void lock_cache_read(); virtual void unlock_cache(); - virtual unsigned long long update_cache_info(const string &target); + virtual unsigned long long update_cache_info(const std::string &target); virtual bool cache_too_big(unsigned long long current_size) const; virtual unsigned long long get_cache_size(); - virtual void update_and_purge(const string &new_file); - virtual void purge_file(const string &file); - -#if 0 - static BESCache3 *get_instance(BESKeys *keys, const string &cache_dir_key, const string &prefix_key, const string &size_key); -#endif + virtual void update_and_purge(const std::string &new_file); + virtual void purge_file(const std::string &file); - void dump(ostream &strm) const override; + void dump(std::ostream &strm) const override; }; } // namespace libdap diff --git a/Makefile.am b/Makefile.am index 8fcb6bc3d..dafafbf5d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -87,8 +87,6 @@ dmr_memory_cache_SOURCES = dmr_mcache_test.cc dmr_memory_cache_LDADD = libdapclient.la libdap.la dmr_memory_cache_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/http_dap $(CURL_CFLAGS) - - LEX_YACC_EXTRA = das.lex das.yy dds.lex dds.yy ce_expr.lex ce_expr.yy \ Error.lex Error.yy @@ -132,7 +130,6 @@ cccc: # dist-hook: # if test -n "$$VERSION"; then cat $$VERSION NEWS > NEWS.tmp; mv NEWS.tmp NEWS; fi - # Build linux RPMs. Use the environment variable 'RPM_OPTIONS' to pass in # extra options like --nodeps and --macros diff --git a/parser-util.cc b/parser-util.cc index 6481148f0..943d76b87 100644 --- a/parser-util.cc +++ b/parser-util.cc @@ -339,12 +339,12 @@ int check_uint32(const char *val, unsigned int &v) { return FALSE; } // See above. - else if (tmp > DODS_UINT_MAX) { + if (tmp > DODS_UINT_MAX) { return FALSE; - } else { - v = (unsigned int)tmp; - return TRUE; } + + v = (unsigned int)tmp; + return TRUE; } int check_int64(const char *val) { @@ -362,19 +362,8 @@ int check_int64(const char *val) { if (errno == ERANGE) { return FALSE; } -#if 0 - // This could be combined with the above, or course, but I'm making it - // separate to highlight the test. On 64-bit linux boxes 'long' may be - // 64-bits and so 'v' can hold more than a DODS_INT32. jhrg 3/23/10 - // - // Removed - Coverity says it can never be false. Makes sense. jhrg 5/10/16 - else if (v <= DODS_LLONG_MAX && v >= DODS_LLONG_MIN) { - return FALSE; - } -#endif - else { - return TRUE; - } + + return TRUE; } int check_uint64(const char *val) { @@ -398,11 +387,13 @@ int check_uint64(const char *val) { if (errno == ERANGE) { return FALSE; - } else if (v > DODS_ULLONG_MAX) { // 2^61 + } + + if (v > DODS_ULLONG_MAX) { // 2^61 return FALSE; - } else { - return v; } + + return TRUE; } // Check first for system errors (like numbers so small they convert diff --git a/retired/VCPP/BeforeInstall.txt b/retired/VCPP/BeforeInstall.txt index 18ccd45ce..04b2b1cf5 100644 --- a/retired/VCPP/BeforeInstall.txt +++ b/retired/VCPP/BeforeInstall.txt @@ -1 +1 @@ -In addition to installing libdap 3.8.2, this will also install other run-time libraries libdap uses. However, this will not install the software need to compile our source code on the Windows XP platform. For more information about building our software from source, see http://scm.opendap.org/trac/wiki/XP or contact us at support at opendap.org. +In addition to installing libdap 3.8.2, this will also install other run-time libraries libdap uses. However, this will not install the software need to compile our source code on the Windows XP platform. For more information about building our software from source, see http://scm.opendap.org/trac/wiki/XP or contact us at support at opendap.org. diff --git a/unit-tests/ArrayTest.cc b/unit-tests/ArrayTest.cc index aaf4cd2ef..acedb8c00 100644 --- a/unit-tests/ArrayTest.cc +++ b/unit-tests/ArrayTest.cc @@ -149,6 +149,8 @@ class ArrayTest : public TestFixture { CPPUNIT_TEST(duplicate_cardinal_test); CPPUNIT_TEST(duplicate_string_test); CPPUNIT_TEST(duplicate_structure_test); + CPPUNIT_TEST(zero_length_array_test); + CPPUNIT_TEST(deep_copy_cardinal_test); CPPUNIT_TEST_SUITE_END(); @@ -324,6 +326,37 @@ class ArrayTest : public TestFixture { a = 0; } + void zero_length_array_test() { + Int16 i16("i16"); + Array a("zero_len", &i16); + a.append_dim(0, "empty"); + CPPUNIT_ASSERT_EQUAL(0, a.length()); + CPPUNIT_ASSERT_EQUAL((int64_t)0, a.length_ll()); + CPPUNIT_ASSERT_EQUAL((unsigned int)0, a.width()); + CPPUNIT_ASSERT_EQUAL((int64_t)0, a.width_ll()); + } + + void deep_copy_cardinal_test() { + Int16 i16("i16"); + Array a1("a1", &i16); + a1.append_dim(4, "d0"); + + dods_int16 v1[4] = {10, 20, 30, 40}; + a1.set_value(v1, 4); + + Array a2(a1); + + dods_int16 v2[4] = {1, 2, 3, 4}; + a1.set_value(v2, 4); + + dods_int16 out[4] = {0, 0, 0, 0}; + a2.value(out); + CPPUNIT_ASSERT(out[0] == 10); + CPPUNIT_ASSERT(out[1] == 20); + CPPUNIT_ASSERT(out[2] == 30); + CPPUNIT_ASSERT(out[3] == 40); + } + void duplicate_string_test() { Array::Dim_iter i = d_string->dim_begin(); CPPUNIT_ASSERT(d_string->dimension_size(i) == 4); diff --git a/unit-tests/BaseTypeTest.cc b/unit-tests/BaseTypeTest.cc new file mode 100644 index 000000000..5a051c2e2 --- /dev/null +++ b/unit-tests/BaseTypeTest.cc @@ -0,0 +1,428 @@ +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +#include +#include + +#include +#include +#include +#include + +#include "AttrTable.h" +#include "BaseType.h" +#include "BaseTypeFactory.h" +#include "ConstraintEvaluator.h" +#include "D4Attributes.h" +#include "D4Group.h" +#include "D4StreamMarshaller.h" +#include "D4StreamUnMarshaller.h" +#include "DDS.h" +#include "DMR.h" +#include "Error.h" +#include "InternalErr.h" +#include "Marshaller.h" +#include "Structure.h" +#include "UnMarshaller.h" +#include "XMLWriter.h" +#include "run_tests_cppunit.h" + +using namespace CppUnit; +using namespace libdap; +using namespace std; + +namespace { + +class DummyBase : public BaseType { +public: + DummyBase(const string &n, const Type &t, bool is_dap4 = false) : BaseType(n, t, is_dap4) {} + DummyBase(const string &n, const string &d, const Type &t, bool is_dap4 = false) : BaseType(n, d, t, is_dap4) {} + DummyBase(const DummyBase &rhs) : BaseType(rhs) {} + DummyBase &operator=(const DummyBase &rhs) = default; + + BaseType *ptr_duplicate() override { return new DummyBase(*this); } + + unsigned int buf2val(void **val) override { + if (!val) + throw InternalErr(__FILE__, __LINE__, "NULL pointer"); + if (!*val) + *val = new int(7); + return sizeof(int); + } + + unsigned int val2buf(void *val, bool) override { + if (!val) + throw InternalErr(__FILE__, __LINE__, "NULL pointer"); + return sizeof(int); + } + + void compute_checksum(Crc32 &checksum) override { (void)checksum; } + + void print_val(ostream &out, string space = "", bool print_decl_p = true, bool is_root_grp = true) override { + if (print_decl_p) { + print_decl(out, space, true, false, false, is_root_grp); + out << " = 7;\n"; + } else { + out << "7"; + } + } + + void set_parent_public(BaseType *p) { BaseType::set_parent(p); } +}; + +class ReadableBase : public DummyBase { +public: + int read_count = 0; + + ReadableBase(const string &n, const Type &t, bool is_dap4 = false) : DummyBase(n, t, is_dap4) {} + + bool read() override { + ++read_count; + set_read_p(true); + return true; + } +}; + +class FakeMarshaller : public Marshaller { +public: + void put_byte(dods_byte) override {} + void put_int16(dods_int16) override {} + void put_int32(dods_int32) override {} + void put_float32(dods_float32) override {} + void put_float64(dods_float64) override {} + void put_uint16(dods_uint16) override {} + void put_uint32(dods_uint32) override {} + void put_str(const string &) override {} + void put_url(const string &) override {} + void put_opaque(char *, unsigned int) override {} + void put_int(int) override {} + void put_vector(char *, int, Vector &) override {} + void put_vector(char *, int, int, Vector &) override {} + void dump(ostream &strm) const override { strm << "FakeMarshaller"; } +}; + +class FakeUnMarshaller : public UnMarshaller { +public: + void get_byte(dods_byte &) override {} + void get_int16(dods_int16 &) override {} + void get_int32(dods_int32 &) override {} + void get_float32(dods_float32 &) override {} + void get_float64(dods_float64 &) override {} + void get_uint16(dods_uint16 &) override {} + void get_uint32(dods_uint32 &) override {} + void get_str(string &) override {} + void get_url(string &) override {} + void get_opaque(char *, unsigned int) override {} + void get_int(int &) override {} + void get_vector(char **, unsigned int &, Vector &) override {} + void get_vector(char **, unsigned int &, int, Vector &) override {} + void dump(ostream &strm) const override { strm << "FakeUnMarshaller"; } +}; + +static D4Attributes *make_d4_attrs_with_int8(const string &name) { + auto *attrs = new D4Attributes(); + auto *attr = new D4Attribute(name, attr_int8_c); + attr->add_value("1"); + attrs->add_attribute_nocopy(attr); + return attrs; +} + +} // namespace + +class BaseTypeTest : public TestFixture { + CPPUNIT_TEST_SUITE(BaseTypeTest); + CPPUNIT_TEST(test_to_string_and_dump); + CPPUNIT_TEST(test_copy_and_assignment_deep_attributes); + CPPUNIT_TEST(test_transform_to_dap4); + CPPUNIT_TEST(test_transform_to_dap2_empty_attr_table); + CPPUNIT_TEST(test_transform_to_dap2_preserve_attr_table); + CPPUNIT_TEST(test_fqn_and_print_decl_dap4); + CPPUNIT_TEST(test_print_decl_constrained_and_constraint_info); + CPPUNIT_TEST(test_print_xml_writer_dap2_and_dap4); + CPPUNIT_TEST(test_transfer_attributes_adds); + CPPUNIT_TEST(test_transfer_attributes_missing_container); + CPPUNIT_TEST(test_set_parent_validation_and_get_ancestor); + CPPUNIT_TEST(test_read_default_and_intern_data); + CPPUNIT_TEST(test_intern_data_dap4_throws); + CPPUNIT_TEST(test_serialize_deserialize_throws); + CPPUNIT_TEST(test_ops_and_width_throw); + CPPUNIT_TEST(test_is_dap4_projected); + CPPUNIT_TEST_SUITE_END(); + +public: + void test_to_string_and_dump() { + DummyBase b("a", "dataset", dods_int32_c, false); + string s = b.toString(); + CPPUNIT_ASSERT(s.find("a") != string::npos); + CPPUNIT_ASSERT(s.find("Int32") != string::npos); + CPPUNIT_ASSERT(s.find("dataset") != string::npos); + + ostringstream oss; + b.dump(oss); + string d = oss.str(); + CPPUNIT_ASSERT(d.find("BaseType::dump") != string::npos); + CPPUNIT_ASSERT(d.find("name: a") != string::npos); + } + + void test_copy_and_assignment_deep_attributes() { + DummyBase a("a", dods_int32_c, true); + a.get_attr_table().append_attr("units", "String", "m"); + a.set_attributes_nocopy(make_d4_attrs_with_int8("d4")); + + DummyBase copy(a); + CPPUNIT_ASSERT(copy.get_attr_table().get_size() == a.get_attr_table().get_size()); + CPPUNIT_ASSERT(copy.attributes() != a.attributes()); + CPPUNIT_ASSERT(copy.attributes()->find("d4") != nullptr); + + DummyBase assigned("b", dods_int32_c, true); + assigned = a; + CPPUNIT_ASSERT(assigned.get_attr_table().get_size() == a.get_attr_table().get_size()); + CPPUNIT_ASSERT(assigned.attributes() != a.attributes()); + CPPUNIT_ASSERT(assigned.attributes()->find("d4") != nullptr); + } + + void test_transform_to_dap4() { + DummyBase src("var", dods_int32_c, false); + src.get_attr_table().append_attr("units", "String", "m"); + + D4Group root("/"); + src.transform_to_dap4(&root, &root); + + BaseType *dest = root.var("var", true); + CPPUNIT_ASSERT(dest != nullptr); + CPPUNIT_ASSERT(dest->is_dap4()); + CPPUNIT_ASSERT(!dest->attributes()->empty()); + } + + void test_transform_to_dap2_empty_attr_table() { + DummyBase src("v", dods_int32_c, true); + src.set_attributes_nocopy(make_d4_attrs_with_int8("d4")); + + vector *vars = src.transform_to_dap2(nullptr, false); + CPPUNIT_ASSERT(vars != nullptr); + CPPUNIT_ASSERT(vars->size() == 1); + BaseType *dest = (*vars)[0]; + + CPPUNIT_ASSERT(!dest->is_dap4()); + CPPUNIT_ASSERT(dest->get_attr_table().get_size() > 0); + CPPUNIT_ASSERT(dest->get_attr_table().get_name() == "v"); + + delete dest; + delete vars; + } + + void test_transform_to_dap2_preserve_attr_table() { + DummyBase src("v", dods_int32_c, true); + src.get_attr_table().append_attr("units", "String", "m"); + + vector *vars = src.transform_to_dap2(nullptr, false); + CPPUNIT_ASSERT(vars != nullptr); + BaseType *dest = (*vars)[0]; + + CPPUNIT_ASSERT(!dest->is_dap4()); + CPPUNIT_ASSERT(dest->get_attr_table().get_size() == 1); + + delete dest; + delete vars; + } + + void test_fqn_and_print_decl_dap4() { + DummyBase top("top", dods_int32_c, false); + CPPUNIT_ASSERT_EQUAL(string("top"), top.FQN()); + + Structure parent("parent"); + auto *child = new DummyBase("child", dods_int32_c, false); + parent.add_var_nocopy(child); + CPPUNIT_ASSERT_EQUAL(string("parent.child"), child->FQN()); + + D4Group root("/"); + auto *grp = new D4Group("grp"); + root.add_group_nocopy(grp); + + auto *var = new DummyBase("var", dods_int32_c, true); + grp->add_var_nocopy(var); + + CPPUNIT_ASSERT_EQUAL(string("/grp/var"), var->FQN()); + + ostringstream oss; + var->print_decl(oss, "", true, false, false, false); + CPPUNIT_ASSERT(oss.str().find("/grp/var") != string::npos); + + ostringstream oss2; + var->print_decl(oss2, "", true, false, false, false, true); + CPPUNIT_ASSERT(oss2.str().find("/grp/") != string::npos); + } + + void test_print_decl_constrained_and_constraint_info() { + DummyBase b("x", dods_int32_c, false); + b.set_send_p(false); + + ostringstream oss; + b.print_decl(oss, "", true, false, true); + CPPUNIT_ASSERT(oss.str().empty()); + + b.set_send_p(true); + ostringstream oss2; + b.print_decl(oss2, "", true, true, false); + CPPUNIT_ASSERT(oss2.str().find("Send True") != string::npos); + + FILE *tmp = tmpfile(); + CPPUNIT_ASSERT(tmp != nullptr); + b.print_decl(tmp, "", true, false, false, true); + fclose(tmp); + } + + void test_print_xml_writer_dap2_and_dap4() { + DummyBase dap2("a", dods_int32_c, false); + dap2.get_attr_table().append_attr("units", "String", "m"); + XMLWriter xml2; + dap2.print_xml_writer(xml2, false); + string doc2 = xml2.get_doc(); + CPPUNIT_ASSERT(doc2.find("units") != string::npos); + + DummyBase dap4("b", dods_int32_c, true); + dap4.set_attributes_nocopy(make_d4_attrs_with_int8("d4")); + XMLWriter xml4; + dap4.print_xml_writer(xml4, false); + string doc4 = xml4.get_doc(); + CPPUNIT_ASSERT(doc4.find("name=\"b\"") != string::npos); + + DummyBase constrained("c", dods_int32_c, false); + constrained.set_send_p(false); + XMLWriter xmlc; + constrained.print_xml_writer(xmlc, true); + string docc = xmlc.get_doc(); + CPPUNIT_ASSERT(docc.find("name=\"c\"") == string::npos); + + ostringstream oss; + dap2.print_xml(oss, "", false); + CPPUNIT_ASSERT(oss.str().find("units") != string::npos); + + FILE *tmp = tmpfile(); + CPPUNIT_ASSERT(tmp != nullptr); + dap2.print_xml(tmp, "", false); + fclose(tmp); + } + + void test_transfer_attributes_adds() { + DummyBase b("var", dods_int32_c, false); + AttrTable root; + AttrTable *container = root.append_container("var"); + container->append_attr("units", "String", "m"); + AttrTable *sub = container->append_container("sub"); + sub->append_attr("color", "String", "red"); + + b.transfer_attributes(&root); + AttrTable &bt_attrs = b.get_attr_table(); + CPPUNIT_ASSERT(bt_attrs.get_size() >= 2); + CPPUNIT_ASSERT(bt_attrs.get_attr("units") == "m"); + CPPUNIT_ASSERT(bt_attrs.get_attr_table("sub") != nullptr); + } + + void test_transfer_attributes_missing_container() { + DummyBase b("var", dods_int32_c, false); + AttrTable root; + root.append_container("other"); + + b.transfer_attributes(&root); + CPPUNIT_ASSERT(b.get_attr_table().get_size() == 0); + } + + void test_set_parent_validation_and_get_ancestor() { + DummyBase child("child", dods_int32_c, false); + DummyBase bad_parent("bad", dods_int32_c, false); + CPPUNIT_ASSERT_THROW(child.set_parent_public(&bad_parent), InternalErr); + + D4Group root("/"); + auto *grp = new D4Group("grp"); + root.add_group_nocopy(grp); + + auto *var = new DummyBase("var", dods_int32_c, false); + grp->add_var_nocopy(var); + + BaseType *ancestor = var->get_ancestor(); + CPPUNIT_ASSERT(ancestor == &root); + } + + void test_read_default_and_intern_data() { + DummyBase b("v", dods_int32_c, false); + CPPUNIT_ASSERT_THROW(b.read(), InternalErr); + + b.set_read_p(true); + CPPUNIT_ASSERT(b.read()); + + DummyBase synth("s", dods_int32_c, false); + synth.set_synthesized_p(true); + synth.set_read_p(true); + CPPUNIT_ASSERT(!synth.read_p()); + + ReadableBase r("r", dods_int32_c, false); + ConstraintEvaluator eval; + BaseTypeFactory factory; + DDS dds(&factory); + r.intern_data(eval, dds); + CPPUNIT_ASSERT(r.read_p()); + CPPUNIT_ASSERT(r.read_count == 1); + + ReadableBase r2("r2", dods_int32_c, false); + r2.intern_data(); + CPPUNIT_ASSERT(r2.read_p()); + CPPUNIT_ASSERT(r2.read_count == 1); + } + + void test_intern_data_dap4_throws() { + DummyBase b("v", dods_int32_c, true); + ConstraintEvaluator eval; + BaseTypeFactory factory; + DDS dds(&factory); + CPPUNIT_ASSERT_THROW(b.intern_data(eval, dds), Error); + } + + void test_serialize_deserialize_throws() { + DummyBase b("v", dods_int32_c, false); + ConstraintEvaluator eval; + BaseTypeFactory factory; + DDS dds(&factory); + FakeMarshaller fm; + FakeUnMarshaller fu; + + CPPUNIT_ASSERT_THROW(b.serialize(eval, dds, fm, false), InternalErr); + CPPUNIT_ASSERT_THROW(b.deserialize(fu, &dds, false), InternalErr); + + stringstream ss(ios::in | ios::out | ios::binary); + D4StreamMarshaller d4m(ss, true, false); + D4StreamUnMarshaller d4u(ss, false); + DMR dmr; + + CPPUNIT_ASSERT_THROW(b.serialize(d4m, dmr, false), InternalErr); + CPPUNIT_ASSERT_THROW(b.deserialize(d4u, dmr), InternalErr); + } + + void test_ops_and_width_throw() { + DummyBase b("v", dods_int32_c, false); + DummyBase other("o", dods_int32_c, false); + CPPUNIT_ASSERT_THROW(b.ops(&other, 0), InternalErr); + CPPUNIT_ASSERT_THROW(b.d4_ops(&other, 0), InternalErr); + CPPUNIT_ASSERT_THROW(b.width(false), InternalErr); + CPPUNIT_ASSERT_THROW(b.width_ll(false), InternalErr); + } + + void test_is_dap4_projected() { + DummyBase b("v", dods_int32_c, true); + vector inventory; + b.set_send_p(false); + CPPUNIT_ASSERT(!b.is_dap4_projected(inventory)); + CPPUNIT_ASSERT(inventory.empty()); + + b.set_send_p(true); + b.set_attributes_nocopy(make_d4_attrs_with_int8("d4")); + CPPUNIT_ASSERT(b.is_dap4_projected(inventory)); + CPPUNIT_ASSERT(!inventory.empty()); + } +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(BaseTypeTest); + +int main(int argc, char *argv[]) { return run_tests(argc, argv) ? 0 : 1; } diff --git a/unit-tests/CMakeLists.txt b/unit-tests/CMakeLists.txt index 87f8a91b3..307e266f9 100644 --- a/unit-tests/CMakeLists.txt +++ b/unit-tests/CMakeLists.txt @@ -21,6 +21,8 @@ set(TESTS_DAP_ONLY D4AttributesTest.cc D4EnumTest.cc chunked_iostream_test.cc D4AsyncDocTest.cc DMRTest.cc DmrRoundTripTest.cc DmrToDap2Test.cc D4FilterClauseTest.cc IsDap4ProjectedTest.cc MarshallerFutureTest.cc TempFileTest.cc + DAPCache3Test.cc D4StreamRoundTripTest.cc ConstraintEvaluatorTest.cc MarshallerThreadTest.cc + BaseTypeTest.cc ) # BigArrayTest.cc seems to break things. jhrg 6/12/25 @@ -72,6 +74,9 @@ foreach(src ${TESTS_DAP_ONLY}) build_test(${tgt} ${src}) endforeach() +# DAPCache3 is not part of libdap, so compile its implementation into the test. +target_sources(DAPCache3Test PRIVATE ${CMAKE_SOURCE_DIR}/DAPCache3.cc) + foreach(src ${TESTS_DAPCLIENT}) get_filename_component(tgt ${src} NAME_WE) build_test(${tgt} ${src}) diff --git a/unit-tests/ConstraintEvaluatorTest.cc b/unit-tests/ConstraintEvaluatorTest.cc new file mode 100644 index 000000000..ae49b36c3 --- /dev/null +++ b/unit-tests/ConstraintEvaluatorTest.cc @@ -0,0 +1,66 @@ +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +#include +#include + +#include + +#include "BaseTypeFactory.h" +#include "ConstraintEvaluator.h" +#include "DDS.h" +#include "Error.h" +#include "Int32.h" + +#include "run_tests_cppunit.h" + +using namespace CppUnit; +using namespace libdap; +using namespace std; + +class ConstraintEvaluatorTest : public TestFixture { + CPPUNIT_TEST_SUITE(ConstraintEvaluatorTest); + CPPUNIT_TEST(test_simple_and_expression); + CPPUNIT_TEST(test_invalid_expression_throws); + CPPUNIT_TEST_SUITE_END(); + +public: + void test_simple_and_expression() { + BaseTypeFactory factory; + DDS dds(&factory); + + Int32 *a = new Int32("a"); + a->set_value(5); + dds.add_var_nocopy(a); + + ConstraintEvaluator eval; + eval.parse_constraint("a&a>3", dds); + CPPUNIT_ASSERT(eval.eval_selection(dds, "")); + + ConstraintEvaluator eval1; + eval1.parse_constraint("a&a>3&a<10", dds); + CPPUNIT_ASSERT(eval1.eval_selection(dds, "")); + + ConstraintEvaluator eval2; + eval2.parse_constraint("&a>7&a<10", dds); + CPPUNIT_ASSERT(!eval2.eval_selection(dds, "")); + } + + void test_invalid_expression_throws() { + BaseTypeFactory factory; + DDS dds(&factory); + + Int32 *a = new Int32("a"); + a->set_value(5); + dds.add_var_nocopy(a); + + ConstraintEvaluator eval; + CPPUNIT_ASSERT_THROW(eval.parse_constraint("a>>3", dds), Error); + } +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(ConstraintEvaluatorTest); + +int main(int argc, char *argv[]) { return run_tests(argc, argv) ? 0 : 1; } diff --git a/unit-tests/D4StreamRoundTripTest.cc b/unit-tests/D4StreamRoundTripTest.cc new file mode 100644 index 000000000..f8dfa793a --- /dev/null +++ b/unit-tests/D4StreamRoundTripTest.cc @@ -0,0 +1,104 @@ +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +#include +#include + +#include +#include +#include +#include +#include + +#include "D4StreamMarshaller.h" +#include "D4StreamUnMarshaller.h" +#include "run_tests_cppunit.h" + +using namespace CppUnit; +using namespace libdap; +using namespace std; + +class D4StreamRoundTripTest : public TestFixture { + CPPUNIT_TEST_SUITE(D4StreamRoundTripTest); + CPPUNIT_TEST(test_round_trip_scalars_and_vectors); + CPPUNIT_TEST(test_truncated_stream_throws); + CPPUNIT_TEST_SUITE_END(); + +public: + void test_round_trip_scalars_and_vectors() { + stringstream ss(ios::in | ios::out | ios::binary); + + const dods_int32 i32_min = std::numeric_limits::min(); + const dods_int64 i64_max = std::numeric_limits::max(); + const dods_uint64 u64_max = std::numeric_limits::max(); + const dods_float32 f32_inf = std::numeric_limits::infinity(); + const dods_float64 f64_nan = std::numeric_limits::quiet_NaN(); + + { + D4StreamMarshaller marshaller(ss, true, false); + marshaller.put_int32(i32_min); + marshaller.put_int64(i64_max); + marshaller.put_uint64(u64_max); + marshaller.put_float32(f32_inf); + marshaller.put_float64(f64_nan); + marshaller.put_str("hello"); + + dods_float32 f32_vals[3] = {1.5f, -2.0f, 0.0f}; + marshaller.put_vector_float32(reinterpret_cast(f32_vals), 3); + + dods_float64 f64_vals[2] = {3.25, -4.5}; + marshaller.put_vector_float64(reinterpret_cast(f64_vals), 2); + } + + ss.seekg(0, ios::beg); + + D4StreamUnMarshaller unmarshaller(ss, false); + + dods_int32 i32_out; + dods_int64 i64_out; + dods_uint64 u64_out; + dods_float32 f32_out; + dods_float64 f64_out; + string s_out; + + unmarshaller.get_int32(i32_out); + unmarshaller.get_int64(i64_out); + unmarshaller.get_uint64(u64_out); + unmarshaller.get_float32(f32_out); + unmarshaller.get_float64(f64_out); + unmarshaller.get_str(s_out); + + CPPUNIT_ASSERT_EQUAL(i32_min, i32_out); + CPPUNIT_ASSERT_EQUAL(i64_max, i64_out); + CPPUNIT_ASSERT_EQUAL(u64_max, u64_out); + CPPUNIT_ASSERT(std::isinf(f32_out) && f32_out > 0); + CPPUNIT_ASSERT(std::isnan(f64_out)); + CPPUNIT_ASSERT_EQUAL(string("hello"), s_out); + + dods_float32 f32_out_vals[3] = {0.0f, 0.0f, 0.0f}; + unmarshaller.get_vector_float32(reinterpret_cast(f32_out_vals), 3); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.5, f32_out_vals[0], 1e-6); + CPPUNIT_ASSERT_DOUBLES_EQUAL(-2.0, f32_out_vals[1], 1e-6); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, f32_out_vals[2], 1e-6); + + dods_float64 f64_out_vals[2] = {0.0, 0.0}; + unmarshaller.get_vector_float64(reinterpret_cast(f64_out_vals), 2); + CPPUNIT_ASSERT_DOUBLES_EQUAL(3.25, f64_out_vals[0], 1e-12); + CPPUNIT_ASSERT_DOUBLES_EQUAL(-4.5, f64_out_vals[1], 1e-12); + } + + void test_truncated_stream_throws() { + string data("\x01\x02\x03", 3); + stringstream ss(data, ios::in | ios::binary); + D4StreamUnMarshaller unmarshaller(ss, false); + + dods_int32 i32_out = 0; + CPPUNIT_ASSERT_THROW(unmarshaller.get_int32(i32_out), std::istream::failure); + } +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(D4StreamRoundTripTest); + +int main(int argc, char *argv[]) { return run_tests(argc, argv) ? 0 : 1; } diff --git a/unit-tests/DAPCache3Test.cc b/unit-tests/DAPCache3Test.cc new file mode 100644 index 000000000..484d0b03d --- /dev/null +++ b/unit-tests/DAPCache3Test.cc @@ -0,0 +1,119 @@ +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "DAPCache3.h" +#include "InternalErr.h" +#include "run_tests_cppunit.h" +#include "test_config.h" + +using namespace CppUnit; +using namespace libdap; +using namespace std; + +namespace { + +bool file_exists(const string &path) { return !path.empty() && (::access(path.c_str(), F_OK) == 0); } + +string make_temp_dir() { + string tmpl = string(TEST_BUILD_DIR) + "/dapcache3test.XXXXXX"; + vector buf(tmpl.begin(), tmpl.end()); + buf.push_back('\0'); + char *dir = ::mkdtemp(buf.data()); + if (!dir) + throw InternalErr(__FILE__, __LINE__, string("mkdtemp failed: ") + ::strerror(errno)); + return string(dir); +} + +void cleanup_cache_files(const string &dir, const string &prefix) { + DIR *dip = ::opendir(dir.c_str()); + if (!dip) + return; + + struct dirent *dit; + while ((dit = ::readdir(dip)) != nullptr) { + string name = dit->d_name; + if (name == "." || name == "..") + continue; + if (!prefix.empty() && name.find(prefix) != 0) + continue; + ::unlink((dir + "/" + name).c_str()); + } + + ::closedir(dip); +} + +} // namespace + +class DAPCache3Test : public TestFixture { + CPPUNIT_TEST_SUITE(DAPCache3Test); + CPPUNIT_TEST(test_get_cache_file_name_mangle_and_prefix); + CPPUNIT_TEST(test_create_and_read_lock_roundtrip); + CPPUNIT_TEST_SUITE_END(); + + string d_cache_dir; + DAPCache3 *d_cache = nullptr; + +public: + void setUp() override { + if (d_cache_dir.empty()) { + d_cache_dir = make_temp_dir(); + d_cache = DAPCache3::get_instance(d_cache_dir, "tst", 1024 * 1024); + } + } + + void tearDown() override { cleanup_cache_files(d_cache_dir, "tst#"); } + + void test_get_cache_file_name_mangle_and_prefix() { + string expected = d_cache_dir + "/tst#a#b#c"; + CPPUNIT_ASSERT_EQUAL(expected, d_cache->get_cache_file_name("/a/b/c.nc", true)); + + string expected_raw = d_cache_dir + "/tst#raw_name"; + CPPUNIT_ASSERT_EQUAL(expected_raw, d_cache->get_cache_file_name("raw_name", false)); + } + + void test_create_and_read_lock_roundtrip() { + string path = d_cache->get_cache_file_name("/data/test.bin", true); + + int fd = -1; + CPPUNIT_ASSERT(d_cache->create_and_lock(path, fd)); + CPPUNIT_ASSERT(fd >= 0); + + const char payload[] = "abc"; + ssize_t written = ::write(fd, payload, sizeof(payload) - 1); + CPPUNIT_ASSERT_EQUAL((ssize_t)(sizeof(payload) - 1), written); + + d_cache->exclusive_to_shared_lock(fd); + d_cache->unlock_and_close(fd); + + int fd2 = -1; + CPPUNIT_ASSERT(d_cache->get_read_lock(path, fd2)); + CPPUNIT_ASSERT(fd2 >= 0); + + char buf[4] = {0, 0, 0, 0}; + ssize_t read_bytes = ::read(fd2, buf, sizeof(payload) - 1); + CPPUNIT_ASSERT_EQUAL((ssize_t)(sizeof(payload) - 1), read_bytes); + + d_cache->unlock_and_close(fd2); + + CPPUNIT_ASSERT(file_exists(path)); + CPPUNIT_ASSERT_EQUAL(string("abc"), string(buf, sizeof(payload) - 1)); + } +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(DAPCache3Test); + +int main(int argc, char *argv[]) { return run_tests(argc, argv) ? 0 : 1; } diff --git a/unit-tests/Makefile.am b/unit-tests/Makefile.am index e7d2283fb..f148258a4 100644 --- a/unit-tests/Makefile.am +++ b/unit-tests/Makefile.am @@ -3,7 +3,7 @@ SUBDIRS = cache-testsuite # Tests -AUTOMAKE_OPTIONS = foreign +AUTOMAKE_OPTIONS = foreign subdir-objects # Headers in 'tests' are used by the arrayT unit tests. @@ -76,10 +76,6 @@ test_config.h: test_config.h.in Makefile D4-xml.tar.gz: D4-xml/DMR_*[0-9].xml tar -czf $@ $^ -# Override the default -# check: HTTPConnectTest -# ./HTTPConnectTest -d - ############################################################################ # Unit Tests # @@ -92,17 +88,15 @@ UNIT_TESTS = marshT arrayT attrTableT structT sequenceT ddsT dasT \ ServerFunctionsListUnitTest Int8Test Int16Test UInt16Test \ Int32Test UInt32Test Int64Test UInt64Test Float32Test Float64Test \ D4BaseTypeFactoryTest BaseTypeFactoryTest util_mitTest ErrorTest \ - MarshallerFutureTest - -# ErrorAssignmentTest - -# Moved to http+dap/unit-tests HTTPConnectTest HTTPCacheTest + MarshallerFutureTest ConstraintEvaluatorTest MarshallerThreadTest \ + DAPCache3Test BaseTypeTest # Unit tests for DAP4-only code. jhrg 2/4/22 UNIT_TESTS += D4MarshallerTest D4UnMarshallerTest D4DimensionsTest \ D4EnumDefsTest D4GroupTest D4ParserSax2Test D4AttributesTest D4EnumTest \ chunked_iostream_test D4AsyncDocTest DMRTest D4FilterClauseTest \ - D4SequenceTest DmrRoundTripTest DmrToDap2Test IsDap4ProjectedTest + D4SequenceTest DmrRoundTripTest DmrToDap2Test IsDap4ProjectedTest \ + D4StreamRoundTripTest else UNIT_TESTS = @@ -122,7 +116,7 @@ endif # kln 11/19/24 if CPPUNIT if USE_BA -UNIT_TESTS +=BigArrayTest +UNIT_TESTS += BigArrayTest endif endif @@ -191,6 +185,15 @@ DDXParserTest_CPPFLAGS = $(AM_CPPFLAGS) $(XML2_CFLAGS) generalUtilTest_SOURCES = generalUtilTest.cc +DAPCache3Test_SOURCES = DAPCache3Test.cc ../DAPCache3.cc ../DAPCache3.h + +ConstraintEvaluatorTest_SOURCES = ConstraintEvaluatorTest.cc + +MarshallerThreadTest_SOURCES = MarshallerThreadTest.cc + +D4StreamRoundTripTest_SOURCES = D4StreamRoundTripTest.cc +BaseTypeTest_SOURCES = BaseTypeTest.cc + # HTTPCacheTest_SOURCES = HTTPCacheTest.cc # HTTPCacheTest_CPPFLAGS = $(AM_CPPFLAGS) $(CURL_CFLAGS) # HTTPCacheTest_LDADD = ../libdapclient.la ../libdap.la $(AM_LDADD) diff --git a/unit-tests/MarshallerThreadTest.cc b/unit-tests/MarshallerThreadTest.cc new file mode 100644 index 000000000..a63bac197 --- /dev/null +++ b/unit-tests/MarshallerThreadTest.cc @@ -0,0 +1,49 @@ +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +#include +#include + +#include +#include +#include + +#include "MarshallerThread.h" +#include "run_tests_cppunit.h" + +using namespace CppUnit; +using namespace libdap; +using namespace std; + +class MarshallerThreadTest : public TestFixture { + CPPUNIT_TEST_SUITE(MarshallerThreadTest); + CPPUNIT_TEST(test_write_thread_to_stream); + CPPUNIT_TEST_SUITE_END(); + +public: + void test_write_thread_to_stream() { + MarshallerThread mt; + stringstream out(ios::in | ios::out | ios::binary); + + const string payload = "threaded-write"; + char *buf = new char[payload.size()]; + memcpy(buf, payload.data(), payload.size()); + + { + Locker lock(mt.get_mutex(), mt.get_cond(), mt.get_child_thread_count()); + mt.increment_child_thread_count(); + mt.start_thread(&MarshallerThread::write_thread, out, buf, payload.size()); + } + + // Wait for the child thread to finish. + { Locker wait(mt.get_mutex(), mt.get_cond(), mt.get_child_thread_count()); } + + CPPUNIT_ASSERT_EQUAL(payload, out.str()); + } +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(MarshallerThreadTest); + +int main(int argc, char *argv[]) { return run_tests(argc, argv) ? 0 : 1; } diff --git a/unit-tests/chunked_iostream_test.cc b/unit-tests/chunked_iostream_test.cc index 4d4be871d..1999cbfb8 100644 --- a/unit-tests/chunked_iostream_test.cc +++ b/unit-tests/chunked_iostream_test.cc @@ -29,6 +29,7 @@ #include // std::exception #include #include +#include #include #include "chunked_istream.h" @@ -363,6 +364,35 @@ class chunked_iostream_test : public TestFixture { } } + static string roundtrip_in_memory(const string &payload, int buf_size) { + stringstream ss(ios::in | ios::out | ios::binary); + { + chunked_ostream out(ss, buf_size); + out.write(payload.data(), payload.size()); + out.flush(); + } + + ss.seekg(0, ios::beg); + chunked_istream in(ss, buf_size); + string result; + char buf[16]; + while (!in.eof()) { + in.read(buf, sizeof(buf)); + auto count = in.gcount(); + if (count > 0) + result.append(buf, count); + } + return result; + } + + void test_roundtrip_exact_and_off_by_one_in_memory() { + const string exact = "ABCDEFGH"; // 8 bytes + const string off_by_one = "ABCDEFGHI"; // 9 bytes + + CPPUNIT_ASSERT(roundtrip_in_memory(exact, 8) == exact); + CPPUNIT_ASSERT(roundtrip_in_memory(off_by_one, 8) == off_by_one); + } + // these are the tests void test_write_1_read_1_small_file() { single_char_write(small_file, 32); @@ -674,6 +704,7 @@ class chunked_iostream_test : public TestFixture { CPPUNIT_TEST(test_write_24_read_24_big_file_2_error); CPPUNIT_TEST(test_write_9000_read_5000_big_file_3); + CPPUNIT_TEST(test_roundtrip_exact_and_off_by_one_in_memory); CPPUNIT_TEST_SUITE_END(); }; diff --git a/unit-tests/generalUtilTest.cc b/unit-tests/generalUtilTest.cc index 4f42e8017..dfe3b0ba9 100644 --- a/unit-tests/generalUtilTest.cc +++ b/unit-tests/generalUtilTest.cc @@ -65,6 +65,8 @@ class generalUtilTest : public TestFixture { CPPUNIT_TEST(munge_error_message_test); CPPUNIT_TEST(id2xml_test); CPPUNIT_TEST(xml2id_test); + CPPUNIT_TEST(id2www_ce_test); + CPPUNIT_TEST(escape_double_quotes_test); CPPUNIT_TEST(glob_test_1); CPPUNIT_TEST(glob_test_2); @@ -331,6 +333,18 @@ class generalUtilTest : public TestFixture { CPPUNIT_ASSERT(xml2id("'abc'def") == "'abc'def"); CPPUNIT_ASSERT(xml2id(""abc"def"") == "\"abc\"def\""); } + + void id2www_ce_test() { + CPPUNIT_ASSERT(id2www_ce("a*b") == "a%2ab"); + CPPUNIT_ASSERT(id2www_ce("a*b*c") == "a%2ab%2ac"); + } + + void escape_double_quotes_test() { + string s = "a\"b\"c"; + string escaped = escape_double_quotes(s); + CPPUNIT_ASSERT(escaped == "a\\\"b\\\"c"); + CPPUNIT_ASSERT(unescape_double_quotes(escaped) == s); + } }; CPPUNIT_TEST_SUITE_REGISTRATION(generalUtilTest); diff --git a/unit-tests/parserUtilTest.cc b/unit-tests/parserUtilTest.cc index a5fcb380b..dd22388b7 100644 --- a/unit-tests/parserUtilTest.cc +++ b/unit-tests/parserUtilTest.cc @@ -45,6 +45,12 @@ class parserUtilTest : public TestFixture { CPPUNIT_TEST(check_byte_test); CPPUNIT_TEST(check_float32_test); CPPUNIT_TEST(check_float64_test); + CPPUNIT_TEST(check_int32_test); + CPPUNIT_TEST(check_uint32_test); + CPPUNIT_TEST(check_int64_test); + CPPUNIT_TEST(check_uint64_test); + CPPUNIT_TEST(get_int32_test); + CPPUNIT_TEST(get_uint32_test); CPPUNIT_TEST_SUITE_END(); @@ -122,6 +128,44 @@ class parserUtilTest : public TestFixture { CPPUNIT_ASSERT(!check_float64("2.0E-308")); CPPUNIT_ASSERT(!check_float64("-2.0E-308")); } + + void check_int32_test() { + CPPUNIT_ASSERT(check_int32("2147483647")); + CPPUNIT_ASSERT(check_int32("-2147483648")); + CPPUNIT_ASSERT(!check_int32("2147483648")); + CPPUNIT_ASSERT(!check_int32("-2147483649")); + } + + void check_uint32_test() { + CPPUNIT_ASSERT(check_uint32("0")); + CPPUNIT_ASSERT(check_uint32("4294967295")); + CPPUNIT_ASSERT(!check_uint32("4294967296")); + CPPUNIT_ASSERT(!check_uint32("-1")); + } + + void check_int64_test() { + CPPUNIT_ASSERT(check_int64("9223372036854775807")); + CPPUNIT_ASSERT(check_int64("-9223372036854775808")); + CPPUNIT_ASSERT(!check_int64("9223372036854775808")); + CPPUNIT_ASSERT(!check_int64("-9223372036854775809")); + } + + void check_uint64_test() { + CPPUNIT_ASSERT(check_uint64("0")); + CPPUNIT_ASSERT(check_uint64("18446744073709551615")); + CPPUNIT_ASSERT(!check_uint64("18446744073709551616")); + CPPUNIT_ASSERT(!check_uint64("-1")); + } + + void get_int32_test() { + CPPUNIT_ASSERT_EQUAL(123, get_int32("123")); + CPPUNIT_ASSERT_THROW(get_int32("not_an_int"), Error); + } + + void get_uint32_test() { + CPPUNIT_ASSERT_EQUAL((unsigned int)42, get_uint32("42")); + CPPUNIT_ASSERT_THROW(get_uint32("-1"), Error); + } }; CPPUNIT_TEST_SUITE_REGISTRATION(parserUtilTest);