diff --git a/.gitignore b/.gitignore index 9d9f8446c..2fdaaa782 100644 --- a/.gitignore +++ b/.gitignore @@ -51,7 +51,7 @@ Makefile /dballe/msg/ltypes.tex /dballe/msg/tranges.dox /dballe/msg/tranges.tex -/dballe/tut_test +/dballe/test-dballe /dballe/dump-core-info /fortran/check_attrs /fortran/check_fdballe diff --git a/configure.ac b/configure.ac index db169ff99..8e72950e9 100644 --- a/configure.ac +++ b/configure.ac @@ -120,8 +120,6 @@ fi dnl Check for wreport PKG_CHECK_MODULES(WREPORT,libwreport >= 3.0) -PKG_CHECK_EXISTS([libwreport-test >= 3.0], [have_wreport_test=yes], [have_wreport_test=no]) - dnl Check for sqlite3 PKG_CHECK_MODULES(SQLITE3, sqlite3) @@ -199,21 +197,6 @@ then fi AM_CONDITIONAL([LUA], [test x"$have_lua" = x"yes"]) - -dnl Check for wibble -PKG_CHECK_EXISTS([libwibble], [have_wibble=yes], [have_wibble=no]) -if test x$have_wibble = xyes && test x$have_wreport_test = xyes -then - PKG_CHECK_MODULES(WREPORT_TEST, libwreport-test) - PKG_CHECK_MODULES(WIBBLE, libwibble) - enable_tests=yes -else - test x$have_wibble = xno && AC_MSG_WARN([libwibble not found, tests will be disabled]) - test x$have_wreport_test = xno && AC_MSG_WARN([libwreport-test not found, tests will be disabled]) - enable_tests=no -fi - - dnl Check for popt AC_CHECK_HEADER(popt.h, AC_DEFINE(HAVE_POPT_H, 1, popt.h has been found), AC_MSG_ERROR([*** popt.h not found. Check 'config.log' for more details.]) @@ -290,7 +273,6 @@ AM_CONDITIONAL([HAVE_ODBC], [test x"$have_odbc" = x"yes"]) AM_CONDITIONAL([DO_DBALLEF], [test x"$enable_dballef" = x"yes"]) AM_CONDITIONAL([DO_DBALLE_PYTHON], [test x"$enable_dballe_python" = x"yes"]) AM_CONDITIONAL([DO_DOCS], [test x"$enable_docs" = x"yes"]) -AM_CONDITIONAL([DO_TESTS], [test x"$enable_tests" = x"yes"]) AM_CONDITIONAL([HAVE_LATEX2HTML], [test x$have_latex2html = xyes]) AM_CONDITIONAL([HAVE_RST2HTML], [test x$have_rst2html = xyes]) @@ -311,11 +293,6 @@ libdballe.pc libdballef.pc ]) AC_OUTPUT -#dballe/bench/Makefile -#examples/Makefile - -dnl tests/Makefile -dnl doc/Makefile AC_MSG_NOTICE([ =================================================== @@ -326,7 +303,6 @@ AS_HELP_STRING([MySQL:], [$have_mysql]) AS_HELP_STRING([Fortran:], [$enable_dballef]) AS_HELP_STRING([Python:], [$enable_dballe_python]) AS_HELP_STRING([documentation:], [$enable_docs]) -AS_HELP_STRING([tests:], [$enable_tests]) AS_HELP_STRING([latex2html:], [$have_latex2html]) AS_HELP_STRING([rst2html:], [$have_rst2html]) ===================================================]) diff --git a/dballe/Makefile.am b/dballe/Makefile.am index 83465d7d2..9058c2ca8 100644 --- a/dballe/Makefile.am +++ b/dballe/Makefile.am @@ -304,7 +304,7 @@ EXTRA_DIST += \ # Unit testing # -check_PROGRAMS = tut_test +check_PROGRAMS = test-dballe TESTS_ENVIRONMENT = $(top_srcdir)/extra/runtest #TESTS = $(check_PROGRAMS) @@ -322,96 +322,94 @@ dist_noinst_HEADERS = \ msg/tests-lua.h \ db/tests.h -tut_test_SOURCES = \ - core/tests/tut-main.cpp \ +test_dballe_SOURCES = \ + tests-main.cpp \ core/tests.cc \ - file-tut.cc \ - message-tut.cc \ - types-tut.cc \ - record-tut.cc \ - query-tut.cc \ - var-tut.cc \ - core/aliases-tut.cc \ - core/stlutils-tut.cc \ - core/defs-tut.cc \ - core/var-tut.cc \ - core/values-tut.cc \ - core/file-tut.cc \ - core/record-tut.cc \ - core/query-tut.cc \ - core/structbuf-tut.cc \ - core/csv-tut.cc \ - core/matcher-tut.cc \ - core/match-wreport-tut.cc \ - core/varmatch-tut.cc \ - core/json-tut.cc \ + file-test.cc \ + message-test.cc \ + types-test.cc \ + record-test.cc \ + query-test.cc \ + var-test.cc \ + core/aliases-test.cc \ + core/stlutils-test.cc \ + core/defs-test.cc \ + core/var-test.cc \ + core/values-test.cc \ + core/file-test.cc \ + core/record-test.cc \ + core/query-test.cc \ + core/structbuf-test.cc \ + core/csv-test.cc \ + core/matcher-test.cc \ + core/match-wreport-test.cc \ + core/varmatch-test.cc \ + core/json-test.cc \ memdb/tests.cc \ - memdb/valuestorage-tut.cc \ - memdb/index-tut.cc \ - memdb/match-tut.cc \ - memdb/results-tut.cc \ - memdb/station-tut.cc \ - memdb/valuebase-tut.cc \ - memdb/stationvalue-tut.cc \ - memdb/levtr-tut.cc \ - memdb/value-tut.cc \ - memdb/memdb-tut.cc \ - memdb/serializer-tut.cc \ + memdb/valuestorage-test.cc \ + memdb/index-test.cc \ + memdb/match-test.cc \ + memdb/results-test.cc \ + memdb/station-test.cc \ + memdb/valuebase-test.cc \ + memdb/stationvalue-test.cc \ + memdb/levtr-test.cc \ + memdb/value-test.cc \ + memdb/memdb-test.cc \ + memdb/serializer-test.cc \ msg/tests.cc \ - msg/vars-tut.cc \ - msg/context-tut.cc \ - msg/codec-tut.cc \ - msg/msg-tut.cc \ - msg/wr_codec_generic-tut.cc \ - msg/wr_codec-tut.cc \ - msg/wr_import-tut.cc \ - msg/wr_export-tut.cc \ - msg/aof_codec-tut.cc \ + msg/vars-test.cc \ + msg/context-test.cc \ + msg/codec-test.cc \ + msg/msg-test.cc \ + msg/wr_codec_generic-test.cc \ + msg/wr_codec-test.cc \ + msg/wr_import-test.cc \ + msg/wr_export-test.cc \ + msg/aof_codec-test.cc \ db/tests.cc \ - db/querybuf-tut.cc \ - db/trace-tut.cc \ - db/sql/repinfo-tut.cc \ - db/sql/station-tut.cc \ - db/sql/levtr-tut.cc \ - db/sql/datav6-tut.cc \ - db/sql/attrv6-tut.cc \ - db/sqlite/internals-tut.cc \ - db/db-basic-tut.cc \ - db/db-misc-tut.cc \ - db/db-query-station-tut.cc \ - db/db-query-data-tut.cc \ - db/db-query-summary-tut.cc \ - db/db-import-tut.cc \ - db/db-export-tut.cc \ - db/mem/repinfo-tut.cc \ - db/mem/cursor-tut.cc \ - db/summary-tut.cc \ - simple/msgapi-tut.cc \ - cmdline/dbadb-tut.cc \ - simple/dbapi-tut.cc + db/querybuf-test.cc \ + db/trace-test.cc \ + db/sql/repinfo-test.cc \ + db/sql/station-test.cc \ + db/sql/levtr-test.cc \ + db/sql/datav6-test.cc \ + db/sql/attrv6-test.cc \ + db/sqlite/internals-test.cc \ + db/db-basic-test.cc \ + db/db-misc-test.cc \ + db/db-query-station-test.cc \ + db/db-query-data-test.cc \ + db/db-query-summary-test.cc \ + db/db-import-test.cc \ + db/db-export-test.cc \ + db/mem/repinfo-test.cc \ + db/mem/cursor-test.cc \ + db/summary-test.cc \ + simple/msgapi-test.cc \ + cmdline/dbadb-test.cc \ + simple/dbapi-test.cc if LUA -tut_test_SOURCES += \ - msg/lua-tut.cc \ +test_dballe_SOURCES += \ + msg/lua-test.cc \ msg/tests-lua.cc endif if HAVE_LIBPQ -tut_test_SOURCES += \ - db/postgresql/internals-tut.cc +test_dballe_SOURCES += \ + db/postgresql/internals-test.cc endif if HAVE_MYSQL -tut_test_SOURCES += \ - db/mysql/internals-tut.cc +test_dballe_SOURCES += \ + db/mysql/internals-test.cc endif if HAVE_ODBC -tut_test_SOURCES += \ - db/odbc/internals-tut.cc +test_dballe_SOURCES += \ + db/odbc/internals-test.cc endif -tut_test_LDADD = \ +test_dballe_LDADD = \ libdballe.la \ - $(common_libs) \ - $(WREPORT_TEST_LIBS) \ - $(WIBBLE_LIBS) + $(common_libs) # # Benchmark diff --git a/dballe/cmdline/dbadb-test.cc b/dballe/cmdline/dbadb-test.cc new file mode 100644 index 000000000..1e7de24fa --- /dev/null +++ b/dballe/cmdline/dbadb-test.cc @@ -0,0 +1,67 @@ +#include "config.h" +#include "db/tests.h" +#include "cmdline/dbadb.h" +#include "core/arrayfile.h" +#include "msg/codec.h" +#include "msg/msg.h" +#include "config.h" + +using namespace dballe; +using namespace dballe::cmdline; +using namespace dballe::tests; +using namespace wreport; +using namespace std; + +namespace { + +class Tests : public FixtureTestCase +{ + using FixtureTestCase::FixtureTestCase; + + void register_tests() override + { + add_method("import", [](Fixture& f) { + Dbadb dbadb(*f.db); + + // Import a synop + cmdline::Reader reader; + wassert(actual(dbadb.do_import(dballe::tests::datafile("bufr/obs0-1.22.bufr"), reader)) == 0); + + // Export forcing report as temp + core::Query query; + core::ArrayFile file(File::BUFR); + wassert(actual(dbadb.do_export(query, file, "generic", "ship")) == 0); + + wassert(actual(file.msgs.size()) == 1u); + + // Decode results + auto importer = msg::Importer::create(File::BUFR); + Messages msgs = importer->from_binary(file.msgs[0]); + wassert(actual(msgs.size()) == 1u); + Msg& msg = Msg::downcast(msgs[0]); + + // Ensure they're ships + wassert(actual(msg.type) == MSG_SHIP); + + // Check 001194 [SIM] Report mnemonic(CCITTIA5), too + const Var* var = msg.get_rep_memo_var(); + wassert(actual(var).istrue()); + wassert(actual(var->enqc()).istrue()); + wassert(actual(var->enq()) == "ship"); + }); + } +}; + +Tests tg1("cmdline_dbadb_mem", nullptr, db::MEM); +Tests tg2("cmdline_dbadb_v6_sqlite", "SQLITE", db::V6); +#ifdef HAVE_ODBC +Tests tg4("cmdline_dbadb_v6_odbc", "ODBC", db::V6); +#endif +#ifdef HAVE_LIBPQ +Tests tg6("cmdline_dbadb_v6_postgresql", "POSTGRESQL", db::V6); +#endif +#ifdef HAVE_MYSQL +Tests tg8("cmdline_dbadb_v6_mysql", "MYSQL", db::V6); +#endif + +} diff --git a/dballe/cmdline/dbadb-tut.cc b/dballe/cmdline/dbadb-tut.cc deleted file mode 100644 index fe0d54147..000000000 --- a/dballe/cmdline/dbadb-tut.cc +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2005--2015 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "config.h" -#include "db/tests.h" -#include "cmdline/dbadb.h" -#include "core/arrayfile.h" -#include "msg/codec.h" -#include "msg/msg.h" -#include "config.h" - -using namespace dballe; -using namespace dballe::cmdline; -using namespace wreport; -using namespace wibble::tests; -using namespace std; - -namespace { - -typedef dballe::tests::DBFixture Fixture; -typedef dballe::tests::db_test_group test_group; -typedef test_group::Test Test; - -std::vector tests { - // Test simple queries - Test("utils", [](Fixture& f) { - // Resolve NULL and the empty string - ensure_equals(dbadb::parse_op_report(*f.db, NULL), (const char*)NULL); - ensure_equals(dbadb::parse_op_report(*f.db, ""), (const char*)NULL); - - // Resolve from names - wassert(actual(dbadb::parse_op_report(*f.db, "synop")) == "synop"); - - // Resolve from nonexisting numbers - try { - dbadb::parse_op_report(*f.db, "11234"); - ensure(false); - } catch (...) {} - - // Resolve from nonexisting names - try { - dbadb::parse_op_report(*f.db, "lolcats"); - ensure(false); - } catch (...) {} - }), - Test("import", [](Fixture& f) { - Dbadb dbadb(*f.db); - - // Import a synop - cmdline::Reader reader; - ensure_equals(dbadb.do_import(dballe::tests::datafile("bufr/obs0-1.22.bufr"), reader), 0); - - // Export forcing report as temp - core::Query query; - core::ArrayFile file(File::BUFR); - ensure_equals(dbadb.do_export(query, file, "generic", "ship"), 0); - - ensure_equals(file.msgs.size(), 1u); - - // Decode results - auto importer = msg::Importer::create(File::BUFR); - Messages msgs = importer->from_binary(file.msgs[0]); - ensure_equals(msgs.size(), 1u); - Msg& msg = Msg::downcast(msgs[0]); - - // Ensure they're ships - ensure_equals(msg.type, MSG_SHIP); - - // Check 001194 [SIM] Report mnemonic(CCITTIA5), too - const Var* var = msg.get_rep_memo_var(); - ensure(var != 0); - ensure(var->enqc() != 0); - ensure_equals(var->enq(), "ship"); - }), -}; - -test_group tg1("cmdline_dbadb_mem", nullptr, db::MEM, tests); -test_group tg2("cmdline_dbadb_v6_sqlite", "SQLITE", db::V6, tests); -#ifdef HAVE_ODBC -test_group tg4("cmdline_dbadb_v6_odbc", "ODBC", db::V6, tests); -#endif -#ifdef HAVE_LIBPQ -test_group tg6("cmdline_dbadb_v6_postgresql", "POSTGRESQL", db::V6, tests); -#endif -#ifdef HAVE_MYSQL -test_group tg8("cmdline_dbadb_v6_mysql", "MYSQL", db::V6, tests); -#endif - -} diff --git a/dballe/cmdline/dbadb.cc b/dballe/cmdline/dbadb.cc index 4d82f7038..efe043280 100644 --- a/dballe/cmdline/dbadb.cc +++ b/dballe/cmdline/dbadb.cc @@ -32,19 +32,6 @@ using namespace std; namespace dballe { namespace cmdline { -namespace dbadb { - -const char* parse_op_report(DB& db, const char* name) -{ - if (name != 0 && name[0] != 0) - { - return name; - } else - return NULL; -} - -} - namespace { struct Importer : public Action @@ -144,7 +131,7 @@ int Dbadb::do_export(const Query& query, File& file, const char* output_template opts.template_name = output_template; if (forced_repmemo) - forced_repmemo = dbadb::parse_op_report(db, forced_repmemo); + forced_repmemo = forced_repmemo; auto exporter = msg::Exporter::create(file.encoding(), opts); db.export_msgs(query, [&](unique_ptr&& msg) { diff --git a/dballe/cmdline/dbadb.h b/dballe/cmdline/dbadb.h index 80e87501f..3c8fdb2e5 100644 --- a/dballe/cmdline/dbadb.h +++ b/dballe/cmdline/dbadb.h @@ -31,26 +31,6 @@ struct DB; namespace cmdline { -namespace dbadb { - -/** - * Parse a report name from command line. - * - * If \a name is NULL or the empty string, it returns NULL to signal that no - * value was provided. - * - * Raises an exception if \a name is not the empty string and an invalid report - * name. - * - * @param name - * String with a report number or name - * @returns - * The validated report name, or NULL if \a name was the empty string. - */ -const char* parse_op_report(DB& db, const char* name=NULL); - -} - class Dbadb { protected: diff --git a/dballe/core/aliases-test.cc b/dballe/core/aliases-test.cc new file mode 100644 index 000000000..34e92ef0b --- /dev/null +++ b/dballe/core/aliases-test.cc @@ -0,0 +1,26 @@ +#include "core/tests.h" +#include "core/aliases.h" + +using namespace dballe; +using namespace dballe::tests; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("resolve", []() { + wassert(actual_varcode(varcode_alias_resolve("block")) == WR_VAR(0, 1, 1)); + wassert(actual_varcode(varcode_alias_resolve("station")) == WR_VAR(0, 1, 2)); + wassert(actual_varcode(varcode_alias_resolve("height")) == WR_VAR(0, 7, 30)); + wassert(actual_varcode(varcode_alias_resolve("heightbaro")) == WR_VAR(0, 7, 31)); + wassert(actual_varcode(varcode_alias_resolve("name")) == WR_VAR(0, 1, 19)); + wassert(actual_varcode(varcode_alias_resolve("cippolippo")) == 0); + }); + } +} test("core_aliases"); + +} diff --git a/dballe/core/aliases-tut.cc b/dballe/core/aliases-tut.cc deleted file mode 100644 index 2132e27f4..000000000 --- a/dballe/core/aliases-tut.cc +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2005--2010 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "core/tests.h" -#include "core/aliases.h" - -using namespace dballe; - -namespace tut { - -struct aliases_shar -{ - aliases_shar() - { - } - - ~aliases_shar() - { - } -}; -TESTGRP(aliases); - - -// Test variable creation -template<> template<> -void to::test<1>() -{ - ensure_equals(varcode_alias_resolve("block"), WR_VAR(0, 1, 1)); - ensure_equals(varcode_alias_resolve("station"), WR_VAR(0, 1, 2)); - ensure_equals(varcode_alias_resolve("height"), WR_VAR(0, 7, 30)); - ensure_equals(varcode_alias_resolve("heightbaro"), WR_VAR(0, 7, 31)); - ensure_equals(varcode_alias_resolve("name"), WR_VAR(0, 1, 19)); - ensure_equals(varcode_alias_resolve("cippolippo"), 0); -} - -} - -/* vim:set ts=4 sw=4: */ diff --git a/dballe/core/csv-test.cc b/dballe/core/csv-test.cc new file mode 100644 index 000000000..a72054548 --- /dev/null +++ b/dballe/core/csv-test.cc @@ -0,0 +1,217 @@ +#include "tests.h" +#include "csv.h" +#include +#include + +using namespace std; +using namespace dballe; +using namespace dballe::tests; + +namespace { + +class TestCSVWriter : public CSVWriter +{ +public: + stringstream buf; + virtual void flush_row() + { + buf << row; + } +}; + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + // Test CSV string escaping + add_method("escape", []() { + stringstream s; + csv_output_quoted_string(s, ""); + wassert(actual(s.str()) == ""); + wassert(actual(CSVReader::unescape(s.str())) == ""); + + s.str(std::string()); + csv_output_quoted_string(s, "1"); + wassert(actual(s.str()) == "1"); + wassert(actual(CSVReader::unescape(s.str())) == "1"); + + s.str(std::string()); + csv_output_quoted_string(s, "12"); + wassert(actual(s.str()) == "12"); + wassert(actual(CSVReader::unescape(s.str())) == "12"); + + s.str(std::string()); + csv_output_quoted_string(s, "123"); + wassert(actual(s.str()) == "123"); + wassert(actual(CSVReader::unescape(s.str())) == "123"); + + s.str(std::string()); + csv_output_quoted_string(s, ","); + wassert(actual(s.str()) == "\",\""); + wassert(actual(CSVReader::unescape(s.str())) == ","); + + s.str(std::string()); + csv_output_quoted_string(s, "antani, blinda"); + wassert(actual(s.str()) == "\"antani, blinda\""); + wassert(actual(CSVReader::unescape(s.str())) == "antani, blinda"); + + s.str(std::string()); + csv_output_quoted_string(s, "antani, \"blinda\""); + wassert(actual(s.str()) == "\"antani, \"\"blinda\"\"\""); + wassert(actual(CSVReader::unescape(s.str())) == "antani, \"blinda\""); + + s.str(std::string()); + csv_output_quoted_string(s, "\""); + wassert(actual(s.str()) == "\"\"\"\""); + wassert(actual(CSVReader::unescape(s.str())) == "\""); + + s.str(std::string()); + csv_output_quoted_string(s, "\",\""); + wassert(actual(s.str()) == "\"\"\",\"\"\""); + wassert(actual(CSVReader::unescape(s.str())) == "\",\""); + + wassert(actual(CSVReader::unescape("\"")) == "\""); + wassert(actual(CSVReader::unescape("\"\"")) == ""); + wassert(actual(CSVReader::unescape("\"\"\"")) == "\""); + wassert(actual(CSVReader::unescape("\"\"\"\"")) == "\""); + wassert(actual(CSVReader::unescape("\"\"\"\"\"")) == "\"\""); + wassert(actual(CSVReader::unescape("a\"b")) == "a\"b"); + }); + + // Test CSV reader + add_method("reader", []() { + { + stringstream in(""); + CSVReader reader(in); + wassert(actual(reader.next()).isfalse()); + } + + { + stringstream in("\n"); + CSVReader reader(in); + wassert(actual(reader.next()).istrue()); + wassert(actual(reader.cols.size()) == 1); + wassert(actual(reader.cols[0]) == ""); + wassert(actual(reader.next()).isfalse()); + } + + { + stringstream in("\r\n"); + CSVReader reader(in); + wassert(actual(reader.next()).istrue()); + wassert(actual(reader.cols.size()) == 1); + wassert(actual(reader.cols[0]) == ""); + wassert(actual(reader.next()).isfalse()); + } + + { + stringstream in("1,2\r\n"); + CSVReader reader(in); + wassert(actual(reader.next()).istrue()); + wassert(actual(reader.cols.size()) == 2u); + wassert(actual(reader.cols[0]) == "1"); + wassert(actual(reader.cols[1]) == "2"); + wassert(actual(reader.next()).isfalse()); + } + + { + stringstream in("1\r\n2\r\n"); + CSVReader reader(in); + wassert(actual(reader.next()).istrue()); + wassert(actual(reader.cols.size()) == 1u); + wassert(actual(reader.cols[0]) == "1"); + wassert(actual(reader.next()).istrue()); + wassert(actual(reader.cols.size()) == 1u); + wassert(actual(reader.cols[0]) == "2"); + wassert(actual(reader.next()).isfalse()); + } + + { + stringstream in("1,2\n"); + CSVReader reader(in); + wassert(actual(reader.next()).istrue()); + wassert(actual(reader.cols.size()) == 2u); + wassert(actual(reader.cols[0]) == "1"); + wassert(actual(reader.cols[1]) == "2"); + wassert(actual(reader.next()).isfalse()); + } + + { + stringstream in( + "1,\",\",2\n" + "antani,,blinda\n" + ",\n" + ); + CSVReader reader(in); + + wassert(actual(reader.next()).istrue()); + wassert(actual(reader.cols.size()) == 3u); + wassert(actual(reader.cols[0]) == "1"); + wassert(actual(reader.cols[1]) == ","); + wassert(actual(reader.cols[2]) == "2"); + + wassert(actual(reader.next()).istrue()); + wassert(actual(reader.cols.size()) == 3u); + wassert(actual(reader.cols[0]) == "antani"); + wassert(actual(reader.cols[1]) == ""); + wassert(actual(reader.cols[2]) == "blinda"); + + wassert(actual(reader.next()).istrue()); + wassert(actual(reader.cols.size()) == 2u); + wassert(actual(reader.cols[0]) == ""); + wassert(actual(reader.cols[1]) == ""); + + wassert(actual(reader.next()).isfalse()); + } + + { + stringstream in("1,2"); + CSVReader reader(in); + + wassert(actual(reader.next()).istrue()); + wassert(actual(reader.cols.size()) == 2u); + wassert(actual(reader.cols[0]) == "1"); + wassert(actual(reader.cols[1]) == "2"); + + wassert(actual(reader.next()).isfalse()); + } + + { + stringstream in("\"1\"\r\n"); + CSVReader reader(in); + + wassert(actual(reader.next()).istrue()); + wassert(actual(reader.cols.size()) == 1u); + wassert(actual(reader.cols[0]) == "1"); + + wassert(actual(reader.next()).isfalse()); + } + }); + + // Test write/read cycles + add_method("writer", []() { + TestCSVWriter out; + out.add_value(1); + out.add_value("\""); + out.add_value("'"); + out.add_value(","); + out.add_value("\n"); + out.flush_row(); + + out.buf.seekg(0); + CSVReader in(out.buf); + wassert(actual(in.next()).istrue()); + wassert(actual(in.cols.size()) == 5); + wassert(actual(in.as_int(0)) == 1); + wassert(actual(in.cols[1]) == "\""); + wassert(actual(in.cols[2]) == "'"); + wassert(actual(in.cols[3]) == ","); + wassert(actual(in.cols[4]) == "\n"); + wassert(actual(in.next()).isfalse()); + }); + } +} test("core_csv"); + +} diff --git a/dballe/core/csv-tut.cc b/dballe/core/csv-tut.cc deleted file mode 100644 index 1e51b7df7..000000000 --- a/dballe/core/csv-tut.cc +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright (C) 2011--2014 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Author: Enrico Zini - */ - -#include "tests.h" -#include "csv.h" - -#include -#include - -using namespace std; -using namespace dballe; -using namespace wibble::tests; - -namespace tut { - -struct core_csv_shar { -}; -TESTGRP(core_csv); - -// Test CSV string escaping -template<> template<> -void to::test<1>() -{ - stringstream s; - csv_output_quoted_string(s, ""); - ensure_equals(s.str(), ""); - ensure_equals(CSVReader::unescape(s.str()), ""); - - s.str(std::string()); - csv_output_quoted_string(s, "1"); - ensure_equals(s.str(), "1"); - ensure_equals(CSVReader::unescape(s.str()), "1"); - - s.str(std::string()); - csv_output_quoted_string(s, "12"); - ensure_equals(s.str(), "12"); - ensure_equals(CSVReader::unescape(s.str()), "12"); - - s.str(std::string()); - csv_output_quoted_string(s, "123"); - ensure_equals(s.str(), "123"); - ensure_equals(CSVReader::unescape(s.str()), "123"); - - s.str(std::string()); - csv_output_quoted_string(s, ","); - ensure_equals(s.str(), "\",\""); - ensure_equals(CSVReader::unescape(s.str()), ","); - - s.str(std::string()); - csv_output_quoted_string(s, "antani, blinda"); - ensure_equals(s.str(), "\"antani, blinda\""); - ensure_equals(CSVReader::unescape(s.str()), "antani, blinda"); - - s.str(std::string()); - csv_output_quoted_string(s, "antani, \"blinda\""); - ensure_equals(s.str(), "\"antani, \"\"blinda\"\"\""); - ensure_equals(CSVReader::unescape(s.str()), "antani, \"blinda\""); - - s.str(std::string()); - csv_output_quoted_string(s, "\""); - ensure_equals(s.str(), "\"\"\"\""); - ensure_equals(CSVReader::unescape(s.str()), "\""); - - s.str(std::string()); - csv_output_quoted_string(s, "\",\""); - ensure_equals(s.str(), "\"\"\",\"\"\""); - ensure_equals(CSVReader::unescape(s.str()), "\",\""); - - ensure_equals(CSVReader::unescape("\""), "\""); - ensure_equals(CSVReader::unescape("\"\""), ""); - ensure_equals(CSVReader::unescape("\"\"\""), "\""); - ensure_equals(CSVReader::unescape("\"\"\"\""), "\""); - ensure_equals(CSVReader::unescape("\"\"\"\"\""), "\"\""); - ensure_equals(CSVReader::unescape("a\"b"), "a\"b"); -} - -// Test CSV reader -template<> template<> -void to::test<2>() -{ - { - stringstream in(""); - CSVReader reader(in); - ensure(!reader.next()); - } - - { - stringstream in("\n"); - CSVReader reader(in); - ensure(reader.next()); - wassert(actual(reader.cols.size()) == 1); - wassert(actual(reader.cols[0]) == ""); - ensure(!reader.next()); - } - - { - stringstream in("\r\n"); - CSVReader reader(in); - ensure(reader.next()); - wassert(actual(reader.cols.size()) == 1); - wassert(actual(reader.cols[0]) == ""); - ensure(!reader.next()); - } - - { - stringstream in("1,2\r\n"); - CSVReader reader(in); - ensure(reader.next()); - ensure_equals(reader.cols.size(), 2u); - ensure_equals(reader.cols[0], "1"); - ensure_equals(reader.cols[1], "2"); - ensure(!reader.next()); - } - - { - stringstream in("1\r\n2\r\n"); - CSVReader reader(in); - ensure(reader.next()); - ensure_equals(reader.cols.size(), 1u); - ensure_equals(reader.cols[0], "1"); - ensure(reader.next()); - ensure_equals(reader.cols.size(), 1u); - ensure_equals(reader.cols[0], "2"); - ensure(!reader.next()); - } - - { - stringstream in("1,2\n"); - CSVReader reader(in); - ensure(reader.next()); - ensure_equals(reader.cols.size(), 2u); - ensure_equals(reader.cols[0], "1"); - ensure_equals(reader.cols[1], "2"); - ensure(!reader.next()); - } - - { - stringstream in( - "1,\",\",2\n" - "antani,,blinda\n" - ",\n" - ); - CSVReader reader(in); - - ensure(reader.next()); - ensure_equals(reader.cols.size(), 3u); - ensure_equals(reader.cols[0], "1"); - ensure_equals(reader.cols[1], ","); - ensure_equals(reader.cols[2], "2"); - - ensure(reader.next()); - ensure_equals(reader.cols.size(), 3u); - ensure_equals(reader.cols[0], "antani"); - ensure_equals(reader.cols[1], ""); - ensure_equals(reader.cols[2], "blinda"); - - ensure(reader.next()); - ensure_equals(reader.cols.size(), 2u); - ensure_equals(reader.cols[0], ""); - ensure_equals(reader.cols[1], ""); - - ensure(!reader.next()); - } - - { - stringstream in("1,2"); - CSVReader reader(in); - - ensure(reader.next()); - ensure_equals(reader.cols.size(), 2u); - ensure_equals(reader.cols[0], "1"); - ensure_equals(reader.cols[1], "2"); - - ensure(!reader.next()); - } - - { - stringstream in("\"1\"\r\n"); - CSVReader reader(in); - - ensure(reader.next()); - ensure_equals(reader.cols.size(), 1u); - ensure_equals(reader.cols[0], "1"); - - ensure(!reader.next()); - } -} - -namespace { - -class TestCSVWriter : public CSVWriter -{ -public: - stringstream buf; - virtual void flush_row() - { - buf << row; - } -}; - -} - -// Test write/read cycles -template<> template<> -void to::test<3>() -{ - TestCSVWriter out; - out.add_value(1); - out.add_value("\""); - out.add_value("'"); - out.add_value(","); - out.add_value("\n"); - out.flush_row(); - - out.buf.seekg(0); - CSVReader in(out.buf); - wassert(actual(in.next()).istrue()); - wassert(actual(in.cols.size()) == 5); - wassert(actual(in.as_int(0)) == 1); - wassert(actual(in.cols[1]) == "\""); - wassert(actual(in.cols[2]) == "'"); - wassert(actual(in.cols[3]) == ","); - wassert(actual(in.cols[4]) == "\n"); - wassert(actual(in.next()).isfalse()); -} - -} - -// vim:set ts=4 sw=4: diff --git a/dballe/core/defs-test.cc b/dballe/core/defs-test.cc new file mode 100644 index 000000000..c91e2cf56 --- /dev/null +++ b/dballe/core/defs-test.cc @@ -0,0 +1,61 @@ +#include "core/tests.h" +#include "core/defs.h" + +using namespace dballe; +using namespace wreport::tests; +using namespace std; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("ident", []() { + wassert(actual(Ident()) == Ident()); + wassert(actual(Ident("foo")) == Ident("foo")); + wassert(actual(Ident().is_missing()).istrue()); + wassert(actual(Ident("foo").is_missing()).isfalse()); + Ident test; + wassert(actual((const char*)test == nullptr).istrue()); + test = "antani"; + wassert(actual((const char*)test) == "antani"); + Ident test1 = test; + wassert(actual(test) == Ident("antani")); + wassert(actual(test1) == Ident("antani")); + test1 = test1; + wassert(actual(test) == Ident("antani")); + wassert(actual(test1) == Ident("antani")); + test1 = Ident("blinda"); + wassert(actual(test) == Ident("antani")); + wassert(actual(test1) == Ident("blinda")); + test = move(test1); + wassert(actual(test) == Ident("blinda")); + wassert(actual(test1.is_missing()).istrue()); + Ident test2(move(test)); + wassert(actual(test.is_missing()).istrue()); + wassert(actual(test2) == Ident("blinda")); + test = string("foo"); + wassert(actual(test) == Ident("foo")); + wassert(actual(test2) == Ident("blinda")); + wassert(actual(Ident("foo") == Ident("foo")).istrue()); + wassert(actual(Ident("foo") <= Ident("foo")).istrue()); + wassert(actual(Ident("foo") >= Ident("foo")).istrue()); + wassert(actual(Ident("foo") == Ident("bar")).isfalse()); + wassert(actual(Ident("foo") != Ident("foo")).isfalse()); + wassert(actual(Ident("foo") != Ident("bar")).istrue()); + wassert(actual(Ident("antani") < Ident("blinda")).istrue()); + wassert(actual(Ident("antani") <= Ident("blinda")).istrue()); + wassert(actual(Ident("antani") > Ident("blinda")).isfalse()); + wassert(actual(Ident("antani") >= Ident("blinda")).isfalse()); + wassert(actual(Ident("blinda") < Ident("antani")).isfalse()); + wassert(actual(Ident("blinda") <= Ident("antani")).isfalse()); + wassert(actual(Ident("blinda") > Ident("antani")).istrue()); + wassert(actual(Ident("blinda") >= Ident("antani")).istrue()); + }); + } +} test("core_defs"); + +} diff --git a/dballe/core/defs-tut.cc b/dballe/core/defs-tut.cc deleted file mode 100644 index fbb029df2..000000000 --- a/dballe/core/defs-tut.cc +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2005--2015 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "core/tests.h" -#include "core/defs.h" - -using namespace dballe; -using namespace wibble::tests; -using namespace std; - -namespace { - -typedef dballe::tests::test_group<> test_group; -typedef test_group::Test Test; -typedef test_group::Fixture Fixture; - -std::vector tests { - Test("ident", [](Fixture& f) { - wassert(actual(Ident()) == Ident()); - wassert(actual(Ident("foo")) == Ident("foo")); - wassert(actual(Ident().is_missing()).istrue()); - wassert(actual(Ident("foo").is_missing()).isfalse()); - Ident test; - wassert(actual((const char*)test == nullptr).istrue()); - test = "antani"; - wassert(actual((const char*)test) == "antani"); - Ident test1 = test; - wassert(actual(test) == Ident("antani")); - wassert(actual(test1) == Ident("antani")); - test1 = test1; - wassert(actual(test) == Ident("antani")); - wassert(actual(test1) == Ident("antani")); - test1 = Ident("blinda"); - wassert(actual(test) == Ident("antani")); - wassert(actual(test1) == Ident("blinda")); - test = move(test1); - wassert(actual(test) == Ident("blinda")); - wassert(actual(test1.is_missing()).istrue()); - Ident test2(move(test)); - wassert(actual(test.is_missing()).istrue()); - wassert(actual(test2) == Ident("blinda")); - test = string("foo"); - wassert(actual(test) == Ident("foo")); - wassert(actual(test2) == Ident("blinda")); - wassert(actual(Ident("foo") == Ident("foo")).istrue()); - wassert(actual(Ident("foo") <= Ident("foo")).istrue()); - wassert(actual(Ident("foo") >= Ident("foo")).istrue()); - wassert(actual(Ident("foo") == Ident("bar")).isfalse()); - wassert(actual(Ident("foo") != Ident("foo")).isfalse()); - wassert(actual(Ident("foo") != Ident("bar")).istrue()); - wassert(actual(Ident("antani") < Ident("blinda")).istrue()); - wassert(actual(Ident("antani") <= Ident("blinda")).istrue()); - wassert(actual(Ident("antani") > Ident("blinda")).isfalse()); - wassert(actual(Ident("antani") >= Ident("blinda")).isfalse()); - wassert(actual(Ident("blinda") < Ident("antani")).isfalse()); - wassert(actual(Ident("blinda") <= Ident("antani")).isfalse()); - wassert(actual(Ident("blinda") > Ident("antani")).istrue()); - wassert(actual(Ident("blinda") >= Ident("antani")).istrue()); - }), -}; - -test_group newtg("dballe_core_defs", tests); - -} diff --git a/dballe/core/file-test.cc b/dballe/core/file-test.cc new file mode 100644 index 000000000..79cc7f5ae --- /dev/null +++ b/dballe/core/file-test.cc @@ -0,0 +1,21 @@ +#include "core/tests.h" +#include "core/file.h" + +using namespace dballe; +using namespace dballe::tests; +using namespace std; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("empty", []() { + }); + } +} test("core_file"); + +} diff --git a/dballe/core/file-tut.cc b/dballe/core/file-tut.cc deleted file mode 100644 index 8c34e1ad1..000000000 --- a/dballe/core/file-tut.cc +++ /dev/null @@ -1,92 +0,0 @@ -/* - * DB-ALLe - Archive for punctual meteorological data - * - * Copyright (C) 2005,2006 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "core/tests.h" -#include "core/file.h" - -using namespace dballe; -using namespace std; - -namespace tut { - -struct file_shar -{ - file_shar() - { - } - - ~file_shar() - { - } -}; -TESTGRP(file); - -template<> template<> -void to::test<1>() -{ -} - -#if 0 -static void test_parse(const char* src, int line, const char* fname, struct bufr_match* m) -{ - bufr_message msg; - int found; - char* str; - int val; - - /* Create the message to test */ - CHECKED(bufr_message_create(&msg)); - - /* Resetting an empty message should do no harm */ - bufr_message_reset(msg); - - /* Read the data from file */ - CHECKED(bufr_file_read(file, msg, &found)); - tb_fail_unless(found == 1); - - /* Parse it */ - CHECKED(bufr_message_parse(msg)); - - /* Check the parsed values */ - test_bufr(src, line, msg, m); - - /* Try reencoding it */ - bufr_message_reset_encoded(msg); - CHECKED(bufr_message_encode(msg)); - CHECKED(bufrex_message_get_raw(msg, &str, &val)); - fail_unless(val != 0); - fail_unless(str[0] != 0); - - /* fprintf(stderr, "Encoded:\n - - -\n%.*s\n - - -\n", val, str); */ - - /* Try reparsing it */ - bufr_message_reset_decoded(msg); - CHECKED(bufr_message_parse(msg)); - - /* Check the reparsed values */ - test_bufr(src, line, msg, m); - - bufr_message_delete(msg); - bufr_file_delete(file); -} -#endif - -} diff --git a/dballe/core/json-test.cc b/dballe/core/json-test.cc new file mode 100644 index 000000000..a990f7eaa --- /dev/null +++ b/dballe/core/json-test.cc @@ -0,0 +1,105 @@ +#include "tests.h" +#include "json.h" + +using namespace std; +using namespace dballe; +using namespace dballe::tests; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("null", []() { + // null value + string out; + core::JSONWriter writer(out); + writer.add_null(); + wassert(actual(out) == "null"); + }); + add_method("bool", []() { + // bool value + string out; + core::JSONWriter writer(out); + + writer.add(true); + wassert(actual(out) == "true"); + + out.clear(); + writer.add(false); + wassert(actual(out) == "false"); + }); + add_method("int", []() { + // int value + string out; + core::JSONWriter writer(out); + + writer.add(1); + wassert(actual(out) == "1"); + + out.clear(); + writer.add(-1234567); + wassert(actual(out) == "-1234567"); + }); + add_method("double", []() { + // double value + string out; + core::JSONWriter writer(out); + writer.add(1.1); + wassert(actual(out) == "1.100000"); + + out.clear(); + writer.add(-1.1); + wassert(actual(out) == "-1.100000"); + + out.clear(); + writer.add(1.0); + wassert(actual(out) == "1.0"); + + out.clear(); + writer.add(-1.0); + wassert(actual(out) == "-1.0"); + }); + add_method("string", []() { + // string value + string out; + core::JSONWriter writer(out); + writer.add(""); + wassert(actual(out) == "\"\""); + + out.clear(); + writer.add("antani"); + wassert(actual(out) == "\"antani\""); + + out.clear(); + writer.add("\n"); + wassert(actual(out) == "\"\\n\""); + }); + add_method("list", []() { + // list value + string out; + core::JSONWriter writer(out); + writer.start_list(); + writer.add(""); + writer.add(1); + writer.add(1.0); + writer.end_list(); + wassert(actual(out) == "[\"\",1,1.0]"); + }); + add_method("mapping", []() { + // mapping value + string out; + core::JSONWriter writer(out); + writer.start_mapping(); + writer.add("", 1); + writer.add("antani", 1.0); + writer.end_mapping(); + wassert(actual(out) == "{\"\":1,\"antani\":1.0}"); + }); + }; +} test("core_json"); + +} diff --git a/dballe/core/json-tut.cc b/dballe/core/json-tut.cc deleted file mode 100644 index e3af2e13a..000000000 --- a/dballe/core/json-tut.cc +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2015 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Author: Enrico Zini - */ - -#include "tests.h" -#include "json.h" - -using namespace std; -using namespace wibble::tests; -using namespace dballe; - -namespace { - -typedef dballe::tests::test_group<> test_group; -typedef test_group::Test Test; -typedef test_group::Fixture Fixture; - -std::vector tests { - Test("null", [](Fixture& f) { - // null value - string out; - core::JSONWriter writer(out); - writer.add_null(); - wassert(actual(out) == "null"); - }), - Test("bool", [](Fixture& f) { - // bool value - string out; - core::JSONWriter writer(out); - - writer.add(true); - wassert(actual(out) == "true"); - - out.clear(); - writer.add(false); - wassert(actual(out) == "false"); - }), - Test("int", [](Fixture& f) { - // int value - string out; - core::JSONWriter writer(out); - - writer.add(1); - wassert(actual(out) == "1"); - - out.clear(); - writer.add(-1234567); - wassert(actual(out) == "-1234567"); - }), - Test("double", [](Fixture& f) { - // double value - string out; - core::JSONWriter writer(out); - writer.add(1.1); - wassert(actual(out) == "1.100000"); - - out.clear(); - writer.add(-1.1); - wassert(actual(out) == "-1.100000"); - - out.clear(); - writer.add(1.0); - wassert(actual(out) == "1.0"); - - out.clear(); - writer.add(-1.0); - wassert(actual(out) == "-1.0"); - }), - Test("string", [](Fixture& f) { - // string value - string out; - core::JSONWriter writer(out); - writer.add(""); - wassert(actual(out) == "\"\""); - - out.clear(); - writer.add("antani"); - wassert(actual(out) == "\"antani\""); - - out.clear(); - writer.add("\n"); - wassert(actual(out) == "\"\\n\""); - }), - Test("list", [](Fixture& f) { - // list value - string out; - core::JSONWriter writer(out); - writer.start_list(); - writer.add(""); - writer.add(1); - writer.add(1.0); - writer.end_list(); - wassert(actual(out) == "[\"\",1,1.0]"); - }), - Test("mapping", [](Fixture& f) { - // mapping value - string out; - core::JSONWriter writer(out); - writer.start_mapping(); - writer.add("", 1); - writer.add("antani", 1.0); - writer.end_mapping(); - wassert(actual(out) == "{\"\":1,\"antani\":1.0}"); - }), -}; - -test_group newtg("core_json", tests); - -} diff --git a/dballe/core/match-wreport-test.cc b/dballe/core/match-wreport-test.cc new file mode 100644 index 000000000..bb2246a23 --- /dev/null +++ b/dballe/core/match-wreport-test.cc @@ -0,0 +1,530 @@ +#include "tests.h" +#include "match-wreport.h" +#include "var.h" +#include +#include +#include +#include +#include + +#include +#include + +using namespace std; +using namespace dballe; +using namespace dballe::tests; +using namespace wreport; + +namespace { + +std::unique_ptr get_matcher(const char* q) +{ + return Matcher::create(*dballe::tests::query_from_string(q)); +} + +struct Fixture : public dballe::tests::Fixture +{ + Tables tables; + + Fixture() + { + tables.load_bufr(BufrTableID(0, 0, 0, 24, 0)); + } +}; + +class TestSubset : public FixtureTestCase +{ + using FixtureTestCase::FixtureTestCase; + + void register_tests() override + { + // Test var_id matcher + add_method("var_id", [](Fixture& f) { + auto m = get_matcher("context_id=1"); + + Subset s(f.tables); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + + s.store_variable_i(WR_VAR(0, 1, 1), 1); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + + s.back().seta(newvar(WR_VAR(0, 33, 195), 1)); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_YES); + }); + + // Test station_id matcher + add_method("station_id", [](Fixture& f) { + auto m = get_matcher("ana_id=1"); + + Tables tables; + tables.load_bufr(BufrTableID(200, 0, 0, 14, 1)); + + Subset s(tables); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + + s.store_variable_i(WR_VAR(0, 1, 1), 1); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + + s.store_variable_i(WR_VAR(0, 1, 192), 1); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_YES); + }); + + // Test station WMO matcher + add_method("station_wmo", [](Fixture& f) { + { + auto m = get_matcher("block=11"); + + Subset s(f.tables); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + + s.store_variable_i(WR_VAR(0, 1, 1), 1); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + + s.back().seti(11); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_YES); + + s.store_variable_i(WR_VAR(0, 1, 2), 222); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_YES); + } + + { + auto m = get_matcher("block=11, station=222"); + + Subset s(f.tables); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + + s.store_variable_i(WR_VAR(0, 1, 1), 1); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + + s.back().seti(11); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + + s.store_variable_i(WR_VAR(0, 1, 2), 22); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + + s.back().seti(222); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_YES); + + s[0].seti(1); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + + s[0] = var(WR_VAR(0, 1, 192)); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + } + }); + + // Test date matcher + add_method("date", [](Fixture& f) { + { + auto m = get_matcher("yearmin=2000"); + + Subset s(f.tables); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + + s.store_variable_i(WR_VAR(0, 4, 1), 1999); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + + s[0].seti(2000); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_YES); + } + { + auto m = get_matcher("yearmax=2000"); + + Subset s(f.tables); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + + s.store_variable_i(WR_VAR(0, 4, 1), 2001); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + + s[0].seti(2000); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_YES); + } + { + auto m = get_matcher("yearmin=2000, yearmax=2010"); + + Subset s(f.tables); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + + s.store_variable_i(WR_VAR(0, 4, 1), 1999); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + + s[0].seti(2011); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + + s[0].seti(2000); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_YES); + + s[0].seti(2005); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_YES); + + s[0].seti(2010); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_YES); + } + }); + + // Test coordinates matcher + add_method("coords", [](Fixture& f) { + { + auto m = get_matcher("latmin=45.00"); + + Subset s(f.tables); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + + s.store_variable_d(WR_VAR(0, 5, 1), 43.0); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + + s[0].setd(45.0); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_YES); + s[0].setd(46.0); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_YES); + } + { + auto m = get_matcher("latmax=45.00"); + + Subset s(f.tables); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + + s.store_variable_d(WR_VAR(0, 5, 1), 46.0); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + + s[0].setd(45.0); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_YES); + s[0].setd(44.0); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_YES); + } + { + auto m = get_matcher("lonmin=11.00, lonmax=180.0"); + + Subset s(f.tables); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + + s.store_variable_d(WR_VAR(0, 6, 1), 10.0); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + + s[0].setd(11.0); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_YES); + s[0].setd(12.0); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_YES); + } + { + auto m = get_matcher("lonmin=-180, lonmax=11.0"); + + Subset s(f.tables); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + + s.store_variable_d(WR_VAR(0, 6, 1), 12.0); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + + s[0].setd(11.0); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_YES); + s[0].setd(10.0); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_YES); + } + { + auto m = get_matcher("latmin=45.0, latmax=46.0, lonmin=10.0, lonmax=12.0"); + + Subset s(f.tables); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + + s.store_variable_d(WR_VAR(0, 5, 1), 45.5); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + + s.store_variable_d(WR_VAR(0, 6, 1), 13.0); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + + s[1].setd(11.0); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_YES); + } + }); + + // Test rep_memo matcher + add_method("rep_memo", [](Fixture& f) { + auto m = get_matcher("rep_memo=synop"); + + Tables tables; + tables.load_bufr(BufrTableID(200, 0, 0, 14, 1)); + + Subset s(tables); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + + wassert(s.store_variable_c(WR_VAR(0, 1, 194), "temp")); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_NO); + + s[0].setc("synop"); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_YES); + }); + + // Test empty matcher + add_method("empty", [](Fixture& f) { + std::unique_ptr m = Matcher::create(*Query::create()); + + Subset s(f.tables); + wassert(actual_matcher_result(m->match(MatchedSubset(s))) == matcher::MATCH_YES); + }); + } +} test1("core_match_wreport_subset"); + + +struct FixtureBulletin : public dballe::tests::Fixture +{ + BufrBulletin* bulletin = nullptr; + + ~FixtureBulletin() { delete bulletin; } + + BufrBulletin& get_bulletin() + { + delete bulletin; + bulletin = BufrBulletin::create().release(); + BufrBulletin& b = *bulletin; + b.edition_number = 4; + b.originating_centre = 200; + b.originating_subcentre = 0; + b.master_table_version_number = 14; + b.master_table_version_number_local = 0; + b.load_tables(); + return b; + } +}; + +class TestBulletin : public FixtureTestCase +{ + using FixtureTestCase::FixtureTestCase; + + void register_tests() override + { + // Test var_id matcher + add_method("var_id", [](Fixture& f) { + auto m = get_matcher("context_id=1"); + + BufrBulletin& b = f.get_bulletin(); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(1).store_variable_i(WR_VAR(0, 1, 1), 1); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(1).back().seta(newvar(WR_VAR(0, 33, 195), 1)); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_YES); + }); + + // Test station_id matcher + add_method("station_id", [](Fixture& f) { + auto m = get_matcher("ana_id=1"); + + BufrBulletin& b = f.get_bulletin(); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(1).store_variable_i(WR_VAR(0, 1, 1), 1); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(0).store_variable_i(WR_VAR(0, 1, 192), 1); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_YES); + }); + + // Test station WMO matcher + add_method("station_wmo", [](Fixture& f) { + { + auto m = get_matcher("block=11"); + + BufrBulletin& b = f.get_bulletin(); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(1).store_variable_i(WR_VAR(0, 1, 1), 1); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(1).back().seti(11); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_YES); + + b.obtain_subset(1).store_variable_i(WR_VAR(0, 1, 2), 222); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_YES); + } + + { + auto m = get_matcher("block=11, station=222"); + + BufrBulletin& b = f.get_bulletin(); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(0).store_variable_i(WR_VAR(0, 1, 1), 1); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(0).back().seti(11); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(0).store_variable_i(WR_VAR(0, 1, 2), 22); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(0).back().seti(222); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_YES); + + b.obtain_subset(0)[0].seti(1); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(0)[0] = var(WR_VAR(0, 1, 192)); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + } + + // Valid block and station must be in the same subset + { + auto m = get_matcher("block=11, station=222"); + + BufrBulletin& b = f.get_bulletin(); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(0).store_variable_i(WR_VAR(0, 1, 1), 11); + b.obtain_subset(1).store_variable_i(WR_VAR(0, 1, 2), 222); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(0).store_variable_i(WR_VAR(0, 1, 2), 222); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_YES); + } + }); + + // Test date matcher + add_method("date", [](Fixture& f) { + { + auto m = get_matcher("yearmin=2000"); + + BufrBulletin& b = f.get_bulletin(); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(0).store_variable_i(WR_VAR(0, 4, 1), 1999); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(0)[0].seti(2000); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_YES); + } + { + auto m = get_matcher("yearmax=2000"); + + BufrBulletin& b = f.get_bulletin(); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(0).store_variable_i(WR_VAR(0, 4, 1), 2001); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(0)[0].seti(2000); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_YES); + } + { + auto m = get_matcher("yearmin=2000, yearmax=2010"); + + BufrBulletin& b = f.get_bulletin(); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(0).store_variable_i(WR_VAR(0, 4, 1), 1999); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(0)[0].seti(2011); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(0)[0].seti(2000); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_YES); + + b.obtain_subset(0)[0].seti(2005); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_YES); + + b.obtain_subset(0)[0].seti(2010); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_YES); + } + }); + + // Test coordinates matcher + add_method("coords", [](Fixture& f) { + { + auto m = get_matcher("latmin=45.0"); + + BufrBulletin& b = f.get_bulletin(); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(1).store_variable_d(WR_VAR(0, 5, 1), 43.0); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(1)[0].setd(45.0); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_YES); + b.obtain_subset(1)[0].setd(46.0); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_YES); + } + { + auto m = get_matcher("latmax=45.0"); + + BufrBulletin& b = f.get_bulletin(); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(1).store_variable_d(WR_VAR(0, 5, 1), 46.0); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(1)[0].setd(45.0); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_YES); + b.obtain_subset(1)[0].setd(44.0); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_YES); + } + { + auto m = get_matcher("lonmin=11.00, lonmax=180.0"); + + BufrBulletin& b = f.get_bulletin(); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(0).store_variable_d(WR_VAR(0, 6, 1), 10.0); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(0)[0].setd(11.0); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_YES); + b.obtain_subset(0)[0].setd(12.0); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_YES); + } + { + auto m = get_matcher("lonmin=-180, lonmax=11.0"); + + BufrBulletin& b = f.get_bulletin(); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(0).store_variable_d(WR_VAR(0, 6, 1), 12.0); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(0)[0].setd(11.0); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_YES); + b.obtain_subset(0)[0].setd(10.0); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_YES); + } + { + auto m = get_matcher("latmin=45.0, latmax=46.0, lonmin=10.0, lonmax=12.0"); + + BufrBulletin& b = f.get_bulletin(); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(0).store_variable_d(WR_VAR(0, 5, 1), 45.5); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(0).store_variable_d(WR_VAR(0, 6, 1), 13.0); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(0)[1].setd(11.0); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_YES); + } + }); + + // Test rep_memo matcher + add_method("rep_memo", [](Fixture& f) { + auto m = get_matcher("rep_memo=synop"); + + BufrBulletin& b = f.get_bulletin(); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(0).store_variable_c(WR_VAR(0, 1, 194), "temp"); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_NO); + + b.obtain_subset(0)[0].setc("synop"); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_YES); + }); + + // Test empty matcher + add_method("empty", [](Fixture& f) { + std::unique_ptr m = Matcher::create(*Query::create()); + + BufrBulletin& b = f.get_bulletin(); + wassert(actual_matcher_result(m->match(MatchedBulletin(b))) == matcher::MATCH_YES); + }); + } +} test2("core_match_wreport_bulletin"); + +} diff --git a/dballe/core/match-wreport-tut.cc b/dballe/core/match-wreport-tut.cc deleted file mode 100644 index 1812a3fde..000000000 --- a/dballe/core/match-wreport-tut.cc +++ /dev/null @@ -1,537 +0,0 @@ -#include "tests.h" -#include "match-wreport.h" -#include "var.h" -#include -#include -#include -#include - -#include -#include - -using namespace std; -using namespace dballe; -using namespace wreport; - -namespace tut { - -struct match_wreport_shar { - BufrBulletin* bulletin; - Tables tables; - - match_wreport_shar() - : bulletin(0) - { - tables.load_bufr(BufrTableID(0, 0, 0, 24, 0)); - } - ~match_wreport_shar() - { - if (bulletin) delete bulletin; - } - BufrBulletin& init() - { - if (bulletin) delete bulletin; - bulletin = BufrBulletin::create().release(); - BufrBulletin& b = *bulletin; - b.edition_number = 4; - b.originating_centre = 200; - b.originating_subcentre = 0; - b.master_table_version_number = 14; - b.master_table_version_number_local = 0; - b.load_tables(); - return b; - } - - std::unique_ptr get_matcher(const char* q) - { - return Matcher::create(*dballe::tests::query_from_string(q)); - } -}; -TESTGRP(match_wreport); - -// Test var_id matcher -template<> template<> -void to::test<1>() -{ - auto m = get_matcher("context_id=1"); - - Subset s(tables); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - - s.store_variable_i(WR_VAR(0, 1, 1), 1); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - - s.back().seta(newvar(WR_VAR(0, 33, 195), 1)); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_YES); -} - -// Test station_id matcher -template<> template<> -void to::test<2>() -{ - auto m = get_matcher("ana_id=1"); - - Tables tables; - tables.load_bufr(BufrTableID(200, 0, 0, 14, 1)); - - Subset s(tables); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - - s.store_variable_i(WR_VAR(0, 1, 1), 1); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - - s.store_variable_i(WR_VAR(0, 1, 192), 1); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_YES); -} - -// Test station WMO matcher -template<> template<> -void to::test<3>() -{ - { - auto m = get_matcher("block=11"); - - Subset s(tables); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - - s.store_variable_i(WR_VAR(0, 1, 1), 1); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - - s.back().seti(11); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_YES); - - s.store_variable_i(WR_VAR(0, 1, 2), 222); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_YES); - } - - { - auto m = get_matcher("block=11, station=222"); - - Subset s(tables); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - - s.store_variable_i(WR_VAR(0, 1, 1), 1); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - - s.back().seti(11); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - - s.store_variable_i(WR_VAR(0, 1, 2), 22); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - - s.back().seti(222); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_YES); - - s[0].seti(1); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - - s[0] = var(WR_VAR(0, 1, 192)); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - } -} - -// Test date matcher -template<> template<> -void to::test<4>() -{ - { - auto m = get_matcher("yearmin=2000"); - - Subset s(tables); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - - s.store_variable_i(WR_VAR(0, 4, 1), 1999); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - - s[0].seti(2000); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_YES); - } - { - auto m = get_matcher("yearmax=2000"); - - Subset s(tables); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - - s.store_variable_i(WR_VAR(0, 4, 1), 2001); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - - s[0].seti(2000); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_YES); - } - { - auto m = get_matcher("yearmin=2000, yearmax=2010"); - - Subset s(tables); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - - s.store_variable_i(WR_VAR(0, 4, 1), 1999); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - - s[0].seti(2011); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - - s[0].seti(2000); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_YES); - - s[0].seti(2005); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_YES); - - s[0].seti(2010); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_YES); - } -} - -// Test coordinates matcher -template<> template<> -void to::test<5>() -{ - { - auto m = get_matcher("latmin=45.00"); - - Subset s(tables); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - - s.store_variable_d(WR_VAR(0, 5, 1), 43.0); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - - s[0].setd(45.0); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_YES); - s[0].setd(46.0); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_YES); - } - { - auto m = get_matcher("latmax=45.00"); - - Subset s(tables); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - - s.store_variable_d(WR_VAR(0, 5, 1), 46.0); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - - s[0].setd(45.0); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_YES); - s[0].setd(44.0); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_YES); - } - { - auto m = get_matcher("lonmin=11.00, lonmax=180.0"); - - Subset s(tables); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - - s.store_variable_d(WR_VAR(0, 6, 1), 10.0); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - - s[0].setd(11.0); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_YES); - s[0].setd(12.0); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_YES); - } - { - auto m = get_matcher("lonmin=-180, lonmax=11.0"); - - Subset s(tables); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - - s.store_variable_d(WR_VAR(0, 6, 1), 12.0); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - - s[0].setd(11.0); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_YES); - s[0].setd(10.0); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_YES); - } - { - auto m = get_matcher("latmin=45.0, latmax=46.0, lonmin=10.0, lonmax=12.0"); - - Subset s(tables); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - - s.store_variable_d(WR_VAR(0, 5, 1), 45.5); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - - s.store_variable_d(WR_VAR(0, 6, 1), 13.0); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - - s[1].setd(11.0); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_YES); - } -} - -// Test rep_memo matcher -template<> template<> -void to::test<6>() -{ - auto m = get_matcher("rep_memo=synop"); - - Tables tables; - tables.load_bufr(BufrTableID(200, 0, 0, 14, 1)); - - Subset s(tables); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - - s.store_variable_c(WR_VAR(0, 1, 194), "temp"); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_NO); - - s[0].setc("synop"); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_YES); -} - -// Test empty matcher -template<> template<> -void to::test<7>() -{ - std::unique_ptr m = Matcher::create(*Query::create()); - - Subset s(tables); - ensure(m->match(MatchedSubset(s)) == matcher::MATCH_YES); -} - -// Test var_id matcher -template<> template<> -void to::test<8>() -{ - auto m = get_matcher("context_id=1"); - - BufrBulletin& b = init(); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(1).store_variable_i(WR_VAR(0, 1, 1), 1); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(1).back().seta(newvar(WR_VAR(0, 33, 195), 1)); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_YES); -} - -// Test station_id matcher -template<> template<> -void to::test<9>() -{ - auto m = get_matcher("ana_id=1"); - - BufrBulletin& b = init(); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(1).store_variable_i(WR_VAR(0, 1, 1), 1); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(0).store_variable_i(WR_VAR(0, 1, 192), 1); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_YES); -} - -// Test station WMO matcher -template<> template<> -void to::test<10>() -{ - { - auto m = get_matcher("block=11"); - - BufrBulletin& b = init(); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(1).store_variable_i(WR_VAR(0, 1, 1), 1); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(1).back().seti(11); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_YES); - - b.obtain_subset(1).store_variable_i(WR_VAR(0, 1, 2), 222); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_YES); - } - - { - auto m = get_matcher("block=11, station=222"); - - BufrBulletin& b = init(); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(0).store_variable_i(WR_VAR(0, 1, 1), 1); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(0).back().seti(11); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(0).store_variable_i(WR_VAR(0, 1, 2), 22); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(0).back().seti(222); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_YES); - - b.obtain_subset(0)[0].seti(1); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(0)[0] = var(WR_VAR(0, 1, 192)); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - } - - // Valid block and station must be in the same subset - { - auto m = get_matcher("block=11, station=222"); - - BufrBulletin& b = init(); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(0).store_variable_i(WR_VAR(0, 1, 1), 11); - b.obtain_subset(1).store_variable_i(WR_VAR(0, 1, 2), 222); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(0).store_variable_i(WR_VAR(0, 1, 2), 222); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_YES); - } -} - -// Test date matcher -template<> template<> -void to::test<11>() -{ - { - auto m = get_matcher("yearmin=2000"); - - BufrBulletin& b = init(); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(0).store_variable_i(WR_VAR(0, 4, 1), 1999); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(0)[0].seti(2000); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_YES); - } - { - auto m = get_matcher("yearmax=2000"); - - BufrBulletin& b = init(); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(0).store_variable_i(WR_VAR(0, 4, 1), 2001); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(0)[0].seti(2000); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_YES); - } - { - auto m = get_matcher("yearmin=2000, yearmax=2010"); - - BufrBulletin& b = init(); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(0).store_variable_i(WR_VAR(0, 4, 1), 1999); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(0)[0].seti(2011); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(0)[0].seti(2000); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_YES); - - b.obtain_subset(0)[0].seti(2005); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_YES); - - b.obtain_subset(0)[0].seti(2010); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_YES); - } -} - -// Test coordinates matcher -template<> template<> -void to::test<12>() -{ - { - auto m = get_matcher("latmin=45.0"); - - BufrBulletin& b = init(); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(1).store_variable_d(WR_VAR(0, 5, 1), 43.0); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(1)[0].setd(45.0); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_YES); - b.obtain_subset(1)[0].setd(46.0); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_YES); - } - { - auto m = get_matcher("latmax=45.0"); - - BufrBulletin& b = init(); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(1).store_variable_d(WR_VAR(0, 5, 1), 46.0); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(1)[0].setd(45.0); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_YES); - b.obtain_subset(1)[0].setd(44.0); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_YES); - } - { - auto m = get_matcher("lonmin=11.00, lonmax=180.0"); - - BufrBulletin& b = init(); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(0).store_variable_d(WR_VAR(0, 6, 1), 10.0); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(0)[0].setd(11.0); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_YES); - b.obtain_subset(0)[0].setd(12.0); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_YES); - } - { - auto m = get_matcher("lonmin=-180, lonmax=11.0"); - - BufrBulletin& b = init(); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(0).store_variable_d(WR_VAR(0, 6, 1), 12.0); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(0)[0].setd(11.0); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_YES); - b.obtain_subset(0)[0].setd(10.0); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_YES); - } - { - auto m = get_matcher("latmin=45.0, latmax=46.0, lonmin=10.0, lonmax=12.0"); - - BufrBulletin& b = init(); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(0).store_variable_d(WR_VAR(0, 5, 1), 45.5); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(0).store_variable_d(WR_VAR(0, 6, 1), 13.0); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(0)[1].setd(11.0); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_YES); - } -} - -// Test rep_memo matcher -template<> template<> -void to::test<13>() -{ - auto m = get_matcher("rep_memo=synop"); - - BufrBulletin& b = init(); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(0).store_variable_c(WR_VAR(0, 1, 194), "temp"); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_NO); - - b.obtain_subset(0)[0].setc("synop"); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_YES); -} - -// Test empty matcher -template<> template<> -void to::test<14>() -{ - std::unique_ptr m = Matcher::create(*Query::create()); - - BufrBulletin& b = init(); - ensure(m->match(MatchedBulletin(b)) == matcher::MATCH_YES); -} - -} diff --git a/dballe/core/matcher-test.cc b/dballe/core/matcher-test.cc new file mode 100644 index 000000000..b91a339c1 --- /dev/null +++ b/dballe/core/matcher-test.cc @@ -0,0 +1,247 @@ +#include "tests.h" +#include "matcher.h" +#include "record.h" + +#include +#include + +using namespace std; +using namespace dballe; +using namespace dballe::core; +using namespace dballe::tests; + +namespace { + +std::unique_ptr get_matcher(const char* q) +{ + return Matcher::create(*dballe::tests::query_from_string(q)); +} + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + // Test var_id matcher + add_method("var_id", []() { + auto m = get_matcher("context_id=1"); + + core::Record matched; + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + + matched.set("data_id", 2); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + + matched.set("data_id", 1); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_YES); + }); + + // Test station_id matcher + add_method("station_id", []() { + auto m = get_matcher("ana_id=1"); + + core::Record matched; + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + + matched.set("ana_id", 2); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + + matched.set("ana_id", 1); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_YES); + }); + + // Test station WMO matcher + add_method("station_wmo", []() { + { + auto m = get_matcher("block=11"); + + core::Record matched; + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + + matched.set("block", 1); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + + matched.set("block", 11); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_YES); + + matched.set("station", 222); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_YES); + } + + { + auto m = get_matcher("block=11, station=222"); + + core::Record matched; + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + + matched.set("block", 1); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + + matched.set("block", 11); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + + matched.set("station", 22); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + + matched.set("station", 222); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_YES); + + matched.set("block", 1); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + + matched.unset("block"); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + } + }); + + // Test date matcher + add_method("date", []() { + { + auto m = get_matcher("yearmin=2000"); + + core::Record matched; + wassert(actual(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + + matched.set("year", 1999); + wassert(actual(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + + matched.set("year", 2000); + wassert(actual(m->match(MatchedRecord(matched))) == matcher::MATCH_YES); + } + { + auto m = get_matcher("yearmax=2000"); + + core::Record matched; + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + + matched.set("year", 2001); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + + matched.set("year", 2000); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_YES); + } + { + auto m = get_matcher("yearmin=2000, yearmax=2010"); + + core::Record matched; + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + + matched.set("year", 1999); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + + matched.set("year", 2011); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + + matched.set("year", 2000); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_YES); + + matched.set("year", 2005); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_YES); + + matched.set("year", 2010); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_YES); + } + }); + + // Test coordinates matcher + add_method("coords", []() { + { + auto m = get_matcher("latmin=45.00"); + + core::Record matched; + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + + matched.set("lat", 43.0); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + + matched.set("lat", 45.0); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_YES); + matched.set("lat", 46.0); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_YES); + } + { + auto m = get_matcher("latmax=45.00"); + + core::Record matched; + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + + matched.set("lat", 4600000); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + + matched.set("lat", 4500000); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_YES); + matched.set("lat", 4400000); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_YES); + } + { + auto m = get_matcher("lonmin=45.00, lonmax=180.0"); + + core::Record matched; + wassert(actual(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + + matched.set("lon", 4300000); + wassert(actual(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + + matched.set("lon", 4500000); + wassert(actual(m->match(MatchedRecord(matched))) == matcher::MATCH_YES); + matched.set("lon", 4500000); + wassert(actual(m->match(MatchedRecord(matched))) == matcher::MATCH_YES); + } + { + auto m = get_matcher("lonmin=-180, lonmax=45.0"); + + core::Record matched; + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + + matched.set("lon", 4600000); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + + matched.set("lon", 4500000); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_YES); + matched.set("lon", 4400000); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_YES); + } + { + auto m = get_matcher("latmin=45.0, latmax=46.0, lonmin=10.0, lonmax=12.0"); + + core::Record matched; + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + + matched.set("lat", 4550000); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + + matched.set("lon", 1300000); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + + matched.set("lon", 1100000); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_YES); + } + }); + + // Test rep_memo matcher + add_method("rep_memo", []() { + auto m = get_matcher("rep_memo=synop"); + + core::Record matched; + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + + matched.set("rep_memo", "temp"); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); + + matched.set("rep_memo", "synop"); + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_YES); + }); + + // Test empty matcher + add_method("empty", []() { + auto query = dballe::Query::create(); + std::unique_ptr m = Matcher::create(*query); + + core::Record matched; + wassert(actual_matcher_result(m->match(MatchedRecord(matched))) == matcher::MATCH_YES); + }); + } +} test("core_matcher"); + +} diff --git a/dballe/core/matcher-tut.cc b/dballe/core/matcher-tut.cc deleted file mode 100644 index f399d351f..000000000 --- a/dballe/core/matcher-tut.cc +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright (C) 2009--2015 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Author: Enrico Zini - */ - -#include "tests.h" -#include "matcher.h" -#include "record.h" - -#include -#include - -using namespace std; -using namespace dballe; -using namespace dballe::core; -using namespace wibble::tests; - -namespace tut { - -struct matcher_shar { - std::unique_ptr get_matcher(const char* q) - { - return Matcher::create(*dballe::tests::query_from_string(q)); - } -}; -TESTGRP(matcher); - -// Test var_id matcher -template<> template<> -void to::test<1>() -{ - auto m = get_matcher("context_id=1"); - - core::Record matched; - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_NO); - - matched.set("data_id", 2); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_NO); - - matched.set("data_id", 1); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_YES); -} - -// Test station_id matcher -template<> template<> -void to::test<2>() -{ - auto m = get_matcher("ana_id=1"); - - core::Record matched; - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_NO); - - matched.set("ana_id", 2); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_NO); - - matched.set("ana_id", 1); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_YES); -} - -// Test station WMO matcher -template<> template<> -void to::test<3>() -{ - { - auto m = get_matcher("block=11"); - - core::Record matched; - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_NO); - - matched.set("block", 1); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_NO); - - matched.set("block", 11); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_YES); - - matched.set("station", 222); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_YES); - } - - { - auto m = get_matcher("block=11, station=222"); - - core::Record matched; - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_NO); - - matched.set("block", 1); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_NO); - - matched.set("block", 11); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_NO); - - matched.set("station", 22); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_NO); - - matched.set("station", 222); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_YES); - - matched.set("block", 1); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_NO); - - matched.unset("block"); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_NO); - } -} - -// Test date matcher -template<> template<> -void to::test<4>() -{ - { - auto m = get_matcher("yearmin=2000"); - - core::Record matched; - wassert(actual(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); - - matched.set("year", 1999); - wassert(actual(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); - - matched.set("year", 2000); - wassert(actual(m->match(MatchedRecord(matched))) == matcher::MATCH_YES); - } - { - auto m = get_matcher("yearmax=2000"); - - core::Record matched; - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_NO); - - matched.set("year", 2001); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_NO); - - matched.set("year", 2000); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_YES); - } - { - auto m = get_matcher("yearmin=2000, yearmax=2010"); - - core::Record matched; - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_NO); - - matched.set("year", 1999); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_NO); - - matched.set("year", 2011); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_NO); - - matched.set("year", 2000); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_YES); - - matched.set("year", 2005); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_YES); - - matched.set("year", 2010); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_YES); - } -} - -// Test coordinates matcher -template<> template<> -void to::test<5>() -{ - { - auto m = get_matcher("latmin=45.00"); - - core::Record matched; - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_NO); - - matched.set("lat", 43.0); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_NO); - - matched.set("lat", 45.0); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_YES); - matched.set("lat", 46.0); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_YES); - } - { - auto m = get_matcher("latmax=45.00"); - - core::Record matched; - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_NO); - - matched.set("lat", 4600000); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_NO); - - matched.set("lat", 4500000); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_YES); - matched.set("lat", 4400000); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_YES); - } - { - auto m = get_matcher("lonmin=45.00, lonmax=180.0"); - - core::Record matched; - wassert(actual(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); - - matched.set("lon", 4300000); - wassert(actual(m->match(MatchedRecord(matched))) == matcher::MATCH_NO); - - matched.set("lon", 4500000); - wassert(actual(m->match(MatchedRecord(matched))) == matcher::MATCH_YES); - matched.set("lon", 4500000); - wassert(actual(m->match(MatchedRecord(matched))) == matcher::MATCH_YES); - } - { - auto m = get_matcher("lonmin=-180, lonmax=45.0"); - - core::Record matched; - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_NO); - - matched.set("lon", 4600000); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_NO); - - matched.set("lon", 4500000); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_YES); - matched.set("lon", 4400000); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_YES); - } - { - auto m = get_matcher("latmin=45.0, latmax=46.0, lonmin=10.0, lonmax=12.0"); - - core::Record matched; - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_NO); - - matched.set("lat", 4550000); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_NO); - - matched.set("lon", 1300000); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_NO); - - matched.set("lon", 1100000); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_YES); - } -} - -// Test rep_memo matcher -template<> template<> -void to::test<6>() -{ - auto m = get_matcher("rep_memo=synop"); - - core::Record matched; - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_NO); - - matched.set("rep_memo", "temp"); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_NO); - - matched.set("rep_memo", "synop"); - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_YES); -} - -// Test empty matcher -template<> template<> -void to::test<7>() -{ - auto query = dballe::Query::create(); - std::unique_ptr m = Matcher::create(*query); - - core::Record matched; - ensure(m->match(MatchedRecord(matched)) == matcher::MATCH_YES); -} - -} - -// vim:set ts=4 sw=4: diff --git a/dballe/core/matcher.cc b/dballe/core/matcher.cc index 3ffff3bfa..f773dba93 100644 --- a/dballe/core/matcher.cc +++ b/dballe/core/matcher.cc @@ -55,6 +55,17 @@ matcher::Result Matched::lon_in_range(int val, int min, int max) namespace matcher { +std::string result_format(Result res) +{ + switch (res) + { + case MATCH_YES: return "yes"; + case MATCH_NO: return "no"; + case MATCH_NA: return "n/a"; + } + return "unknown"; +} + struct And : public Matcher { /** diff --git a/dballe/core/matcher.h b/dballe/core/matcher.h index 0efcf5d96..1d98c4a6f 100644 --- a/dballe/core/matcher.h +++ b/dballe/core/matcher.h @@ -16,6 +16,9 @@ enum Result { MATCH_NA // Match not applicable to this item }; +/// Format a Result into a string +std::string result_format(Result res); + } /** diff --git a/dballe/core/query-test.cc b/dballe/core/query-test.cc new file mode 100644 index 000000000..261565cd8 --- /dev/null +++ b/dballe/core/query-test.cc @@ -0,0 +1,203 @@ +#include "core/tests.h" +#include "core/query.h" +#include "core/record.h" +#include + +using namespace std; +using namespace dballe; +using namespace dballe::tests; +using namespace wreport; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("all_unset", []() { + core::Query q; + wassert(actual(q.ana_id) == MISSING_INT); + wassert(actual(q.prio_min) == MISSING_INT); + wassert(actual(q.prio_max) == MISSING_INT); + wassert(actual(q.rep_memo) == ""); + wassert(actual(q.mobile) == MISSING_INT); + wassert(actual(q.ident.is_missing()).istrue()); + wassert(actual(q.latrange) == LatRange()); + wassert(actual(q.lonrange) == LonRange()); + wassert(actual(q.datetime) == DatetimeRange()); + wassert(actual(q.level) == Level()); + wassert(actual(q.trange) == Trange()); + wassert(actual(q.varcodes.size()) == 0); + wassert(actual(q.query) == ""); + wassert(actual(q.ana_filter) == ""); + wassert(actual(q.data_filter) == ""); + wassert(actual(q.attr_filter) == ""); + wassert(actual(q.limit) == MISSING_INT); + wassert(actual(q.block) == MISSING_INT); + wassert(actual(q.station) == MISSING_INT); + wassert(actual(q.data_id) == MISSING_INT); + }); + add_method("all_set", []() { + core::Record rec; + rec.set("ana_id", 4); + rec.set("priority", 1); + rec.set("rep_memo", "foo"); + rec.set("mobile", 0); + rec.set("ident", "bar"); + rec.set("lat", 44.123); + rec.set("lon", 11.123); + rec.set(Datetime(2000, 1, 2, 12, 30, 45)); + rec.set(Level(10, 11, 12, 13)); + rec.set(Trange(20, 21, 22)); + rec.set("var", "B12101"); + rec.set("query", "best"); + rec.set("ana_filter", "B01001=1"); + rec.set("data_filter", "B12101>260"); + rec.set("attr_filter", "B33007>50"); + rec.set("limit", 100); + rec.set("block", 16); + rec.set("station", 404); + rec.set("context_id", 42); + + core::Query q; + q.set_from_record(rec); + wassert(actual(q.ana_id) == 4); + wassert(actual(q.prio_min) == 1); + wassert(actual(q.prio_max) == 1); + wassert(actual(q.rep_memo) == "foo"); + wassert(actual(q.mobile) == 0); + wassert(actual(q.ident.is_missing()).isfalse()); + wassert(actual(q.ident.get()) == "bar"); + wassert(actual(q.latrange) == LatRange(44.123, 44.123)); + wassert(actual(q.lonrange) == LonRange(11.123, 11.123)); + wassert(actual(q.datetime) == DatetimeRange(2000, 1, 2, 12, 30, 45, 2000, 1, 2, 12, 30, 45)); + wassert(actual(q.level) == Level(10, 11, 12, 13)); + wassert(actual(q.trange) == Trange(20, 21, 22)); + wassert(actual(q.varcodes.size()) == 1); + wassert(actual(*q.varcodes.begin()) == WR_VAR(0, 12, 101)); + wassert(actual(q.query) == "best"); + wassert(actual(q.ana_filter) == "B01001=1"); + wassert(actual(q.data_filter) == "B12101>260"); + wassert(actual(q.attr_filter) == "B33007>50"); + wassert(actual(q.limit) == 100); + wassert(actual(q.block) == 16); + wassert(actual(q.station) == 404); + wassert(actual(q.data_id) == 42); + }); + add_method("prio", []() { + core::Query q; + q.set_from_test_string("priority=11"); + wassert(actual(q.prio_min) == 11); + wassert(actual(q.prio_max) == 11); + + q.clear(); + q.set_from_test_string("priomin=12"); + wassert(actual(q.prio_min) == 12); + wassert(actual(q.prio_max) == MISSING_INT); + + q.clear(); + q.set_from_test_string("priomax=12"); + wassert(actual(q.prio_min) == MISSING_INT); + wassert(actual(q.prio_max) == 12); + + q.clear(); + q.set_from_test_string("priomin=11, priomax=22"); + wassert(actual(q.prio_min) == 11); + wassert(actual(q.prio_max) == 22); + + q.clear(); + q.set_from_test_string("priomin=11, priomax=22, priority=16"); + wassert(actual(q.prio_min) == 16); + wassert(actual(q.prio_max) == 16); + }); + add_method("lat", []() { + core::Query q; + q.set_from_test_string("lat=45.0"); + wassert(actual(q.latrange) == LatRange(45.0, 45.0)); + + q.clear(); + q.set_from_test_string("latmin=45.0"); + wassert(actual(q.latrange) == LatRange(45.0, LatRange::DMAX)); + + q.clear(); + q.set_from_test_string("latmax=45.0"); + wassert(actual(q.latrange) == LatRange(LatRange::DMIN, 45.0)); + + q.clear(); + q.set_from_test_string("latmin=40.0, latmax=50.0"); + wassert(actual(q.latrange) == LatRange(40.0, 50.0)); + + q.clear(); + q.set_from_test_string("latmin=40.0, latmax=50.0, lat=42.0"); + wassert(actual(q.latrange) == LatRange(42.0, 42.0)); + }); + add_method("lon", []() { + core::Query q; + q.set_from_test_string("lon=45.0"); + wassert(actual(q.lonrange) == LonRange(45.0, 45.0)); + + q.clear(); + try { + q.set_from_test_string("lonmin=45.0"); + wassert(actual(false).istrue()); + } catch (std::exception& e) { + wassert(actual(e.what()).contains("open ended range")); + } + wassert(actual(q.lonrange) == LonRange()); + + q.clear(); + q.set_from_test_string("lonmin=40.0, lonmax=50.0"); + wassert(actual(q.lonrange) == LonRange(40.0, 50.0)); + + q.clear(); + q.set_from_test_string("lonmin=40.0, lonmax=50.0, lon=42.0"); + wassert(actual(q.lonrange) == LonRange(42.0, 42.0)); + }); + add_method("datetime", []() { + core::Query q; + q.set_from_test_string("year=2015"); + wassert(actual(q.datetime) == DatetimeRange(2015, 1, 1, 0 , 0, 0, 2015, 12, 31, 23, 59, 59)); + + q.clear(); + q.set_from_test_string("year=2015, monthmin=1, monthmax=2"); + wassert(actual(q.datetime) == DatetimeRange(2015, 1, 1, 0 , 0, 0, 2015, 2, 28, 23, 59, 59)); + + q.clear(); + q.set_from_test_string("year=2015, monthmin=2, day=28"); + wassert(actual(q.datetime) == DatetimeRange(2015, 2, 28, 0 , 0, 0, 2015, 12, 28, 23, 59, 59)); + + q.clear(); + q.set_from_test_string("yearmin=2000, yearmax=2012, month=2, min=30"); + wassert(actual(q.datetime) == DatetimeRange(2000, 2, 1, 0, 30, 0, 2012, 2, 29, 23, 30, 59)); + + q.clear(); + q.set_from_test_string("yearmin=2010, yearmax=2012, year=2000, month=2, min=30"); + wassert(actual(q.datetime) == DatetimeRange(2000, 2, 1, 0, 30, 0, 2000, 2, 29, 23, 30, 59)); + }); + add_method("varlist", []() { + core::Query q; + q.set_from_test_string("var=B12101"); + wassert(actual(q.varcodes.size()) == 1); + wassert(actual(*q.varcodes.begin()) == WR_VAR(0, 12, 101)); + + q.clear(); + q.set_from_test_string("varlist=B12101,B01001"); + wassert(actual(q.varcodes.size()) == 2); + wassert(actual(*q.varcodes.begin()) == WR_VAR(0, 1, 1)); + wassert(actual(*q.varcodes.rbegin()) == WR_VAR(0, 12, 101)); + + q.clear(); + q.set_from_test_string("varlist=B12101,B01001, var=B12102"); + wassert(actual(q.varcodes.size()) == 1); + wassert(actual(*q.varcodes.begin()) == WR_VAR(0, 12, 102)); + }); + add_method("modifiers", []() { + wassert(actual(core::Query::parse_modifiers("best")) == DBA_DB_MODIFIER_BEST); + wassert(actual(core::Query::parse_modifiers("details")) == DBA_DB_MODIFIER_SUMMARY_DETAILS); + }); + } +} test("core_query"); + +} diff --git a/dballe/core/query-tut.cc b/dballe/core/query-tut.cc deleted file mode 100644 index da4579a19..000000000 --- a/dballe/core/query-tut.cc +++ /dev/null @@ -1,203 +0,0 @@ -#include "core/tests.h" -#include "core/query.h" -#include "core/record.h" -#include - -using namespace std; -using namespace wibble::tests; -using namespace dballe; -using namespace wreport; - -namespace { - -typedef dballe::tests::test_group<> test_group; -typedef test_group::Test Test; -typedef test_group::Fixture Fixture; - -std::vector tests { - Test("all_unset", [](Fixture& f) { - core::Query q; - wassert(actual(q.ana_id) == MISSING_INT); - wassert(actual(q.prio_min) == MISSING_INT); - wassert(actual(q.prio_max) == MISSING_INT); - wassert(actual(q.rep_memo) == ""); - wassert(actual(q.mobile) == MISSING_INT); - wassert(actual(q.ident.is_missing()).istrue()); - wassert(actual(q.latrange) == LatRange()); - wassert(actual(q.lonrange) == LonRange()); - wassert(actual(q.datetime) == DatetimeRange()); - wassert(actual(q.level) == Level()); - wassert(actual(q.trange) == Trange()); - wassert(actual(q.varcodes.size()) == 0); - wassert(actual(q.query) == ""); - wassert(actual(q.ana_filter) == ""); - wassert(actual(q.data_filter) == ""); - wassert(actual(q.attr_filter) == ""); - wassert(actual(q.limit) == MISSING_INT); - wassert(actual(q.block) == MISSING_INT); - wassert(actual(q.station) == MISSING_INT); - wassert(actual(q.data_id) == MISSING_INT); - }), - Test("all_set", [](Fixture& f) { - core::Record rec; - rec.set("ana_id", 4); - rec.set("priority", 1); - rec.set("rep_memo", "foo"); - rec.set("mobile", 0); - rec.set("ident", "bar"); - rec.set("lat", 44.123); - rec.set("lon", 11.123); - rec.set(Datetime(2000, 1, 2, 12, 30, 45)); - rec.set(Level(10, 11, 12, 13)); - rec.set(Trange(20, 21, 22)); - rec.set("var", "B12101"); - rec.set("query", "best"); - rec.set("ana_filter", "B01001=1"); - rec.set("data_filter", "B12101>260"); - rec.set("attr_filter", "B33007>50"); - rec.set("limit", 100); - rec.set("block", 16); - rec.set("station", 404); - rec.set("context_id", 42); - - core::Query q; - q.set_from_record(rec); - wassert(actual(q.ana_id) == 4); - wassert(actual(q.prio_min) == 1); - wassert(actual(q.prio_max) == 1); - wassert(actual(q.rep_memo) == "foo"); - wassert(actual(q.mobile) == 0); - wassert(actual(q.ident.is_missing()).isfalse()); - wassert(actual(q.ident.get()) == "bar"); - wassert(actual(q.latrange) == LatRange(44.123, 44.123)); - wassert(actual(q.lonrange) == LonRange(11.123, 11.123)); - wassert(actual(q.datetime) == DatetimeRange(2000, 1, 2, 12, 30, 45, 2000, 1, 2, 12, 30, 45)); - wassert(actual(q.level) == Level(10, 11, 12, 13)); - wassert(actual(q.trange) == Trange(20, 21, 22)); - wassert(actual(q.varcodes.size()) == 1); - wassert(actual(*q.varcodes.begin()) == WR_VAR(0, 12, 101)); - wassert(actual(q.query) == "best"); - wassert(actual(q.ana_filter) == "B01001=1"); - wassert(actual(q.data_filter) == "B12101>260"); - wassert(actual(q.attr_filter) == "B33007>50"); - wassert(actual(q.limit) == 100); - wassert(actual(q.block) == 16); - wassert(actual(q.station) == 404); - wassert(actual(q.data_id) == 42); - }), - Test("prio", [](Fixture& f) { - core::Query q; - q.set_from_test_string("priority=11"); - wassert(actual(q.prio_min) == 11); - wassert(actual(q.prio_max) == 11); - - q.clear(); - q.set_from_test_string("priomin=12"); - wassert(actual(q.prio_min) == 12); - wassert(actual(q.prio_max) == MISSING_INT); - - q.clear(); - q.set_from_test_string("priomax=12"); - wassert(actual(q.prio_min) == MISSING_INT); - wassert(actual(q.prio_max) == 12); - - q.clear(); - q.set_from_test_string("priomin=11, priomax=22"); - wassert(actual(q.prio_min) == 11); - wassert(actual(q.prio_max) == 22); - - q.clear(); - q.set_from_test_string("priomin=11, priomax=22, priority=16"); - wassert(actual(q.prio_min) == 16); - wassert(actual(q.prio_max) == 16); - }), - Test("lat", [](Fixture& f) { - core::Query q; - q.set_from_test_string("lat=45.0"); - wassert(actual(q.latrange) == LatRange(45.0, 45.0)); - - q.clear(); - q.set_from_test_string("latmin=45.0"); - wassert(actual(q.latrange) == LatRange(45.0, LatRange::DMAX)); - - q.clear(); - q.set_from_test_string("latmax=45.0"); - wassert(actual(q.latrange) == LatRange(LatRange::DMIN, 45.0)); - - q.clear(); - q.set_from_test_string("latmin=40.0, latmax=50.0"); - wassert(actual(q.latrange) == LatRange(40.0, 50.0)); - - q.clear(); - q.set_from_test_string("latmin=40.0, latmax=50.0, lat=42.0"); - wassert(actual(q.latrange) == LatRange(42.0, 42.0)); - }), - Test("lon", [](Fixture& f) { - core::Query q; - q.set_from_test_string("lon=45.0"); - wassert(actual(q.lonrange) == LonRange(45.0, 45.0)); - - q.clear(); - try { - q.set_from_test_string("lonmin=45.0"); - wassert(actual(false).istrue()); - } catch (std::exception& e) { - wassert(actual(e.what()).contains("open ended range")); - } - wassert(actual(q.lonrange) == LonRange()); - - q.clear(); - q.set_from_test_string("lonmin=40.0, lonmax=50.0"); - wassert(actual(q.lonrange) == LonRange(40.0, 50.0)); - - q.clear(); - q.set_from_test_string("lonmin=40.0, lonmax=50.0, lon=42.0"); - wassert(actual(q.lonrange) == LonRange(42.0, 42.0)); - }), - Test("datetime", [](Fixture& f) { - core::Query q; - q.set_from_test_string("year=2015"); - wassert(actual(q.datetime) == DatetimeRange(2015, 1, 1, 0 , 0, 0, 2015, 12, 31, 23, 59, 59)); - - q.clear(); - q.set_from_test_string("year=2015, monthmin=1, monthmax=2"); - wassert(actual(q.datetime) == DatetimeRange(2015, 1, 1, 0 , 0, 0, 2015, 2, 28, 23, 59, 59)); - - q.clear(); - q.set_from_test_string("year=2015, monthmin=2, day=28"); - wassert(actual(q.datetime) == DatetimeRange(2015, 2, 28, 0 , 0, 0, 2015, 12, 28, 23, 59, 59)); - - q.clear(); - q.set_from_test_string("yearmin=2000, yearmax=2012, month=2, min=30"); - wassert(actual(q.datetime) == DatetimeRange(2000, 2, 1, 0, 30, 0, 2012, 2, 29, 23, 30, 59)); - - q.clear(); - q.set_from_test_string("yearmin=2010, yearmax=2012, year=2000, month=2, min=30"); - wassert(actual(q.datetime) == DatetimeRange(2000, 2, 1, 0, 30, 0, 2000, 2, 29, 23, 30, 59)); - }), - Test("varlist", [](Fixture& f) { - core::Query q; - q.set_from_test_string("var=B12101"); - wassert(actual(q.varcodes.size()) == 1); - wassert(actual(*q.varcodes.begin()) == WR_VAR(0, 12, 101)); - - q.clear(); - q.set_from_test_string("varlist=B12101,B01001"); - wassert(actual(q.varcodes.size()) == 2); - wassert(actual(*q.varcodes.begin()) == WR_VAR(0, 1, 1)); - wassert(actual(*q.varcodes.rbegin()) == WR_VAR(0, 12, 101)); - - q.clear(); - q.set_from_test_string("varlist=B12101,B01001, var=B12102"); - wassert(actual(q.varcodes.size()) == 1); - wassert(actual(*q.varcodes.begin()) == WR_VAR(0, 12, 102)); - }), - Test("modifiers", [](Fixture& f) { - wassert(actual(core::Query::parse_modifiers("best")) == DBA_DB_MODIFIER_BEST); - wassert(actual(core::Query::parse_modifiers("details")) == DBA_DB_MODIFIER_SUMMARY_DETAILS); - }), -}; - -test_group newtg("core_query", tests); - -} diff --git a/dballe/core/record-test.cc b/dballe/core/record-test.cc new file mode 100644 index 000000000..22a754a31 --- /dev/null +++ b/dballe/core/record-test.cc @@ -0,0 +1,256 @@ +#include "core/tests.h" +#include "core/record.h" + +using namespace dballe::tests; +using namespace dballe; +using namespace std; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { +#define fail_unless_int_is(keyvar, param, value) do { \ + int found; \ + int val; \ + CHECKED(dba_record_##keyvar##_enqi(rec, param, &val, &found)); \ + ensure_equals(found, 1); \ + ensure_equals(val, value); \ + } while (0) + +#define fail_unless_double_is(keyvar, param, value) do { \ + int found; \ + double val; \ + CHECKED(dba_record_##keyvar##_enqd(rec, param, &val, &found)); \ + ensure_equals(found, 1); \ + ensure_equals(val, value); \ + } while (0) + +#define fail_unless_char_is(keyvar, param, value) do { \ + const char* val; \ + CHECKED(dba_record_##keyvar##_enqc(rec, param, &val)); \ + ensure(val != NULL); \ + ensure_equals(string(val), string(value)); \ + } while (0) + + + add_method("keyword_name", []() { + // Keyword name resolution + using namespace dballe::core; + wassert(actual(core::Record::keyword_byname("cippo") == DBA_KEY_ERROR).istrue()); + wassert(actual(core::Record::keyword_byname("zzzip") == DBA_KEY_ERROR).istrue()); + + wassert(actual(core::Record::keyword_byname("ana_id") == DBA_KEY_ANA_ID).istrue()); + wassert(actual(core::Record::keyword_byname_len("ana_idi", 6) == DBA_KEY_ANA_ID).istrue()); + wassert(actual(core::Record::keyword_info(DBA_KEY_ANA_ID)->desc) == "Station database ID"); + + wassert(actual(core::Record::keyword_byname("yearmin") == DBA_KEY_YEARMIN).istrue()); + wassert(actual(core::Record::keyword_info(DBA_KEY_YEARMIN)->desc) == "Year or minimum year queried"); + + wassert(actual(core::Record::keyword_byname("lat") == DBA_KEY_LAT).istrue()); + wassert(actual(core::Record::keyword_info(DBA_KEY_LAT)->desc) == "Latitude"); + + wassert(actual(core::Record::keyword_byname("lon") == DBA_KEY_LON).istrue()); + wassert(actual(core::Record::keyword_info(DBA_KEY_LON)->desc) == "Longitude"); + }); + add_method("get_set", []() { + // Check that things don't exist at the beginning + core::Record rec; + wassert(actual(rec.get("ana_id")).isfalse()); + wassert(actual(rec.get("lat")).isfalse()); + wassert(actual(rec.get("B20001")).isfalse()); + wassert(actual(rec.get("B20003")).isfalse()); + + // Set various things + rec.set("ana_id", -10); + rec.set("lat", 1234567); + rec.set("lon", 76.54321); + rec.set("yearmin", "1976"); + rec.set("B20001", "456"); + rec.set("B20003", "456"); + + // Check that they now exist + wassert(actual(rec.get("ana_id")).istrue()); + wassert(actual(rec.get("lat")).istrue()); + wassert(actual(rec.get("B20001")).istrue()); + wassert(actual(rec.get("B20003")).istrue()); + + // Check that they have the right value + wassert(actual(rec.get("ana_id")->enqi()) == -10); + wassert(actual(rec.get("ana_id")->enqd()) == -10.0); + wassert(actual(rec.get("lon")->enqi()) == 7654321); + wassert(actual(rec.get("lon")->enqd()) == 76.54321); + wassert(actual(rec.get("lon")->enqc()) == "7654321"); + wassert(actual(rec.get("lat")->enqd()) == 12.34567); + wassert(actual(rec.get("yearmin")->enqd()) == 1976.0); + wassert(actual(rec.get("B20001")->enqd()) == 4560.0); + wassert(actual(rec.get("B20003")->enqd()) == 456); + + // See if unset works for keywords + rec.unset("lat"); + wassert(actual(rec.get("lat")).isfalse()); + + // See if unset works for variables + rec.unset("B20001"); + wassert(actual(rec.get("B20001")).isfalse()); + + /* fprintf(stderr, "IVAL: %d\n", ival); */ + /* fprintf(stderr, "DVAL: %f\n", fval); */ + /* + { + int i = 7654321; + double f = (double)i / 100000; + fprintf(stderr, "I: %d, F: %f\n", i, f); + } + */ + + // See if clear clears + rec.clear(); + wassert(actual(rec.get("lat")).isfalse()); + wassert(actual(rec.get("B20003")).isfalse()); + + rec.clear(); + wassert(actual(rec.get("lat")).isfalse()); + wassert(actual(rec.get("B20003")).isfalse()); + }); + add_method("ident", []() { + // This used to cause a segfault + core::Record rec; + rec.setc("ident", "nosort"); + + core::Record rec1; + rec = rec; + rec.setc("ident", "nosort"); + }); + add_method("comparisons", []() { + core::Record rec; + rec.set("ana_id", -10); + rec.set("lat", 1234567); + rec.set("lon", 76.54321); + rec.set("yearmin", "1976"); + rec.set("B20001", "456"); + rec.set("B20003", "456"); + + core::Record rec1; + rec1 = rec; + wassert(actual(rec == rec1).istrue()); + wassert(actual(rec1 == rec).istrue()); + wassert(actual(!(rec != rec1)).istrue()); + wassert(actual(!(rec1 != rec)).istrue()); + rec1.seti("yearmin", 1975); + wassert(actual(rec != rec1).istrue()); + wassert(actual(rec1 != rec).istrue()); + wassert(actual(!(rec == rec1)).istrue()); + wassert(actual(!(rec1 == rec)).istrue()); + + rec1 = rec; + wassert(actual(rec == rec1).istrue()); + wassert(actual(rec1 == rec).istrue()); + rec1.unset("yearmin"); + wassert(actual(rec != rec1).istrue()); + wassert(actual(rec1 != rec).istrue()); + + rec1 = rec; + wassert(actual(rec == rec1).istrue()); + wassert(actual(rec1 == rec).istrue()); + rec1.set("B20001", "45"); + wassert(actual(rec != rec1).istrue()); + wassert(actual(rec1 != rec).istrue()); + + rec1 = rec; + wassert(actual(rec == rec1).istrue()); + wassert(actual(rec1 == rec).istrue()); + rec1.unset("B20001"); + wassert(actual(rec != rec1).istrue()); + wassert(actual(rec1 != rec).istrue()); + }); + add_method("get_set_level", []() { + core::Record rec; + wassert(actual(rec.get_level()) == Level()); + + rec.set("leveltype1", 1); + rec.set("l1", 0); + rec.set("leveltype2", 2); + rec.set("l2", 3); + wassert(actual(rec.get_level()) == Level(1, 0, 2, 3)); + + rec.set(Level(9, 8)); + wassert(actual(rec.enq("leveltype1", 0)) == 9); + wassert(actual(rec.enq("l1", 0)) == 8); + wassert(actual(rec.enq("leveltype2", 0)) == 0); + wassert(actual(rec.enq("l2", 0)) == 0); + wassert(actual(rec.get_level()) == Level(9, 8)); + }); + add_method("get_set_trange", []() { + core::Record rec; + wassert(actual(rec.get_trange()) == Trange()); + + rec.set("pindicator", 11); + rec.set("p1", 22); + rec.set("p2", 33); + wassert(actual(rec.get_trange()) == Trange(11, 22, 33)); + + rec.set(Trange(7, 6)); + wassert(actual(rec.enq("pindicator", 0)) == 7); + wassert(actual(rec.enq("p1", 0)) == 6); + wassert(actual(rec.enq("p2", 0)) == 0); + wassert(actual(rec.get_trange()) == Trange(7, 6)); + }); + add_method("get_set_datetime", []() { + core::Record rec; + rec.set(Datetime(2013, 11, 1, 12, 0, 0)); + wassert(actual(rec.get_datetime()) == Datetime(2013, 11, 1, 12)); + + rec.set(Datetime(2012, 5, 15, 17, 30, 30)); + Datetime dt = rec.get_datetime(); + wassert(actual(dt) == Datetime(2012, 5, 15, 17, 30, 30)); + }); + add_method("iter", []() { + // Test iteration + using namespace dballe::core; + core::Record rec; + rec.set("priority", 1); + rec.set(Datetime(2013, 11, 1, 12, 0, 0)); + rec.set("var_related", "B12123"); + + unsigned count = 0; + bool res = rec.iter_keys([&count](dba_keyword k, const wreport::Var& v) { + ++count; + return true; + }); + + wassert(actual(res).istrue()); + wassert(actual(count) == 8); + }); + add_method("extremes", []() { + // Test querying extremes by Datetime + core::Record rec; + + DatetimeRange dtr = wcallchecked(rec.get_datetimerange()); + wassert(actual(dtr.min.is_missing()).istrue()); + wassert(actual(dtr.max.is_missing()).istrue()); + + static const int NA = MISSING_INT; + + wassert(rec.set(DatetimeRange(2010, 1, 1, 0, 0, 0, NA, NA, NA, NA, NA, NA))); + dtr = wcallchecked(rec.get_datetimerange()); + wassert(actual(dtr.min) == Datetime(2010, 1, 1, 0, 0, 0)); + wassert(actual(dtr.max.is_missing()).istrue()); + + wassert(rec.set(DatetimeRange(NA, NA, NA, NA, NA, NA, 2011, 2, 3, 4, 5, 6))); + dtr = wcallchecked(rec.get_datetimerange()); + wassert(actual(dtr.min.is_missing()).istrue()); + wassert(actual(dtr.max) == Datetime(2011, 2, 3, 4, 5, 6)); + + wassert(rec.set(DatetimeRange(2010, 1, 1, 0, 0, 0, 2011, 2, 3, 4, 5, 6))); + dtr = wcallchecked(rec.get_datetimerange()); + wassert(actual(dtr.min) == Datetime(2010, 1, 1, 0, 0, 0)); + wassert(actual(dtr.max) == Datetime(2011, 2, 3, 4, 5, 6)); + }); + } +} test("core_record"); + +} diff --git a/dballe/core/record-tut.cc b/dballe/core/record-tut.cc deleted file mode 100644 index fd771be2e..000000000 --- a/dballe/core/record-tut.cc +++ /dev/null @@ -1,258 +0,0 @@ -#include "core/tests.h" -#include "core/record.h" - -using namespace wibble::tests; -using namespace dballe; -using namespace std; - -namespace { - -typedef dballe::tests::test_group<> test_group; -typedef test_group::Test Test; -typedef test_group::Fixture Fixture; - -#define fail_unless_int_is(keyvar, param, value) do { \ - int found; \ - int val; \ - CHECKED(dba_record_##keyvar##_enqi(rec, param, &val, &found)); \ - ensure_equals(found, 1); \ - ensure_equals(val, value); \ - } while (0) - -#define fail_unless_double_is(keyvar, param, value) do { \ - int found; \ - double val; \ - CHECKED(dba_record_##keyvar##_enqd(rec, param, &val, &found)); \ - ensure_equals(found, 1); \ - ensure_equals(val, value); \ - } while (0) - -#define fail_unless_char_is(keyvar, param, value) do { \ - const char* val; \ - CHECKED(dba_record_##keyvar##_enqc(rec, param, &val)); \ - ensure(val != NULL); \ - ensure_equals(string(val), string(value)); \ - } while (0) - - -std::vector tests { - Test("keyword_name", [](Fixture& f) { - // Keyword name resolution - using namespace dballe::core; - ensure(core::Record::keyword_byname("cippo") == DBA_KEY_ERROR); - ensure(core::Record::keyword_byname("zzzip") == DBA_KEY_ERROR); - - ensure(core::Record::keyword_byname("ana_id") == DBA_KEY_ANA_ID); - ensure(core::Record::keyword_byname_len("ana_idi", 6) == DBA_KEY_ANA_ID); - wassert(actual(core::Record::keyword_info(DBA_KEY_ANA_ID)->desc) == "Station database ID"); - - ensure(core::Record::keyword_byname("yearmin") == DBA_KEY_YEARMIN); - wassert(actual(core::Record::keyword_info(DBA_KEY_YEARMIN)->desc) == "Year or minimum year queried"); - - ensure(core::Record::keyword_byname("lat") == DBA_KEY_LAT); - wassert(actual(core::Record::keyword_info(DBA_KEY_LAT)->desc) == "Latitude"); - - ensure(core::Record::keyword_byname("lon") == DBA_KEY_LON); - wassert(actual(core::Record::keyword_info(DBA_KEY_LON)->desc) == "Longitude"); - }), - Test("get_set", [](Fixture& f) { - // Check that things don't exist at the beginning - core::Record rec; - ensure(rec.get("ana_id") == NULL); - ensure(rec.get("lat") == NULL); - ensure(rec.get("B20001") == NULL); - ensure(rec.get("B20003") == NULL); - - // Set various things - rec.set("ana_id", -10); - rec.set("lat", 1234567); - rec.set("lon", 76.54321); - rec.set("yearmin", "1976"); - rec.set("B20001", "456"); - rec.set("B20003", "456"); - - // Check that they now exist - ensure(rec.get("ana_id") != NULL); - ensure(rec.get("lat") != NULL); - ensure(rec.get("B20001") != NULL); - ensure(rec.get("B20003") != NULL); - - // Check that they have the right value - wassert(actual(rec.get("ana_id")->enqi()) == -10); - wassert(actual(rec.get("ana_id")->enqd()) == -10.0); - wassert(actual(rec.get("lon")->enqi()) == 7654321); - wassert(actual(rec.get("lon")->enqd()) == 76.54321); - wassert(actual(rec.get("lon")->enqc()) == "7654321"); - wassert(actual(rec.get("lat")->enqd()) == 12.34567); - wassert(actual(rec.get("yearmin")->enqd()) == 1976.0); - wassert(actual(rec.get("B20001")->enqd()) == 4560.0); - wassert(actual(rec.get("B20003")->enqd()) == 456); - - // See if unset works for keywords - rec.unset("lat"); - ensure(rec.get("lat") == NULL); - - // See if unset works for variables - rec.unset("B20001"); - ensure(rec.get("B20001") == NULL); - - /* fprintf(stderr, "IVAL: %d\n", ival); */ - /* fprintf(stderr, "DVAL: %f\n", fval); */ - /* - { - int i = 7654321; - double f = (double)i / 100000; - fprintf(stderr, "I: %d, F: %f\n", i, f); - } - */ - - // See if clear clears - rec.clear(); - ensure(rec.get("lat") == NULL); - ensure(rec.get("B20003") == NULL); - - rec.clear(); - ensure(rec.get("lat") == NULL); - ensure(rec.get("B20003") == NULL); - }), - Test("ident", [](Fixture& f) { - // This used to cause a segfault - core::Record rec; - rec.setc("ident", "nosort"); - - core::Record rec1; - rec = rec; - rec.setc("ident", "nosort"); - }), - Test("comparisons", [](Fixture& f) { - core::Record rec; - rec.set("ana_id", -10); - rec.set("lat", 1234567); - rec.set("lon", 76.54321); - rec.set("yearmin", "1976"); - rec.set("B20001", "456"); - rec.set("B20003", "456"); - - core::Record rec1; - rec1 = rec; - ensure(rec == rec1); - ensure(rec1 == rec); - ensure(!(rec != rec1)); - ensure(!(rec1 != rec)); - rec1.seti("yearmin", 1975); - ensure(rec != rec1); - ensure(rec1 != rec); - ensure(!(rec == rec1)); - ensure(!(rec1 == rec)); - - rec1 = rec; - ensure(rec == rec1); - ensure(rec1 == rec); - rec1.unset("yearmin"); - ensure(rec != rec1); - ensure(rec1 != rec); - - rec1 = rec; - ensure(rec == rec1); - ensure(rec1 == rec); - rec1.set("B20001", "45"); - ensure(rec != rec1); - ensure(rec1 != rec); - - rec1 = rec; - ensure(rec == rec1); - ensure(rec1 == rec); - rec1.unset("B20001"); - ensure(rec != rec1); - ensure(rec1 != rec); - }), - Test("get_set_level", [](Fixture& f) { - core::Record rec; - ensure_equals(rec.get_level(), Level()); - - rec.set("leveltype1", 1); - rec.set("l1", 0); - rec.set("leveltype2", 2); - rec.set("l2", 3); - ensure_equals(rec.get_level(), Level(1, 0, 2, 3)); - - rec.set(Level(9, 8)); - ensure_equals(rec.enq("leveltype1", 0), 9); - ensure_equals(rec.enq("l1", 0), 8); - ensure_equals(rec.enq("leveltype2", 0), 0); - ensure_equals(rec.enq("l2", 0), 0); - ensure_equals(rec.get_level(), Level(9, 8)); - }), - Test("get_set_trange", [](Fixture& f) { - core::Record rec; - ensure_equals(rec.get_trange(), Trange()); - - rec.set("pindicator", 11); - rec.set("p1", 22); - rec.set("p2", 33); - ensure_equals(rec.get_trange(), Trange(11, 22, 33)); - - rec.set(Trange(7, 6)); - ensure_equals(rec.enq("pindicator", 0), 7); - ensure_equals(rec.enq("p1", 0), 6); - ensure_equals(rec.enq("p2", 0), 0); - ensure_equals(rec.get_trange(), Trange(7, 6)); - }), - Test("get_set_datetime", [](Fixture& f) { - core::Record rec; - rec.set(Datetime(2013, 11, 1, 12, 0, 0)); - wassert(actual(rec.get_datetime()) == Datetime(2013, 11, 1, 12)); - - rec.set(Datetime(2012, 5, 15, 17, 30, 30)); - Datetime dt = rec.get_datetime(); - wassert(actual(dt) == Datetime(2012, 5, 15, 17, 30, 30)); - }), - Test("iter", [](Fixture& f) { - // Test iteration - using namespace dballe::core; - core::Record rec; - rec.set("priority", 1); - rec.set(Datetime(2013, 11, 1, 12, 0, 0)); - rec.set("var_related", "B12123"); - - unsigned count = 0; - bool res = rec.iter_keys([&count](dba_keyword k, const wreport::Var& v) { - ++count; - return true; - }); - - wassert(actual(res).istrue()); - wassert(actual(count) == 8); - }), - Test("extremes", [](Fixture& f) { - // Test querying extremes by Datetime - core::Record rec; - - DatetimeRange dtr; - - wrunchecked(dtr = rec.get_datetimerange()); - wassert(actual(dtr.min.is_missing()).istrue()); - wassert(actual(dtr.max.is_missing()).istrue()); - - static const int NA = MISSING_INT; - - wrunchecked(rec.set(DatetimeRange(2010, 1, 1, 0, 0, 0, NA, NA, NA, NA, NA, NA))); - wrunchecked(dtr = rec.get_datetimerange()); - wassert(actual(dtr.min) == Datetime(2010, 1, 1, 0, 0, 0)); - wassert(actual(dtr.max.is_missing()).istrue()); - - wrunchecked(rec.set(DatetimeRange(NA, NA, NA, NA, NA, NA, 2011, 2, 3, 4, 5, 6))); - wrunchecked(dtr = rec.get_datetimerange()); - wassert(actual(dtr.min.is_missing()).istrue()); - wassert(actual(dtr.max) == Datetime(2011, 2, 3, 4, 5, 6)); - - wrunchecked(rec.set(DatetimeRange(2010, 1, 1, 0, 0, 0, 2011, 2, 3, 4, 5, 6))); - wrunchecked(dtr = rec.get_datetimerange()); - wassert(actual(dtr.min) == Datetime(2010, 1, 1, 0, 0, 0)); - wassert(actual(dtr.max) == Datetime(2011, 2, 3, 4, 5, 6)); - }), -}; - -test_group newtg("dballe_core_record", tests); - -} diff --git a/dballe/core/stlutils-test.cc b/dballe/core/stlutils-test.cc new file mode 100644 index 000000000..d4014beb0 --- /dev/null +++ b/dballe/core/stlutils-test.cc @@ -0,0 +1,145 @@ +#include "core/tests.h" +#include "stlutils.h" + +using namespace dballe; +using namespace dballe::stl; +using namespace wreport::tests; +using namespace std; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + // Test Intersection + add_method("intersection", []() { + unique_ptr > sequences(new stl::Sequences); + + vector a; + a.push_back(1); a.push_back(2); a.push_back(3); + sequences->add(a); + + vector b; + b.push_back(2); b.push_back(3); + sequences->add(b); + + vector c; + c.push_back(0); c.push_back(2); + sequences->add(c); + + Intersection intersection; + + Intersection::const_iterator i = intersection.begin(sequences); + wassert(actual(i != intersection.end()).istrue()); + wassert(actual(*i) == 2); + ++i; + wassert(actual(i == intersection.end()).istrue()); + }); + + // Test Intersection + add_method("intersection1", []() { + unique_ptr > sequences(new stl::Sequences); + + vector a; + a.push_back(1); + sequences->add(a); + + vector b; + b.push_back(1); + sequences->add(b); + + Intersection intersection; + Intersection::const_iterator i = intersection.begin(sequences); + wassert(actual(i != intersection.end()).istrue()); + wassert(actual(*i) == 1); + ++i; + wassert(actual(i == intersection.end()).istrue()); + }); + + // Test SetIntersection + add_method("set_intersection", []() { + stl::SetIntersection intersection; + + set a; + a.insert(1); a.insert(2); a.insert(3); + intersection.add(a); + + set b; + b.insert(2); b.insert(3); + intersection.add(b); + + set c; + c.insert(0); c.insert(2); + intersection.add(c); + + stl::SetIntersection::const_iterator i = intersection.begin(); + wassert(actual(i != intersection.end()).istrue()); + wassert(actual(*i) == 2); + ++i; + wassert(actual(i == intersection.end()).istrue()); + }); + + // Test Union + add_method("union", []() { + unique_ptr > sequences(new stl::Sequences); + vector a; + sequences->add(a); + vector b; + sequences->add(b); + + Union ab; + Union::const_iterator i = ab.begin(sequences); + wassert(actual(i == ab.end()).istrue()); + }); + + // Test Union + add_method("union1", []() { + unique_ptr > sequences(new stl::Sequences); + vector a; + a.push_back(1); + sequences->add(a); + vector b; + sequences->add(b); + + Union ab; + Union::const_iterator i = ab.begin(sequences); + wassert(actual(i != ab.end()).istrue()); + wassert(actual(*i) == 1); + ++i; + wassert(actual(i == ab.end()).istrue()); + }); + + // Test Union + add_method("union2", []() { + unique_ptr > sequences(new stl::Sequences); + vector a; + a.push_back(1); + a.push_back(2); + sequences->add(a); + vector b; + b.push_back(2); + b.push_back(3); + sequences->add(b); + + Union ab; + Union::const_iterator i = ab.begin(sequences); + wassert(actual(i != ab.end()).istrue()); + wassert(actual(*i) == 1); + ++i; + wassert(actual(i != ab.end()).istrue()); + wassert(actual(*i) == 2); + ++i; + wassert(actual(i != ab.end()).istrue()); + wassert(actual(*i) == 3); + ++i; + wassert(actual(i == ab.end()).istrue()); + }); + } +} test("core_stlutils"); + +} + +#include "stlutils.tcc" diff --git a/dballe/core/stlutils-tut.cc b/dballe/core/stlutils-tut.cc deleted file mode 100644 index fee73c9a2..000000000 --- a/dballe/core/stlutils-tut.cc +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (C) 2013 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "dballe/core/tests.h" -#include "stlutils.h" - -using namespace dballe; -using namespace dballe::stl; -using namespace wibble::tests; -using namespace std; - -namespace tut { - -struct core_stlutils_shar -{ -}; - -TESTGRP(core_stlutils); - -// Test Intersection -template<> template<> void to::test<1>() -{ - unique_ptr > sequences(new stl::Sequences); - - vector a; - a.push_back(1); a.push_back(2); a.push_back(3); - sequences->add(a); - - vector b; - b.push_back(2); b.push_back(3); - sequences->add(b); - - vector c; - c.push_back(0); c.push_back(2); - sequences->add(c); - - Intersection intersection; - - Intersection::const_iterator i = intersection.begin(sequences); - wassert(actual(i != intersection.end()).istrue()); - wassert(actual(*i) == 2); - ++i; - wassert(actual(i == intersection.end()).istrue()); -} - -// Test Intersection -template<> template<> void to::test<2>() -{ - unique_ptr > sequences(new stl::Sequences); - - vector a; - a.push_back(1); - sequences->add(a); - - vector b; - b.push_back(1); - sequences->add(b); - - Intersection intersection; - Intersection::const_iterator i = intersection.begin(sequences); - wassert(actual(i != intersection.end()).istrue()); - wassert(actual(*i) == 1); - ++i; - wassert(actual(i == intersection.end()).istrue()); -} - -// Test SetIntersection -template<> template<> void to::test<3>() -{ - stl::SetIntersection intersection; - - set a; - a.insert(1); a.insert(2); a.insert(3); - intersection.add(a); - - set b; - b.insert(2); b.insert(3); - intersection.add(b); - - set c; - c.insert(0); c.insert(2); - intersection.add(c); - - stl::SetIntersection::const_iterator i = intersection.begin(); - wassert(actual(i != intersection.end()).istrue()); - wassert(actual(*i) == 2); - ++i; - wassert(actual(i == intersection.end()).istrue()); -} - -// Test Union -template<> template<> void to::test<4>() -{ - unique_ptr > sequences(new stl::Sequences); - vector a; - sequences->add(a); - vector b; - sequences->add(b); - - Union ab; - Union::const_iterator i = ab.begin(sequences); - wassert(actual(i == ab.end()).istrue()); -} - -// Test Union -template<> template<> void to::test<5>() -{ - unique_ptr > sequences(new stl::Sequences); - vector a; - a.push_back(1); - sequences->add(a); - vector b; - sequences->add(b); - - Union ab; - Union::const_iterator i = ab.begin(sequences); - wassert(actual(i != ab.end()).istrue()); - wassert(actual(*i) == 1); - ++i; - wassert(actual(i == ab.end()).istrue()); -} - -// Test Union -template<> template<> void to::test<6>() -{ - unique_ptr > sequences(new stl::Sequences); - vector a; - a.push_back(1); - a.push_back(2); - sequences->add(a); - vector b; - b.push_back(2); - b.push_back(3); - sequences->add(b); - - Union ab; - Union::const_iterator i = ab.begin(sequences); - wassert(actual(i != ab.end()).istrue()); - wassert(actual(*i) == 1); - ++i; - wassert(actual(i != ab.end()).istrue()); - wassert(actual(*i) == 2); - ++i; - wassert(actual(i != ab.end()).istrue()); - wassert(actual(*i) == 3); - ++i; - wassert(actual(i == ab.end()).istrue()); -} - -} - -#include "stlutils.tcc" diff --git a/dballe/core/structbuf-test.cc b/dballe/core/structbuf-test.cc new file mode 100644 index 000000000..1e743c339 --- /dev/null +++ b/dballe/core/structbuf-test.cc @@ -0,0 +1,69 @@ +#include "tests.h" +#include "structbuf.h" + +using namespace dballe; +using namespace wreport; +using namespace dballe::tests; +using namespace std; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + // Test an in-memory structbuf + add_method("memory", []() { + Structbuf buf; + wassert(actual(buf.size()) == 0); + wassert(actual(buf.is_file_backed()).isfalse()); + + buf.append(1); + wassert(actual(buf.size()) == 1); + wassert(actual(buf.is_file_backed()).isfalse()); + + buf.append(2); + wassert(actual(buf.size()) == 2); + wassert(actual(buf.is_file_backed()).isfalse()); + + buf.append(3); + wassert(actual(buf.size()) == 3); + wassert(actual(buf.is_file_backed()).isfalse()); + + buf.ready_to_read(); + for (unsigned i = 0; i < 3; ++i) + wassert(actual(buf[i]) == i + 1); + }); + + // Test an file-backed structbuf + add_method("file", []() { + Structbuf buf; + buf.append(1); + buf.append(2); + buf.append(3); + wassert(actual(buf.size()) == 3); + wassert(actual(buf.is_file_backed()).isfalse()); + + buf.append(4); + wassert(actual(buf.size()) == 4); + wassert(actual(buf.is_file_backed()).istrue()); + + buf.append(5); + wassert(actual(buf.size()) == 5); + buf.append(6); + wassert(actual(buf.size()) == 6); + buf.append(7); + wassert(actual(buf.size()) == 7); + buf.append(8); + wassert(actual(buf.size()) == 8); + + buf.ready_to_read(); + for (unsigned i = 0; i < 8; ++i) + wassert(actual(buf[i]) == i + 1); + }); + } +} test("core_structbuf"); + +} diff --git a/dballe/core/structbuf-tut.cc b/dballe/core/structbuf-tut.cc deleted file mode 100644 index f3f41e68b..000000000 --- a/dballe/core/structbuf-tut.cc +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2014 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "tests.h" -#include "structbuf.h" - -using namespace dballe; -using namespace wreport; -using namespace wibble::tests; -using namespace std; - -namespace tut { - -struct core_structbuf_shar -{ -}; -TESTGRP(core_structbuf); - -// Test an in-memory structbuf -template<> template<> -void to::test<1>() -{ - Structbuf buf; - wassert(actual(buf.size()) == 0); - wassert(actual(buf.is_file_backed()).isfalse()); - - buf.append(1); - wassert(actual(buf.size()) == 1); - wassert(actual(buf.is_file_backed()).isfalse()); - - buf.append(2); - wassert(actual(buf.size()) == 2); - wassert(actual(buf.is_file_backed()).isfalse()); - - buf.append(3); - wassert(actual(buf.size()) == 3); - wassert(actual(buf.is_file_backed()).isfalse()); - - buf.ready_to_read(); - for (unsigned i = 0; i < 3; ++i) - wassert(actual(buf[i]) == i + 1); -} - -// Test an file-backed structbuf -template<> template<> -void to::test<2>() -{ - Structbuf buf; - buf.append(1); - buf.append(2); - buf.append(3); - wassert(actual(buf.size()) == 3); - wassert(actual(buf.is_file_backed()).isfalse()); - - buf.append(4); - wassert(actual(buf.size()) == 4); - wassert(actual(buf.is_file_backed()).istrue()); - - buf.append(5); - wassert(actual(buf.size()) == 5); - buf.append(6); - wassert(actual(buf.size()) == 6); - buf.append(7); - wassert(actual(buf.size()) == 7); - buf.append(8); - wassert(actual(buf.size()) == 8); - - buf.ready_to_read(); - for (unsigned i = 0; i < 8; ++i) - wassert(actual(buf[i]) == i + 1); -} - -} - - diff --git a/dballe/core/tests.cc b/dballe/core/tests.cc index 5197a4493..d3e5c1352 100644 --- a/dballe/core/tests.cc +++ b/dballe/core/tests.cc @@ -1,13 +1,14 @@ #include "tests.h" #include "record.h" -#include +#include "matcher.h" +#include #include #include #include #include -using namespace wibble; using namespace wreport; +using namespace dballe::tests; using namespace std; namespace dballe { @@ -214,28 +215,22 @@ std::string datafile(const std::string& fname) return testdatadir + "/" + fname; } -unique_ptr _open_test_data(const wibble::tests::Location& loc, const char* filename, File::Encoding type) +unique_ptr open_test_data(const char* filename, File::Encoding type) { - try { - return unique_ptr(File::create(type, datafile(filename), "r")); - } catch (wreport::error& e) { - throw tut::failure(loc.msg(e.what())); - } + return unique_ptr(File::create(type, datafile(filename), "r")); } -BinaryMessage _read_rawmsg(const wibble::tests::Location& loc, const char* filename, File::Encoding type) +BinaryMessage read_rawmsg(const char* filename, File::Encoding type) { - try { - unique_ptr f = _open_test_data(loc, filename, type); - BinaryMessage res = f->read(); - inner_ensure(res); - return res; - } catch (wreport::error& e) { - throw tut::failure(loc.msg(e.what())); - } + unique_ptr f = wcallchecked(open_test_data(filename, type)); + BinaryMessage res = f->read(); + wassert(actual(res).istrue()); + return res; } -void TestRecordValEqual::check(WIBBLE_TEST_LOCPRM) const +#if 0 + void vars_equal(const Record& expected) { return TestRecordVarsEqual(this->actual, expected); } +void TestRecordValEqual::check() const { const wreport::Var* evar = expected.get(name); const wreport::Var* avar = actual.get(name); @@ -259,14 +254,15 @@ void TestRecordValEqual::check(WIBBLE_TEST_LOCPRM) const ss << "is unset"; else ss << "has " << avar->format(); - wibble_test_location.fail_test(ss.str()); + throw TestFailed(ss.str()); } } +#endif -void TestRecordVarsEqual::check(WIBBLE_TEST_LOCPRM) const +void ActualRecord::vars_equal(const Values& expected) const { - WIBBLE_TEST_INFO(locinfo); - const auto& act = core::Record::downcast(actual); + WREPORT_TEST_INFO(locinfo); + const auto& act = core::Record::downcast(_actual); locinfo() << "Expected: " /* << expected.to_string() */ << " actual: " << act.to_string(); const vector& vars1 = act.vars(); @@ -278,7 +274,7 @@ void TestRecordVarsEqual::check(WIBBLE_TEST_LOCPRM) const { std::stringstream ss; ss << "records have a different number of variables. Expected is " << vars2.size() << " and actual has " << vars1.size(); - wibble_test_location.fail_test(ss.str()); + throw TestFailed(ss.str()); } for (unsigned i = 0; i < vars1.size(); ++i) @@ -290,7 +286,7 @@ void TestRecordVarsEqual::check(WIBBLE_TEST_LOCPRM) const << varcode_format(vars2[i]->code()) << "=" << vars2[i]->format("") << " and actual has " << varcode_format(vars1[i]->code()) << "=" << vars1[i]->format(""); - wibble_test_location.fail_test(ss.str()); + throw TestFailed(ss.str()); } } } @@ -298,9 +294,9 @@ void TestRecordVarsEqual::check(WIBBLE_TEST_LOCPRM) const void set_record_from_string(Record& rec, const std::string& s) { auto& r = core::Record::downcast(rec); - str::Split splitter(", ", s); - for (str::Split::const_iterator i = splitter.begin(); i != splitter.end(); ++i) - r.set_from_string(i->c_str()); + str::Split split(s, ", "); + for (const auto& i: split) + r.set_from_string(i.c_str()); } unique_ptr record_from_string(const std::string& s) @@ -325,5 +321,21 @@ core::Query core_query_from_string(const std::string& s) return q; } +void ActualMatcherResult::operator==(int expected) const +{ + if (expected == _actual) return; + std::stringstream ss; + ss << "actual match result is " << matcher::result_format((matcher::Result)_actual) << " but it should be " << matcher::result_format((matcher::Result)expected); + throw TestFailed(ss.str()); +} + +void ActualMatcherResult::operator!=(int expected) const +{ + if (expected != _actual) return; + std::stringstream ss; + ss << "actual match result is " << matcher::result_format((matcher::Result)_actual) << " but it should not be"; + throw TestFailed(ss.str()); +} + } } diff --git a/dballe/core/tests.h b/dballe/core/tests.h index a7be90293..a7a18b2a1 100644 --- a/dballe/core/tests.h +++ b/dballe/core/tests.h @@ -1,29 +1,9 @@ -/* - * core/test-utils-core - Test utility functions for the core module - * - * Copyright (C) 2005--2015 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - #include #include #include #include #include +#include #include #include #include @@ -31,33 +11,10 @@ #include #include -#define wcallchecked(func) \ - [&]() { try { \ - return func; \ - } catch (tut::failure) { \ - throw; \ - } catch (std::exception& e) { \ - wibble_test_location.fail_test(wibble_test_location_info, __FILE__, __LINE__, #func, e.what()); \ - } }() - namespace dballe { namespace tests { -/** - * Base class for test fixtures. - * - * A fixture will have a constructor and a destructor to do setup/teardown, and - * a reset() function to be called inbetween tests. - * - * Fixtures do not need to descend from Fixture: this implementation is - * provided as a default for tests that do not need one, or as a base for - * fixtures that do not need reset(). - */ -struct Fixture -{ - // Reset inbetween tests - void reset() {} -}; +using namespace wreport::tests; /** * Check if a test can be run. @@ -67,141 +24,7 @@ struct Fixture */ bool test_can_run(const std::string& group_name, const std::string& test_name); -/** - * Alternative to tut::test_group. - * - * Tests are registered using a vector of lambdas, and it implements smarter - * matching on group names and test names. - * - * There can be any number of tests. - * - * This could not have been implemented on top of the existing tut::test_group, - * because all its working components are declared private and are not - * accessible to subclasses. - */ -template -struct test_group : public tut::group_base -{ - typedef std::function test_func_t; - typedef T Fixture; - - struct Test - { - std::string name; - test_func_t func; - - Test(const std::string& name, test_func_t func) - : name(name), func(func) {} - }; - typedef std::vector Tests; - - /// Name of this test group - std::string name; - /// Storage for all our tests. - Tests tests; - /// Current test when running them in sequence - typename std::vector::iterator cur_test; - /// Fixture for the tests - T* fixture = 0; - - test_group(const char* name, const std::vector& tests) - : name(name), tests(tests) - { - // register itself - tut::runner.get().register_group(name, this); - } - - virtual T* create_fixture() - { - return new T; - } - - void delete_fixture() - { - try { - delete fixture; - } catch (std::exception& e) { - fprintf(stderr, "Warning: exception caught while deleting fixture for test group %s: %s", name.c_str(), e.what()); - } - fixture = 0; - } - - void rewind() override - { - delete_fixture(); - cur_test = tests.begin(); - } - - tut::test_result run_next() override - { - // Skip those tests that should not run - while (cur_test != tests.end() && !test_can_run(name, cur_test->name)) - ++cur_test; - - if (cur_test == tests.end()) - { - delete_fixture(); - throw tut::no_more_tests(); - } - - tut::test_result res = run(cur_test); - ++cur_test; - return res; - } - - // execute one test - tut::test_result run_test(int n) override - { - --n; // From 1-based to 0-based - if (n < 0 || (size_t)n >= tests.size()) - throw tut::beyond_last_test(); - - if (!test_can_run(name, tests[n].name)) - throw tut::beyond_last_test(); - - tut::test_result res = run(tests.begin() + n); - delete_fixture(); - return res; - } - - tut::test_result run(typename std::vector::iterator test) - { - int pos = test - tests.begin() + 1; - // Create fixture if it does not exist yet - if (!fixture) - { - try { - fixture = create_fixture(); - } catch (std::exception& e) { - fprintf(stderr, "Warning: exception caught while creating fixture for test group %s: %s", name.c_str(), e.what()); - return tut::test_result(name, pos, tut::test_result::ex_ctor, e); - } - } else { - try { - fixture->reset(); - } catch (std::exception& e) { - fprintf(stderr, "Warning: exception caught while resetting the fixture for test group %s: %s", name.c_str(), e.what()); - return tut::test_result(name, pos, tut::test_result::ex_ctor, e); - } - } - try { - test->func(*fixture); - } catch (const tut::failure& e) { - // test failed because of ensure() or similar method - return tut::test_result(name, pos, tut::test_result::fail, e); - } catch (const std::exception& e) { - // test failed with std::exception - return tut::test_result(name, pos, tut::test_result::ex, e); - } catch (...) { - // test failed with unknown exception - return tut::test_result(name, pos, tut::test_result::ex); - } - - return tut::test_result(name, pos, tut::test_result::ok); - } -}; - - +#if 0 // Some utility random generator functions static inline int rnd(int min, int max) @@ -227,20 +50,18 @@ static inline bool rnd(double prob) { return (rnd(0, 100) < prob*100) ? true : false; } +#endif // Message reading functions /// Return the pathname of a test file std::string datafile(const std::string& fname); -std::unique_ptr _open_test_data(const wibble::tests::Location& loc, const char* filename, File::Encoding type); -#define open_test_data(filename, type) dballe::tests::_open_test_data(wibble::tests::Location(__FILE__, __LINE__, "open " #filename " " #type), (filename), (type)) -#define inner_open_test_data(filename, type) dballe::tests::_open_test_data(wibble::tests::Location(loc, __FILE__, __LINE__, #filename " " #type), (filename), (type)) +std::unique_ptr open_test_data(const char* filename, File::Encoding type); -BinaryMessage _read_rawmsg(const wibble::tests::Location& loc, const char* filename, File::Encoding type); -#define read_rawmsg(filename, type) dballe::tests::_read_rawmsg(wibble::tests::Location(__FILE__, __LINE__, "load " #filename " " #type), (filename), (type)) -#define inner_read_rawmsg(filename, type) dballe::tests::_read_rawmsg(wibble::tests::Location(loc, __FILE__, __LINE__, "load " #filename " " #type), (filename), (type)) +BinaryMessage read_rawmsg(const char* filename, File::Encoding type); +#if 0 /// Check that actual and expected have the same vars struct TestRecordValEqual { @@ -252,10 +73,9 @@ struct TestRecordValEqual TestRecordValEqual(const dballe::Record& actual, const dballe::Record& expected, const char* name, bool with_missing_int=false) : actual(actual), expected(expected), name(name), with_missing_int(with_missing_int) {} - void check(WIBBLE_TEST_LOCPRM) const; + void check() const; }; -/// Check that actual and expected have the same vars struct TestRecordVarsEqual { const dballe::Record& actual; @@ -264,20 +84,25 @@ struct TestRecordVarsEqual TestRecordVarsEqual(const dballe::Record& actual, const dballe::Record& expected) : actual(actual), expected(expected) {} TestRecordVarsEqual(const dballe::Record& actual, const dballe::Values& expected) : actual(actual), expected(expected) {} - void check(WIBBLE_TEST_LOCPRM) const; + void check() const; }; +#endif -struct ActualRecord : public wibble::tests::Actual +struct ActualRecord : public wreport::tests::Actual { - ActualRecord(const dballe::Record& actual) : wibble::tests::Actual(actual) {} + ActualRecord(const dballe::Record& actual) : wreport::tests::Actual(actual) {} +#if 0 TestRecordValEqual equals(const Record& expected, const char* name) { return TestRecordValEqual(this->actual, expected, name); } TestRecordValEqual equals_with_missing_int(const Record& expected, const char* name) { return TestRecordValEqual(this->actual, expected, name, true); } - TestRecordVarsEqual vars_equal(const Record& expected) { return TestRecordVarsEqual(this->actual, expected); } - TestRecordVarsEqual vars_equal(const Values& expected) { return TestRecordVarsEqual(this->actual, expected); } +#endif + /// Check that actual and expected have the same vars + void vars_equal(const Record& expected) const { vars_equal(Values(expected)); } + /// Check that actual and expected have the same vars + void vars_equal(const Values& expected) const; }; // Set a record from a ", "-separated string of assignments @@ -286,13 +111,21 @@ std::unique_ptr record_from_string(const std::string& s); std::unique_ptr query_from_string(const std::string& s); core::Query core_query_from_string(const std::string& s); -} -} +struct ActualMatcherResult : public Actual +{ + using Actual::Actual; -namespace wibble { -namespace tests { + void operator==(int expected) const; + void operator!=(int expected) const; +}; + +inline ActualMatcherResult actual_matcher_result(int actual) { return ActualMatcherResult(actual); } + +using wreport::tests::actual; inline dballe::tests::ActualRecord actual(const dballe::Record& actual) { return dballe::tests::ActualRecord(actual); } +inline ActualCString actual(const dballe::Ident& ident) { return ActualCString(ident); } + } } diff --git a/dballe/core/tests/tut-main.cpp b/dballe/core/tests/tut-main.cpp deleted file mode 100644 index 231368b94..000000000 --- a/dballe/core/tests/tut-main.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include -#include -#include -#include -#include - -namespace tut { - test_runner_singleton runner; -} - -using namespace wibble; -using namespace std; - -void signal_to_exception(int) -{ - throw std::runtime_error("killing signal catched"); -} - -int main(int argc,const char* argv[]) -{ - tut::reporter visi; - - signal(SIGSEGV,signal_to_exception); - signal(SIGILL,signal_to_exception); - - if( (argc == 2 && (! strcmp ("help", argv[1]))) || argc > 3 ) - { - std::cout << "TUT example test application." << std::endl; - std::cout << "Usage: example [regression] | [list] | [ group] [test]" << std::endl; - std::cout << " List all groups: example list" << std::endl; - std::cout << " Run all tests: example regression" << std::endl; - std::cout << " Run one group: example std::auto_ptr" << std::endl; - std::cout << " Run one test: example std::auto_ptr 3" << std::endl;; - } - - // std::cout << "\nFAILURE and EXCEPTION in these tests are FAKE ;)\n\n"; - - tut::runner.get().set_callback(&visi); - - try - { - if( argc == 1 || (argc == 2 && std::string(argv[1]) == "regression") ) - { - tut::runner.get().run_tests(); - } - else if( argc == 2 && std::string(argv[1]) == "list" ) - { - std::cout << "registered test groups:" << std::endl; - tut::groupnames gl = tut::runner.get().list_groups(); - tut::groupnames::const_iterator i = gl.begin(); - tut::groupnames::const_iterator e = gl.end(); - while( i != e ) - { - std::cout << " " << *i << std::endl; - ++i; - } - } - else if( argc == 2 && std::string(argv[1]) != "regression" ) - { - if (strchr(argv[1], '*')) - { - tut::groupnames gl = tut::runner.get().list_groups(); - for (tut::groupnames::const_iterator i = gl.begin(); i != gl.end(); ++i) - if (fnmatch(argv[1], i->c_str(), 0) == 0) - tut::runner.get().run_tests(*i); - } else - tut::runner.get().run_tests(argv[1]); - } - else if( argc == 3 ) - { - tut::runner.get().run_test(argv[1],::atoi(argv[2])); - } - } - catch( const std::exception& ex ) - { - std::cerr << "tut raised exception: " << ex.what() << std::endl; - } - - return min(visi.failures_count + visi.exceptions_count, 120); -} diff --git a/dballe/core/values-test.cc b/dballe/core/values-test.cc new file mode 100644 index 000000000..f5f9899ad --- /dev/null +++ b/dballe/core/values-test.cc @@ -0,0 +1,22 @@ +#include "core/tests.h" +#include "core/values.h" + +using namespace std; +using namespace dballe::tests; +using namespace dballe; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("empty", []() { + }); + } +} test("core_values"); + +} + diff --git a/dballe/core/values-tut.cc b/dballe/core/values-tut.cc deleted file mode 100644 index 3e2defc2f..000000000 --- a/dballe/core/values-tut.cc +++ /dev/null @@ -1,22 +0,0 @@ -#include "core/tests.h" -#include "core/values.h" - -using namespace std; -using namespace wibble::tests; -using namespace dballe; - -namespace { - -typedef dballe::tests::test_group<> test_group; -typedef test_group::Test Test; -typedef test_group::Fixture Fixture; - -std::vector tests { - Test("empty", [](Fixture& f) { - }), -}; - -test_group newtg("core_values", tests); - -} - diff --git a/dballe/core/var-test.cc b/dballe/core/var-test.cc new file mode 100644 index 000000000..513063a78 --- /dev/null +++ b/dballe/core/var-test.cc @@ -0,0 +1,36 @@ +#include "tests.h" +#include "var.h" + +using namespace dballe; +using namespace wreport; +using namespace wreport::tests; +using namespace std; + +namespace tut { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("single", []() { + set codes; + resolve_varlist("B12101", codes); + wassert(actual(codes.size()) == 1); + wassert(actual(*codes.begin()) == WR_VAR(0, 12, 101)); + }); + + add_method("multi", []() { + set codes; + resolve_varlist("B12101,B12103", codes); + wassert(actual(codes.size()) == 2); + set::const_iterator i = codes.begin(); + wassert(actual(*i) == WR_VAR(0, 12, 101)); + ++i; + wassert(actual(*i) == WR_VAR(0, 12, 103)); + }); + } +} test("core_var"); + +} diff --git a/dballe/core/var-tut.cc b/dballe/core/var-tut.cc deleted file mode 100644 index 81c2af9fc..000000000 --- a/dballe/core/var-tut.cc +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2013 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "tests.h" -#include "var.h" - -using namespace dballe; -using namespace wreport; -using namespace wibble::tests; -using namespace std; - -namespace tut { - -struct core_var_shar -{ -}; -TESTGRP(core_var); - -template<> template<> -void to::test<1>() -{ - { - set codes; - resolve_varlist("B12101", codes); - wassert(actual(codes.size()) == 1); - wassert(actual(*codes.begin()) == WR_VAR(0, 12, 101)); - } - - { - set codes; - resolve_varlist("B12101,B12103", codes); - wassert(actual(codes.size()) == 2); - set::const_iterator i = codes.begin(); - wassert(actual(*i) == WR_VAR(0, 12, 101)); - ++i; - wassert(actual(*i) == WR_VAR(0, 12, 103)); - } -} - -} - diff --git a/dballe/core/varmatch-test.cc b/dballe/core/varmatch-test.cc new file mode 100644 index 000000000..e72737719 --- /dev/null +++ b/dballe/core/varmatch-test.cc @@ -0,0 +1,120 @@ +#include "tests.h" +#include "var.h" +#include "varmatch.h" + +using namespace wreport; +using namespace dballe; +using namespace dballe::tests; +using namespace std; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("int", []() { + Var var(varinfo(WR_VAR(0, 1, 1)), 42); + + wassert(actual((*Varmatch::parse("B01001<43"))(var)).istrue()); + wassert(actual((*Varmatch::parse("B01001<42"))(var)).isfalse()); + wassert(actual((*Varmatch::parse("B01001<41"))(var)).isfalse()); + + wassert(actual((*Varmatch::parse("B01001<=43"))(var)).istrue()); + wassert(actual((*Varmatch::parse("B01001<=42"))(var)).istrue()); + wassert(actual((*Varmatch::parse("B01001<=41"))(var)).isfalse()); + + wassert(actual((*Varmatch::parse("B01001>41"))(var)).istrue()); + wassert(actual((*Varmatch::parse("B01001>42"))(var)).isfalse()); + wassert(actual((*Varmatch::parse("B01001>43"))(var)).isfalse()); + + wassert(actual((*Varmatch::parse("B01001>=41"))(var)).istrue()); + wassert(actual((*Varmatch::parse("B01001>=42"))(var)).istrue()); + wassert(actual((*Varmatch::parse("B01001>=43"))(var)).isfalse()); + + wassert(actual((*Varmatch::parse("B01001==42"))(var)).istrue()); + wassert(actual((*Varmatch::parse("B01001=42"))(var)).istrue()); + wassert(actual((*Varmatch::parse("B01001==43"))(var)).isfalse()); + wassert(actual((*Varmatch::parse("B01001=43"))(var)).isfalse()); + + wassert(actual((*Varmatch::parse("B01001<>43"))(var)).istrue()); + wassert(actual((*Varmatch::parse("B01001<>42"))(var)).isfalse()); + + wassert(actual((*Varmatch::parse("41<=B01001<=42"))(var)).istrue()); + wassert(actual((*Varmatch::parse("42<=B01001<=42"))(var)).istrue()); + wassert(actual((*Varmatch::parse("42<=B01001<=43"))(var)).istrue()); + wassert(actual((*Varmatch::parse("40<=B01001<=41"))(var)).isfalse()); + }); + + add_method("decimal", []() { + Var var(varinfo(WR_VAR(0, 12, 101)), 273.15); + + wassert(actual((*Varmatch::parse("B12101<274"))(var)).istrue()); + wassert(actual((*Varmatch::parse("B12101<273.15"))(var)).isfalse()); + wassert(actual((*Varmatch::parse("B12101<273"))(var)).isfalse()); + + wassert(actual((*Varmatch::parse("B12101<=274"))(var)).istrue()); + wassert(actual((*Varmatch::parse("B12101<=273.15"))(var)).istrue()); + wassert(actual((*Varmatch::parse("B12101<=273"))(var)).isfalse()); + + wassert(actual((*Varmatch::parse("B12101>273"))(var)).istrue()); + wassert(actual((*Varmatch::parse("B12101>273.15"))(var)).isfalse()); + wassert(actual((*Varmatch::parse("B12101>274"))(var)).isfalse()); + + wassert(actual((*Varmatch::parse("B12101>=273"))(var)).istrue()); + wassert(actual((*Varmatch::parse("B12101>=273.15"))(var)).istrue()); + wassert(actual((*Varmatch::parse("B12101>=274"))(var)).isfalse()); + + wassert(actual((*Varmatch::parse("B12101==273.15"))(var)).istrue()); + wassert(actual((*Varmatch::parse("B12101=273.15"))(var)).istrue()); + wassert(actual((*Varmatch::parse("B12101==274"))(var)).isfalse()); + wassert(actual((*Varmatch::parse("B12101=274"))(var)).isfalse()); + + wassert(actual((*Varmatch::parse("B12101<>274"))(var)).istrue()); + wassert(actual((*Varmatch::parse("B12101<>273.15"))(var)).isfalse()); + + wassert(actual((*Varmatch::parse("273<=B12101<=273.15"))(var)).istrue()); + wassert(actual((*Varmatch::parse("273.15<=B12101<=273.15"))(var)).istrue()); + wassert(actual((*Varmatch::parse("273.15<=B12101<=274"))(var)).istrue()); + wassert(actual((*Varmatch::parse("272<=B12101<=273"))(var)).isfalse()); + }); + + add_method("string", []() { + Var var(varinfo(WR_VAR(0, 1, 11)), "enrico"); + + wassert(actual((*Varmatch::parse("B01011emanuele"))(var)).istrue()); + wassert(actual((*Varmatch::parse("B01011>enrico"))(var)).isfalse()); + wassert(actual((*Varmatch::parse("B01011>paolo"))(var)).isfalse()); + + wassert(actual((*Varmatch::parse("B01011>=emanuele"))(var)).istrue()); + wassert(actual((*Varmatch::parse("B01011>=enrico"))(var)).istrue()); + wassert(actual((*Varmatch::parse("B01011>=paolo"))(var)).isfalse()); + + wassert(actual((*Varmatch::parse("B01011==enrico"))(var)).istrue()); + wassert(actual((*Varmatch::parse("B01011=enrico"))(var)).istrue()); + wassert(actual((*Varmatch::parse("B01011==paolo"))(var)).isfalse()); + wassert(actual((*Varmatch::parse("B01011=paolo"))(var)).isfalse()); + + wassert(actual((*Varmatch::parse("B01011<>paolo"))(var)).istrue()); + wassert(actual((*Varmatch::parse("B01011<>enrico"))(var)).isfalse()); + + wassert(actual((*Varmatch::parse("emanuele<=B01011<=enrico"))(var)).istrue()); + wassert(actual((*Varmatch::parse("enrico<=B01011<=enrico"))(var)).istrue()); + wassert(actual((*Varmatch::parse("enrico<=B01011<=paolo"))(var)).istrue()); + wassert(actual((*Varmatch::parse("daniele<=B01011<=emanuele"))(var)).isfalse()); + }); + } +} test("core_varmatch"); + +} + diff --git a/dballe/core/varmatch-tut.cc b/dballe/core/varmatch-tut.cc deleted file mode 100644 index 209456c97..000000000 --- a/dballe/core/varmatch-tut.cc +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) 2013 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "tests.h" -#include "var.h" -#include "varmatch.h" - -using namespace dballe; -using namespace wreport; -using namespace wibble::tests; -using namespace std; - -namespace tut { - -struct core_varmatch_shar -{ -}; - -TESTGRP(core_varmatch); - -template<> template<> void to::test<1>() -{ - Var var(varinfo(WR_VAR(0, 1, 1)), 42); - - wassert(actual((*Varmatch::parse("B01001<43"))(var)).istrue()); - wassert(actual((*Varmatch::parse("B01001<42"))(var)).isfalse()); - wassert(actual((*Varmatch::parse("B01001<41"))(var)).isfalse()); - - wassert(actual((*Varmatch::parse("B01001<=43"))(var)).istrue()); - wassert(actual((*Varmatch::parse("B01001<=42"))(var)).istrue()); - wassert(actual((*Varmatch::parse("B01001<=41"))(var)).isfalse()); - - wassert(actual((*Varmatch::parse("B01001>41"))(var)).istrue()); - wassert(actual((*Varmatch::parse("B01001>42"))(var)).isfalse()); - wassert(actual((*Varmatch::parse("B01001>43"))(var)).isfalse()); - - wassert(actual((*Varmatch::parse("B01001>=41"))(var)).istrue()); - wassert(actual((*Varmatch::parse("B01001>=42"))(var)).istrue()); - wassert(actual((*Varmatch::parse("B01001>=43"))(var)).isfalse()); - - wassert(actual((*Varmatch::parse("B01001==42"))(var)).istrue()); - wassert(actual((*Varmatch::parse("B01001=42"))(var)).istrue()); - wassert(actual((*Varmatch::parse("B01001==43"))(var)).isfalse()); - wassert(actual((*Varmatch::parse("B01001=43"))(var)).isfalse()); - - wassert(actual((*Varmatch::parse("B01001<>43"))(var)).istrue()); - wassert(actual((*Varmatch::parse("B01001<>42"))(var)).isfalse()); - - wassert(actual((*Varmatch::parse("41<=B01001<=42"))(var)).istrue()); - wassert(actual((*Varmatch::parse("42<=B01001<=42"))(var)).istrue()); - wassert(actual((*Varmatch::parse("42<=B01001<=43"))(var)).istrue()); - wassert(actual((*Varmatch::parse("40<=B01001<=41"))(var)).isfalse()); -} - -template<> template<> void to::test<2>() -{ - Var var(varinfo(WR_VAR(0, 12, 101)), 273.15); - - wassert(actual((*Varmatch::parse("B12101<274"))(var)).istrue()); - wassert(actual((*Varmatch::parse("B12101<273.15"))(var)).isfalse()); - wassert(actual((*Varmatch::parse("B12101<273"))(var)).isfalse()); - - wassert(actual((*Varmatch::parse("B12101<=274"))(var)).istrue()); - wassert(actual((*Varmatch::parse("B12101<=273.15"))(var)).istrue()); - wassert(actual((*Varmatch::parse("B12101<=273"))(var)).isfalse()); - - wassert(actual((*Varmatch::parse("B12101>273"))(var)).istrue()); - wassert(actual((*Varmatch::parse("B12101>273.15"))(var)).isfalse()); - wassert(actual((*Varmatch::parse("B12101>274"))(var)).isfalse()); - - wassert(actual((*Varmatch::parse("B12101>=273"))(var)).istrue()); - wassert(actual((*Varmatch::parse("B12101>=273.15"))(var)).istrue()); - wassert(actual((*Varmatch::parse("B12101>=274"))(var)).isfalse()); - - wassert(actual((*Varmatch::parse("B12101==273.15"))(var)).istrue()); - wassert(actual((*Varmatch::parse("B12101=273.15"))(var)).istrue()); - wassert(actual((*Varmatch::parse("B12101==274"))(var)).isfalse()); - wassert(actual((*Varmatch::parse("B12101=274"))(var)).isfalse()); - - wassert(actual((*Varmatch::parse("B12101<>274"))(var)).istrue()); - wassert(actual((*Varmatch::parse("B12101<>273.15"))(var)).isfalse()); - - wassert(actual((*Varmatch::parse("273<=B12101<=273.15"))(var)).istrue()); - wassert(actual((*Varmatch::parse("273.15<=B12101<=273.15"))(var)).istrue()); - wassert(actual((*Varmatch::parse("273.15<=B12101<=274"))(var)).istrue()); - wassert(actual((*Varmatch::parse("272<=B12101<=273"))(var)).isfalse()); -} - -template<> template<> void to::test<3>() -{ - Var var(varinfo(WR_VAR(0, 1, 11)), "enrico"); - - wassert(actual((*Varmatch::parse("B01011emanuele"))(var)).istrue()); - wassert(actual((*Varmatch::parse("B01011>enrico"))(var)).isfalse()); - wassert(actual((*Varmatch::parse("B01011>paolo"))(var)).isfalse()); - - wassert(actual((*Varmatch::parse("B01011>=emanuele"))(var)).istrue()); - wassert(actual((*Varmatch::parse("B01011>=enrico"))(var)).istrue()); - wassert(actual((*Varmatch::parse("B01011>=paolo"))(var)).isfalse()); - - wassert(actual((*Varmatch::parse("B01011==enrico"))(var)).istrue()); - wassert(actual((*Varmatch::parse("B01011=enrico"))(var)).istrue()); - wassert(actual((*Varmatch::parse("B01011==paolo"))(var)).isfalse()); - wassert(actual((*Varmatch::parse("B01011=paolo"))(var)).isfalse()); - - wassert(actual((*Varmatch::parse("B01011<>paolo"))(var)).istrue()); - wassert(actual((*Varmatch::parse("B01011<>enrico"))(var)).isfalse()); - - wassert(actual((*Varmatch::parse("emanuele<=B01011<=enrico"))(var)).istrue()); - wassert(actual((*Varmatch::parse("enrico<=B01011<=enrico"))(var)).istrue()); - wassert(actual((*Varmatch::parse("enrico<=B01011<=paolo"))(var)).istrue()); - wassert(actual((*Varmatch::parse("daniele<=B01011<=emanuele"))(var)).isfalse()); -} - -} diff --git a/dballe/db/db-basic-test.cc b/dballe/db/db-basic-test.cc new file mode 100644 index 000000000..633613cd0 --- /dev/null +++ b/dballe/db/db-basic-test.cc @@ -0,0 +1,187 @@ +#include "config.h" +#include "msg/msg.h" +#include "db/tests.h" +#include "db/mem/db.h" + +using namespace dballe; +using namespace dballe::db; +using namespace dballe::tests; +using namespace wreport; +using namespace std; + +namespace { + +class Tests : public FixtureTestCase +{ + using FixtureTestCase::FixtureTestCase; + + void register_tests() override + { + // Test simple queries + add_method("reset", [](Fixture& f) { + // Run twice to see if it is idempotent + auto& db = *f.db; + db.reset(); + db.reset(); + }); + add_method("repinfo", [](Fixture& f) { + // Test repinfo-related functions + auto& db = *f.db; + std::map prios = db.get_repinfo_priorities(); + wassert(actual(prios.find("synop") != prios.end()).istrue()); + wassert(actual(prios["synop"]) == 101); + + int added, deleted, updated; + db.update_repinfo((string(getenv("DBA_TESTDATA")) + "/test-repinfo1.csv").c_str(), &added, &deleted, &updated); + + wassert(actual(added) == 3); + wassert(actual(deleted) == 11); + wassert(actual(updated) == 2); + + prios = db.get_repinfo_priorities(); + wassert(actual(prios.find("fixspnpo") != prios.end()).istrue()); + wassert(actual(prios["fixspnpo"]) == 200); + }); + add_method("vacuum", [](Fixture& f) { + // Just invoke vacuum + auto& db = *f.db; + db.vacuum(); + }); + add_method("simple", [](Fixture& f) { + // Test remove_all + auto& db = *f.db; + db.remove_all(); + std::unique_ptr cur = db.query_data(core::Query()); + wassert(actual(cur->remaining()) == 0); + + // Check that it is idempotent + db.remove_all(); + cur = db.query_data(core::Query()); + wassert(actual(cur->remaining()) == 0); + + // Insert something + wassert(f.populate()); + + cur = db.query_data(core::Query()); + wassert(actual(cur->remaining()) == 4); + + db.remove_all(); + + cur = db.query_data(core::Query()); + wassert(actual(cur->remaining()) == 0); + }); + add_method("stationdata", [](Fixture& f) { + // Test adding station data for different networks + auto& db = *f.db; + db.reset(); + + // Insert two values in two networks + DataValues vals; + vals.info.coords = Coords(12.077, 44.600); + vals.info.report = "synop"; + vals.info.level = Level(103, 2000); + vals.info.trange = Trange::instant(); + vals.info.datetime = Datetime(2014, 1, 1, 0, 0, 0); + vals.values.set("B12101", 273.15); + db.insert_data(vals, true, true); + vals.clear_ids(); + vals.info.report = "temp"; + vals.values.set("B12101", 274.15); + db.insert_data(vals, true, true); + + // Insert station names in both networks + StationValues svals_camse; + svals_camse.info.coords = vals.info.coords; + svals_camse.info.report = "synop"; + svals_camse.values.set("B01019", "Camse"); + db.insert_station_data(svals_camse, true, true); + StationValues svals_esmac; + svals_esmac.info.coords = vals.info.coords; + svals_esmac.info.report = "temp"; + svals_esmac.values.set("B01019", "Esmac"); + db.insert_station_data(svals_esmac, true, true); + + // Query back all the data + auto cur = db.query_stations(core::Query()); + + // Check results + core::Record result; + if (dynamic_cast(f.db)) + { + // For mem databases, we get one record per (station, network) + // combination + wassert(actual(cur->next()).istrue()); + wassert(actual(cur->get_station_id()) == svals_esmac.info.ana_id); + wassert(actual(cur->get_rep_memo()) == "temp"); + cur->to_record(result); + wassert(actual(result["B01019"]) == "Esmac"); + + wassert(actual(cur->next()).istrue()); + wassert(actual(cur->get_station_id()) == svals_camse.info.ana_id); + wassert(actual(cur->get_rep_memo()) == "synop"); + cur->to_record(result); + wassert(actual(result["B01019"]) == "Camse"); + } else { + // For normal databases, we only get one record, with the station + // values merged keeping values for the best networks + wassert(actual(cur->next()).istrue()); + wassert(actual(cur->get_station_id()) == 1); + cur->to_record(result); + wassert(actual(result["B01019"]) == "Camse"); + + wassert(actual(cur->next()).isfalse()); + } + + Messages msgs; + db.export_msgs(core::Query(), [&](unique_ptr&& msg) { msgs.append(move(msg)); return true; }); + wassert(actual(msgs.size()) == 2); + + //msgs.print(stderr); + + wassert(actual(Msg::downcast(msgs[0]).get_rep_memo_var()->enqc()) == "synop"); + wassert(actual(Msg::downcast(msgs[0]).get_st_name_var()->enqc()) == "Camse"); + wassert(actual(Msg::downcast(msgs[0]).get_temp_2m_var()->enqd()) == 273.15); + wassert(actual(Msg::downcast(msgs[1]).get_rep_memo_var()->enqc()) == "temp"); + wassert(actual(Msg::downcast(msgs[1]).get_st_name_var()->enqc()) == "Esmac"); + wassert(actual(Msg::downcast(msgs[1]).get_temp_2m_var()->enqd()) == 274.15); + }); + add_method("query_ident", [](Fixture& f) { + // Try querying by ident + auto& db = *f.db; + + // Insert a mobile station + DataValues vals; + vals.info.report = "synop"; + vals.info.coords = Coords(44.10, 11.50); + vals.info.ident = "foo"; + vals.info.level = Level(1); + vals.info.trange = Trange::instant(); + vals.info.datetime = Datetime(2015, 4, 25, 12, 30, 45); + vals.values.set("B12101", 295.1); + db.insert_data(vals, true, true); + + wassert(actual(db).try_station_query("ident=foo", 1)); + wassert(actual(db).try_station_query("ident=bar", 0)); + wassert(actual(db).try_station_query("mobile=1", 1)); + wassert(actual(db).try_station_query("mobile=0", 0)); + wassert(actual(db).try_data_query("ident=foo", 1)); + wassert(actual(db).try_data_query("ident=bar", 0)); + wassert(actual(db).try_data_query("mobile=1", 1)); + wassert(actual(db).try_data_query("mobile=0", 0)); + }); + } +}; + +Tests tg1("db_basic_mem", nullptr, db::MEM); +Tests tg2("db_basic_v6_sqlite", "SQLITE", db::V6); +#ifdef HAVE_ODBC +Tests tg4("db_basic_v6_odbc", "ODBC", db::V6); +#endif +#ifdef HAVE_LIBPQ +Tests tg6("db_basic_v6_postgresql", "POSTGRESQL", db::V6); +#endif +#ifdef HAVE_MYSQL +Tests tg8("db_basic_v6_mysql", "MYSQL", db::V6); +#endif + +} diff --git a/dballe/db/db-basic-tut.cc b/dballe/db/db-basic-tut.cc deleted file mode 100644 index 0c9ad9c18..000000000 --- a/dballe/db/db-basic-tut.cc +++ /dev/null @@ -1,187 +0,0 @@ -#include "config.h" -#include "msg/msg.h" -#include "db/tests.h" -#include "db/mem/db.h" - -using namespace dballe; -using namespace dballe::db; -using namespace dballe::tests; -using namespace wreport; -using namespace wibble; -using namespace wibble::tests; -using namespace std; - -namespace { - -typedef dballe::tests::DBFixture Fixture; -typedef dballe::tests::db_test_group test_group; -typedef test_group::Test Test; - -std::vector tests { - // Test simple queries - Test("reset", [](Fixture& f) { - // Run twice to see if it is idempotent - auto& db = *f.db; - db.reset(); - db.reset(); - }), - Test("repinfo", [](Fixture& f) { - // Test repinfo-related functions - auto& db = *f.db; - std::map prios = db.get_repinfo_priorities(); - wassert(actual(prios.find("synop") != prios.end()).istrue()); - wassert(actual(prios["synop"]) == 101); - - int added, deleted, updated; - db.update_repinfo((string(getenv("DBA_TESTDATA")) + "/test-repinfo1.csv").c_str(), &added, &deleted, &updated); - - wassert(actual(added) == 3); - wassert(actual(deleted) == 11); - wassert(actual(updated) == 2); - - prios = db.get_repinfo_priorities(); - wassert(actual(prios.find("fixspnpo") != prios.end()).istrue()); - wassert(actual(prios["fixspnpo"]) == 200); - }), - Test("vacuum", [](Fixture& f) { - // Just invoke vacuum - auto& db = *f.db; - db.vacuum(); - }), - Test("simple", [](Fixture& f) { - // Test remove_all - auto& db = *f.db; - db.remove_all(); - std::unique_ptr cur = db.query_data(core::Query()); - wassert(actual(cur->remaining()) == 0); - - // Check that it is idempotent - db.remove_all(); - cur = db.query_data(core::Query()); - wassert(actual(cur->remaining()) == 0); - - // Insert something - wruntest(f.populate); - - cur = db.query_data(core::Query()); - wassert(actual(cur->remaining()) == 4); - - db.remove_all(); - - cur = db.query_data(core::Query()); - wassert(actual(cur->remaining()) == 0); - }), - Test("stationdata", [](Fixture& f) { - // Test adding station data for different networks - auto& db = *f.db; - db.reset(); - - // Insert two values in two networks - DataValues vals; - vals.info.coords = Coords(12.077, 44.600); - vals.info.report = "synop"; - vals.info.level = Level(103, 2000); - vals.info.trange = Trange::instant(); - vals.info.datetime = Datetime(2014, 1, 1, 0, 0, 0); - vals.values.set("B12101", 273.15); - db.insert_data(vals, true, true); - vals.clear_ids(); - vals.info.report = "temp"; - vals.values.set("B12101", 274.15); - db.insert_data(vals, true, true); - - // Insert station names in both networks - StationValues svals_camse; - svals_camse.info.coords = vals.info.coords; - svals_camse.info.report = "synop"; - svals_camse.values.set("B01019", "Camse"); - db.insert_station_data(svals_camse, true, true); - StationValues svals_esmac; - svals_esmac.info.coords = vals.info.coords; - svals_esmac.info.report = "temp"; - svals_esmac.values.set("B01019", "Esmac"); - db.insert_station_data(svals_esmac, true, true); - - // Query back all the data - auto cur = db.query_stations(core::Query()); - - // Check results - core::Record result; - if (dynamic_cast(f.db)) - { - // For mem databases, we get one record per (station, network) - // combination - wassert(actual(cur->next()).istrue()); - wassert(actual(cur->get_station_id()) == svals_esmac.info.ana_id); - wassert(actual(cur->get_rep_memo()) == "temp"); - cur->to_record(result); - wassert(actual(result["B01019"]) == "Esmac"); - - wassert(actual(cur->next()).istrue()); - wassert(actual(cur->get_station_id()) == svals_camse.info.ana_id); - wassert(actual(cur->get_rep_memo()) == "synop"); - cur->to_record(result); - wassert(actual(result["B01019"]) == "Camse"); - } else { - // For normal databases, we only get one record, with the station - // values merged keeping values for the best networks - wassert(actual(cur->next()).istrue()); - wassert(actual(cur->get_station_id()) == 1); - cur->to_record(result); - wassert(actual(result["B01019"]) == "Camse"); - - wassert(actual(cur->next()).isfalse()); - } - - Messages msgs; - db.export_msgs(core::Query(), [&](unique_ptr&& msg) { msgs.append(move(msg)); return true; }); - wassert(actual(msgs.size()) == 2); - - //msgs.print(stderr); - - wassert(actual(Msg::downcast(msgs[0]).get_rep_memo_var()->enqc()) == "synop"); - wassert(actual(Msg::downcast(msgs[0]).get_st_name_var()->enqc()) == "Camse"); - wassert(actual(Msg::downcast(msgs[0]).get_temp_2m_var()->enqd()) == 273.15); - wassert(actual(Msg::downcast(msgs[1]).get_rep_memo_var()->enqc()) == "temp"); - wassert(actual(Msg::downcast(msgs[1]).get_st_name_var()->enqc()) == "Esmac"); - wassert(actual(Msg::downcast(msgs[1]).get_temp_2m_var()->enqd()) == 274.15); - }), - Test("query_ident", [](Fixture& f) { - // Try querying by ident - auto& db = *f.db; - - // Insert a mobile station - DataValues vals; - vals.info.report = "synop"; - vals.info.coords = Coords(44.10, 11.50); - vals.info.ident = "foo"; - vals.info.level = Level(1); - vals.info.trange = Trange::instant(); - vals.info.datetime = Datetime(2015, 4, 25, 12, 30, 45); - vals.values.set("B12101", 295.1); - db.insert_data(vals, true, true); - - wassert(actual(db).try_station_query("ident=foo", 1)); - wassert(actual(db).try_station_query("ident=bar", 0)); - wassert(actual(db).try_station_query("mobile=1", 1)); - wassert(actual(db).try_station_query("mobile=0", 0)); - wassert(actual(db).try_data_query("ident=foo", 1)); - wassert(actual(db).try_data_query("ident=bar", 0)); - wassert(actual(db).try_data_query("mobile=1", 1)); - wassert(actual(db).try_data_query("mobile=0", 0)); - }), -}; - -test_group tg1("db_basic_mem", nullptr, db::MEM, tests); -test_group tg2("db_basic_v6_sqlite", "SQLITE", db::V6, tests); -#ifdef HAVE_ODBC -test_group tg4("db_basic_v6_odbc", "ODBC", db::V6, tests); -#endif -#ifdef HAVE_LIBPQ -test_group tg6("db_basic_v6_postgresql", "POSTGRESQL", db::V6, tests); -#endif -#ifdef HAVE_MYSQL -test_group tg8("db_basic_v6_mysql", "MYSQL", db::V6, tests); -#endif - -} diff --git a/dballe/db/db-export-test.cc b/dballe/db/db-export-test.cc new file mode 100644 index 000000000..5dcecf01b --- /dev/null +++ b/dballe/db/db-export-test.cc @@ -0,0 +1,142 @@ +#include "config.h" +#include "db/tests.h" +#include "db/db.h" +#include "dballe/record.h" +#include "msg/msg.h" + +using namespace dballe; +using namespace dballe::db; +using namespace dballe::tests; +using namespace wreport; +using namespace std; + +namespace { + +struct DBData : public TestDataSet +{ + DBData() + { + data["ds0"].info.coords = Coords(12.34560, 76.54321); + data["ds0"].info.report = "synop"; + data["ds0"].info.datetime = Datetime(1945, 4, 25, 8, 0); + data["ds0"].info.level = Level(1, 2, 0, 3); + data["ds0"].info.trange = Trange(4, 5, 6); + data["ds0"].values.set("B01012", 500); + data["ds1"] = data["ds0"]; + data["ds1"].info.datetime = Datetime(1945, 4, 26, 8, 0); + data["ds1"].values.set("B01012", 400); + + data["ds2"].info.coords = Coords(12.34560, 76.54321); + data["ds2"].info.report = "synop"; + data["ds2"].info.ident = "ciao"; + data["ds2"].info.datetime = Datetime(1945, 4, 26, 8, 0); + data["ds2"].info.level = Level(1, 2, 0, 3); + data["ds2"].info.trange = Trange(4, 5, 6); + data["ds2"].values.set("B01012", 300); + data["ds3"] = data["ds2"]; + data["ds3"].info.report = "metar"; + data["ds3"].values.set("B01012", 200); + } +}; + +class Tests : public FixtureTestCase +{ + using FixtureTestCase::FixtureTestCase; + + void register_tests() override + { + add_method("export", [](Fixture& f) { + // Simple export + auto& db = f.db; + wassert(f.populate()); + + // Put some data in the database and check that it gets exported properly + // Query back the data + Messages messages = dballe::tests::messages_from_db(*db, core::Query()); + wassert(actual(messages.size()) == 4u); + + int synmsg = 2; + int metmsg = 3; + if (Msg::downcast(messages[2]).type == MSG_METAR) + { + // Since the order here is not determined, enforce one + std::swap(synmsg, metmsg); + } + + wassert(actual(Msg::downcast(messages[0]).type) == MSG_SYNOP); + wassert(actual(messages[0], DBA_MSG_LATITUDE) == 12.34560); + wassert(actual(messages[0], DBA_MSG_LONGITUDE) == 76.54321); + wassert(actual(messages[0]).is_undef(DBA_MSG_IDENT)); + wassert(actual(messages[0].get_datetime()) == Datetime(1945, 4, 25, 8, 0, 0)); + wassert(actual(messages[0], WR_VAR(0, 1, 12), Level(1, 2, 0, 3), Trange(4, 5, 6)) == 500); + + wassert(actual(Msg::downcast(messages[1]).type) == MSG_SYNOP); + wassert(actual(messages[1], DBA_MSG_LATITUDE) == 12.34560); + wassert(actual(messages[1], DBA_MSG_LONGITUDE) == 76.54321); + wassert(actual(messages[1]).is_undef(DBA_MSG_IDENT)); + wassert(actual(messages[1].get_datetime()) == Datetime(1945, 4, 26, 8, 0, 0)); + wassert(actual(messages[1], WR_VAR(0, 1, 12), Level(1, 2, 0, 3), Trange(4, 5, 6)) == 400); + + wassert(actual(Msg::downcast(messages[synmsg]).type) == MSG_SYNOP); + wassert(actual(messages[synmsg], DBA_MSG_LATITUDE) == 12.34560); + wassert(actual(messages[synmsg], DBA_MSG_LONGITUDE) == 76.54321); + wassert(actual(messages[synmsg], DBA_MSG_IDENT), "ciao"); + wassert(actual(messages[synmsg].get_datetime()) == Datetime(1945, 4, 26, 8, 0, 0)); + wassert(actual(messages[synmsg], WR_VAR(0, 1, 12), Level(1, 2, 0, 3), Trange(4, 5, 6)) == 300); + + wassert(actual(Msg::downcast(messages[metmsg]).type) == MSG_METAR); + wassert(actual(messages[metmsg], DBA_MSG_LATITUDE) == 12.34560); + wassert(actual(messages[metmsg], DBA_MSG_LONGITUDE) == 76.54321); + wassert(actual(messages[metmsg], DBA_MSG_IDENT), "ciao"); + wassert(actual(messages[metmsg].get_datetime()) == Datetime(1945, 4, 26, 8, 0, 0)); + wassert(actual(messages[metmsg], WR_VAR(0, 1, 12), Level(1, 2, 0, 3), Trange(4, 5, 6)) == 200); + }); + add_method("export", [](Fixture& f) { + // Text exporting of extra station information + auto& db = f.db; + + // Import some data in the station extra information context + StationValues st; + // do not set datetime, level, trange, to insert a station variable + st.info.coords = Coords(45.0, 11.0); + st.info.report = "synop"; + st.values.set("B01001", 10); + db->insert_station_data(st, false, true); + + // Import one real datum + DataValues dv; + dv.info = st.info; + dv.info.datetime = Datetime(2000, 1, 1, 0, 0, 0); + dv.info.level = Level(103, 2000); + dv.info.trange = Trange(254, 0, 0); + dv.values.set("B12101", 290.0); + db->insert_data(dv, false, true); + + // Query back the data + Messages msgs = dballe::tests::messages_from_db(*db, core::Query()); + wassert(actual(msgs.size()) == 1u); + Msg& msg = Msg::downcast(msgs[0]); + + wassert(actual(msg.type) == MSG_SYNOP); + wassert(actual(msgs[0], DBA_MSG_LATITUDE) == 45.0); + wassert(actual(msgs[0], DBA_MSG_LONGITUDE) == 11.0); + wassert(actual(msgs[0]).is_undef(DBA_MSG_IDENT)); + wassert(actual(msgs[0], DBA_MSG_BLOCK) == 10); + wassert(actual(msgs[0], DBA_MSG_TEMP_2M) == 290.0); + }); + } +}; + +Tests tg1("db_export_mem", nullptr, db::MEM); +Tests tg2("db_export_v6_sqlite", "SQLITE", db::V6); +#ifdef HAVE_ODBC +Tests tg4("db_export_v6_odbc", "ODBC", db::V6); +#endif +#ifdef HAVE_LIBPQ +Tests tg6("db_export_v6_postgresql", "POSTGRESQL", db::V6); +#endif +#ifdef HAVE_MYSQL +Tests tg8("db_export_v6_mysql", "MYSQL", db::V6); +#endif + +} diff --git a/dballe/db/db-export-tut.cc b/dballe/db/db-export-tut.cc deleted file mode 100644 index c00aebbb2..000000000 --- a/dballe/db/db-export-tut.cc +++ /dev/null @@ -1,156 +0,0 @@ -#include "config.h" -#include "db/tests.h" -#include "db/db.h" -#include "dballe/record.h" -#include "msg/msg.h" - -using namespace dballe; -using namespace dballe::db; -using namespace wreport; -using namespace wibble::tests; -using namespace std; - -namespace { - -struct DBData : public dballe::tests::TestFixture -{ - DBData() - { - data["ds0"].info.coords = Coords(12.34560, 76.54321); - data["ds0"].info.report = "synop"; - data["ds0"].info.datetime = Datetime(1945, 4, 25, 8, 0); - data["ds0"].info.level = Level(1, 2, 0, 3); - data["ds0"].info.trange = Trange(4, 5, 6); - data["ds0"].values.set("B01012", 500); - data["ds1"] = data["ds0"]; - data["ds1"].info.datetime = Datetime(1945, 4, 26, 8, 0); - data["ds1"].values.set("B01012", 400); - - data["ds2"].info.coords = Coords(12.34560, 76.54321); - data["ds2"].info.report = "synop"; - data["ds2"].info.ident = "ciao"; - data["ds2"].info.datetime = Datetime(1945, 4, 26, 8, 0); - data["ds2"].info.level = Level(1, 2, 0, 3); - data["ds2"].info.trange = Trange(4, 5, 6); - data["ds2"].values.set("B01012", 300); - data["ds3"] = data["ds2"]; - data["ds3"].info.report = "metar"; - data["ds3"].values.set("B01012", 200); - } -}; - -struct Fixture : public dballe::tests::DBFixture -{ - Fixture() - { - } - - void populate_database() - { - wruntest(populate); - } -}; - - -typedef dballe::tests::db_test_group test_group; -typedef test_group::Test Test; - -std::vector tests { - Test("export", [](Fixture& f) { - // Simple export - auto& db = f.db; - f.populate_database(); - - // Put some data in the database and check that it gets exported properly - // Query back the data - Messages messages = dballe::tests::messages_from_db(*db, core::Query()); - ensure_equals(messages.size(), 4u); - - Msg* msgs[4]; - for (unsigned i = 0; i < 4; ++i) - msgs[i] = &Msg::downcast(messages[i]); - - if (msgs[2]->type == MSG_METAR) - { - // Since the order here is not determined, enforce one - Msg* tmp = msgs[2]; - msgs[2] = msgs[3]; - msgs[3] = tmp; - } - - ensure_equals(msgs[0]->type, MSG_SYNOP); - ensure_var_equals(want_var(*msgs[0], DBA_MSG_LATITUDE), 12.34560); - ensure_var_equals(want_var(*msgs[0], DBA_MSG_LONGITUDE), 76.54321); - ensure_msg_undef(*msgs[0], DBA_MSG_IDENT); - wassert(actual(msgs[0]->get_datetime()) == Datetime(1945, 4, 25, 8, 0, 0)); - ensure_var_equals(want_var(*msgs[0], WR_VAR(0, 1, 12), Level(1, 2, 0, 3), Trange(4, 5, 6)), 500); - - ensure_equals(msgs[1]->type, MSG_SYNOP); - ensure_var_equals(want_var(*msgs[1], DBA_MSG_LATITUDE), 12.34560); - ensure_var_equals(want_var(*msgs[1], DBA_MSG_LONGITUDE), 76.54321); - ensure_msg_undef(*msgs[1], DBA_MSG_IDENT); - wassert(actual(msgs[1]->get_datetime()) == Datetime(1945, 4, 26, 8, 0, 0)); - ensure_var_equals(want_var(*msgs[1], WR_VAR(0, 1, 12), Level(1, 2, 0, 3), Trange(4, 5, 6)), 400); - - ensure_equals(msgs[2]->type, MSG_SYNOP); - ensure_var_equals(want_var(*msgs[2], DBA_MSG_LATITUDE), 12.34560); - ensure_var_equals(want_var(*msgs[2], DBA_MSG_LONGITUDE), 76.54321); - ensure_var_equals(want_var(*msgs[2], DBA_MSG_IDENT), "ciao"); - wassert(actual(msgs[2]->get_datetime()) == Datetime(1945, 4, 26, 8, 0, 0)); - ensure_var_equals(want_var(*msgs[2], WR_VAR(0, 1, 12), Level(1, 2, 0, 3), Trange(4, 5, 6)), 300); - - ensure_equals(msgs[3]->type, MSG_METAR); - ensure_var_equals(want_var(*msgs[3], DBA_MSG_LATITUDE), 12.34560); - ensure_var_equals(want_var(*msgs[3], DBA_MSG_LONGITUDE), 76.54321); - ensure_var_equals(want_var(*msgs[3], DBA_MSG_IDENT), "ciao"); - wassert(actual(msgs[3]->get_datetime()) == Datetime(1945, 4, 26, 8, 0, 0)); - ensure_var_equals(want_var(*msgs[3], WR_VAR(0, 1, 12), Level(1, 2, 0, 3), Trange(4, 5, 6)), 200); - }), - Test("export", [](Fixture& f) { - // Text exporting of extra station information - auto& db = f.db; - - // Import some data in the station extra information context - StationValues st; - // do not set datetime, level, trange, to insert a station variable - st.info.coords = Coords(45.0, 11.0); - st.info.report = "synop"; - st.values.set("B01001", 10); - db->insert_station_data(st, false, true); - - // Import one real datum - DataValues dv; - dv.info = st.info; - dv.info.datetime = Datetime(2000, 1, 1, 0, 0, 0); - dv.info.level = Level(103, 2000); - dv.info.trange = Trange(254, 0, 0); - dv.values.set("B12101", 290.0); - db->insert_data(dv, false, true); - - // Query back the data - Messages msgs = dballe::tests::messages_from_db(*db, core::Query()); - ensure_equals(msgs.size(), 1u); - Msg& msg = Msg::downcast(msgs[0]); - - ensure_equals(msg.type, MSG_SYNOP); - ensure_var_equals(want_var(msg, DBA_MSG_LATITUDE), 45.0); - ensure_var_equals(want_var(msg, DBA_MSG_LONGITUDE), 11.0); - ensure_msg_undef(msg, DBA_MSG_IDENT); - ensure_var_equals(want_var(msg, DBA_MSG_BLOCK), 10); - ensure_var_equals(want_var(msg, DBA_MSG_TEMP_2M), 290.0); - }), -}; - -test_group tg1("db_export_mem", nullptr, db::MEM, tests); -test_group tg2("db_export_v6_sqlite", "SQLITE", db::V6, tests); -#ifdef HAVE_ODBC -test_group tg4("db_export_v6_odbc", "ODBC", db::V6, tests); -#endif -#ifdef HAVE_LIBPQ -test_group tg6("db_export_v6_postgresql", "POSTGRESQL", db::V6, tests); -#endif -#ifdef HAVE_MYSQL -test_group tg8("db_export_v6_mysql", "MYSQL", db::V6, tests); -#endif - -} diff --git a/dballe/db/db-import-test.cc b/dballe/db/db-import-test.cc new file mode 100644 index 000000000..16d8d1f83 --- /dev/null +++ b/dballe/db/db-import-test.cc @@ -0,0 +1,332 @@ +#include "config.h" +#include "db/tests.h" +#include "msg/msg.h" +#include "msg/context.h" +#include +#include + +using namespace dballe; +using namespace dballe::db; +using namespace dballe::tests; +using namespace wreport; +using namespace std; + +namespace { + +unsigned diff_msg(Message& first, Message& second, const char* tag) +{ + notes::Collect c(cerr); + int diffs = first.diff(second); + if (diffs) dballe::tests::track_different_msgs(first, second, tag); + return diffs; +} + +static void normalise_datetime(Msg& msg) +{ + msg::Context* ctx = msg.edit_context(Level(), Trange()); + if (!ctx) return; + + // Strip datetime variables + ctx->remove(WR_VAR(0, 4, 1)); + ctx->remove(WR_VAR(0, 4, 2)); + ctx->remove(WR_VAR(0, 4, 3)); + ctx->remove(WR_VAR(0, 4, 4)); + ctx->remove(WR_VAR(0, 4, 5)); + ctx->remove(WR_VAR(0, 4, 6)); +} + + +class Tests : public FixtureTestCase +{ + using FixtureTestCase::FixtureTestCase; + + void register_tests() override + { + add_method("crex", [](Fixture& f) { + auto& db = f.db; + core::Query query; + // Test import/export with all CREX samples + const char** files = dballe::tests::crex_files; + set blacklist; + // These files have no data to import + blacklist.insert("crex/test-synop1.crex"); + blacklist.insert("crex/test-synop3.crex"); + for (int i = 0; files[i] != NULL; ++i) + { + if (blacklist.find(files[i]) != blacklist.end()) continue; + try { + Messages inmsgs = read_msgs(files[i], File::CREX); + Msg& msg = Msg::downcast(inmsgs[0]); + normalise_datetime(msg); + + db->remove_all(); + db->import_msg(msg, NULL, DBA_IMPORT_ATTRS | DBA_IMPORT_FULL_PSEUDOANA); + + // Explicitly set the rep_memo variable that is added during export + msg.set_rep_memo(Msg::repmemo_from_type(msg.type)); + + query.clear(); + query.rep_memo = Msg::repmemo_from_type(msg.type); + + Messages msgs = wcallchecked(dballe::tests::messages_from_db(*db, query)); + wassert(actual(msgs.size()) == 1u); + wassert(actual(diff_msg(msg, msgs[0], "crex")) == 0); + } catch (std::exception& e) { + throw TestFailed(string("[") + files[i] + "] " + e.what()); + } + } + }); + add_method("bufr", [](Fixture& f) { + // Test import/export with all BUFR samples + auto& db = f.db; + core::Query query; + const char** files = dballe::tests::bufr_files; + for (int i = 0; files[i] != NULL; i++) + { + try { + Messages inmsgs = read_msgs(files[i], File::BUFR); + Msg& msg = Msg::downcast(inmsgs[0]); + normalise_datetime(msg); + + db->remove_all(); + wassert(db->import_msg(msg, NULL, DBA_IMPORT_ATTRS | DBA_IMPORT_FULL_PSEUDOANA)); + + query.clear(); + query.rep_memo = Msg::repmemo_from_type(msg.type); + + Messages msgs = dballe::tests::messages_from_db(*db, query); + wassert(actual(msgs.size()) == 1u); + + // Explicitly set the rep_memo variable that is added during export + msg.set_rep_memo(Msg::repmemo_from_type(msg.type)); + + wassert(actual(diff_msg(msg, msgs[0], "bufr")) == 0); + } catch (std::exception& e) { + throw TestFailed(string("[") + files[i] + "] " + e.what()); + } + } + }); + add_method("aof", [](Fixture& f) { + // Test import/export with all AOF samples + auto& db = f.db; + core::Query query; + const char** files = dballe::tests::aof_files; + for (int i = 0; files[i] != NULL; i++) + { + try { + Messages inmsgs = read_msgs(files[i], File::AOF); + Msg& msg = Msg::downcast(inmsgs[0]); + normalise_datetime(msg); + + db->remove_all(); + db->import_msg(msg, NULL, DBA_IMPORT_ATTRS | DBA_IMPORT_FULL_PSEUDOANA); + + // Explicitly set the rep_memo variable that is added during export + msg.set_rep_memo(Msg::repmemo_from_type(msg.type)); + + // db->dump(stderr); + + query.clear(); + query.rep_memo = Msg::repmemo_from_type(msg.type); + + Messages msgs = dballe::tests::messages_from_db(*db, query); + wassert(actual(msgs.size()) == 1u); + wassert(actual(diff_msg(msg, msgs[0], "bufr")) == 0); + } catch (std::exception& e) { + throw TestFailed(string("[") + files[i] + "] " + e.what()); + } + } + }); + add_method("multi", [](Fixture& f) { + // Check that multiple messages are correctly identified during export + auto& db = f.db; + core::Query query; + + // msg1 has latitude 33.88 + // msg2 has latitude 46.22 + Messages msgs1 = read_msgs("bufr/obs0-1.22.bufr", File::BUFR); + Messages msgs2 = read_msgs("bufr/obs0-3.504.bufr", File::BUFR); + Msg& msg1 = Msg::downcast(msgs1[0]); + Msg& msg2 = Msg::downcast(msgs2[0]); + + normalise_datetime(msg1); + normalise_datetime(msg2); + + db->remove_all(); + db->import_msg(msg1, NULL, DBA_IMPORT_ATTRS | DBA_IMPORT_FULL_PSEUDOANA); + db->import_msg(msg2, NULL, DBA_IMPORT_ATTRS | DBA_IMPORT_FULL_PSEUDOANA); + + // Explicitly set the rep_memo variable that is added during export + msg1.set_rep_memo(Msg::repmemo_from_type(msg1.type)); + msg2.set_rep_memo(Msg::repmemo_from_type(msg2.type)); + + query.clear(); + query.rep_memo = Msg::repmemo_from_type(msg1.type); + + // Warning: this test used to fail with older versions of MySQL. + // See http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=397597 + Messages msgs = dballe::tests::messages_from_db(*db, query); + wassert(actual(msgs.size()) == 2u); + + // Compare the two dba_msg + wassert(actual(diff_msg(msg1, msgs[0], "synop1")) == 0); + wassert(actual(diff_msg(msg2, msgs[1], "synop2")) == 0); + }); + add_method("auto_repinfo", [](Fixture& f) { + // Check automatic repinfo allocation + auto& db = f.db; + core::Query query; + Messages msgs = read_msgs("bufr/generic-new-repmemo.bufr", File::BUFR); + Msg& msg = Msg::downcast(msgs[0]); + + db->remove_all(); + db->import_msg(msg, NULL, DBA_IMPORT_ATTRS | DBA_IMPORT_FULL_PSEUDOANA); + + query.clear(); + query.rep_memo = "enrico"; + + Messages outmsgs = dballe::tests::messages_from_db(*db, query); + wassert(actual(outmsgs.size()) == 1u); + // Compare the two dba_msg + wassert(actual(diff_msg(msg, outmsgs[0], "enrico")) == 0); + }); + add_method("station_only", [](Fixture& f) { + // Check that a message that only contains station variables does get imported + auto& db = f.db; + Messages msgs = read_msgs("bufr/generic-onlystation.bufr", File::BUFR); + + db->remove_all(); + db->import_msg(msgs[0], NULL, DBA_IMPORT_ATTRS | DBA_IMPORT_FULL_PSEUDOANA); + + std::unique_ptr cur = db->query_stations(core::Query()); + wassert(actual(cur->remaining()) == 1); + wassert(actual(cur->next()).istrue()); + + core::Record result; + cur->to_record(result); + + const std::vector& vars = result.vars(); + wassert(actual(vars.size()) == 5); + wassert(actual(varcode_format(vars[0]->code())) == "B01019"); + wassert(actual(vars[0]->format()) == "My beautifull station"); + wassert(actual(varcode_format(vars[1]->code())) == "B01194"); + wassert(actual(vars[1]->format()) == "generic"); + wassert(actual(varcode_format(vars[2]->code())) == "B05001"); + wassert(actual(vars[2]->format()) == "45.00000"); + wassert(actual(varcode_format(vars[3]->code())) == "B06001"); + wassert(actual(vars[3]->format()) == "10.00000"); + wassert(actual(varcode_format(vars[4]->code())) == "B07030"); + wassert(actual(vars[4]->format()) == "22.3"); + }); + add_method("station_only_no_vars", [](Fixture& f) { + // Check that a message that only contains station variables does get imported + auto& db = f.db; + core::Record query; + Messages msgs = read_msgs("bufr/arpa-station.bufr", File::BUFR); + db->remove_all(); + try { + db->import_msg(msgs[0], NULL, DBA_IMPORT_ATTRS | DBA_IMPORT_FULL_PSEUDOANA); + wassert(actual(false).istrue()); + } catch (error_notfound& e) { + // ok. + } + + // Redo it with manually generated messages, this should not get imported + { + db->remove_all(); + Msg msg; + msg.type = MSG_GENERIC; + msg.set_rep_memo("synop"); + msg.set_latitude(44.53000); + msg.set_longitude(11.30000); + try { + db->import_msg(msg, NULL, DBA_IMPORT_ATTRS | DBA_IMPORT_FULL_PSEUDOANA); + wassert(actual(false).istrue()); + } catch (error_notfound& e) { + // ok. + } + } + + // Same but with a datetime set. This should not get imported, but it + // currently does because of a bug. I need to preserve the bug until + // the software that relies on it has been migrated to use standard + // DB-All.e features. + { + db->remove_all(); + Msg msg; + msg.type = MSG_GENERIC; + msg.set_rep_memo("synop"); + msg.set_latitude(44.53000); + msg.set_longitude(11.30000); + msg.set_datetime(Datetime(1000, 1, 1, 0, 0, 0)); +#warning TODO: fix this test to give an error once we do not need to support this bug anymore + //try { + db->import_msg(msg, NULL, DBA_IMPORT_ATTRS | DBA_IMPORT_FULL_PSEUDOANA); + //wassert(actual(false).istrue()); + //} catch (error_notfound& e) { + // ok. + //} + } + }); + add_method("import_dirty", [](Fixture& f) { + // Try importing into a dirty database, no attributes involved + auto& db = f.db; + core::Record query; + auto add_common = [](Msg& msg) { + msg.type = MSG_SYNOP; + msg.set_rep_memo("synop"); + msg.set_latitude(45.4); + msg.set_longitude(11.2); + msg.set_datetime(Datetime(2015, 4, 25, 12, 30, 45)); + }; + + // Build test messages + Msg first; + add_common(first); + first.set_block(1); // Station variable + first.set_station(2); // Station variable + first.set_temp_2m(280.1); // Data variable + first.set_wet_temp_2m(275.8); // Data variable + + Msg second; + add_common(second); + second.set_block(5); // Station variable, different value + second.set_station(2); // Station variable, same value + second.set_height_station(101.0); // Station variable, new value + second.set_temp_2m(281.1); // Data variable, different value + second.set_wet_temp_2m(275.8); // Data variable, same value + second.set_humidity(55.6); // Data variable, new value + + // Import the first message + db->remove_all(); + db->import_msg(first, NULL, DBA_IMPORT_FULL_PSEUDOANA | DBA_IMPORT_OVERWRITE); + + // Export and check + Messages export_first = dballe::tests::messages_from_db(*db, "rep_memo=synop"); + wassert(actual(export_first.size()) == 1); + wassert(actual(diff_msg(first, export_first[0], "first")) == 0); + + // Import the second message + db->import_msg(second, NULL, DBA_IMPORT_FULL_PSEUDOANA | DBA_IMPORT_OVERWRITE); + + // Export and check + Messages export_second = dballe::tests::messages_from_db(*db, "rep_memo=synop"); + wassert(actual(export_second.size()) == 1); + wassert(actual(diff_msg(second, export_second[0], "second")) == 0); + }); + } +}; + +Tests tg1("db_import_mem", nullptr, db::MEM); +Tests tg2("db_import_v6_sqlite", "SQLITE", db::V6); +#ifdef HAVE_ODBC +Tests tg4("db_import_v6_odbc", "ODBC", db::V6); +#endif +#ifdef HAVE_LIBPQ +Tests tg6("db_import_v6_postgresql", "POSTGRESQL", db::V6); +#endif +#ifdef HAVE_MYSQL +Tests tg8("db_import_v6_mysql", "MYSQL", db::V6); +#endif + +} diff --git a/dballe/db/db-import-tut.cc b/dballe/db/db-import-tut.cc deleted file mode 100644 index 64c9253cb..000000000 --- a/dballe/db/db-import-tut.cc +++ /dev/null @@ -1,330 +0,0 @@ -#include "config.h" -#include "db/tests.h" -#include "msg/msg.h" -#include "msg/context.h" -#include -#include - -using namespace dballe; -using namespace dballe::db; -using namespace wreport; -using namespace wibble::tests; -using namespace std; - -namespace { - -unsigned diff_msg(Message& first, Message& second, const char* tag) -{ - notes::Collect c(cerr); - int diffs = first.diff(second); - if (diffs) dballe::tests::track_different_msgs(first, second, tag); - return diffs; -} - -static void normalise_datetime(Msg& msg) -{ - msg::Context* ctx = msg.edit_context(Level(), Trange()); - if (!ctx) return; - - // Strip datetime variables - ctx->remove(WR_VAR(0, 4, 1)); - ctx->remove(WR_VAR(0, 4, 2)); - ctx->remove(WR_VAR(0, 4, 3)); - ctx->remove(WR_VAR(0, 4, 4)); - ctx->remove(WR_VAR(0, 4, 5)); - ctx->remove(WR_VAR(0, 4, 6)); -} - - -typedef dballe::tests::DBFixture Fixture; -typedef dballe::tests::db_test_group test_group; -typedef test_group::Test Test; - -std::vector tests { - Test("crex", [](Fixture& f) { - auto& db = f.db; - core::Query query; - // Test import/export with all CREX samples - const char** files = dballe::tests::crex_files; - set blacklist; - // These files have no data to import - blacklist.insert("crex/test-synop1.crex"); - blacklist.insert("crex/test-synop3.crex"); - for (int i = 0; files[i] != NULL; ++i) - { - if (blacklist.find(files[i]) != blacklist.end()) continue; - try { - Messages inmsgs = read_msgs(files[i], File::CREX); - Msg& msg = Msg::downcast(inmsgs[0]); - normalise_datetime(msg); - - db->remove_all(); - db->import_msg(msg, NULL, DBA_IMPORT_ATTRS | DBA_IMPORT_FULL_PSEUDOANA); - - // Explicitly set the rep_memo variable that is added during export - msg.set_rep_memo(Msg::repmemo_from_type(msg.type)); - - query.clear(); - query.rep_memo = Msg::repmemo_from_type(msg.type); - - Messages msgs = wcallchecked(dballe::tests::messages_from_db(*db, query)); - ensure_equals(msgs.size(), 1u); - wassert(actual(diff_msg(msg, msgs[0], "crex")) == 0); - } catch (std::exception& e) { - throw tut::failure(string("[") + files[i] + "] " + e.what()); - } - } - }), - Test("bufr", [](Fixture& f) { - // Test import/export with all BUFR samples - auto& db = f.db; - core::Query query; - const char** files = dballe::tests::bufr_files; - for (int i = 0; files[i] != NULL; i++) - { - try { - Messages inmsgs = read_msgs(files[i], File::BUFR); - Msg& msg = Msg::downcast(inmsgs[0]); - normalise_datetime(msg); - - db->remove_all(); - wrunchecked(db->import_msg(msg, NULL, DBA_IMPORT_ATTRS | DBA_IMPORT_FULL_PSEUDOANA)); - - query.clear(); - query.rep_memo = Msg::repmemo_from_type(msg.type); - - Messages msgs = dballe::tests::messages_from_db(*db, query); - ensure_equals(msgs.size(), 1u); - - // Explicitly set the rep_memo variable that is added during export - msg.set_rep_memo(Msg::repmemo_from_type(msg.type)); - - wassert(actual(diff_msg(msg, msgs[0], "bufr")) == 0); - } catch (std::exception& e) { - throw tut::failure(string("[") + files[i] + "] " + e.what()); - } - } - }), - Test("aof", [](Fixture& f) { - // Test import/export with all AOF samples - auto& db = f.db; - core::Query query; - const char** files = dballe::tests::aof_files; - for (int i = 0; files[i] != NULL; i++) - { - try { - Messages inmsgs = read_msgs(files[i], File::AOF); - Msg& msg = Msg::downcast(inmsgs[0]); - normalise_datetime(msg); - - db->remove_all(); - db->import_msg(msg, NULL, DBA_IMPORT_ATTRS | DBA_IMPORT_FULL_PSEUDOANA); - - // Explicitly set the rep_memo variable that is added during export - msg.set_rep_memo(Msg::repmemo_from_type(msg.type)); - - // db->dump(stderr); - - query.clear(); - query.rep_memo = Msg::repmemo_from_type(msg.type); - - Messages msgs = dballe::tests::messages_from_db(*db, query); - ensure_equals(msgs.size(), 1u); - wassert(actual(diff_msg(msg, msgs[0], "bufr")) == 0); - } catch (std::exception& e) { - throw tut::failure(string("[") + files[i] + "] " + e.what()); - } - } - }), - Test("multi", [](Fixture& f) { - // Check that multiple messages are correctly identified during export - auto& db = f.db; - core::Query query; - - // msg1 has latitude 33.88 - // msg2 has latitude 46.22 - Messages msgs1 = read_msgs("bufr/obs0-1.22.bufr", File::BUFR); - Messages msgs2 = read_msgs("bufr/obs0-3.504.bufr", File::BUFR); - Msg& msg1 = Msg::downcast(msgs1[0]); - Msg& msg2 = Msg::downcast(msgs2[0]); - - normalise_datetime(msg1); - normalise_datetime(msg2); - - db->remove_all(); - db->import_msg(msg1, NULL, DBA_IMPORT_ATTRS | DBA_IMPORT_FULL_PSEUDOANA); - db->import_msg(msg2, NULL, DBA_IMPORT_ATTRS | DBA_IMPORT_FULL_PSEUDOANA); - - // Explicitly set the rep_memo variable that is added during export - msg1.set_rep_memo(Msg::repmemo_from_type(msg1.type)); - msg2.set_rep_memo(Msg::repmemo_from_type(msg2.type)); - - query.clear(); - query.rep_memo = Msg::repmemo_from_type(msg1.type); - - // Warning: this test used to fail with older versions of MySQL. - // See http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=397597 - Messages msgs = dballe::tests::messages_from_db(*db, query); - ensure_equals(msgs.size(), 2u); - - // Compare the two dba_msg - wassert(actual(diff_msg(msg1, msgs[0], "synop1")) == 0); - wassert(actual(diff_msg(msg2, msgs[1], "synop2")) == 0); - }), - Test("auto_repinfo", [](Fixture& f) { - // Check automatic repinfo allocation - auto& db = f.db; - core::Query query; - Messages msgs = read_msgs("bufr/generic-new-repmemo.bufr", File::BUFR); - Msg& msg = Msg::downcast(msgs[0]); - - db->remove_all(); - db->import_msg(msg, NULL, DBA_IMPORT_ATTRS | DBA_IMPORT_FULL_PSEUDOANA); - - query.clear(); - query.rep_memo = "enrico"; - - Messages outmsgs = dballe::tests::messages_from_db(*db, query); - wassert(actual(outmsgs.size()) == 1u); - // Compare the two dba_msg - wassert(actual(diff_msg(msg, outmsgs[0], "enrico")) == 0); - }), - Test("station_only", [](Fixture& f) { - // Check that a message that only contains station variables does get imported - auto& db = f.db; - Messages msgs = read_msgs("bufr/generic-onlystation.bufr", File::BUFR); - - db->remove_all(); - db->import_msg(msgs[0], NULL, DBA_IMPORT_ATTRS | DBA_IMPORT_FULL_PSEUDOANA); - - std::unique_ptr cur = db->query_stations(core::Query()); - wassert(actual(cur->remaining()) == 1); - wassert(actual(cur->next()).istrue()); - - core::Record result; - cur->to_record(result); - - const std::vector& vars = result.vars(); - wassert(actual(vars.size()) == 5); - wassert(actual(varcode_format(vars[0]->code())) == "B01019"); - wassert(actual(vars[0]->format()) == "My beautifull station"); - wassert(actual(varcode_format(vars[1]->code())) == "B01194"); - wassert(actual(vars[1]->format()) == "generic"); - wassert(actual(varcode_format(vars[2]->code())) == "B05001"); - wassert(actual(vars[2]->format()) == "45.00000"); - wassert(actual(varcode_format(vars[3]->code())) == "B06001"); - wassert(actual(vars[3]->format()) == "10.00000"); - wassert(actual(varcode_format(vars[4]->code())) == "B07030"); - wassert(actual(vars[4]->format()) == "22.3"); - }), - Test("station_only_no_vars", [](Fixture& f) { - // Check that a message that only contains station variables does get imported - auto& db = f.db; - core::Record query; - Messages msgs = read_msgs("bufr/arpa-station.bufr", File::BUFR); - db->remove_all(); - try { - db->import_msg(msgs[0], NULL, DBA_IMPORT_ATTRS | DBA_IMPORT_FULL_PSEUDOANA); - wassert(actual(false).istrue()); - } catch (error_notfound& e) { - // ok. - } - - // Redo it with manually generated messages, this should not get imported - { - db->remove_all(); - Msg msg; - msg.type = MSG_GENERIC; - msg.set_rep_memo("synop"); - msg.set_latitude(44.53000); - msg.set_longitude(11.30000); - try { - db->import_msg(msg, NULL, DBA_IMPORT_ATTRS | DBA_IMPORT_FULL_PSEUDOANA); - wassert(actual(false).istrue()); - } catch (error_notfound& e) { - // ok. - } - } - - // Same but with a datetime set. This should not get imported, but it - // currently does because of a bug. I need to preserve the bug until - // the software that relies on it has been migrated to use standard - // DB-All.e features. - { - db->remove_all(); - Msg msg; - msg.type = MSG_GENERIC; - msg.set_rep_memo("synop"); - msg.set_latitude(44.53000); - msg.set_longitude(11.30000); - msg.set_datetime(Datetime(1000, 1, 1, 0, 0, 0)); -#warning TODO: fix this test to give an error once we do not need to support this bug anymore - //try { - db->import_msg(msg, NULL, DBA_IMPORT_ATTRS | DBA_IMPORT_FULL_PSEUDOANA); - //wassert(actual(false).istrue()); - //} catch (error_notfound& e) { - // ok. - //} - } - }), - Test("import_dirty", [](Fixture& f) { - // Try importing into a dirty database, no attributes involved - auto& db = f.db; - core::Record query; - auto add_common = [](Msg& msg) { - msg.type = MSG_SYNOP; - msg.set_rep_memo("synop"); - msg.set_latitude(45.4); - msg.set_longitude(11.2); - msg.set_datetime(Datetime(2015, 4, 25, 12, 30, 45)); - }; - - // Build test messages - Msg first; - add_common(first); - first.set_block(1); // Station variable - first.set_station(2); // Station variable - first.set_temp_2m(280.1); // Data variable - first.set_wet_temp_2m(275.8); // Data variable - - Msg second; - add_common(second); - second.set_block(5); // Station variable, different value - second.set_station(2); // Station variable, same value - second.set_height_station(101.0); // Station variable, new value - second.set_temp_2m(281.1); // Data variable, different value - second.set_wet_temp_2m(275.8); // Data variable, same value - second.set_humidity(55.6); // Data variable, new value - - // Import the first message - db->remove_all(); - db->import_msg(first, NULL, DBA_IMPORT_FULL_PSEUDOANA | DBA_IMPORT_OVERWRITE); - - // Export and check - Messages export_first = dballe::tests::messages_from_db(*db, "rep_memo=synop"); - wassert(actual(export_first.size()) == 1); - wassert(actual(diff_msg(first, export_first[0], "first")) == 0); - - // Import the second message - db->import_msg(second, NULL, DBA_IMPORT_FULL_PSEUDOANA | DBA_IMPORT_OVERWRITE); - - // Export and check - Messages export_second = dballe::tests::messages_from_db(*db, "rep_memo=synop"); - wassert(actual(export_second.size()) == 1); - wassert(actual(diff_msg(second, export_second[0], "second")) == 0); - }), -}; - -test_group tg1("db_import_mem", nullptr, db::MEM, tests); -test_group tg2("db_import_v6_sqlite", "SQLITE", db::V6, tests); -#ifdef HAVE_ODBC -test_group tg4("db_import_v6_odbc", "ODBC", db::V6, tests); -#endif -#ifdef HAVE_LIBPQ -test_group tg6("db_import_v6_postgresql", "POSTGRESQL", db::V6, tests); -#endif -#ifdef HAVE_MYSQL -test_group tg8("db_import_v6_mysql", "MYSQL", db::V6, tests); -#endif - -} diff --git a/dballe/db/db-misc-test.cc b/dballe/db/db-misc-test.cc new file mode 100644 index 000000000..b9aecdebe --- /dev/null +++ b/dballe/db/db-misc-test.cc @@ -0,0 +1,925 @@ +#include "config.h" +#include "db/tests.h" +#include "db/querybuf.h" +#include "db/mem/db.h" +#include + +using namespace dballe; +using namespace dballe::db; +using namespace dballe::tests; +using namespace wreport; +using namespace std; + +namespace { + +struct NavileDataSet : public TestDataSet +{ + NavileDataSet() + { + stations["synop"].info.coords = Coords(44.5008, 11.3288); + stations["synop"].info.report = "synop"; + stations["synop"].values.set("B07030", 78); // Height + } +}; + +unsigned run_attr_query_station(DB& db, int data_id, Values& dest) +{ + unsigned count = 0; + db.attr_query_station(data_id, [&](unique_ptr var) { dest.set(move(var)); ++count; }); + return count; +} + +unsigned run_attr_query_data(DB& db, int data_id, Values& dest) +{ + unsigned count = 0; + db.attr_query_data(data_id, [&](unique_ptr var) { dest.set(move(var)); ++count; }); + return count; +} + + +class Tests : public FixtureTestCase +{ + using FixtureTestCase::FixtureTestCase; + + void register_tests() override + { + add_method("insert", [](Fixture& f) { + // Test a simple insert round trip + auto& db = *f.db; + + // Insert some data + NavileDataSet ds; + ds.data["synop"].info = ds.stations["synop"].info; + ds.data["synop"].info.datetime = Datetime(2013, 10, 16, 10); + ds.data["synop"].values.set(WR_VAR(0, 12, 101), 16.5); + wassert(f.populate_database(ds)); + + Values attrs; + attrs.set("B33007", 50); + wassert(db.attr_insert_data(ds.data["synop"].values[WR_VAR(0, 12, 101)].data_id, attrs)); + + // Query and verify the station data + { + auto cur = db.query_stations(core::Query()); + wassert(actual(cur->remaining()) == 1); + cur->next(); + wassert(actual(cur).station_vars_match(ds.stations["synop"])); + } + + // Query and verify the measured data + { + auto cur = db.query_data(core::Query()); + wassert(actual(cur->remaining()) == 1); + cur->next(); + wassert(actual(cur).data_context_matches(ds.data["synop"])); + wassert(actual(cur).data_var_matches(ds.data["synop"], WR_VAR(0, 12, 101))); + } + + // Query and verify attributes + { + int count = 0; + unique_ptr attr; + wassert(db.attr_query_data(ds.data["synop"].values[WR_VAR(0, 12, 101)].data_id, [&](std::unique_ptr&& var) { + ++count; + attr = move(var); + })); + wassert(actual(count) == 1); + wassert(actual(attr->code()) == WR_VAR(0, 33, 7)); + wassert(actual(attr->enq(MISSING_INT)) == 50); + } + }); + add_method("insert_perms", [](Fixture& f) { + // Test insert + auto& db = *f.db; + OldDballeTestDataSet oldf; + + // Check if adding a nonexisting station when not allowed causes an error + try { + db.insert_data(oldf.data["synop"], false, false); + throw TestFailed("error_consistency should have been thrown"); + } catch (error_consistency& e) { + wassert(actual(e.what()).contains("insert a station entry when it is forbidden")); + } catch (error_notfound& e) { + wassert(actual(e.what()).contains("station not found")); + } + wassert(actual(oldf.data["synop"].info.ana_id) == MISSING_INT); + wassert(actual(oldf.data["synop"].values["B01011"].data_id) == MISSING_INT); + wassert(actual(oldf.data["synop"].values["B01012"].data_id) == MISSING_INT); + oldf.data["synop"].clear_ids(); + + // Insert the record + wassert(db.insert_data(oldf.data["synop"], false, true)); + oldf.data["synop"].clear_ids(); + // Check if duplicate updates are allowed by insert + wassert(db.insert_data(oldf.data["synop"], true, false)); + oldf.data["synop"].clear_ids(); + // Check if overwrites are trapped by insert_new + oldf.data["synop"].values.set("B01011", "DB-All.e?"); + try { + db.insert_data(oldf.data["synop"], false, false); + throw TestFailed("wreport::error should have been thrown"); + } catch (wreport::error& e) { + wassert(actual(e.what()).matches("refusing to overwrite existing data|cannot replace an existing value|Duplicate entry")); + } + }); + add_method("insert_twice", [](Fixture& f) { + // Test double station insert + auto& db = *f.db; + OldDballeTestDataSet oldf; + + // Insert the record twice + wassert(db.insert_data(oldf.data["synop"], false, true)); + // This should fail, refusing to replace station info + oldf.data["synop"].values.set("B01011", "DB-All.e?"); + try { + db.insert_data(oldf.data["synop"], false, true); + throw TestFailed("wreport::error should have been thrown"); + } catch (wreport::error& e) { + wassert(actual(e.what()).matches("refusing to overwrite existing data|cannot replace an existing value|Duplicate entry")); + } + }); + add_method("query_station", [](Fixture& f) { + // Test station query + auto& db = *f.db; + OldDballeTestDataSet oldf; + wassert(f.populate_database(oldf)); + + // Iterate the station database + auto cur = db.query_stations(core::Query()); + + if (dynamic_cast(f.db)) + { + // Memdb has one station entry per (lat, lon, ident, network) + wassert(actual(cur->remaining()) == 2); + + wassert(actual(cur->next()).istrue()); + wassert(actual(cur->get_lat()) == 12.34560); + wassert(actual(cur->get_lon()) == 76.54320); + wassert(actual(cur->get_rep_memo()) == "synop"); + wassert(actual((void*)cur->get_ident()) == (void*)0); + wassert(actual(cur).station_keys_match(oldf.stations["synop"].info)); + + wassert(actual(cur->next()).istrue()); + wassert(actual(cur->get_lat()) == 12.34560); + wassert(actual(cur->get_lon()) == 76.54320); + wassert(actual(cur->get_rep_memo()) == "metar"); + wassert(actual((void*)cur->get_ident()) == (void*)0); + wassert(actual(cur).station_keys_match(oldf.stations["metar"].info)); + } else { + // V5 and V6 have one station entry (lat, lon, ident) + wassert(actual(cur->remaining()) == 1); + + // There should be an item + wassert(actual(cur->next()).istrue()); + wassert(actual(cur->get_lat()) == 12.34560); + wassert(actual(cur->get_lon()) == 76.54320); + wassert(actual((void*)cur->get_ident()) == (void*)0); + + // Check that the result matches + wassert(actual(cur).station_keys_match(oldf.stations["metar"].info)); + + // There should be only one item + } + wassert(actual(cur->remaining()) == 0); + wassert(actual(cur->next()).isfalse()); + }); + add_method("query_best", [](Fixture& f) { + // Test querybest + auto& db = *f.db; + OldDballeTestDataSet oldf; + wassert(f.populate_database(oldf)); + + //if (db.server_type == ORACLE || db.server_type == POSTGRES) + // return; + + // Prepare a query + core::Query query; + query.latrange = LatRange(1000000, LatRange::IMAX); + query.query = "best"; + + // Make the query + auto cur = db.query_data(query); + + wassert(actual(cur->remaining()) == 4); + + // There should be four items + wassert(actual(cur->next()).istrue()); + wassert(actual(cur->get_lat()) == 12.34560); + wassert(actual(cur->get_lon()) == 76.54320); + wassert(actual((void*)cur->get_ident()) == (void*)0); + wassert(actual((void*)cur->get_rep_memo()).istrue()); + wassert(actual(cur->get_rep_memo()) == "synop"); + wassert(actual(cur->get_level()) == Level(10, 11, 15, 22)); + wassert(actual(cur->get_trange()) == Trange(20, 111, 122)); + wassert(actual(cur->get_varcode()) == WR_VAR(0, 1, 11)); + wassert(actual(cur->get_var().code()) == WR_VAR(0, 1, 11)); + wassert(actual(cur->remaining()) == 3); + wassert(actual(cur->next()).istrue()); + wassert(actual(cur->remaining()) == 2); + wassert(actual(cur->next()).istrue()); + wassert(actual(cur->remaining()) == 1); + wassert(actual(cur->next()).istrue()); + wassert(actual(cur->remaining()) == 0); + + // Now there should not be anything anymore + wassert(actual(cur->next()).isfalse()); + }); + add_method("delete", [](Fixture& f) { + // Test deletion + auto& db = *f.db; + OldDballeTestDataSet oldf; + wassert(f.populate_database(oldf)); + + // 4 items to begin with + core::Query query; + auto cur = db.query_data(query); + wassert(actual(cur->remaining()) == 4); + cur->discard_rest(); + + query.clear(); + query.datetime = DatetimeRange(Datetime(1945, 4, 25, 8, 10), Datetime()); + db.remove(query); + + // 2 remaining after remove + query.clear(); + cur = db.query_data(query); + wassert(actual(cur->remaining()) == 2); + cur->discard_rest(); + + // Did it remove the right ones? + query.clear(); + query.latrange = LatRange(10.0, LatRange::DMAX); + cur = db.query_data(query); + wassert(actual(cur->remaining()) == 2); + wassert(actual(cur->next()).istrue()); + wassert(actual(cur).data_context_matches(oldf.data["synop"])); + + Varcode last_code = 0; + for (unsigned i = 0; i < 2; ++i) + { + // Check that varcodes do not repeat + if (last_code != 0) + wassert(actual(cur->get_varcode()) != last_code); + last_code = cur->get_varcode(); + + switch (last_code) + { + case WR_VAR(0, 1, 11): + case WR_VAR(0, 1, 12): + wassert(actual(cur).data_var_matches(oldf.data["synop"], last_code)); + break; + default: + throw TestFailed("got a varcode that we did not ask for: " + varcode_format(last_code)); + } + + if (i == 0) + { + /* The item should have two data in it */ + wassert(actual(cur->next()).istrue()); + } else { + wassert(actual(cur->next()).isfalse()); + } + } + }); + add_method("query_datetime", [](Fixture& f) { + // Test datetime queries + auto& db = *f.db; + + /* Prepare test data */ + DataValues base; + base.info.coords = Coords(12.0, 48.0); + base.info.report = "synop"; + base.info.level = Level(1, 0, 1, 0); + base.info.trange = Trange(1, 0, 0); + base.values.set("B01012", 500); + +#define WANTRESULT(querystr, ab) do { \ + core::Record result; \ + auto cur = db.query_data(*dballe::tests::query_from_string(querystr)); \ + wassert(actual(cur->remaining()) == 1); \ + wassert(actual(cur->next()).istrue()); \ + cur->to_record(result); \ + wassert(actual(cur->remaining()) == 0); \ + wassert(actual_varcode(result.vars()[0]->code()) == WR_VAR(0, 1, 12)); \ + wassert(actual(cur->get_datetime()) == ab.info.datetime); \ + cur->discard_rest(); \ + } while(0) + + DataValues a, b; + + /* Year */ + db.reset(); + a = base; + a.info.datetime = Datetime(2005); + db.insert_data(a, false, true); + b = base; + b.info.datetime = Datetime(2006); + db.insert_data(b, false, false); + WANTRESULT("yearmin=2006", b); + WANTRESULT("yearmax=2005", a); + WANTRESULT("year=2006", b); + + /* Month */ + db.reset(); + a = base; + a.info.datetime = Datetime(2006, 4); + db.insert_data(a, false, true); + b = base; + b.info.datetime = Datetime(2006, 5); + db.insert_data(b, false, false); + WANTRESULT("year=2006, monthmin=5", b); + WANTRESULT("year=2006, monthmax=4", a); + WANTRESULT("year=2006, month=5", b); + + /* Day */ + db.reset(); + a = base; + a.info.datetime = Datetime(2006, 5, 2); + db.insert_data(a, false, true); + b = base; + b.info.datetime = Datetime(2006, 5, 3); + db.insert_data(b, false, false); + WANTRESULT("year=2006, month=5, daymin=3", b); + WANTRESULT("year=2006, month=5, daymax=2", a); + WANTRESULT("year=2006, month=5, day=3", b); + + /* Hour */ + db.reset(); + a = base; + a.info.datetime = Datetime(2006, 5, 3, 12); + db.insert_data(a, false, true); + b = base; + b.info.datetime = Datetime(2006, 5, 3, 13); + db.insert_data(b, false, false); + WANTRESULT("year=2006, month=5, day=3, hourmin=13", b); + WANTRESULT("year=2006, month=5, day=3, hourmax=12", a); + WANTRESULT("year=2006, month=5, day=3, hour=13", b); + + /* Minute */ + db.reset(); + a = base; + a.info.datetime = Datetime(2006, 5, 3, 12, 29); + db.insert_data(a, false, true); + b = base; + b.info.datetime = Datetime(2006, 5, 3, 12, 30); + db.insert_data(b, false, false); + WANTRESULT("year=2006, month=5, day=3, hour=12, minumin=30", b); + WANTRESULT("year=2006, month=5, day=3, hour=12, minumax=29", a); + WANTRESULT("year=2006, month=5, day=3, hour=12, min=30", b); + }); + add_method("attrs", [](Fixture& f) { + // Test QC + auto& db = *f.db; + OldDballeTestDataSet oldf; + wassert(f.populate_database(oldf)); + + core::Query query; + core::Record result; + query.latrange.set(1000000, LatRange::IMAX); + auto cur = db.query_data(query); + + // Move the cursor to B01011 + int context_id; + bool found = false; + while (cur->next()) + { + cur->to_record(result); + if (result.vars()[0]->code() == WR_VAR(0, 1, 11)) + { + context_id = cur->attr_reference_id(); + cur->discard_rest(); + found = true; + break; + } + } + wassert(actual(found).istrue()); + + // Insert new attributes about this report + Values qc; + qc.set("B33002", 2); + qc.set("B33003", 5); + qc.set("B33005", 33); + db.attr_insert_data(context_id, qc); + + // Query back the data + qc.clear(); + wassert(actual(run_attr_query_data(db, context_id, qc)) == 3); + + const auto* attr = qc.get("B33002"); + wassert(actual(attr).istrue()); + wassert(actual(attr->var->enqi()) == 2); + + attr = qc.get("B33003"); + wassert(actual(attr).istrue()); + wassert(actual(attr->var->enqi()) == 5); + + attr = qc.get("B33005"); + wassert(actual(attr).istrue()); + wassert(actual(attr->var->enqi()) == 33); + + // Delete a couple of items + vector codes; + codes.push_back(WR_VAR(0, 33, 2)); + codes.push_back(WR_VAR(0, 33, 5)); + db.attr_remove_data(context_id, codes); + + // Deleting non-existing items should not fail. Also try creating a + // query with just one item + codes.clear(); + codes.push_back(WR_VAR(0, 33, 2)); + db.attr_remove_data(context_id, codes); + + /* Query back the data */ + qc.clear(); + wassert(actual(run_attr_query_data(db, context_id, qc)) == 1); + + wassert(actual(qc.get("B33002")).isfalse()); + wassert(actual(qc.get("B33005")).isfalse()); + attr = qc.get("B33003"); + wassert(actual(attr).istrue()); + wassert(actual(attr->var->enqi()) == 5); + /*dba_error_remove_callback(DBA_ERR_NONE, crash, 0);*/ + }); + add_method("query_station", [](Fixture& f) { + // Test station queries + auto& db = *f.db; + OldDballeTestDataSet oldf; + wassert(f.populate_database(oldf)); + + auto cur = db.query_stations(*query_from_string("rep_memo=synop")); + wassert(actual(cur->remaining()) == 1); + + wassert(actual(cur->next()).istrue()); + wassert(actual(cur->next()).isfalse()); + }); + add_method("attrs1", [](Fixture& f) { + // Test attributes + auto& db = *f.db; + OldDballeTestDataSet oldf; + + // Insert a data record + db.insert_data(oldf.data["synop"], true, true); + + Values qc; + qc.set("B01007", 1); + qc.set("B02048", 2); + qc.set("B05040", 3); + qc.set("B05041", 4); + qc.set("B05043", 5); + qc.set("B33032", 6); + qc.set("B07024", 7); + qc.set("B05021", 8); + qc.set("B07025", 9); + qc.set("B05022", 10); + db.attr_insert_data(oldf.data["synop"].values[WR_VAR(0, 1, 11)].data_id, qc); + + // Query back the B01011 variable to read the attr reference id + auto cur = db.query_data(*query_from_string("var=B01011")); + wassert(actual(cur->remaining()) == 1); + cur->next(); + int attr_id = cur->attr_reference_id(); + cur->discard_rest(); + + qc.clear(); + wassert(actual(run_attr_query_data(db, attr_id, qc)) == 10); + + // Check that all the attributes come out + wassert(actual(qc.size()) == 10); + wassert(actual_varcode(qc["B01007"].var->code()) == WR_VAR(0, 1, 7)); wassert(actual(*qc["B01007"].var) == 1); + wassert(actual_varcode(qc["B02048"].var->code()) == WR_VAR(0, 2, 48)); wassert(actual(*qc["B02048"].var) == 2); + wassert(actual_varcode(qc["B05021"].var->code()) == WR_VAR(0, 5, 21)); wassert(actual(*qc["B05021"].var) == 8); + wassert(actual_varcode(qc["B05022"].var->code()) == WR_VAR(0, 5, 22)); wassert(actual(*qc["B05022"].var) == 10); + wassert(actual_varcode(qc["B05040"].var->code()) == WR_VAR(0, 5, 40)); wassert(actual(*qc["B05040"].var) == 3); + wassert(actual_varcode(qc["B05041"].var->code()) == WR_VAR(0, 5, 41)); wassert(actual(*qc["B05041"].var) == 4); + wassert(actual_varcode(qc["B05043"].var->code()) == WR_VAR(0, 5, 43)); wassert(actual(*qc["B05043"].var) == 5); + wassert(actual_varcode(qc["B07024"].var->code()) == WR_VAR(0, 7, 24)); wassert(actual(*qc["B07024"].var) == 7); + wassert(actual_varcode(qc["B07025"].var->code()) == WR_VAR(0, 7, 25)); wassert(actual(*qc["B07025"].var) == 9); + wassert(actual_varcode(qc["B33032"].var->code()) == WR_VAR(0, 33, 32)); wassert(actual(*qc["B33032"].var) == 6); + }); + add_method("longitude_wrap", [](Fixture& f) { + // Test longitude wrapping around + auto& db = *f.db; + OldDballeTestDataSet oldf; + + // Insert a data record + db.insert_data(oldf.data["synop"], true, true); + + auto cur = db.query_data(*query_from_string("latmin=10.0, latmax=15.0, lonmin=70.0, lonmax=-160.0")); + wassert(actual(cur->remaining()) == 2); + cur->discard_rest(); + }); + add_method("query_ana_filter", [](Fixture& f) { + // Test numeric comparisons in ana_filter + auto& db = *f.db; + OldDballeTestDataSet oldf; + wassert(f.populate_database(oldf)); + + auto cur = db.query_data(*query_from_string("rep_memo=metar, var=B01011")); + wassert(actual(cur->remaining()) == 1); + + // Move the cursor to B01011 + cur->next(); + int context_id = cur->attr_reference_id(); + cur->discard_rest(); + + // Insert new attributes about this report + Values qc; + qc.set("B01001", 50); + qc.set("B01008", "50"); + db.attr_insert_data(context_id, qc); + + // Try queries filtered by numeric attributes + cur = db.query_data(*query_from_string("rep_memo=metar, var=B01011, attr_filter=B01001=50")); + wassert(actual(cur->remaining()) == 1); + cur->discard_rest(); + + cur = db.query_data(*query_from_string("rep_memo=metar, var=B01011, attr_filter=B01001<=50")); + wassert(actual(cur->remaining()) == 1); + cur->discard_rest(); + + cur = db.query_data(*query_from_string("rep_memo=metar, var=B01011, attr_filter=B01001<51")); + wassert(actual(cur->remaining()) == 1); + cur->discard_rest(); + + cur = db.query_data(*query_from_string("rep_memo=metar, var=B01011, attr_filter=B01001<8")); + wassert(actual(cur->remaining()) == 0); + cur->discard_rest(); + + // Try queries filtered by string attributes + cur = db.query_data(*query_from_string("rep_memo=metar, var=B01011, attr_filter=B01008=50")); + wassert(actual(cur->remaining()) == 1); + cur->discard_rest(); + + cur = db.query_data(*query_from_string("rep_memo=metar, var=B01011, attr_filter=B01008<=50")); + wassert(actual(cur->remaining()) == 1); + cur->discard_rest(); + + cur = db.query_data(*query_from_string("rep_memo=metar, var=B01011, attr_filter=B01008<8")); + wassert(actual(cur->remaining()) == 1); + cur->discard_rest(); + + cur = db.query_data(*query_from_string("rep_memo=metar, var=B01011, attr_filter=B01008<100")); + wassert(actual(cur->remaining()) == 0); + cur->discard_rest(); + }); + add_method("query_station_best", [](Fixture& f) { +#warning BEST queries of station values are not yet implemented for memdb + if (dynamic_cast(f.db)) + return; + auto& db = *f.db; + + // Reproduce a querybest scenario which produced invalid SQL + OldDballeTestDataSet oldf; + wassert(f.populate_database(oldf)); + + core::Query q; + q.datetime = DatetimeRange(Datetime(1000, 1, 1, 0, 0, 0), Datetime(1000, 1, 1, 0, 0, 0)); + q.query = "best"; + auto cur = db.query_data(q); + while (cur->next()) + { + } + }); + add_method("query_best_bug1", [](Fixture& f) { + auto& db = *f.db; + // Reproduce a querybest scenario which produced always the same data record + + // Import lots + const char** files = dballe::tests::bufr_files; + for (int i = 0; files[i] != NULL; i++) + { + Messages inmsgs = read_msgs(files[i], File::BUFR); + wassert(db.import_msg(inmsgs[0], NULL, DBA_IMPORT_ATTRS | DBA_IMPORT_FULL_PSEUDOANA | DBA_IMPORT_OVERWRITE)); + } + + // Query all with best + auto cur = db.query_data(*query_from_string("var=B12101, query=best")); + unsigned orig_count = cur->remaining(); + unsigned count = 0; + int id_data = 0; + unsigned id_data_changes = 0; + while (cur->next()) + { + ++count; + if (cur->attr_reference_id() != id_data) + { + id_data = cur->attr_reference_id(); + ++id_data_changes; + } + } + + wassert(actual(count) > 1); + wassert(actual(id_data_changes) == count); + wassert(actual(count) == orig_count); + }); + add_method("query_invalid_sql", [](Fixture& f) { + auto& db = *f.db; + // Reproduce a query that generated invalid SQL on V6 + OldDballeTestDataSet oldf; + wassert(f.populate_database(oldf)); + + // All DB + db.query_stations(*query_from_string("leveltype1=103, l1=2000")); + }); + add_method("fd_leaks", [](Fixture& f) { + // Test connect leaks + StationValues vals; + // Set station data + vals.info.coords = Coords(12.34560, 76.54320); + vals.info.report = "synop"; + vals.values.set("B07030", 42.0); // Height + + // Assume a max open file limit of 1100 + for (unsigned i = 0; i < 1100; ++i) + { + std::unique_ptr db = f.create_db(); + vals.clear_ids(); + wassert(db->insert_station_data(vals, true, true)); + } + }); + add_method("update", [](Fixture& f) { + auto& db = *f.db; + // Test value update + OldDballeTestDataSet oldf; + + DataValues dataset = oldf.data["synop"]; + db.insert_data(dataset, true, true); + Values attrs; + attrs.set("B33007", 50); + db.attr_insert_data(dataset.values["B01012"].data_id, attrs); + + core::Query q; + q.latrange.set(12.34560, 12.34560); + q.lonrange.set(76.54320, 76.54320); + q.datetime = DatetimeRange(Datetime(1945, 4, 25, 8, 0, 0), Datetime(1945, 4, 25, 8, 0, 0)); + q.rep_memo = "synop"; + q.level = Level(10, 11, 15, 22); + q.trange = Trange(20, 111, 122); + q.varcodes.insert(WR_VAR(0, 1, 12)); + + // Query the initial value + auto cur = db.query_data(q); + wassert(actual(cur->remaining()) == 1); + cur->next(); + int ana_id = cur->get_station_id(); + wreport::Var var = cur->get_var(); + wassert(actual(var.enqi()) == 300); + + // Query the attributes and check that they are there + Values qattrs; + wassert(actual(run_attr_query_data(db, cur->attr_reference_id(), qattrs)) == 1); + wassert(actual(qattrs["B33007"].var->enq(MISSING_INT)) == 50); + + // Update it + DataValues update; + update.info.ana_id = ana_id; + update.info.report = "synop"; + update.info.datetime = q.datetime.min; + update.info.level = q.level; + update.info.trange = q.trange; + update.values.set(var.code(), 200); + db.insert_data(update, true, false); + + // Query again + cur = db.query_data(q); + wassert(actual(cur->remaining()) == 1); + cur->next(); + var = cur->get_var(); + wassert(actual(var.enqi()) == 200); + + qattrs.clear(); + wassert(actual(run_attr_query_data(db, cur->attr_reference_id(), qattrs)) == 1); + wassert(actual(qattrs["B33007"].var->enq(MISSING_INT)) == 50); + }); + add_method("query_stepbystep", [](Fixture& f) { + auto& db = *f.db; + // Try a query checking all the steps + OldDballeTestDataSet oldf; + wassert(f.populate_database(oldf)); + + // Make the query + auto cur = db.query_data(*query_from_string("latmin=10.0")); + wassert(actual(cur->remaining()) == 4); + + wassert(actual(cur->next()).istrue()); + // remaining() should decrement + wassert(actual(cur->remaining()) == 3); + // results should match what was inserted + wassert(actual(cur).data_matches(oldf.data["synop"])); + // just call to_record now, to check if in the next call old variables are removed + core::Record result; + cur->to_record(result); + + wassert(actual(cur->next()).istrue()); + wassert(actual(cur->remaining()) == 2); + wassert(actual(cur).data_matches(oldf.data["synop"])); + + // Variables from the previous to_record should be removed + cur->to_record(result); + wassert(actual(result.vars().size()) == 1u); + + + wassert(actual(cur->next()).istrue()); + wassert(actual(cur->remaining()) == 1); + wassert(actual(cur).data_matches(oldf.data["metar"])); + + wassert(actual(cur->next()).istrue()); + wassert(actual(cur->remaining()) == 0); + wassert(actual(cur).data_matches(oldf.data["metar"])); + + // Now there should not be anything anymore + wassert(actual(cur->remaining()) == 0); + wassert(actual(cur->next()).isfalse()); + }); + add_method("insert_stationinfo_twice", [](Fixture& f) { + // Test double insert of station info + auto& db = *f.db; + + NavileDataSet ds; + + //wassert(actual(f.db).empty()); + db.insert_station_data(ds.stations["synop"], true, true); + db.insert_station_data(ds.stations["synop"], true, true); + + // Query station data and ensure there is only one info (height) + core::Query query; + auto cur = db.query_station_data(query); + wassert(actual(cur->remaining()) == 1); + cur->next(); + wassert(actual(cur).station_vars_match(ds.stations["synop"])); + }); + add_method("insert_stationinfo_twice1", [](Fixture& f) { + // Test double insert of station info + auto& db = *f.db; + + NavileDataSet ds; + ds.stations["metar"] = ds.stations["synop"]; + ds.stations["metar"].info.report = "metar"; + db.insert_station_data(ds.stations["synop"], true, true); + db.insert_station_data(ds.stations["metar"], true, true); + + // Query station data and ensure there is only one info (height) + core::Query query; + auto cur = db.query_station_data(query); + wassert(actual(cur->remaining()) == 2); + + // Ensure that the network info is preserved + // Use a sorted vector because while all DBs group by report, not all DBs + // sort by report name. + vector reports; + while (cur->next()) + reports.push_back(cur->get_rep_memo()); + std::sort(reports.begin(), reports.end()); + wassert(actual(reports[0]) == "metar"); + wassert(actual(reports[1]) == "synop"); + }); + add_method("insert_undefined_level2", [](Fixture& f) { + // Test handling of values with undefined leveltype2 and l2 + auto& db = *f.db; + OldDballeTestDataSet oldf; + + // Insert with undef leveltype2 and l2 + DataValues dataset; + dataset.info = oldf.data["synop"].info; + dataset.info.level = Level(44, 55); + dataset.info.trange = Trange(20); + dataset.values.set("B01012", 300); + db.insert_data(dataset, true, true); + + // Query it back + auto cur = db.query_data(*query_from_string("leveltype1=44, l1=55")); + wassert(actual(cur->remaining()) == 1); + + wassert(actual(cur->next()).istrue()); + core::Record result; + cur->to_record(result); + wassert(actual(result.get_level()) == Level(44, 55)); + wassert(actual(result.get_trange()) == Trange(20)); + + wassert(actual(cur->next()).isfalse()); + }); + add_method("query_undefined_level2", [](Fixture& f) { + // Test handling of values with undefined leveltype2 and l2 + auto& db = *f.db; + OldDballeTestDataSet oldf; + wassert(f.populate_database(oldf)); + + // Query with undef leveltype2 and l2 + auto cur = db.query_data(*query_from_string("leveltype1=10, l1=11")); + wassert(actual(cur->remaining()) == 4); + cur->discard_rest(); + }); + add_method("query_bad_attrfilter", [](Fixture& f) { + // Query with an incorrect attr_filter + auto& db = *f.db; + OldDballeTestDataSet oldf; + wassert(f.populate_database(oldf)); + + try { + db.query_data(*query_from_string("attr_filter=B12001")); + } catch (error_consistency& e) { + wassert(actual(e.what()).matches("B12001 is not a valid filter|cannot find any operator in filter 'B12001'")); + } + }); + add_method("query_best_priomax", [](Fixture& f) { + // Test querying priomax together with query=best + auto& db = *f.db; + + // Prepare the common parts of some data + DataValues insert; + insert.info.coords = Coords(1.0, 1.0); + insert.info.level = Level(1, 0); + insert.info.trange = Trange(254, 0, 0); + insert.info.datetime = Datetime(2009, 11, 11, 0, 0, 0); + + // 1,synop,synop,101,oss,0 + // 2,metar,metar,81,oss,0 + // 3,temp,sounding,98,oss,2 + // 4,pilot,wind profile,80,oss,2 + // 9,buoy,buoy,50,oss,31 + // 10,ship,synop ship,99,oss,1 + // 11,tempship,temp ship,100,oss,2 + // 12,airep,airep,82,oss,4 + // 13,amdar,amdar,97,oss,4 + // 14,acars,acars,96,oss,4 + // 42,pollution,pollution,199,oss,8 + // 200,satellite,NOAA satellites,41,oss,255 + // 255,generic,generic data,1000,?,255 + static const char* rep_memos[] = { "synop", "metar", "temp", "pilot", "buoy", "ship", "tempship", "airep", "amdar", "acars", "pollution", "satellite", "generic", NULL }; + for (const char** i = rep_memos; *i; ++i) + { + insert.clear_ids(); + insert.info.report = *i; + insert.values.set("B12101", (int)(i - rep_memos)); + db.insert_data(insert, false, true); + } + + // Query with querybest only + { + core::Query query; + query.query = "best"; + query.datetime = DatetimeRange(Datetime(2009, 11, 11, 0, 0, 0), Datetime(2009, 11, 11, 0, 0, 0)); + query.varcodes.insert(WR_VAR(0, 12, 101)); + auto cur = db.query_data(query); + + wassert(actual(cur->remaining()) == 1); + + wassert(actual(cur->next()).istrue()); + core::Record result; + cur->to_record(result); + + wassert(actual(result.get("rep_memo")).istrue()); + wassert(actual(result.enq("rep_memo", "")) == "generic"); + + cur->discard_rest(); + } + + // Query with querybest and priomax + { + core::Query query; + query.prio_max = 100; + query.query = "best"; + query.datetime = DatetimeRange(Datetime(2009, 11, 11, 0, 0, 0), Datetime(2009, 11, 11, 0, 0, 0)); + query.varcodes.insert(WR_VAR(0, 12, 101)); + auto cur = db.query_data(query); + wassert(actual(cur->remaining()) == 1); + + wassert(actual(cur->next()).istrue()); + core::Record result; + cur->to_record(result); + + wassert(actual(result.get("rep_memo")).istrue()); + wassert(actual(result.enq("rep_memo", "")) == "tempship"); + + cur->discard_rest(); + } + }); + add_method("query_repmemo_in_results", [](Fixture& f) { + // Ensure that rep_memo is set in the results + auto& db = *f.db; + wassert(f.populate()); + + core::Record res; + auto cur = db.query_data(core::Query()); + while (cur->next()) + { + cur->to_record(res); + wassert(actual(res["rep_memo"].isset()).istrue()); + } + }); + } +}; + +Tests tg1("db_misc_mem", nullptr, db::MEM); +Tests tg2("db_misc_v6_sqlite", "SQLITE", db::V6); +#ifdef HAVE_ODBC +Tests tg4("db_misc_v6_odbc", "ODBC", db::V6); +#endif +#ifdef HAVE_LIBPQ +Tests tg6("db_misc_v6_postgresql", "POSTGRESQL", db::V6); +#endif +#ifdef HAVE_MYSQL +Tests tg8("db_misc_v6_mysql", "MYSQL", db::V6); +#endif + +} diff --git a/dballe/db/db-misc-tut.cc b/dballe/db/db-misc-tut.cc deleted file mode 100644 index 764266fb2..000000000 --- a/dballe/db/db-misc-tut.cc +++ /dev/null @@ -1,929 +0,0 @@ -#include "config.h" -#include "db/tests.h" -#include "db/querybuf.h" -#include "db/mem/db.h" -#include - -using namespace dballe; -using namespace dballe::db; -using namespace dballe::tests; -using namespace wreport; -using namespace wibble; -using namespace wibble::tests; -using namespace std; - -namespace { - -struct Fixture : public dballe::tests::DBFixture -{ - StationValues ds_st_navile; - - Fixture() - { - ds_st_navile.info.coords = Coords(44.5008, 11.3288); - ds_st_navile.info.report = "synop"; - ds_st_navile.values.set("B07030", 78); // Height - } - - void reset() - { - dballe::tests::DBFixture::reset(); - ds_st_navile.clear_ids(); - } -}; - -unsigned run_attr_query_station(DB& db, int data_id, Values& dest) -{ - unsigned count = 0; - db.attr_query_station(data_id, [&](unique_ptr var) { dest.set(move(var)); ++count; }); - return count; -} - -unsigned run_attr_query_data(DB& db, int data_id, Values& dest) -{ - unsigned count = 0; - db.attr_query_data(data_id, [&](unique_ptr var) { dest.set(move(var)); ++count; }); - return count; -} - - -typedef dballe::tests::db_test_group test_group; -typedef test_group::Test Test; - -std::vector tests { - Test("insert", [](Fixture& f) { - // Test a simple insert round trip - auto& db = *f.db; - - // Insert some data - wrunchecked(db.insert_station_data(f.ds_st_navile, true, true)); - DataValues vals; - vals.info = f.ds_st_navile.info; - vals.info.datetime = Datetime(2013, 10, 16, 10); - vals.values.set(WR_VAR(0, 12, 101), 16.5); - wrunchecked(db.insert_data(vals, true, true)); - Values attrs; - attrs.set("B33007", 50); - wrunchecked(db.attr_insert_data(vals.values[WR_VAR(0, 12, 101)].data_id, attrs)); - - // Query and verify the station data - { - auto cur = db.query_stations(core::Query()); - wassert(actual(cur->remaining()) == 1); - cur->next(); - wassert(actual(cur).station_vars_match(f.ds_st_navile)); - } - - // Query and verify the measured data - { - auto cur = db.query_data(core::Query()); - wassert(actual(cur->remaining()) == 1); - cur->next(); - wassert(actual(cur).data_context_matches(vals)); - wassert(actual(cur).data_var_matches(vals, WR_VAR(0, 12, 101))); - } - - // Query and verify attributes - { - int count = 0; - unique_ptr attr; - wrunchecked(db.attr_query_data(vals.values[WR_VAR(0, 12, 101)].data_id, [&](std::unique_ptr&& var) { - ++count; - attr = move(var); - })); - wassert(actual(count) == 1); - wassert(actual(attr->code()) == WR_VAR(0, 33, 7)); - wassert(actual(attr->enq(MISSING_INT)) == 50); - } - }), - Test("insert_perms", [](Fixture& f) { - // Test insert - auto& db = *f.db; - OldDballeTestFixture oldf; - - // Check if adding a nonexisting station when not allowed causes an error - try { - db.insert_data(oldf.data["synop"], false, false); - ensure(false); - } catch (error_consistency& e) { - wassert(actual(e.what()).contains("insert a station entry when it is forbidden")); - } catch (error_notfound& e) { - wassert(actual(e.what()).contains("station not found")); - } - wassert(actual(oldf.data["synop"].info.ana_id) == MISSING_INT); - wassert(actual(oldf.data["synop"].values["B01011"].data_id) == MISSING_INT); - wassert(actual(oldf.data["synop"].values["B01012"].data_id) == MISSING_INT); - oldf.data["synop"].clear_ids(); - - // Insert the record - wrunchecked(db.insert_data(oldf.data["synop"], false, true)); - oldf.data["synop"].clear_ids(); - // Check if duplicate updates are allowed by insert - wrunchecked(db.insert_data(oldf.data["synop"], true, false)); - oldf.data["synop"].clear_ids(); - // Check if overwrites are trapped by insert_new - oldf.data["synop"].values.set("B01011", "DB-All.e?"); - try { - db.insert_data(oldf.data["synop"], false, false); - ensure(false); - } catch (wreport::error& e) { - wassert(actual(e.what()).matches("refusing to overwrite existing data|cannot replace an existing value|Duplicate entry")); - } - }), - Test("insert_twice", [](Fixture& f) { - // Test double station insert - auto& db = *f.db; - OldDballeTestFixture oldf; - - // Insert the record twice - wrunchecked(db.insert_data(oldf.data["synop"], false, true)); - // This should fail, refusing to replace station info - oldf.data["synop"].values.set("B01011", "DB-All.e?"); - try { - db.insert_data(oldf.data["synop"], false, true); - ensure(false); - } catch (wreport::error& e) { - wassert(actual(e.what()).matches("refusing to overwrite existing data|cannot replace an existing value|Duplicate entry")); - } - }), - Test("query_station", [](Fixture& f) { - // Test station query - auto& db = *f.db; - OldDballeTestFixture oldf; - wruntest(f.populate_database, oldf); - - // Iterate the station database - auto cur = db.query_stations(core::Query()); - - if (dynamic_cast(f.db)) - { - // Memdb has one station entry per (lat, lon, ident, network) - wassert(actual(cur->remaining()) == 2); - - wassert(actual(cur->next()).istrue()); - wassert(actual(cur->get_lat()) == 12.34560); - wassert(actual(cur->get_lon()) == 76.54320); - wassert(actual(cur->get_rep_memo()) == "synop"); - wassert(actual((void*)cur->get_ident()) == (void*)0); - wassert(actual(cur).station_keys_match(oldf.stations["synop"].info)); - - wassert(actual(cur->next()).istrue()); - wassert(actual(cur->get_lat()) == 12.34560); - wassert(actual(cur->get_lon()) == 76.54320); - wassert(actual(cur->get_rep_memo()) == "metar"); - wassert(actual((void*)cur->get_ident()) == (void*)0); - wassert(actual(cur).station_keys_match(oldf.stations["metar"].info)); - } else { - // V5 and V6 have one station entry (lat, lon, ident) - wassert(actual(cur->remaining()) == 1); - - // There should be an item - wassert(actual(cur->next()).istrue()); - wassert(actual(cur->get_lat()) == 12.34560); - wassert(actual(cur->get_lon()) == 76.54320); - wassert(actual((void*)cur->get_ident()) == (void*)0); - - // Check that the result matches - wassert(actual(cur).station_keys_match(oldf.stations["metar"].info)); - - // There should be only one item - } - wassert(actual(cur->remaining()) == 0); - wassert(actual(cur->next()).isfalse()); - }), - Test("query_best", [](Fixture& f) { - // Test querybest - auto& db = *f.db; - OldDballeTestFixture oldf; - wruntest(f.populate_database, oldf); - - //if (db.server_type == ORACLE || db.server_type == POSTGRES) - // return; - - // Prepare a query - core::Query query; - query.latrange = LatRange(1000000, LatRange::IMAX); - query.query = "best"; - - // Make the query - auto cur = db.query_data(query); - - wassert(actual(cur->remaining()) == 4); - - // There should be four items - wassert(actual(cur->next()).istrue()); - wassert(actual(cur->get_lat()) == 12.34560); - wassert(actual(cur->get_lon()) == 76.54320); - wassert(actual((void*)cur->get_ident()) == (void*)0); - wassert(actual((void*)cur->get_rep_memo()).istrue()); - wassert(actual(cur->get_rep_memo()) == "synop"); - wassert(actual(cur->get_level()) == Level(10, 11, 15, 22)); - wassert(actual(cur->get_trange()) == Trange(20, 111, 122)); - wassert(actual(cur->get_varcode()) == WR_VAR(0, 1, 11)); - wassert(actual(cur->get_var().code()) == WR_VAR(0, 1, 11)); - wassert(actual(cur->remaining()) == 3); - wassert(actual(cur->next()).istrue()); - wassert(actual(cur->remaining()) == 2); - wassert(actual(cur->next()).istrue()); - wassert(actual(cur->remaining()) == 1); - wassert(actual(cur->next()).istrue()); - wassert(actual(cur->remaining()) == 0); - - // Now there should not be anything anymore - ensure(!cur->next()); - }), - Test("delete", [](Fixture& f) { - // Test deletion - auto& db = *f.db; - OldDballeTestFixture oldf; - wruntest(f.populate_database, oldf); - - // 4 items to begin with - core::Query query; - auto cur = db.query_data(query); - ensure_equals(cur->remaining(), 4); - cur->discard_rest(); - - query.clear(); - query.datetime = DatetimeRange(Datetime(1945, 4, 25, 8, 10), Datetime()); - db.remove(query); - - // 2 remaining after remove - query.clear(); - cur = db.query_data(query); - ensure_equals(cur->remaining(), 2); - cur->discard_rest(); - - // Did it remove the right ones? - query.clear(); - query.latrange = LatRange(10.0, LatRange::DMAX); - cur = db.query_data(query); - ensure_equals(cur->remaining(), 2); - ensure(cur->next()); - wassert(actual(cur).data_context_matches(oldf.data["synop"])); - - Varcode last_code = 0; - for (unsigned i = 0; i < 2; ++i) - { - // Check that varcodes do not repeat - if (last_code != 0) - wassert(actual(cur->get_varcode()) != last_code); - last_code = cur->get_varcode(); - - switch (last_code) - { - case WR_VAR(0, 1, 11): - case WR_VAR(0, 1, 12): - wassert(actual(cur).data_var_matches(oldf.data["synop"], last_code)); - break; - default: - ensure(false); - } - - if (i == 0) - { - /* The item should have two data in it */ - ensure(cur->next()); - } else { - ensure(!cur->next()); - } - } - }), - Test("query_datetime", [](Fixture& f) { - // Test datetime queries - auto& db = *f.db; - - /* Prepare test data */ - DataValues base; - base.info.coords = Coords(12.0, 48.0); - base.info.report = "synop"; - base.info.level = Level(1, 0, 1, 0); - base.info.trange = Trange(1, 0, 0); - base.values.set("B01012", 500); - -#define WANTRESULT(querystr, ab) do { \ - core::Record result; \ - auto cur = db.query_data(*dballe::tests::query_from_string(querystr)); \ - wassert(actual(cur->remaining()) == 1); \ - wassert(actual(cur->next()).istrue()); \ - cur->to_record(result); \ - wassert(actual(cur->remaining()) == 0); \ - ensure_varcode_equals(result.vars()[0]->code(), WR_VAR(0, 1, 12)); \ - ensure(cur->get_datetime() == ab.info.datetime); \ - cur->discard_rest(); \ - } while(0) - - DataValues a, b; - - /* Year */ - db.reset(); - a = base; - a.info.datetime = Datetime(2005); - db.insert_data(a, false, true); - b = base; - b.info.datetime = Datetime(2006); - db.insert_data(b, false, false); - WANTRESULT("yearmin=2006", b); - WANTRESULT("yearmax=2005", a); - WANTRESULT("year=2006", b); - - /* Month */ - db.reset(); - a = base; - a.info.datetime = Datetime(2006, 4); - db.insert_data(a, false, true); - b = base; - b.info.datetime = Datetime(2006, 5); - db.insert_data(b, false, false); - WANTRESULT("year=2006, monthmin=5", b); - WANTRESULT("year=2006, monthmax=4", a); - WANTRESULT("year=2006, month=5", b); - - /* Day */ - db.reset(); - a = base; - a.info.datetime = Datetime(2006, 5, 2); - db.insert_data(a, false, true); - b = base; - b.info.datetime = Datetime(2006, 5, 3); - db.insert_data(b, false, false); - WANTRESULT("year=2006, month=5, daymin=3", b); - WANTRESULT("year=2006, month=5, daymax=2", a); - WANTRESULT("year=2006, month=5, day=3", b); - - /* Hour */ - db.reset(); - a = base; - a.info.datetime = Datetime(2006, 5, 3, 12); - db.insert_data(a, false, true); - b = base; - b.info.datetime = Datetime(2006, 5, 3, 13); - db.insert_data(b, false, false); - WANTRESULT("year=2006, month=5, day=3, hourmin=13", b); - WANTRESULT("year=2006, month=5, day=3, hourmax=12", a); - WANTRESULT("year=2006, month=5, day=3, hour=13", b); - - /* Minute */ - db.reset(); - a = base; - a.info.datetime = Datetime(2006, 5, 3, 12, 29); - db.insert_data(a, false, true); - b = base; - b.info.datetime = Datetime(2006, 5, 3, 12, 30); - db.insert_data(b, false, false); - WANTRESULT("year=2006, month=5, day=3, hour=12, minumin=30", b); - WANTRESULT("year=2006, month=5, day=3, hour=12, minumax=29", a); - WANTRESULT("year=2006, month=5, day=3, hour=12, min=30", b); - }), - Test("attrs", [](Fixture& f) { - // Test QC - auto& db = *f.db; - OldDballeTestFixture oldf; - wruntest(f.populate_database, oldf); - - core::Query query; - core::Record result; - query.latrange.set(1000000, LatRange::IMAX); - auto cur = db.query_data(query); - - // Move the cursor to B01011 - int context_id; - bool found = false; - while (cur->next()) - { - cur->to_record(result); - if (result.vars()[0]->code() == WR_VAR(0, 1, 11)) - { - context_id = cur->attr_reference_id(); - cur->discard_rest(); - found = true; - break; - } - } - ensure(found); - - // Insert new attributes about this report - Values qc; - qc.set("B33002", 2); - qc.set("B33003", 5); - qc.set("B33005", 33); - db.attr_insert_data(context_id, qc); - - // Query back the data - qc.clear(); - wassert(actual(run_attr_query_data(db, context_id, qc)) == 3); - - const auto* attr = qc.get("B33002"); - ensure(attr != NULL); - ensure_equals(attr->var->enqi(), 2); - - attr = qc.get("B33003"); - ensure(attr != NULL); - ensure_equals(attr->var->enqi(), 5); - - attr = qc.get("B33005"); - ensure(attr != NULL); - ensure_equals(attr->var->enqi(), 33); - - // Delete a couple of items - vector codes; - codes.push_back(WR_VAR(0, 33, 2)); - codes.push_back(WR_VAR(0, 33, 5)); - db.attr_remove_data(context_id, codes); - - // Deleting non-existing items should not fail. Also try creating a - // query with just one item - codes.clear(); - codes.push_back(WR_VAR(0, 33, 2)); - db.attr_remove_data(context_id, codes); - - /* Query back the data */ - qc.clear(); - wassert(actual(run_attr_query_data(db, context_id, qc)) == 1); - - ensure(qc.get("B33002") == nullptr); - ensure(qc.get("B33005") == nullptr); - attr = qc.get("B33003"); - ensure(attr != NULL); - ensure_equals(attr->var->enqi(), 5); - /*dba_error_remove_callback(DBA_ERR_NONE, crash, 0);*/ - }), - Test("query_station", [](Fixture& f) { - // Test station queries - auto& db = *f.db; - OldDballeTestFixture oldf; - wruntest(f.populate_database, oldf); - - auto cur = db.query_stations(*query_from_string("rep_memo=synop")); - ensure_equals(cur->remaining(), 1); - - ensure(cur->next()); - ensure(!cur->next()); - }), - Test("attrs1", [](Fixture& f) { - // Test attributes - auto& db = *f.db; - OldDballeTestFixture oldf; - - // Insert a data record - db.insert_data(oldf.data["synop"], true, true); - - Values qc; - qc.set("B01007", 1); - qc.set("B02048", 2); - qc.set("B05040", 3); - qc.set("B05041", 4); - qc.set("B05043", 5); - qc.set("B33032", 6); - qc.set("B07024", 7); - qc.set("B05021", 8); - qc.set("B07025", 9); - qc.set("B05022", 10); - db.attr_insert_data(oldf.data["synop"].values[WR_VAR(0, 1, 11)].data_id, qc); - - // Query back the B01011 variable to read the attr reference id - auto cur = db.query_data(*query_from_string("var=B01011")); - ensure_equals(cur->remaining(), 1); - cur->next(); - int attr_id = cur->attr_reference_id(); - cur->discard_rest(); - - qc.clear(); - wassert(actual(run_attr_query_data(db, attr_id, qc)) == 10); - - // Check that all the attributes come out - ensure_equals(qc.size(), 10); - ensure_varcode_equals(qc["B01007"].var->code(), WR_VAR(0, 1, 7)); ensure_var_equals(*qc["B01007"].var, 1); - ensure_varcode_equals(qc["B02048"].var->code(), WR_VAR(0, 2, 48)); ensure_var_equals(*qc["B02048"].var, 2); - ensure_varcode_equals(qc["B05021"].var->code(), WR_VAR(0, 5, 21)); ensure_var_equals(*qc["B05021"].var, 8); - ensure_varcode_equals(qc["B05022"].var->code(), WR_VAR(0, 5, 22)); ensure_var_equals(*qc["B05022"].var, 10); - ensure_varcode_equals(qc["B05040"].var->code(), WR_VAR(0, 5, 40)); ensure_var_equals(*qc["B05040"].var, 3); - ensure_varcode_equals(qc["B05041"].var->code(), WR_VAR(0, 5, 41)); ensure_var_equals(*qc["B05041"].var, 4); - ensure_varcode_equals(qc["B05043"].var->code(), WR_VAR(0, 5, 43)); ensure_var_equals(*qc["B05043"].var, 5); - ensure_varcode_equals(qc["B07024"].var->code(), WR_VAR(0, 7, 24)); ensure_var_equals(*qc["B07024"].var, 7); - ensure_varcode_equals(qc["B07025"].var->code(), WR_VAR(0, 7, 25)); ensure_var_equals(*qc["B07025"].var, 9); - ensure_varcode_equals(qc["B33032"].var->code(), WR_VAR(0, 33, 32)); ensure_var_equals(*qc["B33032"].var, 6); - }), - Test("longitude_wrap", [](Fixture& f) { - // Test longitude wrapping around - auto& db = *f.db; - OldDballeTestFixture oldf; - - // Insert a data record - db.insert_data(oldf.data["synop"], true, true); - - auto cur = db.query_data(*query_from_string("latmin=10.0, latmax=15.0, lonmin=70.0, lonmax=-160.0")); - ensure_equals(cur->remaining(), 2); - cur->discard_rest(); - }), - Test("query_ana_filter", [](Fixture& f) { - // Test numeric comparisons in ana_filter - auto& db = *f.db; - OldDballeTestFixture oldf; - wruntest(f.populate_database, oldf); - - auto cur = db.query_data(*query_from_string("rep_memo=metar, var=B01011")); - ensure_equals(cur->remaining(), 1); - - // Move the cursor to B01011 - cur->next(); - int context_id = cur->attr_reference_id(); - cur->discard_rest(); - - // Insert new attributes about this report - Values qc; - qc.set("B01001", 50); - qc.set("B01008", "50"); - db.attr_insert_data(context_id, qc); - - // Try queries filtered by numeric attributes - cur = db.query_data(*query_from_string("rep_memo=metar, var=B01011, attr_filter=B01001=50")); - ensure_equals(cur->remaining(), 1); - cur->discard_rest(); - - cur = db.query_data(*query_from_string("rep_memo=metar, var=B01011, attr_filter=B01001<=50")); - ensure_equals(cur->remaining(), 1); - cur->discard_rest(); - - cur = db.query_data(*query_from_string("rep_memo=metar, var=B01011, attr_filter=B01001<51")); - ensure_equals(cur->remaining(), 1); - cur->discard_rest(); - - cur = db.query_data(*query_from_string("rep_memo=metar, var=B01011, attr_filter=B01001<8")); - ensure_equals(cur->remaining(), 0); - cur->discard_rest(); - - // Try queries filtered by string attributes - cur = db.query_data(*query_from_string("rep_memo=metar, var=B01011, attr_filter=B01008=50")); - ensure_equals(cur->remaining(), 1); - cur->discard_rest(); - - cur = db.query_data(*query_from_string("rep_memo=metar, var=B01011, attr_filter=B01008<=50")); - ensure_equals(cur->remaining(), 1); - cur->discard_rest(); - - cur = db.query_data(*query_from_string("rep_memo=metar, var=B01011, attr_filter=B01008<8")); - ensure_equals(cur->remaining(), 1); - cur->discard_rest(); - - cur = db.query_data(*query_from_string("rep_memo=metar, var=B01011, attr_filter=B01008<100")); - ensure_equals(cur->remaining(), 0); - cur->discard_rest(); - }), - Test("query_station_best", [](Fixture& f) { -#warning BEST queries of station values are not yet implemented for memdb - if (dynamic_cast(f.db)) - return; - auto& db = *f.db; - - // Reproduce a querybest scenario which produced invalid SQL - OldDballeTestFixture oldf; - wruntest(f.populate_database, oldf); - - core::Query q; - q.datetime = DatetimeRange(Datetime(1000, 1, 1, 0, 0, 0), Datetime(1000, 1, 1, 0, 0, 0)); - q.query = "best"; - auto cur = db.query_data(q); - while (cur->next()) - { - } - }), - Test("query_best_bug1", [](Fixture& f) { - auto& db = *f.db; - // Reproduce a querybest scenario which produced always the same data record - - // Import lots - const char** files = dballe::tests::bufr_files; - for (int i = 0; files[i] != NULL; i++) - { - Messages inmsgs = read_msgs(files[i], File::BUFR); - wrunchecked(db.import_msg(inmsgs[0], NULL, DBA_IMPORT_ATTRS | DBA_IMPORT_FULL_PSEUDOANA | DBA_IMPORT_OVERWRITE)); - } - - // Query all with best - auto cur = db.query_data(*query_from_string("var=B12101, query=best")); - unsigned orig_count = cur->remaining(); - unsigned count = 0; - int id_data = 0; - unsigned id_data_changes = 0; - while (cur->next()) - { - ++count; - if (cur->attr_reference_id() != id_data) - { - id_data = cur->attr_reference_id(); - ++id_data_changes; - } - } - - ensure(count > 1); - ensure_equals(id_data_changes, count); - ensure_equals(count, orig_count); - }), - Test("query_invalid_sql", [](Fixture& f) { - auto& db = *f.db; - // Reproduce a query that generated invalid SQL on V6 - OldDballeTestFixture oldf; - wruntest(f.populate_database, oldf); - - // All DB - db.query_stations(*query_from_string("leveltype1=103, l1=2000")); - }), - Test("fd_leaks", [](Fixture& f) { - // Test connect leaks - StationValues vals; - // Set station data - vals.info.coords = Coords(12.34560, 76.54320); - vals.info.report = "synop"; - vals.values.set("B07030", 42.0); // Height - - // Assume a max open file limit of 1100 - for (unsigned i = 0; i < 1100; ++i) - { - std::unique_ptr db = f.create_db(); - vals.clear_ids(); - wrunchecked(db->insert_station_data(vals, true, true)); - } - }), - Test("update", [](Fixture& f) { - auto& db = *f.db; - // Test value update - OldDballeTestFixture oldf; - - DataValues dataset = oldf.data["synop"]; - db.insert_data(dataset, true, true); - Values attrs; - attrs.set("B33007", 50); - db.attr_insert_data(dataset.values["B01012"].data_id, attrs); - - core::Query q; - q.latrange.set(12.34560, 12.34560); - q.lonrange.set(76.54320, 76.54320); - q.datetime = DatetimeRange(Datetime(1945, 4, 25, 8, 0, 0), Datetime(1945, 4, 25, 8, 0, 0)); - q.rep_memo = "synop"; - q.level = Level(10, 11, 15, 22); - q.trange = Trange(20, 111, 122); - q.varcodes.insert(WR_VAR(0, 1, 12)); - - // Query the initial value - auto cur = db.query_data(q); - wassert(actual(cur->remaining()) == 1); - cur->next(); - int ana_id = cur->get_station_id(); - wreport::Var var = cur->get_var(); - wassert(actual(var.enqi()) == 300); - - // Query the attributes and check that they are there - Values qattrs; - wassert(actual(run_attr_query_data(db, cur->attr_reference_id(), qattrs)) == 1); - wassert(actual(qattrs["B33007"].var->enq(MISSING_INT)) == 50); - - // Update it - DataValues update; - update.info.ana_id = ana_id; - update.info.report = "synop"; - update.info.datetime = q.datetime.min; - update.info.level = q.level; - update.info.trange = q.trange; - update.values.set(var.code(), 200); - db.insert_data(update, true, false); - - // Query again - cur = db.query_data(q); - wassert(actual(cur->remaining()) == 1); - cur->next(); - var = cur->get_var(); - wassert(actual(var.enqi()) == 200); - - qattrs.clear(); - wassert(actual(run_attr_query_data(db, cur->attr_reference_id(), qattrs)) == 1); - wassert(actual(qattrs["B33007"].var->enq(MISSING_INT)) == 50); - }), - Test("query_stepbystep", [](Fixture& f) { - auto& db = *f.db; - // Try a query checking all the steps - OldDballeTestFixture oldf; - wruntest(f.populate_database, oldf); - - // Make the query - auto cur = db.query_data(*query_from_string("latmin=10.0")); - wassert(actual(cur->remaining()) == 4); - - wassert(actual(cur->next()).istrue()); - // remaining() should decrement - wassert(actual(cur->remaining()) == 3); - // results should match what was inserted - wassert(actual(cur).data_matches(oldf.data["synop"])); - // just call to_record now, to check if in the next call old variables are removed - core::Record result; - cur->to_record(result); - - ensure(cur->next()); - wassert(actual(cur->remaining()) == 2); - wassert(actual(cur).data_matches(oldf.data["synop"])); - - // Variables from the previous to_record should be removed - cur->to_record(result); - wassert(actual(result.vars().size()) == 1u); - - - ensure(cur->next()); - wassert(actual(cur->remaining()) == 1); - wassert(actual(cur).data_matches(oldf.data["metar"])); - - ensure(cur->next()); - wassert(actual(cur->remaining()) == 0); - wassert(actual(cur).data_matches(oldf.data["metar"])); - - // Now there should not be anything anymore - wassert(actual(cur->remaining()) == 0); - ensure(!cur->next()); - }), - Test("insert_stationinfo_twice", [](Fixture& f) { - // Test double insert of station info - auto& db = *f.db; - - //wassert(actual(f.db).empty()); - db.insert_station_data(f.ds_st_navile, true, true); - db.insert_station_data(f.ds_st_navile, true, true); - - // Query station data and ensure there is only one info (height) - core::Query query; - auto cur = db.query_station_data(query); - wassert(actual(cur->remaining()) == 1); - cur->next(); - wassert(actual(cur).station_vars_match(f.ds_st_navile)); - }), - Test("insert_stationinfo_twice1", [](Fixture& f) { - // Test double insert of station info - auto& db = *f.db; - StationValues ds_st_navile_metar(f.ds_st_navile); - ds_st_navile_metar.info.report = "metar"; - db.insert_station_data(f.ds_st_navile, true, true); - db.insert_station_data(ds_st_navile_metar, true, true); - - // Query station data and ensure there is only one info (height) - core::Query query; - auto cur = db.query_station_data(query); - wassert(actual(cur->remaining()) == 2); - - // Ensure that the network info is preserved - // Use a sorted vector because while all DBs group by report, not all DBs - // sort by report name. - vector reports; - while (cur->next()) - reports.push_back(cur->get_rep_memo()); - std::sort(reports.begin(), reports.end()); - wassert(actual(reports[0]) == "metar"); - wassert(actual(reports[1]) == "synop"); - }), - Test("insert_undefined_level2", [](Fixture& f) { - // Test handling of values with undefined leveltype2 and l2 - auto& db = *f.db; - OldDballeTestFixture oldf; - - // Insert with undef leveltype2 and l2 - DataValues dataset; - dataset.info = oldf.data["synop"].info; - dataset.info.level = Level(44, 55); - dataset.info.trange = Trange(20); - dataset.values.set("B01012", 300); - db.insert_data(dataset, true, true); - - // Query it back - auto cur = db.query_data(*query_from_string("leveltype1=44, l1=55")); - ensure_equals(cur->remaining(), 1); - - ensure(cur->next()); - core::Record result; - cur->to_record(result); - wassert(actual(result.get_level()) == Level(44, 55)); - wassert(actual(result.get_trange()) == Trange(20)); - - ensure(!cur->next()); - }), - Test("query_undefined_level2", [](Fixture& f) { - // Test handling of values with undefined leveltype2 and l2 - auto& db = *f.db; - OldDballeTestFixture oldf; - wruntest(f.populate_database, oldf); - - // Query with undef leveltype2 and l2 - auto cur = db.query_data(*query_from_string("leveltype1=10, l1=11")); - ensure_equals(cur->remaining(), 4); - cur->discard_rest(); - }), - Test("query_bad_attrfilter", [](Fixture& f) { - // Query with an incorrect attr_filter - auto& db = *f.db; - OldDballeTestFixture oldf; - wruntest(f.populate_database, oldf); - - try { - db.query_data(*query_from_string("attr_filter=B12001")); - } catch (error_consistency& e) { - wassert(actual(e.what()).matches("B12001 is not a valid filter|cannot find any operator in filter 'B12001'")); - } - }), - Test("query_best_priomax", [](Fixture& f) { - // Test querying priomax together with query=best - auto& db = *f.db; - - // Prepare the common parts of some data - DataValues insert; - insert.info.coords = Coords(1.0, 1.0); - insert.info.level = Level(1, 0); - insert.info.trange = Trange(254, 0, 0); - insert.info.datetime = Datetime(2009, 11, 11, 0, 0, 0); - - // 1,synop,synop,101,oss,0 - // 2,metar,metar,81,oss,0 - // 3,temp,sounding,98,oss,2 - // 4,pilot,wind profile,80,oss,2 - // 9,buoy,buoy,50,oss,31 - // 10,ship,synop ship,99,oss,1 - // 11,tempship,temp ship,100,oss,2 - // 12,airep,airep,82,oss,4 - // 13,amdar,amdar,97,oss,4 - // 14,acars,acars,96,oss,4 - // 42,pollution,pollution,199,oss,8 - // 200,satellite,NOAA satellites,41,oss,255 - // 255,generic,generic data,1000,?,255 - static const char* rep_memos[] = { "synop", "metar", "temp", "pilot", "buoy", "ship", "tempship", "airep", "amdar", "acars", "pollution", "satellite", "generic", NULL }; - for (const char** i = rep_memos; *i; ++i) - { - insert.clear_ids(); - insert.info.report = *i; - insert.values.set("B12101", (int)(i - rep_memos)); - db.insert_data(insert, false, true); - } - - // Query with querybest only - { - core::Query query; - query.query = "best"; - query.datetime = DatetimeRange(Datetime(2009, 11, 11, 0, 0, 0), Datetime(2009, 11, 11, 0, 0, 0)); - query.varcodes.insert(WR_VAR(0, 12, 101)); - auto cur = db.query_data(query); - - ensure_equals(cur->remaining(), 1); - - ensure(cur->next()); - core::Record result; - cur->to_record(result); - - ensure(result.get("rep_memo") != NULL); - wassert(actual(result.enq("rep_memo", "")) == "generic"); - - cur->discard_rest(); - } - - // Query with querybest and priomax - { - core::Query query; - query.prio_max = 100; - query.query = "best"; - query.datetime = DatetimeRange(Datetime(2009, 11, 11, 0, 0, 0), Datetime(2009, 11, 11, 0, 0, 0)); - query.varcodes.insert(WR_VAR(0, 12, 101)); - auto cur = db.query_data(query); - ensure_equals(cur->remaining(), 1); - - ensure(cur->next()); - core::Record result; - cur->to_record(result); - - ensure(result.get("rep_memo") != NULL); - wassert(actual(result.enq("rep_memo", "")) == "tempship"); - - cur->discard_rest(); - } - }), - Test("query_repmemo_in_results", [](Fixture& f) { - // Ensure that rep_memo is set in the results - auto& db = *f.db; - OldDballeTestFixture oldf; - wruntest(f.populate_database, oldf); - - core::Record res; - auto cur = db.query_data(core::Query()); - while (cur->next()) - { - cur->to_record(res); - wassert(actual(res["rep_memo"].isset()).istrue()); - } - }), -}; - -test_group tg1("db_misc_mem", nullptr, db::MEM, tests); -test_group tg2("db_misc_v6_sqlite", "SQLITE", db::V6, tests); -#ifdef HAVE_ODBC -test_group tg4("db_misc_v6_odbc", "ODBC", db::V6, tests); -#endif -#ifdef HAVE_LIBPQ -test_group tg6("db_misc_v6_postgresql", "POSTGRESQL", db::V6, tests); -#endif -#ifdef HAVE_MYSQL -test_group tg8("db_misc_v6_mysql", "MYSQL", db::V6, tests); -#endif - -} diff --git a/dballe/db/db-query-data-test.cc b/dballe/db/db-query-data-test.cc new file mode 100644 index 000000000..28bd18888 --- /dev/null +++ b/dballe/db/db-query-data-test.cc @@ -0,0 +1,361 @@ +#include "db/tests.h" +#include "config.h" + +using namespace dballe; +using namespace dballe::db; +using namespace dballe::tests; +using namespace wreport; +using namespace std; + +namespace { + +static inline core::Query query_exact(const Datetime& dt) +{ + core::Query query; + query.datetime = DatetimeRange(dt, dt); + return query; +} +static inline core::Query query_min(const Datetime& dt) +{ + core::Query query; + query.datetime = DatetimeRange(dt, Datetime()); + return query; +} +static inline core::Query query_max(const Datetime& dt) +{ + core::Query query; + query.datetime = DatetimeRange(Datetime(), dt); + return query; +} +static inline core::Query query_minmax(const Datetime& min, const Datetime& max) +{ + core::Query query; + query.datetime = DatetimeRange(min, max); + return query; +} + +struct DateHourDataSet : public TestDataSet +{ + DateHourDataSet() + { + DataValues d; + d.info.coords = Coords(12.34560, 76.54320); + d.info.report = "synop"; + d.info.level = Level(10, 11, 15, 22); + d.info.trange = Trange(20, 111, 122); + data["1"] = d; + data["1"].info.datetime = Datetime(2013, 10, 30, 11); + data["1"].values.set("B12101", 11.5); + data["2"] = d; + data["2"].info.datetime = Datetime(2013, 10, 30, 12); + data["2"].values.set("B12101", 12.5); + } +}; + +struct DateDayDataSet : public TestDataSet +{ + DateDayDataSet() + { + DataValues d; + d.info.coords = Coords(12.34560, 76.54320); + d.info.report = "synop"; + d.info.level = Level(10, 11, 15, 22); + d.info.trange = Trange(20, 111, 122); + data["1"] = d; + data["1"].info.datetime = Datetime(2013, 10, 23); + data["1"].values.set("B12101", 23.5); + data["2"] = d; + data["2"].info.datetime = Datetime(2013, 10, 24); + data["2"].values.set("B12101", 24.5); + } +}; + +#define TRY_QUERY(qstring, expected_count) wassert(actual(*f.db).try_data_query(qstring, expected_count)) + +class Tests : public FixtureTestCase +{ + using FixtureTestCase::FixtureTestCase; + + void register_tests() override + { + add_method("ana_id", [](Fixture& f) { + OldDballeTestDataSet oldf; + wassert(f.populate_database(oldf)); + char query[20]; + snprintf(query, 20, "ana_id=%d", oldf.data["synop"].info.ana_id); +#warning FIXME: change after testing if we can move to report-in-station behaviour or not + if (f.db->format() == MEM) + TRY_QUERY(query, 2); + else + TRY_QUERY(query, 4); + TRY_QUERY("ana_id=4242", 0); + }); + add_method("ana_context", [](Fixture& f) { + wassert(f.populate()); + // Query data in station context + core::Query query; + unique_ptr cur = f.db->query_station_data(query); + wassert(actual(cur->remaining()) == 10); + }); + add_method("year", [](Fixture& f) { + wassert(f.populate()); + // Datetime queries + TRY_QUERY("year=1001", 0); + TRY_QUERY("yearmin=1999", 0); + TRY_QUERY("yearmin=1945", 4); + TRY_QUERY("yearmax=1944", 0); + TRY_QUERY("yearmax=1945", 4); + TRY_QUERY("yearmax=2030", 4); + TRY_QUERY("year=1944", 0); + TRY_QUERY("year=1945", 4); + TRY_QUERY("year=1946", 0); + /* + TRY_QUERY(i, DBA_KEY_MONTHMIN, 1); + TRY_QUERY(i, DBA_KEY_MONTHMAX, 12); + TRY_QUERY(i, DBA_KEY_MONTH, 5); + */ + /* + TRY_QUERY(i, DBA_KEY_DAYMIN, 1); + TRY_QUERY(i, DBA_KEY_DAYMAX, 12); + TRY_QUERY(i, DBA_KEY_DAY, 5); + */ + /* + TRY_QUERY(i, DBA_KEY_HOURMIN, 1); + TRY_QUERY(i, DBA_KEY_HOURMAX, 12); + TRY_QUERY(i, DBA_KEY_HOUR, 5); + */ + /* + TRY_QUERY(i, DBA_KEY_MINUMIN, 1); + TRY_QUERY(i, DBA_KEY_MINUMAX, 12); + TRY_QUERY(i, DBA_KEY_MIN, 5); + */ + /* + TRY_QUERY(i, DBA_KEY_SECMIN, 1); + TRY_QUERY(i, DBA_KEY_SECMAX, 12); + TRY_QUERY(i, DBA_KEY_SEC, 5); + */ + }); + add_method("block_station", [](Fixture& f) { + wassert(f.populate()); +#warning FIXME: change after testing if we can move to report-in-station behaviour or not + const int all = (f.db->format() == MEM ? 4 : 4); + // Block and station queries + TRY_QUERY("B01001=1", all); + TRY_QUERY("B01001=2", 0); + TRY_QUERY("B01002=52", all); + TRY_QUERY("B01002=53", 0); + }); + add_method("ana_filter", [](Fixture& f) { +#warning FIXME: change after testing if we can move to report-in-station behaviour or not + const int all = (f.db->format() == MEM ? 4 : 4); + wassert(f.populate()); + // ana_filter queries + TRY_QUERY("ana_filter=block=1", all); + TRY_QUERY("ana_filter=B01001=1", all); + TRY_QUERY("ana_filter=block>1", 0); + TRY_QUERY("ana_filter=B01001>1", 0); + TRY_QUERY("ana_filter=block<=1", all); + TRY_QUERY("ana_filter=B01001<=1", all); + TRY_QUERY("ana_filter=0<=B01001<=2", all); + TRY_QUERY("ana_filter=1<=B01001<=1", all); + TRY_QUERY("ana_filter=2<=B01001<=4", 0); + }); + add_method("data_filter", [](Fixture& f) { + wassert(f.populate()); + // data_filter queries + TRY_QUERY("data_filter=B01011=DB-All.e!", 1); + TRY_QUERY("data_filter=B01012<300", 0); + TRY_QUERY("data_filter=B01012<=300", 1); + TRY_QUERY("data_filter=B01012=300", 1); + TRY_QUERY("data_filter=B01012>=300", 2); + TRY_QUERY("data_filter=B01012>300", 1); + TRY_QUERY("data_filter=B01012<400", 1); + TRY_QUERY("data_filter=B01012<=400", 2); + }); + add_method("latlon", [](Fixture& f) { + wassert(f.populate()); + // latitude/longitude queries + TRY_QUERY("latmin=11.0", 4); + TRY_QUERY("latmin=12.34560", 4); + TRY_QUERY("latmin=13.0", 0); + TRY_QUERY("latmax=11.0", 0); + TRY_QUERY("latmax=12.34560", 4); + TRY_QUERY("latmax=13.0", 4); + TRY_QUERY("latmin=0, latmax=20", 4); + TRY_QUERY("latmin=-90, latmax=20", 4); + TRY_QUERY("latmin=-90, latmax=0", 0); + TRY_QUERY("latmin=10, latmax=90", 4); + TRY_QUERY("latmin=45, latmax=90", 0); + TRY_QUERY("latmin=-90, latmax=90", 4); + TRY_QUERY("lonmin=75, lonmax=77", 4); + TRY_QUERY("lonmin=76.54320, lonmax=76.54320", 4); + TRY_QUERY("lonmin=76.54330, lonmax=77.", 0); + TRY_QUERY("lonmin=77., lonmax=76.54330", 4); + TRY_QUERY("lonmin=77., lonmax=76.54320", 4); + TRY_QUERY("lonmin=77., lonmax=-10", 0); + TRY_QUERY("lonmin=0., lonmax=360.", 4); + TRY_QUERY("lonmin=-180., lonmax=180.", 4); + }); + add_method("mobile", [](Fixture& f) { + wassert(f.populate()); + // fixed/mobile queries + TRY_QUERY("mobile=0", 4); + TRY_QUERY("mobile=1", 0); + }); + // ident queries + // FIXME: we currently have no mobile station data in the samples + //TRY_QUERY(c, DBA_KEY_IDENT_SELECT, "pippo"); + add_method("timerange", [](Fixture& f) { + wassert(f.populate()); + // timerange queries + TRY_QUERY("pindicator=20", 4); + TRY_QUERY("pindicator=21", 0); + TRY_QUERY("p1=111", 4); + TRY_QUERY("p1=112", 0); + TRY_QUERY("p2=121", 0); + TRY_QUERY("p2=122", 2); + TRY_QUERY("p2=123", 2); + }); + add_method("level", [](Fixture& f) { + wassert(f.populate()); + // level queries + TRY_QUERY("leveltype1=10", 4); + TRY_QUERY("leveltype1=11", 0); + TRY_QUERY("leveltype2=15", 4); + TRY_QUERY("leveltype2=16", 0); + TRY_QUERY("l1=11", 4); + TRY_QUERY("l1=12", 0); + TRY_QUERY("l2=22", 4); + TRY_QUERY("l2=23", 0); + }); + add_method("varcode", [](Fixture& f) { + wassert(f.populate()); + // varcode queries + TRY_QUERY("var=B01011", 2); + TRY_QUERY("var=B01012", 2); + TRY_QUERY("var=B01013", 0); + }); + add_method("report", [](Fixture& f) { + wassert(f.populate()); + // report queries + TRY_QUERY("rep_memo=synop", 2); + TRY_QUERY("rep_memo=metar", 2); + TRY_QUERY("rep_memo=temp", 0); + }); + add_method("priority", [](Fixture& f) { + wassert(f.populate()); + // report priority queries + TRY_QUERY("priority=101", 2); + TRY_QUERY("priority=81", 2); + TRY_QUERY("priority=102", 0); + TRY_QUERY("priomin=70", 4); + TRY_QUERY("priomin=80", 4); + TRY_QUERY("priomin=90", 2); + TRY_QUERY("priomin=100", 2); + TRY_QUERY("priomin=110", 0); + TRY_QUERY("priomax=70", 0); + TRY_QUERY("priomax=81", 2); + TRY_QUERY("priomax=100", 2); + TRY_QUERY("priomax=101", 4); + TRY_QUERY("priomax=110", 4); + }); + add_method("context_id", [](Fixture& f) { + wassert(f.populate()); + // get a valid data id + core::Query q; + q.varcodes.insert(WR_VAR(0, 1, 11)); + core::Record res; + auto cur = f.db->query_data(q); + while (cur->next()) + cur->to_record(res); + char valid_query[100]; + snprintf(valid_query, 100, "context_id=%d", res.enq("context_id", MISSING_INT)); + + // context ID queries + TRY_QUERY(valid_query, 1); + TRY_QUERY("context_id=1234567", 0); + }); + add_method("datetime1", [](Fixture& f) { + // Check datetime queries, with data that only differs by its hour + wassert(f.populate()); + auto& db = *f.db; + + // Valid hours: 11 and 12 + + // Exact match + wassert(actual(db).try_data_query(query_exact(Datetime(2013, 10, 30, 10)), 0)); + wassert(actual(db).try_data_query(query_exact(Datetime(2013, 10, 30, 11)), 1)); + wassert(actual(db).try_data_query(query_exact(Datetime(2013, 10, 30, 12)), 1)); + wassert(actual(db).try_data_query(query_exact(Datetime(2013, 10, 30, 13)), 0)); + + // Datemin match + wassert(actual(db).try_data_query(query_min(Datetime(2013, 10, 30, 10)), 2)); + wassert(actual(db).try_data_query(query_min(Datetime(2013, 10, 30, 11)), 2)); + wassert(actual(db).try_data_query(query_min(Datetime(2013, 10, 30, 12)), 1)); + wassert(actual(db).try_data_query(query_min(Datetime(2013, 10, 30, 13)), 0)); + + // Datemax match + wassert(actual(db).try_data_query(query_max(Datetime(2013, 10, 30, 13)), 2)); + wassert(actual(db).try_data_query(query_max(Datetime(2013, 10, 30, 12)), 2)); + wassert(actual(db).try_data_query(query_max(Datetime(2013, 10, 30, 11)), 1)); + wassert(actual(db).try_data_query(query_max(Datetime(2013, 10, 30, 10)), 0)); + + // Date min-max match + wassert(actual(db).try_data_query(query_minmax(Datetime(2013, 10, 30, 10), Datetime(2013, 10, 30, 13)), 2)); + wassert(actual(db).try_data_query(query_minmax(Datetime(2013, 10, 30, 11), Datetime(2013, 10, 30, 12)), 2)); + wassert(actual(db).try_data_query(query_minmax(Datetime(2013, 10, 30, 10), Datetime(2013, 10, 30, 11)), 1)); + wassert(actual(db).try_data_query(query_minmax(Datetime(2013, 10, 30, 12), Datetime(2013, 10, 30, 13)), 1)); + wassert(actual(db).try_data_query(query_minmax(Datetime(2013, 10, 30, 9), Datetime(2013, 10, 30, 10)), 0)); + wassert(actual(db).try_data_query(query_minmax(Datetime(2013, 10, 30, 13), Datetime(2013, 10, 30, 14)), 0)); + }); + add_method("datetime2", [](Fixture& f) { + // Check datetime queries, with data that only differs by its day + wassert(f.populate()); + auto& db = *f.db; + + // Valid days: 23 and 24 + + // Exact match + wassert(actual(db).try_data_query(query_exact(Datetime(2013, 10, 22)), 0)); + wassert(actual(db).try_data_query(query_exact(Datetime(2013, 10, 23)), 1)); + wassert(actual(db).try_data_query(query_exact(Datetime(2013, 10, 24)), 1)); + wassert(actual(db).try_data_query(query_exact(Datetime(2013, 10, 25)), 0)); + + // Datemin match + wassert(actual(db).try_data_query(query_min(Datetime(2013, 10, 22)), 2)); + wassert(actual(db).try_data_query(query_min(Datetime(2013, 10, 23)), 2)); + wassert(actual(db).try_data_query(query_min(Datetime(2013, 10, 24)), 1)); + wassert(actual(db).try_data_query(query_min(Datetime(2013, 10, 25)), 0)); + + // Datemax match + wassert(actual(db).try_data_query(query_max(Datetime(2013, 10, 25)), 2)); + wassert(actual(db).try_data_query(query_max(Datetime(2013, 10, 24)), 2)); + wassert(actual(db).try_data_query(query_max(Datetime(2013, 10, 23)), 1)); + wassert(actual(db).try_data_query(query_max(Datetime(2013, 10, 22)), 0)); + + // Date min-max match + wassert(actual(db).try_data_query(query_minmax(Datetime(2013, 10, 22), Datetime(2013, 10, 25)), 2)); + wassert(actual(db).try_data_query(query_minmax(Datetime(2013, 10, 23), Datetime(2013, 10, 24)), 2)); + wassert(actual(db).try_data_query(query_minmax(Datetime(2013, 10, 23), Datetime(2013, 10, 23)), 1)); + wassert(actual(db).try_data_query(query_minmax(Datetime(2013, 10, 24), Datetime(2013, 10, 24)), 1)); + wassert(actual(db).try_data_query(query_minmax(Datetime(2013, 10, 22), Datetime(2013, 10, 23)), 1)); + wassert(actual(db).try_data_query(query_minmax(Datetime(2013, 10, 24), Datetime(2013, 10, 25)), 1)); + wassert(actual(db).try_data_query(query_minmax(Datetime(2013, 10, 21), Datetime(2013, 10, 22)), 0)); + wassert(actual(db).try_data_query(query_minmax(Datetime(2013, 10, 25), Datetime(2013, 10, 26)), 0)); + }); + } +}; + +Tests tg1("db_query_data_mem", nullptr, db::MEM); +Tests tg2("db_query_data_v6_sqlite", "SQLITE", db::V6); +#ifdef HAVE_ODBC +Tests tg4("db_query_data_v6_odbc", "ODBC", db::V6); +#endif +#ifdef HAVE_LIBPQ +Tests tg6("db_query_data_v6_postgresql", "POSTGRESQL", db::V6); +#endif +#ifdef HAVE_MYSQL +Tests tg8("db_query_data_v6_mysql", "MYSQL", db::V6); +#endif + +} diff --git a/dballe/db/db-query-data-tut.cc b/dballe/db/db-query-data-tut.cc deleted file mode 100644 index 91cbd125a..000000000 --- a/dballe/db/db-query-data-tut.cc +++ /dev/null @@ -1,360 +0,0 @@ -#include "db/tests.h" -#include -#include "config.h" - -using namespace dballe; -using namespace dballe::db; -using namespace dballe::tests; -using namespace wreport; -using namespace wibble; -using namespace wibble::tests; -using namespace std; - -namespace { - -static inline core::Query query_exact(const Datetime& dt) -{ - core::Query query; - query.datetime = DatetimeRange(dt, dt); - return query; -} -static inline core::Query query_min(const Datetime& dt) -{ - core::Query query; - query.datetime = DatetimeRange(dt, Datetime()); - return query; -} -static inline core::Query query_max(const Datetime& dt) -{ - core::Query query; - query.datetime = DatetimeRange(Datetime(), dt); - return query; -} -static inline core::Query query_minmax(const Datetime& min, const Datetime& max) -{ - core::Query query; - query.datetime = DatetimeRange(min, max); - return query; -} - -struct DateHourFixture : public TestFixture -{ - DateHourFixture() - { - DataValues d; - d.info.coords = Coords(12.34560, 76.54320); - d.info.report = "synop"; - d.info.level = Level(10, 11, 15, 22); - d.info.trange = Trange(20, 111, 122); - data["1"] = d; - data["1"].info.datetime = Datetime(2013, 10, 30, 11); - data["1"].values.set("B12101", 11.5); - data["2"] = d; - data["2"].info.datetime = Datetime(2013, 10, 30, 12); - data["2"].values.set("B12101", 12.5); - } -}; - -struct DateDayFixture : public TestFixture -{ - DateDayFixture() - { - DataValues d; - d.info.coords = Coords(12.34560, 76.54320); - d.info.report = "synop"; - d.info.level = Level(10, 11, 15, 22); - d.info.trange = Trange(20, 111, 122); - data["1"] = d; - data["1"].info.datetime = Datetime(2013, 10, 23); - data["1"].values.set("B12101", 23.5); - data["2"] = d; - data["2"].info.datetime = Datetime(2013, 10, 24); - data["2"].values.set("B12101", 24.5); - } -}; - -typedef dballe::tests::DBFixture Fixture; -typedef dballe::tests::db_test_group test_group; -typedef test_group::Test Test; - -#define TRY_QUERY(qstring, expected_count) wassert(actual(*f.db).try_data_query(qstring, expected_count)) - -std::vector tests { - Test("ana_id", [](Fixture& f) { - OldDballeTestFixture oldf; - wruntest(f.populate_database, oldf); -#warning FIXME: change after testing if we can move to report-in-station behaviour or not - if (f.db->format() == MEM) - TRY_QUERY(str::fmtf("ana_id=%d", oldf.data["synop"].info.ana_id), 2); - else - TRY_QUERY(str::fmtf("ana_id=%d", oldf.data["synop"].info.ana_id), 4); - TRY_QUERY("ana_id=4242", 0); - }), - Test("ana_context", [](Fixture& f) { - wruntest(f.populate); - // Query data in station context - core::Query query; - unique_ptr cur = f.db->query_station_data(query); - wassert(actual(cur->remaining()) == 10); - }), - Test("year", [](Fixture& f) { - wruntest(f.populate); - // Datetime queries - TRY_QUERY("year=1001", 0); - TRY_QUERY("yearmin=1999", 0); - TRY_QUERY("yearmin=1945", 4); - TRY_QUERY("yearmax=1944", 0); - TRY_QUERY("yearmax=1945", 4); - TRY_QUERY("yearmax=2030", 4); - TRY_QUERY("year=1944", 0); - TRY_QUERY("year=1945", 4); - TRY_QUERY("year=1946", 0); - /* - TRY_QUERY(i, DBA_KEY_MONTHMIN, 1); - TRY_QUERY(i, DBA_KEY_MONTHMAX, 12); - TRY_QUERY(i, DBA_KEY_MONTH, 5); - */ - /* - TRY_QUERY(i, DBA_KEY_DAYMIN, 1); - TRY_QUERY(i, DBA_KEY_DAYMAX, 12); - TRY_QUERY(i, DBA_KEY_DAY, 5); - */ - /* - TRY_QUERY(i, DBA_KEY_HOURMIN, 1); - TRY_QUERY(i, DBA_KEY_HOURMAX, 12); - TRY_QUERY(i, DBA_KEY_HOUR, 5); - */ - /* - TRY_QUERY(i, DBA_KEY_MINUMIN, 1); - TRY_QUERY(i, DBA_KEY_MINUMAX, 12); - TRY_QUERY(i, DBA_KEY_MIN, 5); - */ - /* - TRY_QUERY(i, DBA_KEY_SECMIN, 1); - TRY_QUERY(i, DBA_KEY_SECMAX, 12); - TRY_QUERY(i, DBA_KEY_SEC, 5); - */ - }), - Test("block_station", [](Fixture& f) { - wruntest(f.populate); -#warning FIXME: change after testing if we can move to report-in-station behaviour or not - const int all = (f.db->format() == MEM ? 4 : 4); - // Block and station queries - TRY_QUERY("B01001=1", all); - TRY_QUERY("B01001=2", 0); - TRY_QUERY("B01002=52", all); - TRY_QUERY("B01002=53", 0); - }), - Test("ana_filter", [](Fixture& f) { -#warning FIXME: change after testing if we can move to report-in-station behaviour or not - const int all = (f.db->format() == MEM ? 4 : 4); - wruntest(f.populate); - // ana_filter queries - TRY_QUERY("ana_filter=block=1", all); - TRY_QUERY("ana_filter=B01001=1", all); - TRY_QUERY("ana_filter=block>1", 0); - TRY_QUERY("ana_filter=B01001>1", 0); - TRY_QUERY("ana_filter=block<=1", all); - TRY_QUERY("ana_filter=B01001<=1", all); - TRY_QUERY("ana_filter=0<=B01001<=2", all); - TRY_QUERY("ana_filter=1<=B01001<=1", all); - TRY_QUERY("ana_filter=2<=B01001<=4", 0); - }), - Test("data_filter", [](Fixture& f) { - wruntest(f.populate); - // data_filter queries - TRY_QUERY("data_filter=B01011=DB-All.e!", 1); - TRY_QUERY("data_filter=B01012<300", 0); - TRY_QUERY("data_filter=B01012<=300", 1); - TRY_QUERY("data_filter=B01012=300", 1); - TRY_QUERY("data_filter=B01012>=300", 2); - TRY_QUERY("data_filter=B01012>300", 1); - TRY_QUERY("data_filter=B01012<400", 1); - TRY_QUERY("data_filter=B01012<=400", 2); - }), - Test("latlon", [](Fixture& f) { - wruntest(f.populate); - // latitude/longitude queries - TRY_QUERY("latmin=11.0", 4); - TRY_QUERY("latmin=12.34560", 4); - TRY_QUERY("latmin=13.0", 0); - TRY_QUERY("latmax=11.0", 0); - TRY_QUERY("latmax=12.34560", 4); - TRY_QUERY("latmax=13.0", 4); - TRY_QUERY("latmin=0, latmax=20", 4); - TRY_QUERY("latmin=-90, latmax=20", 4); - TRY_QUERY("latmin=-90, latmax=0", 0); - TRY_QUERY("latmin=10, latmax=90", 4); - TRY_QUERY("latmin=45, latmax=90", 0); - TRY_QUERY("latmin=-90, latmax=90", 4); - TRY_QUERY("lonmin=75, lonmax=77", 4); - TRY_QUERY("lonmin=76.54320, lonmax=76.54320", 4); - TRY_QUERY("lonmin=76.54330, lonmax=77.", 0); - TRY_QUERY("lonmin=77., lonmax=76.54330", 4); - TRY_QUERY("lonmin=77., lonmax=76.54320", 4); - TRY_QUERY("lonmin=77., lonmax=-10", 0); - TRY_QUERY("lonmin=0., lonmax=360.", 4); - TRY_QUERY("lonmin=-180., lonmax=180.", 4); - }), - Test("mobile", [](Fixture& f) { - wruntest(f.populate); - // fixed/mobile queries - TRY_QUERY("mobile=0", 4); - TRY_QUERY("mobile=1", 0); - }), - // ident queries - // FIXME: we currently have no mobile station data in the samples - //TRY_QUERY(c, DBA_KEY_IDENT_SELECT, "pippo"); - Test("timerange", [](Fixture& f) { - wruntest(f.populate); - // timerange queries - TRY_QUERY("pindicator=20", 4); - TRY_QUERY("pindicator=21", 0); - TRY_QUERY("p1=111", 4); - TRY_QUERY("p1=112", 0); - TRY_QUERY("p2=121", 0); - TRY_QUERY("p2=122", 2); - TRY_QUERY("p2=123", 2); - }), - Test("level", [](Fixture& f) { - wruntest(f.populate); - // level queries - TRY_QUERY("leveltype1=10", 4); - TRY_QUERY("leveltype1=11", 0); - TRY_QUERY("leveltype2=15", 4); - TRY_QUERY("leveltype2=16", 0); - TRY_QUERY("l1=11", 4); - TRY_QUERY("l1=12", 0); - TRY_QUERY("l2=22", 4); - TRY_QUERY("l2=23", 0); - }), - Test("varcode", [](Fixture& f) { - wruntest(f.populate); - // varcode queries - TRY_QUERY("var=B01011", 2); - TRY_QUERY("var=B01012", 2); - TRY_QUERY("var=B01013", 0); - }), - Test("report", [](Fixture& f) { - wruntest(f.populate); - // report queries - TRY_QUERY("rep_memo=synop", 2); - TRY_QUERY("rep_memo=metar", 2); - TRY_QUERY("rep_memo=temp", 0); - }), - Test("priority", [](Fixture& f) { - wruntest(f.populate); - // report priority queries - TRY_QUERY("priority=101", 2); - TRY_QUERY("priority=81", 2); - TRY_QUERY("priority=102", 0); - TRY_QUERY("priomin=70", 4); - TRY_QUERY("priomin=80", 4); - TRY_QUERY("priomin=90", 2); - TRY_QUERY("priomin=100", 2); - TRY_QUERY("priomin=110", 0); - TRY_QUERY("priomax=70", 0); - TRY_QUERY("priomax=81", 2); - TRY_QUERY("priomax=100", 2); - TRY_QUERY("priomax=101", 4); - TRY_QUERY("priomax=110", 4); - }), - Test("context_id", [](Fixture& f) { - wruntest(f.populate); - // get a valid data id - core::Query q; - q.varcodes.insert(WR_VAR(0, 1, 11)); - core::Record res; - auto cur = f.db->query_data(q); - while (cur->next()) - cur->to_record(res); - char valid_query[100]; - snprintf(valid_query, 100, "context_id=%d", res.enq("context_id", MISSING_INT)); - - // context ID queries - TRY_QUERY(valid_query, 1); - TRY_QUERY("context_id=1234567", 0); - }), - Test("datetime1", [](Fixture& f) { - // Check datetime queries, with data that only differs by its hour - wruntest(f.populate); - auto& db = *f.db; - - // Valid hours: 11 and 12 - - // Exact match - wassert(actual(db).try_data_query(query_exact(Datetime(2013, 10, 30, 10)), 0)); - wassert(actual(db).try_data_query(query_exact(Datetime(2013, 10, 30, 11)), 1)); - wassert(actual(db).try_data_query(query_exact(Datetime(2013, 10, 30, 12)), 1)); - wassert(actual(db).try_data_query(query_exact(Datetime(2013, 10, 30, 13)), 0)); - - // Datemin match - wassert(actual(db).try_data_query(query_min(Datetime(2013, 10, 30, 10)), 2)); - wassert(actual(db).try_data_query(query_min(Datetime(2013, 10, 30, 11)), 2)); - wassert(actual(db).try_data_query(query_min(Datetime(2013, 10, 30, 12)), 1)); - wassert(actual(db).try_data_query(query_min(Datetime(2013, 10, 30, 13)), 0)); - - // Datemax match - wassert(actual(db).try_data_query(query_max(Datetime(2013, 10, 30, 13)), 2)); - wassert(actual(db).try_data_query(query_max(Datetime(2013, 10, 30, 12)), 2)); - wassert(actual(db).try_data_query(query_max(Datetime(2013, 10, 30, 11)), 1)); - wassert(actual(db).try_data_query(query_max(Datetime(2013, 10, 30, 10)), 0)); - - // Date min-max match - wassert(actual(db).try_data_query(query_minmax(Datetime(2013, 10, 30, 10), Datetime(2013, 10, 30, 13)), 2)); - wassert(actual(db).try_data_query(query_minmax(Datetime(2013, 10, 30, 11), Datetime(2013, 10, 30, 12)), 2)); - wassert(actual(db).try_data_query(query_minmax(Datetime(2013, 10, 30, 10), Datetime(2013, 10, 30, 11)), 1)); - wassert(actual(db).try_data_query(query_minmax(Datetime(2013, 10, 30, 12), Datetime(2013, 10, 30, 13)), 1)); - wassert(actual(db).try_data_query(query_minmax(Datetime(2013, 10, 30, 9), Datetime(2013, 10, 30, 10)), 0)); - wassert(actual(db).try_data_query(query_minmax(Datetime(2013, 10, 30, 13), Datetime(2013, 10, 30, 14)), 0)); - }), - Test("datetime2", [](Fixture& f) { - // Check datetime queries, with data that only differs by its day - wruntest(f.populate); - auto& db = *f.db; - - // Valid days: 23 and 24 - - // Exact match - wassert(actual(db).try_data_query(query_exact(Datetime(2013, 10, 22)), 0)); - wassert(actual(db).try_data_query(query_exact(Datetime(2013, 10, 23)), 1)); - wassert(actual(db).try_data_query(query_exact(Datetime(2013, 10, 24)), 1)); - wassert(actual(db).try_data_query(query_exact(Datetime(2013, 10, 25)), 0)); - - // Datemin match - wassert(actual(db).try_data_query(query_min(Datetime(2013, 10, 22)), 2)); - wassert(actual(db).try_data_query(query_min(Datetime(2013, 10, 23)), 2)); - wassert(actual(db).try_data_query(query_min(Datetime(2013, 10, 24)), 1)); - wassert(actual(db).try_data_query(query_min(Datetime(2013, 10, 25)), 0)); - - // Datemax match - wassert(actual(db).try_data_query(query_max(Datetime(2013, 10, 25)), 2)); - wassert(actual(db).try_data_query(query_max(Datetime(2013, 10, 24)), 2)); - wassert(actual(db).try_data_query(query_max(Datetime(2013, 10, 23)), 1)); - wassert(actual(db).try_data_query(query_max(Datetime(2013, 10, 22)), 0)); - - // Date min-max match - wassert(actual(db).try_data_query(query_minmax(Datetime(2013, 10, 22), Datetime(2013, 10, 25)), 2)); - wassert(actual(db).try_data_query(query_minmax(Datetime(2013, 10, 23), Datetime(2013, 10, 24)), 2)); - wassert(actual(db).try_data_query(query_minmax(Datetime(2013, 10, 23), Datetime(2013, 10, 23)), 1)); - wassert(actual(db).try_data_query(query_minmax(Datetime(2013, 10, 24), Datetime(2013, 10, 24)), 1)); - wassert(actual(db).try_data_query(query_minmax(Datetime(2013, 10, 22), Datetime(2013, 10, 23)), 1)); - wassert(actual(db).try_data_query(query_minmax(Datetime(2013, 10, 24), Datetime(2013, 10, 25)), 1)); - wassert(actual(db).try_data_query(query_minmax(Datetime(2013, 10, 21), Datetime(2013, 10, 22)), 0)); - wassert(actual(db).try_data_query(query_minmax(Datetime(2013, 10, 25), Datetime(2013, 10, 26)), 0)); - }), -}; - -test_group tg1("db_query_data_mem", nullptr, db::MEM, tests); -test_group tg2("db_query_data_v6_sqlite", "SQLITE", db::V6, tests); -#ifdef HAVE_ODBC -test_group tg4("db_query_data_v6_odbc", "ODBC", db::V6, tests); -#endif -#ifdef HAVE_LIBPQ -test_group tg6("db_query_data_v6_postgresql", "POSTGRESQL", db::V6, tests); -#endif -#ifdef HAVE_MYSQL -test_group tg8("db_query_data_v6_mysql", "MYSQL", db::V6, tests); -#endif - -} diff --git a/dballe/db/db-query-station-test.cc b/dballe/db/db-query-station-test.cc new file mode 100644 index 000000000..53ddca785 --- /dev/null +++ b/dballe/db/db-query-station-test.cc @@ -0,0 +1,211 @@ +#include "config.h" +#include "db/tests.h" +#include "db/mem/db.h" +#include "db/v6/db.h" +#include "db/sql/station.h" + +using namespace dballe; +using namespace dballe::db; +using namespace dballe::tests; +using namespace wreport; +using namespace std; + +namespace { + +struct DBData : public TestDataSet +{ + DBData() + { + stations["st1_synop"].info.coords = Coords(12.34560, 76.54320); + stations["st1_synop"].info.report = "synop"; + stations["st1_synop"].values.set(newvar("block", 1)); + stations["st1_synop"].values.set(newvar("station", 1)); + stations["st1_synop"].values.set(newvar("B07030", 42.0)); // height + stations["st1_metar"].info = stations["st1_synop"].info; + stations["st1_metar"].info.report = "metar"; + stations["st1_metar"].values.set(newvar("block", 1)); + stations["st1_metar"].values.set(newvar("station", 2)); + stations["st1_metar"].values.set(newvar("B07030", 50.0)); // height + stations["st2_temp"].info.coords = Coords(23.45670, 65.43210); + stations["st2_temp"].info.report = "temp"; + stations["st2_temp"].values.set(newvar("block", 3)); + stations["st2_temp"].values.set(newvar("station", 4)); + stations["st2_temp"].values.set(newvar("B07030", 100.0)); // height + stations["st2_metar"].info = stations["st2_temp"].info; + stations["st2_metar"].info.report = "metar"; + stations["st2_metar"].values.set(newvar("block", 3)); + stations["st2_metar"].values.set(newvar("station", 4)); + stations["st2_metar"].values.set(newvar("B07030", 110.0)); // height + data["rec1"].info = stations["st1_metar"].info; + data["rec1"].info.datetime = Datetime(1945, 4, 25, 8); + data["rec1"].info.level = Level(10, 11, 15, 22); + data["rec1"].info.trange = Trange(20, 111, 122); + data["rec1"].values.set("B12101", 290.0); + data["rec2"].info = stations["st2_metar"].info; + data["rec2"].info.datetime = Datetime(1945, 4, 25, 8); + data["rec2"].info.level = Level(10, 11, 15, 22); + data["rec2"].info.trange = Trange(20, 111, 122); + data["rec2"].values.set("B12101", 300.0); + data["rec2"].values.set("B12103", 298.0); + } +}; + +struct Fixture : public DBFixture +{ + const int some; + const int all; + + Fixture(const char* backend, db::Format format) + : DBFixture(backend, format), some(format == MEM ? 2 : 1), all(format == MEM ? 4 : 2) +#warning FIXME: change after testing if we can move to report-in-station behaviour or not + { + } + + void test_setup() + { + DBFixture::test_setup(); + wassert(populate()); + } +}; + +#define TRY_QUERY(qstring, expected_count) wassert(actual(*f.db).try_data_query(qstring, expected_count)) + +class Tests : public FixtureTestCase +{ + using FixtureTestCase::FixtureTestCase; + + void register_tests() override + { + add_method("query_ana_id", [](Fixture& f) { + auto& db = *f.db; + wassert(actual(db).try_station_query("ana_id=1", 1)); + wassert(actual(db).try_station_query("ana_id=2", 1)); + }); + add_method("query_lat_lon", [](Fixture& f) { + auto& db = *f.db; + const auto some = f.some; + const auto all = f.all; + wassert(actual(db).try_station_query("lat=12.00000", 0)); + wassert(actual(db).try_station_query("lat=12.34560", some)); + wassert(actual(db).try_station_query("lat=23.45670", some)); + wassert(actual(db).try_station_query("latmin=12.00000", all)); + wassert(actual(db).try_station_query("latmin=12.34560", all)); + wassert(actual(db).try_station_query("latmin=12.34570", some)); + wassert(actual(db).try_station_query("latmin=23.45670", some)); + wassert(actual(db).try_station_query("latmin=23.45680", 0)); + wassert(actual(db).try_station_query("latmax=12.00000", 0)); + wassert(actual(db).try_station_query("latmax=12.34560", some)); + wassert(actual(db).try_station_query("latmax=12.34570", some)); + wassert(actual(db).try_station_query("latmax=23.45670", all)); + wassert(actual(db).try_station_query("latmax=23.45680", all)); + wassert(actual(db).try_station_query("lon=76.00000", 0)); + wassert(actual(db).try_station_query("lon=76.54320", some)); + wassert(actual(db).try_station_query("lon=65.43210", some)); + wassert(actual(db).try_station_query("lonmin=10., lonmax=20.", 0)); + wassert(actual(db).try_station_query("lonmin=76.54320, lonmax=76.54320", some)); + wassert(actual(db).try_station_query("lonmin=76.54320, lonmax=77.", some)); + wassert(actual(db).try_station_query("lonmin=76.54330, lonmax=77.", 0)); + wassert(actual(db).try_station_query("lonmin=60., lonmax=77.", all)); + wassert(actual(db).try_station_query("lonmin=77., lonmax=76.54310", some)); + wassert(actual(db).try_station_query("lonmin=77., lonmax=76.54320", all)); + wassert(actual(db).try_station_query("lonmin=77., lonmax=-10", 0)); + }); + add_method("query_mobile", [](Fixture& f) { + auto& db = *f.db; + wassert(actual(db).try_station_query("mobile=0", f.all)); + wassert(actual(db).try_station_query("mobile=1", 0)); + }); + add_method("query_ident", [](Fixture& f) { + // FIXME: add some mobile stations to the test fixture to test ident + }); + add_method("query_block_station", [](Fixture& f) { + auto& db = *f.db; + wassert(actual(db).try_station_query("B01001=1", f.some)); + wassert(actual(db).try_station_query("B01001=2", 0)); + wassert(actual(db).try_station_query("B01001=3", f.some)); + wassert(actual(db).try_station_query("B01001=4", 0)); + wassert(actual(db).try_station_query("B01002=1", 1)); + wassert(actual(db).try_station_query("B01002=2", 1)); + wassert(actual(db).try_station_query("B01002=3", 0)); + wassert(actual(db).try_station_query("B01002=4", f.some)); + }); + add_method("query_mobile", [](Fixture& f) { + auto& db = *f.db; + }); + add_method("query_ana_filter", [](Fixture& f) { + auto& db = *f.db; + wassert(actual(db).try_station_query("ana_filter=block=1", f.some)); + wassert(actual(db).try_station_query("ana_filter=block=2", 0)); + wassert(actual(db).try_station_query("ana_filter=block=3", f.some)); + wassert(actual(db).try_station_query("ana_filter=block>=1", f.all)); + wassert(actual(db).try_station_query("ana_filter=B07030=42", 1)); + wassert(actual(db).try_station_query("ana_filter=B07030=50", 1)); + wassert(actual(db).try_station_query("ana_filter=B07030=100", 1)); + wassert(actual(db).try_station_query("ana_filter=B07030=110", 1)); + wassert(actual(db).try_station_query("ana_filter=B07030=120", 0)); + wassert(actual(db).try_station_query("ana_filter=B07030>50", f.some)); +#warning FIXME: change after testing if we can move to report-in-station behaviour or not + if (db.format() == MEM) + wassert(actual(db).try_station_query("ana_filter=B07030>=50", 3)); + else + wassert(actual(db).try_station_query("ana_filter=B07030>=50", 2)); + wassert(actual(db).try_station_query("ana_filter=50<=B07030<=100", 2)); + }); + add_method("query_var", [](Fixture& f) { + auto& db = *f.db; + wassert(actual(db).try_station_query("var=B12101", 2)); + wassert(actual(db).try_station_query("var=B12103", 1)); + wassert(actual(db).try_station_query("varlist=B12101", 2)); + wassert(actual(db).try_station_query("varlist=B12103", 1)); +#warning FIXME: change after testing if we can move to report-in-station behaviour or not + if (db.format() == MEM) + wassert(actual(db).try_station_query("varlist=B12101,B12103", 3)); + else + wassert(actual(db).try_station_query("varlist=B12101,B12103", 2)); + }); + add_method("stations_without_data", [](Fixture& f) { + auto& db = *f.db; + + // Manually insert an orphan station + switch (db.format()) + { + case MEM: + if (auto d = dynamic_cast(f.db)) + d->memdb.stations.obtain_fixed(Coords(11.0, 45.0), "synop"); + break; + case V6: + if (auto d = dynamic_cast(f.db)) + d->station().obtain_id(1100000, 4500000); + break; + case V5: throw error_unimplemented("v5 db is not supported"); + case MESSAGES: throw error_unimplemented("testing stations_without_data on MESSAGES database"); + } + + // Query stations and make sure that they do not appear. They should + // not appear, but they currently do because of a bug. I need to + // preserve the bug until the software that relies on it has been + // migrated to use standard DB-All.e features. + core::Query query; + query.latrange.set(11.0, 11.0); + query.lonrange.set(45.0, 45.0); + auto cur = db.query_stations(query); +#warning TODO: fix this test to give an error once we do not need to support this bug anymore + //wassert(actual(cur->remaining()) == 0); + wassert(actual(cur->remaining()) == 1); + }); + } +}; + +Tests tg1("db_query_station_mem", nullptr, db::MEM); +Tests tg2("db_query_station_v6_sqlite", "SQLITE", db::V6); +#ifdef HAVE_ODBC +Tests tg4("db_query_station_v6_odbc", "ODBC", db::V6); +#endif +#ifdef HAVE_LIBPQ +Tests tg6("db_query_station_v6_postgresql", "POSTGRESQL", db::V6); +#endif +#ifdef HAVE_MYSQL +Tests tg8("db_query_station_v6_mysql", "MYSQL", db::V6); +#endif + +} diff --git a/dballe/db/db-query-station-tut.cc b/dballe/db/db-query-station-tut.cc deleted file mode 100644 index dd6732eac..000000000 --- a/dballe/db/db-query-station-tut.cc +++ /dev/null @@ -1,211 +0,0 @@ -#include "config.h" -#include "db/tests.h" -#include "db/mem/db.h" -#include "db/v6/db.h" -#include "db/sql/station.h" - -using namespace dballe; -using namespace dballe::db; -using namespace dballe::tests; -using namespace wreport; -using namespace wibble; -using namespace wibble::tests; -using namespace std; - -namespace { - -struct DBData : public TestFixture -{ - DBData() - { - stations["st1_synop"].info.coords = Coords(12.34560, 76.54320); - stations["st1_synop"].info.report = "synop"; - stations["st1_synop"].values.set(newvar("block", 1)); - stations["st1_synop"].values.set(newvar("station", 1)); - stations["st1_synop"].values.set(newvar("B07030", 42.0)); // height - stations["st1_metar"].info = stations["st1_synop"].info; - stations["st1_metar"].info.report = "metar"; - stations["st1_metar"].values.set(newvar("block", 1)); - stations["st1_metar"].values.set(newvar("station", 2)); - stations["st1_metar"].values.set(newvar("B07030", 50.0)); // height - stations["st2_temp"].info.coords = Coords(23.45670, 65.43210); - stations["st2_temp"].info.report = "temp"; - stations["st2_temp"].values.set(newvar("block", 3)); - stations["st2_temp"].values.set(newvar("station", 4)); - stations["st2_temp"].values.set(newvar("B07030", 100.0)); // height - stations["st2_metar"].info = stations["st2_temp"].info; - stations["st2_metar"].info.report = "metar"; - stations["st2_metar"].values.set(newvar("block", 3)); - stations["st2_metar"].values.set(newvar("station", 4)); - stations["st2_metar"].values.set(newvar("B07030", 110.0)); // height - data["rec1"].info = stations["st1_metar"].info; - data["rec1"].info.datetime = Datetime(1945, 4, 25, 8); - data["rec1"].info.level = Level(10, 11, 15, 22); - data["rec1"].info.trange = Trange(20, 111, 122); - data["rec1"].values.set("B12101", 290.0); - data["rec2"].info = stations["st2_metar"].info; - data["rec2"].info.datetime = Datetime(1945, 4, 25, 8); - data["rec2"].info.level = Level(10, 11, 15, 22); - data["rec2"].info.trange = Trange(20, 111, 122); - data["rec2"].values.set("B12101", 300.0); - data["rec2"].values.set("B12103", 298.0); - } -}; - -struct Fixture : public dballe::tests::DBFixture -{ - const int some; - const int all; - - Fixture() - : some(db->format() == MEM ? 2 : 1), all(db->format() == MEM ? 4 : 2) -#warning FIXME: change after testing if we can move to report-in-station behaviour or not - { - wruntest(populate); - } - - void reset() - { - dballe::tests::DBFixture::reset(); - wruntest(populate); - } -}; - -typedef dballe::tests::db_test_group test_group; -typedef test_group::Test Test; - -#define TRY_QUERY(qstring, expected_count) wassert(actual(*f.db).try_data_query(qstring, expected_count)) - -std::vector tests { - Test("query_ana_id", [](Fixture& f) { - auto& db = *f.db; - wassert(actual(db).try_station_query("ana_id=1", 1)); - wassert(actual(db).try_station_query("ana_id=2", 1)); - }), - Test("query_lat_lon", [](Fixture& f) { - auto& db = *f.db; - const auto some = f.some; - const auto all = f.all; - wassert(actual(db).try_station_query("lat=12.00000", 0)); - wassert(actual(db).try_station_query("lat=12.34560", some)); - wassert(actual(db).try_station_query("lat=23.45670", some)); - wassert(actual(db).try_station_query("latmin=12.00000", all)); - wassert(actual(db).try_station_query("latmin=12.34560", all)); - wassert(actual(db).try_station_query("latmin=12.34570", some)); - wassert(actual(db).try_station_query("latmin=23.45670", some)); - wassert(actual(db).try_station_query("latmin=23.45680", 0)); - wassert(actual(db).try_station_query("latmax=12.00000", 0)); - wassert(actual(db).try_station_query("latmax=12.34560", some)); - wassert(actual(db).try_station_query("latmax=12.34570", some)); - wassert(actual(db).try_station_query("latmax=23.45670", all)); - wassert(actual(db).try_station_query("latmax=23.45680", all)); - wassert(actual(db).try_station_query("lon=76.00000", 0)); - wassert(actual(db).try_station_query("lon=76.54320", some)); - wassert(actual(db).try_station_query("lon=65.43210", some)); - wassert(actual(db).try_station_query("lonmin=10., lonmax=20.", 0)); - wassert(actual(db).try_station_query("lonmin=76.54320, lonmax=76.54320", some)); - wassert(actual(db).try_station_query("lonmin=76.54320, lonmax=77.", some)); - wassert(actual(db).try_station_query("lonmin=76.54330, lonmax=77.", 0)); - wassert(actual(db).try_station_query("lonmin=60., lonmax=77.", all)); - wassert(actual(db).try_station_query("lonmin=77., lonmax=76.54310", some)); - wassert(actual(db).try_station_query("lonmin=77., lonmax=76.54320", all)); - wassert(actual(db).try_station_query("lonmin=77., lonmax=-10", 0)); - }), - Test("query_mobile", [](Fixture& f) { - auto& db = *f.db; - wassert(actual(db).try_station_query("mobile=0", f.all)); - wassert(actual(db).try_station_query("mobile=1", 0)); - }), - Test("query_ident", [](Fixture& f) { - // FIXME: add some mobile stations to the test fixture to test ident - }), - Test("query_block_station", [](Fixture& f) { - auto& db = *f.db; - wassert(actual(db).try_station_query("B01001=1", f.some)); - wassert(actual(db).try_station_query("B01001=2", 0)); - wassert(actual(db).try_station_query("B01001=3", f.some)); - wassert(actual(db).try_station_query("B01001=4", 0)); - wassert(actual(db).try_station_query("B01002=1", 1)); - wassert(actual(db).try_station_query("B01002=2", 1)); - wassert(actual(db).try_station_query("B01002=3", 0)); - wassert(actual(db).try_station_query("B01002=4", f.some)); - }), - Test("query_mobile", [](Fixture& f) { - auto& db = *f.db; - }), - Test("query_ana_filter", [](Fixture& f) { - auto& db = *f.db; - wassert(actual(db).try_station_query("ana_filter=block=1", f.some)); - wassert(actual(db).try_station_query("ana_filter=block=2", 0)); - wassert(actual(db).try_station_query("ana_filter=block=3", f.some)); - wassert(actual(db).try_station_query("ana_filter=block>=1", f.all)); - wassert(actual(db).try_station_query("ana_filter=B07030=42", 1)); - wassert(actual(db).try_station_query("ana_filter=B07030=50", 1)); - wassert(actual(db).try_station_query("ana_filter=B07030=100", 1)); - wassert(actual(db).try_station_query("ana_filter=B07030=110", 1)); - wassert(actual(db).try_station_query("ana_filter=B07030=120", 0)); - wassert(actual(db).try_station_query("ana_filter=B07030>50", f.some)); -#warning FIXME: change after testing if we can move to report-in-station behaviour or not - if (db.format() == MEM) - wassert(actual(db).try_station_query("ana_filter=B07030>=50", 3)); - else - wassert(actual(db).try_station_query("ana_filter=B07030>=50", 2)); - wassert(actual(db).try_station_query("ana_filter=50<=B07030<=100", 2)); - }), - Test("query_var", [](Fixture& f) { - auto& db = *f.db; - wassert(actual(db).try_station_query("var=B12101", 2)); - wassert(actual(db).try_station_query("var=B12103", 1)); - wassert(actual(db).try_station_query("varlist=B12101", 2)); - wassert(actual(db).try_station_query("varlist=B12103", 1)); -#warning FIXME: change after testing if we can move to report-in-station behaviour or not - if (db.format() == MEM) - wassert(actual(db).try_station_query("varlist=B12101,B12103", 3)); - else - wassert(actual(db).try_station_query("varlist=B12101,B12103", 2)); - }), - Test("stations_without_data", [](Fixture& f) { - auto& db = *f.db; - - // Manually insert an orphan station - switch (db.format()) - { - case MEM: - if (auto d = dynamic_cast(f.db)) - d->memdb.stations.obtain_fixed(Coords(11.0, 45.0), "synop"); - break; - case V6: - if (auto d = dynamic_cast(f.db)) - d->station().obtain_id(1100000, 4500000); - break; - case V5: throw error_unimplemented("v5 db is not supported"); - case MESSAGES: throw error_unimplemented("testing stations_without_data on MESSAGES database"); - } - - // Query stations and make sure that they do not appear. They should - // not appear, but they currently do because of a bug. I need to - // preserve the bug until the software that relies on it has been - // migrated to use standard DB-All.e features. - core::Query query; - query.latrange.set(11.0, 11.0); - query.lonrange.set(45.0, 45.0); - auto cur = db.query_stations(query); -#warning TODO: fix this test to give an error once we do not need to support this bug anymore - //wassert(actual(cur->remaining()) == 0); - wassert(actual(cur->remaining()) == 1); - }), -}; - -test_group tg1("db_query_station_mem", nullptr, db::MEM, tests); -test_group tg2("db_query_station_v6_sqlite", "SQLITE", db::V6, tests); -#ifdef HAVE_ODBC -test_group tg4("db_query_station_v6_odbc", "ODBC", db::V6, tests); -#endif -#ifdef HAVE_LIBPQ -test_group tg6("db_query_station_v6_postgresql", "POSTGRESQL", db::V6, tests); -#endif -#ifdef HAVE_MYSQL -test_group tg8("db_query_station_v6_mysql", "MYSQL", db::V6, tests); -#endif - -} diff --git a/dballe/db/db-query-summary-test.cc b/dballe/db/db-query-summary-test.cc new file mode 100644 index 000000000..07560f040 --- /dev/null +++ b/dballe/db/db-query-summary-test.cc @@ -0,0 +1,274 @@ +#include "config.h" +#include "db/tests.h" + +using namespace dballe; +using namespace dballe::db; +using namespace dballe::tests; +using namespace wreport; +using namespace std; + +namespace { + +struct DBData : public TestDataSet +{ + DBData() + { + stations["st1_synop"].info.coords = Coords(12.34560, 76.54320); + stations["st1_synop"].info.report = "synop"; + stations["st1_synop"].values.set(newvar("B07030", 42.0)); // height + stations["st1_metar"].info = stations["st1_synop"].info; + stations["st1_metar"].info.report = "metar"; + stations["st1_metar"].values.set(newvar("block", 1)); + stations["st1_metar"].values.set(newvar("station", 2)); + stations["st1_metar"].values.set(newvar("B07030", 50.0)); // height + stations["st2_temp"].info.coords = Coords(23.45670, 65.43210); + stations["st2_temp"].info.report = "temp"; + stations["st2_temp"].values.set(newvar("B07030", 100.0)); // height + stations["st2_metar"].info = stations["st2_temp"].info; + stations["st2_metar"].info.report = "metar"; + stations["st2_metar"].values.set(newvar("block", 3)); + stations["st2_metar"].values.set(newvar("station", 4)); + stations["st2_metar"].values.set(newvar("B07030", 110.0)); // height + data["rec1a"].info = stations["st1_metar"].info; + data["rec1a"].info.datetime = Datetime(1945, 4, 25, 8); + data["rec1a"].info.level = Level(10, 11, 15, 22); + data["rec1a"].info.trange = Trange(20, 111, 122); + data["rec1a"].values.set("B12101", 290.0); + data["rec1a"].values.set("B12103", 280.0); + data["rec1b"] = data["rec1a"]; + data["rec1b"].info.datetime = Datetime(1945, 4, 26, 8); + data["rec1b"].values.set("B12101", 291.0); + data["rec1b"].values.set("B12103", 281.0); + data["rec2a"].info = stations["st2_metar"].info; + data["rec2a"].info.datetime = Datetime(1945, 4, 25, 8); + data["rec2a"].info.level = Level(10, 11, 15, 22); + data["rec2a"].info.trange = Trange(20, 111, 122); + data["rec2a"].values.set("B12101", 300.0); + data["rec2a"].values.set("B12103", 298.0); + data["rec2b"] = data["rec2a"]; + data["rec2b"].info.datetime = Datetime(1945, 4, 26, 8); + data["rec2b"].values.set("B12101", 301.0); + data["rec2b"].values.set("B12103", 291.0); + } +}; + +struct Fixture : public DBFixture +{ + using DBFixture::DBFixture; + + int st1_id; + int st2_id; + + void test_setup() + { + DBFixture::test_setup(); + + DBData dbdata; + wassert(populate_database(dbdata)); + st1_id = dbdata.stations["st1_metar"].info.ana_id; + st2_id = dbdata.stations["st2_metar"].info.ana_id; + } +}; + +std::string parm(const char* name, int val) +{ + stringstream out; + out << name << "=" << val; + return out.str(); +} + +class Tests : public FixtureTestCase +{ + using FixtureTestCase::FixtureTestCase; + + void register_tests() override + { + add_method("query_ana_id", [](Fixture& f) { + auto& db = *f.db; + wassert(actual(db).try_summary_query(parm("ana_id", f.st1_id), 2)); + wassert(actual(db).try_summary_query(parm("ana_id", f.st2_id), 2)); + wassert(actual(db).try_summary_query(parm("ana_id", (f.st1_id + f.st2_id) * 2), 0)); + }); +#if 0 + // TODO: summary of station vars is not supported at the moment, waiting for a use case for it + add_method("query_station_vars", [](Fixture& f) { + auto& db = *f.db; + core::Query query; + query.query_station_vars = true; + auto cur = db.query_summary(query); + ensure_equals(cur->test_iterate(), 8); + }); +#endif + add_method("query_year", [](Fixture& f) { + auto& db = *f.db; + wassert(actual(db).try_summary_query("year=1001", 0)); + wassert(actual(db).try_summary_query("yearmin=1999", 0)); + auto check_base = [](const vector& res) { + wassert(actual(res[0].enq("lat", MISSING_INT)) == 1234560); + wassert(actual(res[0].enq("lon", MISSING_INT)) == 7654320); + wassert(actual(res[0].get_level()) == Level(10, 11, 15, 22)); + wassert(actual(res[0].get_trange()) == Trange(20, 111, 122)); + wassert(actual(res[0].enq("var", "")) == "B12101"); + }; + auto check_nodetails = [&](const vector& res) { + wassert(check_base(res)); + wassert(actual(res[0].isset("context_id")).isfalse()); + wassert(actual(res[0].isset("yearmin")).isfalse()); + wassert(actual(res[0].isset("yearmax")).isfalse()); + }; + auto check_details = [&](const vector& res) { + wassert(check_base(res)); + wassert(actual(res[0].enq("context_id", MISSING_INT)) == 2); + DatetimeRange dtr = res[0].get_datetimerange(); + wassert(actual(dtr.min) == Datetime(1945, 4, 25, 8)); + wassert(actual(dtr.max) == Datetime(1945, 4, 26, 8)); + }; + wassert(actual(db).try_summary_query("yearmin=1945", 4, check_nodetails)); + wassert(actual(db).try_summary_query("yearmin=1945, query=details", 4, check_details)); + wassert(actual(db).try_summary_query("yearmax=1944", 0)); + wassert(actual(db).try_summary_query("yearmax=1945", 4)); + wassert(actual(db).try_summary_query("yearmax=2030", 4)); + wassert(actual(db).try_summary_query("year=1944", 0)); + wassert(actual(db).try_summary_query("year=1945", 4)); + wassert(actual(db).try_summary_query("year=1946", 0)); + }); + add_method("query_blockstation", [](Fixture& f) { + auto& db = *f.db; + wassert(actual(db).try_summary_query("B01001=1", 2)); + wassert(actual(db).try_summary_query("B01001=2", 0)); + wassert(actual(db).try_summary_query("B01002=3", 0)); + wassert(actual(db).try_summary_query("B01002=4", 2)); + }); + add_method("query_ana_filter", [](Fixture& f) { + auto& db = *f.db; + wassert(actual(db).try_summary_query("ana_filter=block=1", 2)); + wassert(actual(db).try_summary_query("ana_filter=B01001=1", 2)); + wassert(actual(db).try_summary_query("ana_filter=block>1", 2)); + wassert(actual(db).try_summary_query("ana_filter=B01001>1", 2)); + wassert(actual(db).try_summary_query("ana_filter=block<=1", 2)); + wassert(actual(db).try_summary_query("ana_filter=B01001>3", 0)); + wassert(actual(db).try_summary_query("ana_filter=B01001>=3", 2)); + wassert(actual(db).try_summary_query("ana_filter=B01001<=1", 2)); + wassert(actual(db).try_summary_query("ana_filter=0<=B01001<=2", 2)); + wassert(actual(db).try_summary_query("ana_filter=1<=B01001<=1", 2)); + wassert(actual(db).try_summary_query("ana_filter=2<=B01001<=4", 2)); + wassert(actual(db).try_summary_query("ana_filter=4<=B01001<=6", 0)); + }); + add_method("query_data_filter", [](Fixture& f) { + auto& db = *f.db; + wassert(actual(db).try_summary_query("data_filter=B12101<300.0", 1)); + wassert(actual(db).try_summary_query("data_filter=B12101<=300.0", 2)); + wassert(actual(db).try_summary_query("data_filter=B12101=300.0", 1)); + wassert(actual(db).try_summary_query("data_filter=B12101>=300,0", 1)); + wassert(actual(db).try_summary_query("data_filter=B12101>300.0", 1)); + wassert(actual(db).try_summary_query("data_filter=B12101<400.0", 2)); + wassert(actual(db).try_summary_query("data_filter=B12101<=400.0", 2)); + wassert(actual(db).try_summary_query("data_filter=B12102>400.0", 0)); + }); + add_method("query_lat_lon", [](Fixture& f) { + auto& db = *f.db; + wassert(actual(db).try_summary_query("latmin=11.0", 4)); + wassert(actual(db).try_summary_query("latmin=12.34560", 4)); + wassert(actual(db).try_summary_query("latmin=13.0", 2)); + wassert(actual(db).try_summary_query("latmax=11.0", 0)); + wassert(actual(db).try_summary_query("latmax=12.34560", 2)); + wassert(actual(db).try_summary_query("latmax=13.0", 2)); + wassert(actual(db).try_summary_query("lonmin=75., lonmax=77.", 2)); + wassert(actual(db).try_summary_query("lonmin=76.54320, lonmax=76.54320", 2)); + wassert(actual(db).try_summary_query("lonmin=76.54330, lonmax=77.", 0)); + wassert(actual(db).try_summary_query("lonmin=77., lonmax=76.54310", 2)); + wassert(actual(db).try_summary_query("lonmin=77., lonmax=76.54320", 4)); + wassert(actual(db).try_summary_query("lonmin=77., lonmax=-10", 0)); + }); + add_method("query_mobile", [](Fixture& f) { + auto& db = *f.db; + wassert(actual(db).try_summary_query("mobile=0", 4)); + wassert(actual(db).try_summary_query("mobile=1", 0)); + }); + add_method("query_ident", [](Fixture& f) { + //auto& db = *f.db; + // TODO: add mobile stations to the fixture so we can query ident + }); + add_method("query_timerange", [](Fixture& f) { + auto& db = *f.db; + wassert(actual(db).try_summary_query("pindicator=20", 4)); + wassert(actual(db).try_summary_query("pindicator=21", 0)); + wassert(actual(db).try_summary_query("p1=111", 4)); + wassert(actual(db).try_summary_query("p1=112", 0)); + wassert(actual(db).try_summary_query("p2=121", 0)); + wassert(actual(db).try_summary_query("p2=122", 4)); + wassert(actual(db).try_summary_query("p2=123", 0)); + }); + add_method("query_level", [](Fixture& f) { + auto& db = *f.db; + wassert(actual(db).try_summary_query("leveltype1=10", 4)); + wassert(actual(db).try_summary_query("leveltype1=11", 0)); + wassert(actual(db).try_summary_query("leveltype2=15", 4)); + wassert(actual(db).try_summary_query("leveltype2=16", 0)); + wassert(actual(db).try_summary_query("l1=11", 4)); + wassert(actual(db).try_summary_query("l1=12", 0)); + wassert(actual(db).try_summary_query("l2=22", 4)); + wassert(actual(db).try_summary_query("l2=23", 0)); + }); + add_method("query_var", [](Fixture& f) { + auto& db = *f.db; + wassert(actual(db).try_summary_query("var=B01001", 0)); + wassert(actual(db).try_summary_query("var=B12101", 2)); + wassert(actual(db).try_summary_query("var=B12102", 0)); + }); + add_method("query_rep_memo", [](Fixture& f) { + auto& db = *f.db; + wassert(actual(db).try_summary_query("rep_memo=synop", 0)); + wassert(actual(db).try_summary_query("rep_memo=metar", 4)); + wassert(actual(db).try_summary_query("rep_memo=temp", 0)); + }); + add_method("query_priority", [](Fixture& f) { + auto& db = *f.db; + wassert(actual(db).try_summary_query("priority=101", 0)); + wassert(actual(db).try_summary_query("priority=81", 4)); + wassert(actual(db).try_summary_query("priority=102", 0)); + wassert(actual(db).try_summary_query("priomin=70", 4)); + wassert(actual(db).try_summary_query("priomin=80", 4)); + wassert(actual(db).try_summary_query("priomin=90", 0)); + wassert(actual(db).try_summary_query("priomax=70", 0)); + wassert(actual(db).try_summary_query("priomax=81", 4)); + wassert(actual(db).try_summary_query("priomax=100", 4)); + }); + add_method("query_context_id", [](Fixture& f) { + auto& db = *f.db; + // Collect a vector of valid context IDs + vector context_ids; + // And an invalid one + int sum = 1; + auto cur = db.query_data(core::Query()); + while (cur->next()) + { + context_ids.push_back(cur->attr_reference_id()); + sum += context_ids.back(); + } + + WREPORT_TEST_INFO(info); + + wassert(actual(db).try_summary_query(parm("context_id", sum), 0)); + for (vector::const_iterator i = context_ids.begin(); i != context_ids.end(); ++i) + { + info() << "Context ID " << *i; + wassert(actual(db).try_summary_query(parm("context_id", *i), 1)); + } + }); + } +}; + +Tests tg1("db_query_summary_mem", nullptr, db::MEM); +Tests tg2("db_query_summary_v6_sqlite", "SQLITE", db::V6); +#ifdef HAVE_ODBC +Tests tg4("db_query_summary_v6_odbc", "ODBC", db::V6); +#endif +#ifdef HAVE_LIBPQ +Tests tg6("db_query_summary_v6_postgresql", "POSTGRESQL", db::V6); +#endif +#ifdef HAVE_MYSQL +Tests tg8("db_query_summary_v6_mysql", "MYSQL", db::V6); +#endif + +} diff --git a/dballe/db/db-query-summary-tut.cc b/dballe/db/db-query-summary-tut.cc deleted file mode 100644 index a5d3ead74..000000000 --- a/dballe/db/db-query-summary-tut.cc +++ /dev/null @@ -1,274 +0,0 @@ -#include "config.h" -#include "db/tests.h" -#include - -using namespace dballe; -using namespace dballe::db; -using namespace dballe::tests; -using namespace wreport; -using namespace wibble; -using namespace wibble::tests; -using namespace std; - -namespace { - -struct DBData : public TestFixture -{ - DBData() - { - stations["st1_synop"].info.coords = Coords(12.34560, 76.54320); - stations["st1_synop"].info.report = "synop"; - stations["st1_synop"].values.set(newvar("B07030", 42.0)); // height - stations["st1_metar"].info = stations["st1_synop"].info; - stations["st1_metar"].info.report = "metar"; - stations["st1_metar"].values.set(newvar("block", 1)); - stations["st1_metar"].values.set(newvar("station", 2)); - stations["st1_metar"].values.set(newvar("B07030", 50.0)); // height - stations["st2_temp"].info.coords = Coords(23.45670, 65.43210); - stations["st2_temp"].info.report = "temp"; - stations["st2_temp"].values.set(newvar("B07030", 100.0)); // height - stations["st2_metar"].info = stations["st2_temp"].info; - stations["st2_metar"].info.report = "metar"; - stations["st2_metar"].values.set(newvar("block", 3)); - stations["st2_metar"].values.set(newvar("station", 4)); - stations["st2_metar"].values.set(newvar("B07030", 110.0)); // height - data["rec1a"].info = stations["st1_metar"].info; - data["rec1a"].info.datetime = Datetime(1945, 4, 25, 8); - data["rec1a"].info.level = Level(10, 11, 15, 22); - data["rec1a"].info.trange = Trange(20, 111, 122); - data["rec1a"].values.set("B12101", 290.0); - data["rec1a"].values.set("B12103", 280.0); - data["rec1b"] = data["rec1a"]; - data["rec1b"].info.datetime = Datetime(1945, 4, 26, 8); - data["rec1b"].values.set("B12101", 291.0); - data["rec1b"].values.set("B12103", 281.0); - data["rec2a"].info = stations["st2_metar"].info; - data["rec2a"].info.datetime = Datetime(1945, 4, 25, 8); - data["rec2a"].info.level = Level(10, 11, 15, 22); - data["rec2a"].info.trange = Trange(20, 111, 122); - data["rec2a"].values.set("B12101", 300.0); - data["rec2a"].values.set("B12103", 298.0); - data["rec2b"] = data["rec2a"]; - data["rec2b"].info.datetime = Datetime(1945, 4, 26, 8); - data["rec2b"].values.set("B12101", 301.0); - data["rec2b"].values.set("B12103", 291.0); - } -}; - -struct Fixture : public dballe::tests::DBFixture -{ - int st1_id; - int st2_id; - - Fixture() - { - insert_data(); - } - - void insert_data() - { - DBData dbdata; - wruntest(populate_database, dbdata); - st1_id = dbdata.stations["st1_metar"].info.ana_id; - st2_id = dbdata.stations["st2_metar"].info.ana_id; - } - - void reset() - { - dballe::tests::DBFixture::reset(); - insert_data(); - } -}; - -typedef dballe::tests::db_test_group test_group; -typedef test_group::Test Test; - -std::vector tests { - Test("query_ana_id", [](Fixture& f) { - auto& db = *f.db; - wassert(actual(db).try_summary_query(str::fmtf("ana_id=%d", f.st1_id), 2)); - wassert(actual(db).try_summary_query(str::fmtf("ana_id=%d", f.st2_id), 2)); - wassert(actual(db).try_summary_query(str::fmtf("ana_id=%d", (f.st1_id + f.st2_id) * 2), 0)); - }), -#if 0 - // TODO: summary of station vars is not supported at the moment, waiting for a use case for it - Test("query_station_vars", [](Fixture& f) { - auto& db = *f.db; - core::Query query; - query.query_station_vars = true; - auto cur = db.query_summary(query); - ensure_equals(cur->test_iterate(), 8); - }), -#endif - Test("query_year", [](Fixture& f) { - auto& db = *f.db; - wassert(actual(db).try_summary_query("year=1001", 0)); - wassert(actual(db).try_summary_query("yearmin=1999", 0)); - auto check_base = [](WIBBLE_TEST_LOCPRM, const vector& res) { - wassert(actual(res[0].enq("lat", MISSING_INT)) == 1234560); - wassert(actual(res[0].enq("lon", MISSING_INT)) == 7654320); - wassert(actual(res[0].get_level()) == Level(10, 11, 15, 22)); - wassert(actual(res[0].get_trange()) == Trange(20, 111, 122)); - wassert(actual(res[0].enq("var", "")) == "B12101"); - }; - auto check_nodetails = [&](WIBBLE_TEST_LOCPRM, const vector& res) { - wruntest(check_base, res); - wassert(actual(res[0].isset("context_id")).isfalse()); - wassert(actual(res[0].isset("yearmin")).isfalse()); - wassert(actual(res[0].isset("yearmax")).isfalse()); - }; - auto check_details = [&](WIBBLE_TEST_LOCPRM, const vector& res) { - wruntest(check_base, res); - wassert(actual(res[0].enq("context_id", MISSING_INT)) == 2); - DatetimeRange dtr = res[0].get_datetimerange(); - wassert(actual(dtr.min) == Datetime(1945, 4, 25, 8)); - wassert(actual(dtr.max) == Datetime(1945, 4, 26, 8)); - }; - wassert(actual(db).try_summary_query("yearmin=1945", 4, check_nodetails)); - wassert(actual(db).try_summary_query("yearmin=1945, query=details", 4, check_details)); - wassert(actual(db).try_summary_query("yearmax=1944", 0)); - wassert(actual(db).try_summary_query("yearmax=1945", 4)); - wassert(actual(db).try_summary_query("yearmax=2030", 4)); - wassert(actual(db).try_summary_query("year=1944", 0)); - wassert(actual(db).try_summary_query("year=1945", 4)); - wassert(actual(db).try_summary_query("year=1946", 0)); - }), - Test("query_blockstation", [](Fixture& f) { - auto& db = *f.db; - wassert(actual(db).try_summary_query("B01001=1", 2)); - wassert(actual(db).try_summary_query("B01001=2", 0)); - wassert(actual(db).try_summary_query("B01002=3", 0)); - wassert(actual(db).try_summary_query("B01002=4", 2)); - }), - Test("query_ana_filter", [](Fixture& f) { - auto& db = *f.db; - wassert(actual(db).try_summary_query("ana_filter=block=1", 2)); - wassert(actual(db).try_summary_query("ana_filter=B01001=1", 2)); - wassert(actual(db).try_summary_query("ana_filter=block>1", 2)); - wassert(actual(db).try_summary_query("ana_filter=B01001>1", 2)); - wassert(actual(db).try_summary_query("ana_filter=block<=1", 2)); - wassert(actual(db).try_summary_query("ana_filter=B01001>3", 0)); - wassert(actual(db).try_summary_query("ana_filter=B01001>=3", 2)); - wassert(actual(db).try_summary_query("ana_filter=B01001<=1", 2)); - wassert(actual(db).try_summary_query("ana_filter=0<=B01001<=2", 2)); - wassert(actual(db).try_summary_query("ana_filter=1<=B01001<=1", 2)); - wassert(actual(db).try_summary_query("ana_filter=2<=B01001<=4", 2)); - wassert(actual(db).try_summary_query("ana_filter=4<=B01001<=6", 0)); - }), - Test("query_data_filter", [](Fixture& f) { - auto& db = *f.db; - wassert(actual(db).try_summary_query("data_filter=B12101<300.0", 1)); - wassert(actual(db).try_summary_query("data_filter=B12101<=300.0", 2)); - wassert(actual(db).try_summary_query("data_filter=B12101=300.0", 1)); - wassert(actual(db).try_summary_query("data_filter=B12101>=300,0", 1)); - wassert(actual(db).try_summary_query("data_filter=B12101>300.0", 1)); - wassert(actual(db).try_summary_query("data_filter=B12101<400.0", 2)); - wassert(actual(db).try_summary_query("data_filter=B12101<=400.0", 2)); - wassert(actual(db).try_summary_query("data_filter=B12102>400.0", 0)); - }), - Test("query_lat_lon", [](Fixture& f) { - auto& db = *f.db; - wassert(actual(db).try_summary_query("latmin=11.0", 4)); - wassert(actual(db).try_summary_query("latmin=12.34560", 4)); - wassert(actual(db).try_summary_query("latmin=13.0", 2)); - wassert(actual(db).try_summary_query("latmax=11.0", 0)); - wassert(actual(db).try_summary_query("latmax=12.34560", 2)); - wassert(actual(db).try_summary_query("latmax=13.0", 2)); - wassert(actual(db).try_summary_query("lonmin=75., lonmax=77.", 2)); - wassert(actual(db).try_summary_query("lonmin=76.54320, lonmax=76.54320", 2)); - wassert(actual(db).try_summary_query("lonmin=76.54330, lonmax=77.", 0)); - wassert(actual(db).try_summary_query("lonmin=77., lonmax=76.54310", 2)); - wassert(actual(db).try_summary_query("lonmin=77., lonmax=76.54320", 4)); - wassert(actual(db).try_summary_query("lonmin=77., lonmax=-10", 0)); - }), - Test("query_mobile", [](Fixture& f) { - auto& db = *f.db; - wassert(actual(db).try_summary_query("mobile=0", 4)); - wassert(actual(db).try_summary_query("mobile=1", 0)); - }), - Test("query_ident", [](Fixture& f) { - //auto& db = *f.db; - // TODO: add mobile stations to the fixture so we can query ident - }), - Test("query_timerange", [](Fixture& f) { - auto& db = *f.db; - wassert(actual(db).try_summary_query("pindicator=20", 4)); - wassert(actual(db).try_summary_query("pindicator=21", 0)); - wassert(actual(db).try_summary_query("p1=111", 4)); - wassert(actual(db).try_summary_query("p1=112", 0)); - wassert(actual(db).try_summary_query("p2=121", 0)); - wassert(actual(db).try_summary_query("p2=122", 4)); - wassert(actual(db).try_summary_query("p2=123", 0)); - }), - Test("query_level", [](Fixture& f) { - auto& db = *f.db; - wassert(actual(db).try_summary_query("leveltype1=10", 4)); - wassert(actual(db).try_summary_query("leveltype1=11", 0)); - wassert(actual(db).try_summary_query("leveltype2=15", 4)); - wassert(actual(db).try_summary_query("leveltype2=16", 0)); - wassert(actual(db).try_summary_query("l1=11", 4)); - wassert(actual(db).try_summary_query("l1=12", 0)); - wassert(actual(db).try_summary_query("l2=22", 4)); - wassert(actual(db).try_summary_query("l2=23", 0)); - }), - Test("query_var", [](Fixture& f) { - auto& db = *f.db; - wassert(actual(db).try_summary_query("var=B01001", 0)); - wassert(actual(db).try_summary_query("var=B12101", 2)); - wassert(actual(db).try_summary_query("var=B12102", 0)); - }), - Test("query_rep_memo", [](Fixture& f) { - auto& db = *f.db; - wassert(actual(db).try_summary_query("rep_memo=synop", 0)); - wassert(actual(db).try_summary_query("rep_memo=metar", 4)); - wassert(actual(db).try_summary_query("rep_memo=temp", 0)); - }), - Test("query_priority", [](Fixture& f) { - auto& db = *f.db; - wassert(actual(db).try_summary_query("priority=101", 0)); - wassert(actual(db).try_summary_query("priority=81", 4)); - wassert(actual(db).try_summary_query("priority=102", 0)); - wassert(actual(db).try_summary_query("priomin=70", 4)); - wassert(actual(db).try_summary_query("priomin=80", 4)); - wassert(actual(db).try_summary_query("priomin=90", 0)); - wassert(actual(db).try_summary_query("priomax=70", 0)); - wassert(actual(db).try_summary_query("priomax=81", 4)); - wassert(actual(db).try_summary_query("priomax=100", 4)); - }), - Test("query_context_id", [](Fixture& f) { - auto& db = *f.db; - // Collect a vector of valid context IDs - vector context_ids; - // And an invalid one - int sum = 1; - auto cur = db.query_data(core::Query()); - while (cur->next()) - { - context_ids.push_back(cur->attr_reference_id()); - sum += context_ids.back(); - } - - WIBBLE_TEST_INFO(info); - - wassert(actual(db).try_summary_query(str::fmtf("context_id=%d", sum), 0)); - for (vector::const_iterator i = context_ids.begin(); i != context_ids.end(); ++i) - { - info() << "Context ID " << *i; - wassert(actual(db).try_summary_query(str::fmtf("context_id=%d", *i), 1)); - } - }), -}; - -test_group tg1("db_query_summary_mem", nullptr, db::MEM, tests); -test_group tg2("db_query_summary_v6_sqlite", "SQLITE", db::V6, tests); -#ifdef HAVE_ODBC -test_group tg4("db_query_summary_v6_odbc", "ODBC", db::V6, tests); -#endif -#ifdef HAVE_LIBPQ -test_group tg6("db_query_summary_v6_postgresql", "POSTGRESQL", db::V6, tests); -#endif -#ifdef HAVE_MYSQL -test_group tg8("db_query_summary_v6_mysql", "MYSQL", db::V6, tests); -#endif - -} diff --git a/dballe/db/mem/cursor-test.cc b/dballe/db/mem/cursor-test.cc new file mode 100644 index 000000000..d8a437ad0 --- /dev/null +++ b/dballe/db/mem/cursor-test.cc @@ -0,0 +1,116 @@ +#include "db/tests.h" +#include "db/mem/cursor.h" + +using namespace dballe; +using namespace dballe::db; +using namespace dballe::tests; +using namespace wreport; +using namespace std; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("basic", []() { + using namespace dballe::db::mem::cursor; + + Memdb memdb; + size_t v0 = memdb.insert(Coords(11, 45), "", "synop", Level(1), Trange(254), Datetime(2013, 11, 1, 12), newvar(WR_VAR(0, 12, 101), 15.0)); + size_t vr = memdb.insert(Coords(11, 45), "", "temp", Level(1), Trange(254), Datetime(2013, 11, 1, 12), newvar(WR_VAR(0, 12, 101), 15.0)); + size_t vm = memdb.insert(Coords(11, 45), "LH1234", "synop", Level(1), Trange(254), Datetime(2013, 11, 1, 12), newvar(WR_VAR(0, 12, 101), 15.0)); + size_t vc = memdb.insert(Coords(11, 46), "", "synop", Level(1), Trange(254), Datetime(2013, 11, 1, 12), newvar(WR_VAR(0, 12, 101), 15.0)); + size_t vd = memdb.insert(Coords(11, 45), "", "synop", Level(1), Trange(254), Datetime(2013, 11, 1, 13), newvar(WR_VAR(0, 12, 101), 15.0)); + size_t vl = memdb.insert(Coords(11, 45), "", "synop", Level(2), Trange(254), Datetime(2013, 11, 1, 12), newvar(WR_VAR(0, 12, 101), 15.0)); + size_t vt = memdb.insert(Coords(11, 45), "", "synop", Level(1), Trange(255), Datetime(2013, 11, 1, 12), newvar(WR_VAR(0, 12, 101), 15.0)); + size_t vv = memdb.insert(Coords(11, 45), "", "synop", Level(1), Trange(254), Datetime(2013, 11, 1, 12), newvar(WR_VAR(0, 12, 102), 15.0)); + + DataBestKey k0(memdb.values, v0); + DataBestKey km(memdb.values, vm); + DataBestKey kc(memdb.values, vc); + DataBestKey kr(memdb.values, vr); + DataBestKey kl(memdb.values, vl); + DataBestKey kt(memdb.values, vt); + DataBestKey kd(memdb.values, vd); + DataBestKey kv(memdb.values, vv); + + wassert(actual(k0 < k0).isfalse()); + wassert(actual(k0 < kr).isfalse()); + wassert(actual(k0) < kc); + wassert(actual(k0) < km); + wassert(actual(k0) < kd); + wassert(actual(k0) < kl); + wassert(actual(k0) < kt); + wassert(actual(k0) < kv); + + // kr is the same as k0 + wassert(actual(kr < k0).isfalse()); + wassert(actual(kr < kr).isfalse()); + wassert(actual(kr) < kc); + wassert(actual(kr) < km); + wassert(actual(kr) < kd); + wassert(actual(kr) < kl); + wassert(actual(kr) < kt); + wassert(actual(kr) < kv); + + wassert(actual(km < k0).isfalse()); + wassert(actual(km < kr).isfalse()); + wassert(actual(km) < kc); + wassert(actual(km < km).isfalse()); + wassert(actual(km < kd).isfalse()); + wassert(actual(km < kl).isfalse()); + wassert(actual(km < kt).isfalse()); + wassert(actual(km < kv).isfalse()); + + wassert(actual(kc < k0).isfalse()); + wassert(actual(kc < kr).isfalse()); + wassert(actual(kc < kc).isfalse()); + wassert(actual(kc < km).isfalse()); + wassert(actual(kc < kd).isfalse()); + wassert(actual(kc < kl).isfalse()); + wassert(actual(kc < kt).isfalse()); + wassert(actual(kc < kv).isfalse()); + + wassert(actual(kd < k0).isfalse()); + wassert(actual(kd < kr).isfalse()); + wassert(actual(kd) < kc); + wassert(actual(kd) < km); + wassert(actual(kd < kd).isfalse()); + wassert(actual(kd < kl).isfalse()); + wassert(actual(kd < kt).isfalse()); + wassert(actual(kd < kv).isfalse()); + + wassert(actual(kl < k0).isfalse()); + wassert(actual(kl < kr).isfalse()); + wassert(actual(kl) < kc); + wassert(actual(kl) < km); + wassert(actual(kl) < kd); + wassert(actual(kl < kl).isfalse()); + wassert(actual(kl < kt).isfalse()); + wassert(actual(kl < kv).isfalse()); + + wassert(actual(kt < k0).isfalse()); + wassert(actual(kt < kr).isfalse()); + wassert(actual(kt) < kc); + wassert(actual(kt) < km); + wassert(actual(kt) < kd); + wassert(actual(kt) < kl); + wassert(actual(kt < kt).isfalse()); + wassert(actual(kt < kv).isfalse()); + + wassert(actual(kv < k0).isfalse()); + wassert(actual(kv < kr).isfalse()); + wassert(actual(kv) < kc); + wassert(actual(kv) < km); + wassert(actual(kv) < kd); + wassert(actual(kv) < kl); + wassert(actual(kv) < kt); + wassert(actual(kv < kv).isfalse()); + }); + } +} test("dbmem_cursor"); + +} diff --git a/dballe/db/mem/cursor-tut.cc b/dballe/db/mem/cursor-tut.cc deleted file mode 100644 index 9d347f6e9..000000000 --- a/dballe/db/mem/cursor-tut.cc +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) 2013 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "db/tests.h" -#include "db/mem/cursor.h" - -using namespace dballe; -using namespace dballe::db; -using namespace wreport; -using namespace wibble::tests; -using namespace std; - -namespace tut { - -struct dbmem_cursor_shar -{ - dbmem_cursor_shar() - { - } - - ~dbmem_cursor_shar() - { - } -}; -TESTGRP(dbmem_cursor); - -template<> template<> -void to::test<1>() -{ - using namespace dballe::db::mem::cursor; - - Memdb memdb; - size_t v0 = memdb.insert(Coords(11, 45), "", "synop", Level(1), Trange(254), Datetime(2013, 11, 1, 12), newvar(WR_VAR(0, 12, 101), 15.0)); - size_t vr = memdb.insert(Coords(11, 45), "", "temp", Level(1), Trange(254), Datetime(2013, 11, 1, 12), newvar(WR_VAR(0, 12, 101), 15.0)); - size_t vm = memdb.insert(Coords(11, 45), "LH1234", "synop", Level(1), Trange(254), Datetime(2013, 11, 1, 12), newvar(WR_VAR(0, 12, 101), 15.0)); - size_t vc = memdb.insert(Coords(11, 46), "", "synop", Level(1), Trange(254), Datetime(2013, 11, 1, 12), newvar(WR_VAR(0, 12, 101), 15.0)); - size_t vd = memdb.insert(Coords(11, 45), "", "synop", Level(1), Trange(254), Datetime(2013, 11, 1, 13), newvar(WR_VAR(0, 12, 101), 15.0)); - size_t vl = memdb.insert(Coords(11, 45), "", "synop", Level(2), Trange(254), Datetime(2013, 11, 1, 12), newvar(WR_VAR(0, 12, 101), 15.0)); - size_t vt = memdb.insert(Coords(11, 45), "", "synop", Level(1), Trange(255), Datetime(2013, 11, 1, 12), newvar(WR_VAR(0, 12, 101), 15.0)); - size_t vv = memdb.insert(Coords(11, 45), "", "synop", Level(1), Trange(254), Datetime(2013, 11, 1, 12), newvar(WR_VAR(0, 12, 102), 15.0)); - - DataBestKey k0(memdb.values, v0); - DataBestKey km(memdb.values, vm); - DataBestKey kc(memdb.values, vc); - DataBestKey kr(memdb.values, vr); - DataBestKey kl(memdb.values, vl); - DataBestKey kt(memdb.values, vt); - DataBestKey kd(memdb.values, vd); - DataBestKey kv(memdb.values, vv); - - wassert(!(actual(k0) < k0)); - wassert(!(actual(k0) < kr)); - wassert(actual(k0) < kc); - wassert(actual(k0) < km); - wassert(actual(k0) < kd); - wassert(actual(k0) < kl); - wassert(actual(k0) < kt); - wassert(actual(k0) < kv); - - // kr is the same as k0 - wassert(!(actual(kr) < k0)); - wassert(!(actual(kr) < kr)); - wassert(actual(kr) < kc); - wassert(actual(kr) < km); - wassert(actual(kr) < kd); - wassert(actual(kr) < kl); - wassert(actual(kr) < kt); - wassert(actual(kr) < kv); - - wassert(!(actual(km) < k0)); - wassert(!(actual(km) < kr)); - wassert(actual(km) < kc); - wassert(!(actual(km) < km)); - wassert(!(actual(km) < kd)); - wassert(!(actual(km) < kl)); - wassert(!(actual(km) < kt)); - wassert(!(actual(km) < kv)); - - wassert(!(actual(kc) < k0)); - wassert(!(actual(kc) < kr)); - wassert(!(actual(kc) < kc)); - wassert(!(actual(kc) < km)); - wassert(!(actual(kc) < kd)); - wassert(!(actual(kc) < kl)); - wassert(!(actual(kc) < kt)); - wassert(!(actual(kc) < kv)); - - wassert(!(actual(kd) < k0)); - wassert(!(actual(kd) < kr)); - wassert(actual(kd) < kc); - wassert(actual(kd) < km); - wassert(!(actual(kd) < kd)); - wassert(!(actual(kd) < kl)); - wassert(!(actual(kd) < kt)); - wassert(!(actual(kd) < kv)); - - wassert(!(actual(kl) < k0)); - wassert(!(actual(kl) < kr)); - wassert(actual(kl) < kc); - wassert(actual(kl) < km); - wassert(actual(kl) < kd); - wassert(!(actual(kl) < kl)); - wassert(!(actual(kl) < kt)); - wassert(!(actual(kl) < kv)); - - wassert(!(actual(kt) < k0)); - wassert(!(actual(kt) < kr)); - wassert(actual(kt) < kc); - wassert(actual(kt) < km); - wassert(actual(kt) < kd); - wassert(actual(kt) < kl); - wassert(!(actual(kt) < kt)); - wassert(!(actual(kt) < kv)); - - wassert(!(actual(kv) < k0)); - wassert(!(actual(kv) < kr)); - wassert(actual(kv) < kc); - wassert(actual(kv) < km); - wassert(actual(kv) < kd); - wassert(actual(kv) < kl); - wassert(actual(kv) < kt); - wassert(!(actual(kv) < kv)); -} - -} diff --git a/dballe/db/mem/repinfo-test.cc b/dballe/db/mem/repinfo-test.cc new file mode 100644 index 000000000..5dc0a2769 --- /dev/null +++ b/dballe/db/mem/repinfo-test.cc @@ -0,0 +1,92 @@ +#include "db/tests.h" +#include "db/mem/db.h" +#include "db/mem/repinfo.h" + +using namespace dballe; +using namespace dballe::db::mem; +using namespace dballe::tests; +using namespace std; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("prio", []() { + Repinfo ri; + ri.load(); + + wassert(actual(ri.get_prio("synop")) == 101); + wassert(actual(ri.get_prio("generic")) == 1000); + }); + + add_method("update", []() { + Repinfo ri; + ri.load(); + + wassert(actual(ri.get_prio("synop")) == 101); + + int added, deleted, updated; + ri.update(NULL, &added, &deleted, &updated); + + wassert(actual(added) == 0); + wassert(actual(deleted) == 0); + wassert(actual(updated) == 13); + + wassert(actual(ri.get_prio("synop")) == 101); + }); + + add_method("update_regression", []() { + // Test update from a file that was known to fail + Repinfo ri; + ri.load(); + + wassert(actual(ri.get_prio("synop")) == 101); + + int added, deleted, updated; + ri.update((string(getenv("DBA_TESTDATA")) + "/test-repinfo1.csv").c_str(), &added, &deleted, &updated); + + wassert(actual(added) == 3); + wassert(actual(deleted) == 11); + wassert(actual(updated) == 2); + + wassert(actual(ri.get_prio("synop")) == 101); + wassert(actual(ri.get_prio("FIXspnpo")) == 200); + }); + + add_method("update_negative_prio", []() { + // Test update from a file with a negative priority + Repinfo ri; + ri.load(); + + wassert(actual(ri.get_prio("generic")) == 1000); + + int added, deleted, updated; + ri.update((string(getenv("DBA_TESTDATA")) + "/test-repinfo2.csv").c_str(), &added, &deleted, &updated); + + wassert(actual(added) == 3); + wassert(actual(deleted) == 11); + wassert(actual(updated) == 2); + + wassert(actual(ri.get_prio("generic")) == -5); + }); + + add_method("autocreate", []() { + // Test automatic repinfo creation + Repinfo ri; + ri.load(); + + wassert(actual(ri.get_prio("foobar")) == 1001); + wassert(actual(ri.get_prio("foobar")) == 1001); + wassert(actual(ri.get_prio("barbaz")) == 1002); + wassert(actual(ri.get_prio("barbaz")) == 1002); + wassert(actual(ri.get_prio("foobar")) == 1001); + }); + } +} test("mem_repinfo"); + +} + diff --git a/dballe/db/mem/repinfo-tut.cc b/dballe/db/mem/repinfo-tut.cc deleted file mode 100644 index f47450b12..000000000 --- a/dballe/db/mem/repinfo-tut.cc +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2013 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "db/tests.h" -#include "db/mem/db.h" -#include "db/mem/repinfo.h" - -using namespace dballe; -using namespace dballe::db::mem; -using namespace std; -using namespace wibble::tests; - -namespace tut { - -struct mem_repinfo_shar -{ -}; -TESTGRP(mem_repinfo); - -/* Test simple queries */ -template<> template<> -void to::test<1>() -{ - Repinfo ri; - ri.load(); - - wassert(actual(ri.get_prio("synop")) == 101); - wassert(actual(ri.get_prio("generic")) == 1000); -} - -/* Test update */ -template<> template<> -void to::test<2>() -{ - Repinfo ri; - ri.load(); - - wassert(actual(ri.get_prio("synop")) == 101); - - int added, deleted, updated; - ri.update(NULL, &added, &deleted, &updated); - - ensure_equals(added, 0); - ensure_equals(deleted, 0); - ensure_equals(updated, 13); - - wassert(actual(ri.get_prio("synop")) == 101); -} - -/* Test update from a file that was known to fail */ -template<> template<> -void to::test<3>() -{ - Repinfo ri; - ri.load(); - - wassert(actual(ri.get_prio("synop")) == 101); - - int added, deleted, updated; - ri.update((string(getenv("DBA_TESTDATA")) + "/test-repinfo1.csv").c_str(), &added, &deleted, &updated); - - ensure_equals(added, 3); - ensure_equals(deleted, 11); - ensure_equals(updated, 2); - - wassert(actual(ri.get_prio("synop")) == 101); - wassert(actual(ri.get_prio("FIXspnpo")) == 200); -} - -/* Test update from a file with a negative priority */ -template<> template<> -void to::test<4>() -{ - Repinfo ri; - ri.load(); - - wassert(actual(ri.get_prio("generic")) == 1000); - - int added, deleted, updated; - ri.update((string(getenv("DBA_TESTDATA")) + "/test-repinfo2.csv").c_str(), &added, &deleted, &updated); - - ensure_equals(added, 3); - ensure_equals(deleted, 11); - ensure_equals(updated, 2); - - wassert(actual(ri.get_prio("generic")) == -5); -} - -// Test automatic repinfo creation -template<> template<> -void to::test<5>() -{ - Repinfo ri; - ri.load(); - - wassert(actual(ri.get_prio("foobar")) == 1001); - wassert(actual(ri.get_prio("foobar")) == 1001); - wassert(actual(ri.get_prio("barbaz")) == 1002); - wassert(actual(ri.get_prio("barbaz")) == 1002); - wassert(actual(ri.get_prio("foobar")) == 1001); -} - -} - -/* vim:set ts=4 sw=4: */ - diff --git a/dballe/db/mysql/internals-test.cc b/dballe/db/mysql/internals-test.cc new file mode 100644 index 000000000..dc996b448 --- /dev/null +++ b/dballe/db/mysql/internals-test.cc @@ -0,0 +1,241 @@ +#include "core/tests.h" +#include "internals.h" + +using namespace std; +using namespace dballe; +using namespace dballe::db; +using namespace dballe::tests; +using namespace wreport; + +namespace { + +struct ConnectorFixture : public Fixture +{ + MySQLConnection conn; + + ConnectorFixture() + { + conn.open_test(); + } + + void test_setup() override + { + Fixture::test_setup(); + conn.drop_table_if_exists("dballe_test"); + conn.exec_no_data("CREATE TABLE dballe_test (val INTEGER NOT NULL)"); + } +}; + +class Tests : public FixtureTestCase +{ + using FixtureTestCase::FixtureTestCase; + + void register_tests() override + { + add_method("parse_url", [](Fixture& f) { + // Test parsing urls + mysql::ConnectInfo info; + + info.parse_url("mysql:"); + wassert(actual(info.host) == ""); + wassert(actual(info.user) == ""); + wassert(actual(info.has_passwd).isfalse()); + wassert(actual(info.passwd) == ""); + wassert(actual(info.has_dbname).isfalse()); + wassert(actual(info.dbname) == ""); + wassert(actual(info.port) == 0); + wassert(actual(info.unix_socket) == ""); + + info.parse_url("mysql://"); + wassert(actual(info.host) == ""); + wassert(actual(info.user) == ""); + wassert(actual(info.has_passwd).isfalse()); + wassert(actual(info.passwd) == ""); + wassert(actual(info.has_dbname).isfalse()); + wassert(actual(info.dbname) == ""); + wassert(actual(info.port) == 0); + wassert(actual(info.unix_socket) == ""); + + info.parse_url("mysql://localhost/"); + wassert(actual(info.host) == "localhost"); + wassert(actual(info.user) == ""); + wassert(actual(info.has_passwd).isfalse()); + wassert(actual(info.passwd) == ""); + wassert(actual(info.has_dbname).isfalse()); + wassert(actual(info.dbname) == ""); + wassert(actual(info.port) == 0); + wassert(actual(info.unix_socket) == ""); + + info.parse_url("mysql://localhost:1234/"); + wassert(actual(info.host) == "localhost"); + wassert(actual(info.user) == ""); + wassert(actual(info.has_passwd).isfalse()); + wassert(actual(info.passwd) == ""); + wassert(actual(info.has_dbname).isfalse()); + wassert(actual(info.dbname) == ""); + wassert(actual(info.port) == 1234); + wassert(actual(info.unix_socket) == ""); + + info.parse_url("mysql://localhost:1234/?user=enrico"); + wassert(actual(info.host) == "localhost"); + wassert(actual(info.user) == "enrico"); + wassert(actual(info.has_passwd).isfalse()); + wassert(actual(info.passwd) == ""); + wassert(actual(info.has_dbname).isfalse()); + wassert(actual(info.dbname) == ""); + wassert(actual(info.port) == 1234); + wassert(actual(info.unix_socket) == ""); + + info.parse_url("mysql://localhost:1234/foo?user=enrico"); + wassert(actual(info.host) == "localhost"); + wassert(actual(info.user) == "enrico"); + wassert(actual(info.has_passwd).isfalse()); + wassert(actual(info.passwd) == ""); + wassert(actual(info.has_dbname).istrue()); + wassert(actual(info.dbname) == "foo"); + wassert(actual(info.port) == 1234); + wassert(actual(info.unix_socket) == ""); + + info.parse_url("mysql://localhost:1234/foo?user=enrico&password=secret"); + wassert(actual(info.host) == "localhost"); + wassert(actual(info.user) == "enrico"); + wassert(actual(info.has_passwd).istrue()); + wassert(actual(info.passwd) == "secret"); + wassert(actual(info.has_dbname).istrue()); + wassert(actual(info.dbname) == "foo"); + wassert(actual(info.port) == 1234); + wassert(actual(info.unix_socket) == ""); + + info.parse_url("mysql://localhost/foo?user=enrico&password=secret"); + wassert(actual(info.host) == "localhost"); + wassert(actual(info.user) == "enrico"); + wassert(actual(info.has_passwd).istrue()); + wassert(actual(info.passwd) == "secret"); + wassert(actual(info.has_dbname).istrue()); + wassert(actual(info.dbname) == "foo"); + wassert(actual(info.port) == 0); + wassert(actual(info.unix_socket) == ""); + + info.parse_url("mysql:///foo?user=enrico&password=secret"); + wassert(actual(info.host) == ""); + wassert(actual(info.user) == "enrico"); + wassert(actual(info.has_passwd).istrue()); + wassert(actual(info.passwd) == "secret"); + wassert(actual(info.has_dbname).istrue()); + wassert(actual(info.dbname) == "foo"); + wassert(actual(info.port) == 0); + wassert(actual(info.unix_socket) == ""); + }); + add_method("query_int", [](Fixture& f) { + // Test querying int values + using namespace mysql; + + f.conn.exec_no_data("INSERT INTO dballe_test VALUES (1)"); + f.conn.exec_no_data("INSERT INTO dballe_test VALUES (2)"); + + auto res = f.conn.exec_store("SELECT val FROM dballe_test"); + + int val = 0; + unsigned count = 0; + while (Row row = res.fetch()) + { + val += row.as_int(0); + ++count; + } + wassert(actual(count) == 2); + wassert(actual(val) == 3); + }); + add_method("query_int_null", [](Fixture& f) { + // Test querying int values, with potential NULLs + f.conn.drop_table_if_exists("dballe_testnull"); + f.conn.exec_no_data("CREATE TABLE dballe_testnull (val INTEGER)"); + f.conn.exec_no_data("INSERT INTO dballe_testnull VALUES (NULL)"); + f.conn.exec_no_data("INSERT INTO dballe_testnull VALUES (42)"); + + auto res = f.conn.exec_store("SELECT val FROM dballe_testnull"); + wassert(actual(res.rowcount()) == 2); + + int val = 0; + unsigned count = 0; + unsigned countnulls = 0; + while (auto row = res.fetch()) + { + if (row.isnull(0)) + ++countnulls; + else + val += row.as_int(0); + ++count; + } + + wassert(actual(val) == 42); + wassert(actual(count) == 2); + wassert(actual(countnulls) == 1); + }); + add_method("query_unsigned", [](Fixture& f) { + // Test querying unsigned values + f.conn.drop_table_if_exists("dballe_testbig"); + f.conn.exec_no_data("CREATE TABLE dballe_testbig (val BIGINT)"); + f.conn.exec_no_data("INSERT INTO dballe_testbig VALUES (0xFFFFFFFE)"); + + auto res = f.conn.exec_store("SELECT val FROM dballe_testbig"); + wassert(actual(res.rowcount()) == 1); + + unsigned val = 0; + unsigned count = 0; + while (auto row = res.fetch()) + { + val += row.as_unsigned(0); + ++count; + } + wassert(actual(val) == 0xFFFFFFFE); + wassert(actual(count) == 1); + }); + add_method("query_unsigned_short", [](Fixture& f) { + // Test querying unsigned short values + char buf[200]; + snprintf(buf, 200, "INSERT INTO dballe_test VALUES (%d)", (int)WR_VAR(3, 1, 12)); + f.conn.exec_no_data(buf); + + auto res = f.conn.exec_store("SELECT val FROM dballe_test"); + wassert(actual(res.rowcount()) == 1); + + Varcode val = 0; + unsigned count = 0; + while (auto row = res.fetch()) + { + val = (Varcode)row.as_int(0); + ++count; + } + wassert(actual(count) == 1); + wassert(actual(val) == WR_VAR(3, 1, 12)); + }); + add_method("has_tables", [](Fixture& f) { + // Test has_tables + wassert(actual(f.conn.has_table("this_should_not_exist")).isfalse()); + wassert(actual(f.conn.has_table("dballe_test")).istrue()); + }); + add_method("settings", [](Fixture& f) { + // Test settings + f.conn.drop_table_if_exists("dballe_settings"); + wassert(actual(f.conn.has_table("dballe_settings")).isfalse()); + + wassert(actual(f.conn.get_setting("test_key")) == ""); + + f.conn.set_setting("test_key", "42"); + wassert(actual(f.conn.has_table("dballe_settings")).istrue()); + + wassert(actual(f.conn.get_setting("test_key")) == "42"); + }); + add_method("auto_increment", [](Fixture& f) { + // Test auto_increment + f.conn.drop_table_if_exists("dballe_testai"); + f.conn.exec_no_data("CREATE TABLE dballe_testai (id INTEGER AUTO_INCREMENT PRIMARY KEY, val INTEGER)"); + f.conn.exec_no_data("INSERT INTO dballe_testai (val) VALUES (42)"); + wassert(actual(f.conn.get_last_insert_id()) == 1); + f.conn.exec_no_data("INSERT INTO dballe_testai (val) VALUES (43)"); + wassert(actual(f.conn.get_last_insert_id()) == 2); + }); + } +} test("db_internals_mysql"); + +} diff --git a/dballe/db/mysql/internals-tut.cc b/dballe/db/mysql/internals-tut.cc deleted file mode 100644 index dcb4a6003..000000000 --- a/dballe/db/mysql/internals-tut.cc +++ /dev/null @@ -1,240 +0,0 @@ -#include "core/tests.h" -#include "internals.h" - -using namespace std; -using namespace wibble::tests; -using namespace dballe; -using namespace dballe::db; -using namespace wreport; - -namespace { - -struct Fixture : dballe::tests::Fixture -{ - MySQLConnection conn; - - Fixture() - { - conn.open_test(); - } - - void reset() - { - dballe::tests::Fixture::reset(); - conn.drop_table_if_exists("dballe_test"); - conn.exec_no_data("CREATE TABLE dballe_test (val INTEGER NOT NULL)"); - } -}; - -typedef dballe::tests::test_group test_group; -typedef test_group::Test Test; - -std::vector tests { - Test("parse_url", [](Fixture& f) { - // Test parsing urls - mysql::ConnectInfo info; - - info.parse_url("mysql:"); - wassert(actual(info.host) == ""); - wassert(actual(info.user) == ""); - wassert(actual(info.has_passwd).isfalse()); - wassert(actual(info.passwd) == ""); - wassert(actual(info.has_dbname).isfalse()); - wassert(actual(info.dbname) == ""); - wassert(actual(info.port) == 0); - wassert(actual(info.unix_socket) == ""); - - info.parse_url("mysql://"); - wassert(actual(info.host) == ""); - wassert(actual(info.user) == ""); - wassert(actual(info.has_passwd).isfalse()); - wassert(actual(info.passwd) == ""); - wassert(actual(info.has_dbname).isfalse()); - wassert(actual(info.dbname) == ""); - wassert(actual(info.port) == 0); - wassert(actual(info.unix_socket) == ""); - - info.parse_url("mysql://localhost/"); - wassert(actual(info.host) == "localhost"); - wassert(actual(info.user) == ""); - wassert(actual(info.has_passwd).isfalse()); - wassert(actual(info.passwd) == ""); - wassert(actual(info.has_dbname).isfalse()); - wassert(actual(info.dbname) == ""); - wassert(actual(info.port) == 0); - wassert(actual(info.unix_socket) == ""); - - info.parse_url("mysql://localhost:1234/"); - wassert(actual(info.host) == "localhost"); - wassert(actual(info.user) == ""); - wassert(actual(info.has_passwd).isfalse()); - wassert(actual(info.passwd) == ""); - wassert(actual(info.has_dbname).isfalse()); - wassert(actual(info.dbname) == ""); - wassert(actual(info.port) == 1234); - wassert(actual(info.unix_socket) == ""); - - info.parse_url("mysql://localhost:1234/?user=enrico"); - wassert(actual(info.host) == "localhost"); - wassert(actual(info.user) == "enrico"); - wassert(actual(info.has_passwd).isfalse()); - wassert(actual(info.passwd) == ""); - wassert(actual(info.has_dbname).isfalse()); - wassert(actual(info.dbname) == ""); - wassert(actual(info.port) == 1234); - wassert(actual(info.unix_socket) == ""); - - info.parse_url("mysql://localhost:1234/foo?user=enrico"); - wassert(actual(info.host) == "localhost"); - wassert(actual(info.user) == "enrico"); - wassert(actual(info.has_passwd).isfalse()); - wassert(actual(info.passwd) == ""); - wassert(actual(info.has_dbname).istrue()); - wassert(actual(info.dbname) == "foo"); - wassert(actual(info.port) == 1234); - wassert(actual(info.unix_socket) == ""); - - info.parse_url("mysql://localhost:1234/foo?user=enrico&password=secret"); - wassert(actual(info.host) == "localhost"); - wassert(actual(info.user) == "enrico"); - wassert(actual(info.has_passwd).istrue()); - wassert(actual(info.passwd) == "secret"); - wassert(actual(info.has_dbname).istrue()); - wassert(actual(info.dbname) == "foo"); - wassert(actual(info.port) == 1234); - wassert(actual(info.unix_socket) == ""); - - info.parse_url("mysql://localhost/foo?user=enrico&password=secret"); - wassert(actual(info.host) == "localhost"); - wassert(actual(info.user) == "enrico"); - wassert(actual(info.has_passwd).istrue()); - wassert(actual(info.passwd) == "secret"); - wassert(actual(info.has_dbname).istrue()); - wassert(actual(info.dbname) == "foo"); - wassert(actual(info.port) == 0); - wassert(actual(info.unix_socket) == ""); - - info.parse_url("mysql:///foo?user=enrico&password=secret"); - wassert(actual(info.host) == ""); - wassert(actual(info.user) == "enrico"); - wassert(actual(info.has_passwd).istrue()); - wassert(actual(info.passwd) == "secret"); - wassert(actual(info.has_dbname).istrue()); - wassert(actual(info.dbname) == "foo"); - wassert(actual(info.port) == 0); - wassert(actual(info.unix_socket) == ""); - }), - Test("query_int", [](Fixture& f) { - // Test querying int values - using namespace mysql; - - f.conn.exec_no_data("INSERT INTO dballe_test VALUES (1)"); - f.conn.exec_no_data("INSERT INTO dballe_test VALUES (2)"); - - auto res = f.conn.exec_store("SELECT val FROM dballe_test"); - - int val = 0; - unsigned count = 0; - while (Row row = res.fetch()) - { - val += row.as_int(0); - ++count; - } - wassert(actual(count) == 2); - wassert(actual(val) == 3); - }), - Test("query_int_null", [](Fixture& f) { - // Test querying int values, with potential NULLs - f.conn.drop_table_if_exists("dballe_testnull"); - f.conn.exec_no_data("CREATE TABLE dballe_testnull (val INTEGER)"); - f.conn.exec_no_data("INSERT INTO dballe_testnull VALUES (NULL)"); - f.conn.exec_no_data("INSERT INTO dballe_testnull VALUES (42)"); - - auto res = f.conn.exec_store("SELECT val FROM dballe_testnull"); - wassert(actual(res.rowcount()) == 2); - - int val = 0; - unsigned count = 0; - unsigned countnulls = 0; - while (auto row = res.fetch()) - { - if (row.isnull(0)) - ++countnulls; - else - val += row.as_int(0); - ++count; - } - - wassert(actual(val) == 42); - wassert(actual(count) == 2); - wassert(actual(countnulls) == 1); - }), - Test("query_unsigned", [](Fixture& f) { - // Test querying unsigned values - f.conn.drop_table_if_exists("dballe_testbig"); - f.conn.exec_no_data("CREATE TABLE dballe_testbig (val BIGINT)"); - f.conn.exec_no_data("INSERT INTO dballe_testbig VALUES (0xFFFFFFFE)"); - - auto res = f.conn.exec_store("SELECT val FROM dballe_testbig"); - wassert(actual(res.rowcount()) == 1); - - unsigned val = 0; - unsigned count = 0; - while (auto row = res.fetch()) - { - val += row.as_unsigned(0); - ++count; - } - wassert(actual(val) == 0xFFFFFFFE); - wassert(actual(count) == 1); - }), - Test("query_unsigned_short", [](Fixture& f) { - // Test querying unsigned short values - char buf[200]; - snprintf(buf, 200, "INSERT INTO dballe_test VALUES (%d)", (int)WR_VAR(3, 1, 12)); - f.conn.exec_no_data(buf); - - auto res = f.conn.exec_store("SELECT val FROM dballe_test"); - wassert(actual(res.rowcount()) == 1); - - Varcode val = 0; - unsigned count = 0; - while (auto row = res.fetch()) - { - val = (Varcode)row.as_int(0); - ++count; - } - wassert(actual(count) == 1); - wassert(actual(val) == WR_VAR(3, 1, 12)); - }), - Test("has_tables", [](Fixture& f) { - // Test has_tables - wassert(actual(f.conn.has_table("this_should_not_exist")).isfalse()); - wassert(actual(f.conn.has_table("dballe_test")).istrue()); - }), - Test("settings", [](Fixture& f) { - // Test settings - f.conn.drop_table_if_exists("dballe_settings"); - wassert(actual(f.conn.has_table("dballe_settings")).isfalse()); - - wassert(actual(f.conn.get_setting("test_key")) == ""); - - f.conn.set_setting("test_key", "42"); - wassert(actual(f.conn.has_table("dballe_settings")).istrue()); - - wassert(actual(f.conn.get_setting("test_key")) == "42"); - }), - Test("auto_increment", [](Fixture& f) { - // Test auto_increment - f.conn.drop_table_if_exists("dballe_testai"); - f.conn.exec_no_data("CREATE TABLE dballe_testai (id INTEGER AUTO_INCREMENT PRIMARY KEY, val INTEGER)"); - f.conn.exec_no_data("INSERT INTO dballe_testai (val) VALUES (42)"); - wassert(actual(f.conn.get_last_insert_id()) == 1); - f.conn.exec_no_data("INSERT INTO dballe_testai (val) VALUES (43)"); - wassert(actual(f.conn.get_last_insert_id()) == 2); - }), -}; - -test_group newtg("db_internals_mysql", tests); - -} diff --git a/dballe/db/odbc/internals-test.cc b/dballe/db/odbc/internals-test.cc new file mode 100644 index 000000000..306e70f67 --- /dev/null +++ b/dballe/db/odbc/internals-test.cc @@ -0,0 +1,136 @@ +#include "db/tests.h" +#include "db/odbc/internals.h" +#include + +using namespace dballe; +using namespace dballe::db; +using namespace dballe::tests; +using namespace wreport; +using namespace std; + +namespace { + +struct ConnectorFixture : public Fixture +{ + ODBCConnection conn; + + ConnectorFixture() + { + conn.connect_test(); + } + + void test_setup() override + { + Fixture::test_setup(); + conn.drop_table_if_exists("dballe_test"); + conn.exec("CREATE TABLE dballe_test (val INTEGER NOT NULL)"); + } +}; + +class Tests : public FixtureTestCase +{ + using FixtureTestCase::FixtureTestCase; + + void register_tests() override + { + add_method("query_int", [](Fixture& f) { + // Test querying int values + auto& c = f.conn; + c.exec("INSERT INTO dballe_test VALUES (42)"); + + auto s = c.odbcstatement("SELECT val FROM dballe_test"); + int val = 0; + s->bind_out(1, val); + s->execute(); + unsigned count = 0; + while (s->fetch()) + ++count; + wassert(actual(val) == 42); + wassert(actual(count) == 1); + }); + add_method("query_int_null", [](Fixture& f) { + // Test querying int values, with indicators + auto& c = f.conn; + c.exec("INSERT INTO dballe_test VALUES (42)"); + + auto s = c.odbcstatement("SELECT val FROM dballe_test"); + int val = 0; + SQLLEN ind = 0; + s->bind_out(1, val, ind); + s->execute(); + unsigned count = 0; + while (s->fetch()) + ++count; + wassert(actual(val) == 42); + wassert(actual(ind) != SQL_NULL_DATA); + wassert(actual(count) == 1); + }); + add_method("query_unsigned", [](Fixture& f) { + // Test querying unsigned values + auto& c = f.conn; + c.exec("INSERT INTO dballe_test VALUES (42)"); + + auto s = c.odbcstatement("SELECT val FROM dballe_test"); + unsigned val = 0; + s->bind_out(1, val); + s->execute(); + unsigned count = 0; + while (s->fetch()) + ++count; + wassert(actual(val) == 42); + wassert(actual(count) == 1); + }); + add_method("query_unsigned_null", [](Fixture& f) { + // Test querying unsigned values, with indicators + auto& c = f.conn; + c.exec("INSERT INTO dballe_test VALUES (42)"); + + auto s = c.odbcstatement("SELECT val FROM dballe_test"); + unsigned val = 0; + SQLLEN ind = 0; + s->bind_out(1, val, ind); + s->execute(); + unsigned count = 0; + while (s->fetch()) + ++count; + wassert(actual(val) == 42); + wassert(actual(ind) != SQL_NULL_DATA); + wassert(actual(count) == 1); + }); + add_method("query_unsigned_short", [](Fixture& f) { + // Test querying unsigned short values + auto& c = f.conn; + c.exec("INSERT INTO dballe_test VALUES (42)"); + auto s = c.odbcstatement("SELECT val FROM dballe_test"); + unsigned short val = 0; + s->bind_out(1, val); + s->execute(); + unsigned count = 0; + while (s->fetch()) + ++count; + wassert(actual(val) == 42); + wassert(actual(count) == 1); + }); + add_method("has_tables", [](Fixture& f) { + // Test has_tables + auto& c = f.conn; + wassert(actual(c.has_table("this_should_not_exist")).isfalse()); + wassert(actual(c.has_table("dballe_test")).istrue()); + }); + add_method("settings", [](Fixture& f) { + // Test settings + auto& c = f.conn; + c.drop_table_if_exists("dballe_settings"); + wassert(actual(c.has_table("dballe_settings")).isfalse()); + + wassert(actual(c.get_setting("test_key")) == ""); + + c.set_setting("test_key", "42"); + wassert(actual(c.has_table("dballe_settings")).istrue()); + + wassert(actual(c.get_setting("test_key")) == "42"); + }); + } +} test("db_internals_odbc"); + +} diff --git a/dballe/db/odbc/internals-tut.cc b/dballe/db/odbc/internals-tut.cc deleted file mode 100644 index eb95b80ba..000000000 --- a/dballe/db/odbc/internals-tut.cc +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (C) 2005--2015 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "db/tests.h" -#include "db/odbc/internals.h" -#include - -using namespace dballe; -using namespace dballe::db; -using namespace wreport; -using namespace wibble::tests; -using namespace std; - -namespace { - -struct Fixture : public dballe::tests::DBFixture -{ - ODBCConnection conn; - - Fixture() - { - conn.connect_test(); - } - - void reset() - { - conn.drop_table_if_exists("dballe_test"); - conn.exec("CREATE TABLE dballe_test (val INTEGER NOT NULL)"); - } -}; - -typedef dballe::tests::test_group test_group; -typedef test_group::Test Test; - -std::vector tests { - Test("query_int", [](Fixture& f) { - // Test querying int values - auto& c = f.conn; - c.exec("INSERT INTO dballe_test VALUES (42)"); - - auto s = c.odbcstatement("SELECT val FROM dballe_test"); - int val = 0; - s->bind_out(1, val); - s->execute(); - unsigned count = 0; - while (s->fetch()) - ++count; - wassert(actual(val) == 42); - wassert(actual(count) == 1); - }), - Test("query_int_null", [](Fixture& f) { - // Test querying int values, with indicators - auto& c = f.conn; - c.exec("INSERT INTO dballe_test VALUES (42)"); - - auto s = c.odbcstatement("SELECT val FROM dballe_test"); - int val = 0; - SQLLEN ind = 0; - s->bind_out(1, val, ind); - s->execute(); - unsigned count = 0; - while (s->fetch()) - ++count; - wassert(actual(val) == 42); - wassert(actual(ind) != SQL_NULL_DATA); - wassert(actual(count) == 1); - }), - Test("query_unsigned", [](Fixture& f) { - // Test querying unsigned values - auto& c = f.conn; - c.exec("INSERT INTO dballe_test VALUES (42)"); - - auto s = c.odbcstatement("SELECT val FROM dballe_test"); - unsigned val = 0; - s->bind_out(1, val); - s->execute(); - unsigned count = 0; - while (s->fetch()) - ++count; - wassert(actual(val) == 42); - wassert(actual(count) == 1); - }), - Test("query_unsigned_null", [](Fixture& f) { - // Test querying unsigned values, with indicators - auto& c = f.conn; - c.exec("INSERT INTO dballe_test VALUES (42)"); - - auto s = c.odbcstatement("SELECT val FROM dballe_test"); - unsigned val = 0; - SQLLEN ind = 0; - s->bind_out(1, val, ind); - s->execute(); - unsigned count = 0; - while (s->fetch()) - ++count; - wassert(actual(val) == 42); - wassert(actual(ind) != SQL_NULL_DATA); - wassert(actual(count) == 1); - }), - Test("query_unsigned_short", [](Fixture& f) { - // Test querying unsigned short values - auto& c = f.conn; - c.exec("INSERT INTO dballe_test VALUES (42)"); - auto s = c.odbcstatement("SELECT val FROM dballe_test"); - unsigned short val = 0; - s->bind_out(1, val); - s->execute(); - unsigned count = 0; - while (s->fetch()) - ++count; - wassert(actual(val) == 42); - wassert(actual(count) == 1); - }), - Test("has_tables", [](Fixture& f) { - // Test has_tables - auto& c = f.conn; - ensure(!c.has_table("this_should_not_exist")); - ensure(c.has_table("dballe_test")); - }), - Test("settings", [](Fixture& f) { - // Test settings - auto& c = f.conn; - c.drop_table_if_exists("dballe_settings"); - ensure(!c.has_table("dballe_settings")); - - ensure_equals(c.get_setting("test_key"), ""); - - c.set_setting("test_key", "42"); - ensure(c.has_table("dballe_settings")); - - ensure_equals(c.get_setting("test_key"), "42"); - }), -}; - -test_group tg1("db_internals_odbc", tests); - -} diff --git a/dballe/db/postgresql/internals-test.cc b/dballe/db/postgresql/internals-test.cc new file mode 100644 index 000000000..80a7932a4 --- /dev/null +++ b/dballe/db/postgresql/internals-test.cc @@ -0,0 +1,217 @@ +#include "db/tests.h" +#include "internals.h" + +using namespace dballe; +using namespace dballe::db; +using namespace dballe::tests; +using namespace wreport; +using namespace std; + +namespace { + +struct ConnectorFixture : public Fixture +{ + PostgreSQLConnection conn; + + ConnectorFixture() + { + conn.open_test(); + } + + void test_setup() override + { + Fixture::test_setup(); + conn.drop_table_if_exists("dballe_test"); + conn.exec_no_data("CREATE TABLE dballe_test (val INTEGER NOT NULL)"); + } +}; + +class Tests : public FixtureTestCase +{ + using FixtureTestCase::FixtureTestCase; + + void register_tests() override + { + add_method("int", [](Fixture& f) { + // Test querying int values + auto& conn = f.conn; + conn.exec_no_data("INSERT INTO dballe_test VALUES (1)"); + conn.exec_no_data("INSERT INTO dballe_test VALUES (2)"); + + auto s = conn.exec("SELECT val FROM dballe_test"); + wassert(actual(s.rowcount()) == 2); + + int val = 0; + for (unsigned row = 0; row < 2; ++row) + val += s.get_int4(row, 0); + + wassert(actual(val) == 3); + }); + + add_method("int_null", [](Fixture& f) { + // Test querying int values, with potential NULLs + auto& conn = f.conn; + conn.drop_table_if_exists("dballe_testnull"); + conn.exec_no_data("CREATE TABLE dballe_testnull (val INTEGER)"); + conn.exec_no_data("INSERT INTO dballe_testnull VALUES (NULL)"); + conn.exec_no_data("INSERT INTO dballe_testnull VALUES (42)"); + + auto s = conn.exec("SELECT val FROM dballe_testnull"); + wassert(actual(s.rowcount()) == 2); + + int val = 0; + unsigned countnulls = 0; + for (unsigned row = 0; row < 2; ++row) + { + if (s.is_null(row, 0)) + ++countnulls; + else + val += s.get_int4(row, 0); + } + + wassert(actual(val) == 42); + wassert(actual(countnulls) == 1); + }); + + add_method("unsigned", [](Fixture& f) { + // Test querying unsigned values + auto& conn = f.conn; + conn.drop_table_if_exists("dballe_testbig"); + conn.exec_no_data("CREATE TABLE dballe_testbig (val BIGINT)"); + conn.exec_no_data("INSERT INTO dballe_testbig VALUES (x'FFFFFFFE'::bigint)"); + + auto s = conn.exec("SELECT val FROM dballe_testbig"); + wassert(actual(s.rowcount()) == 1); + + unsigned val = 0; + for (unsigned row = 0; row < 1; ++row) + val += s.get_int8(row, 0); + wassert(actual(val) == 0xFFFFFFFE); + }); + + add_method("unsigned_short", [](Fixture& f) { + // Test querying unsigned short values + auto& conn = f.conn; + conn.drop_table_if_exists("dballe_testshort"); + conn.exec_no_data("CREATE TABLE dballe_testshort (val SMALLINT)"); + conn.exec_no_data("INSERT INTO dballe_testshort VALUES (123)"); + + auto s = conn.exec("SELECT val FROM dballe_testshort"); + wassert(actual(s.rowcount()) == 1); + + Varcode val = 0; + for (unsigned row = 0; row < 1; ++row) + val += s.get_int2(row, 0); + wassert(actual(val) == 123); + }); + + add_method("has_tables", [](Fixture& f) { + // Test has_tables + auto& conn = f.conn; + wassert(actual(conn.has_table("this_should_not_exist")).isfalse()); + wassert(actual(conn.has_table("dballe_test")).istrue()); + }); + + add_method("settings", [](Fixture& f) { + // Test settings + auto& conn = f.conn; + conn.drop_table_if_exists("dballe_settings"); + wassert(actual(conn.has_table("dballe_settings")).isfalse()); + + wassert(actual(conn.get_setting("test_key")) == ""); + + conn.set_setting("test_key", "42"); + wassert(actual(conn.has_table("dballe_settings")).istrue()); + + wassert(actual(conn.get_setting("test_key")) == "42"); + wassert(actual(conn.get_setting("test_key1")) == ""); + }); + + add_method("autoid", [](Fixture& f) { + // Test auto_increment + auto& conn = f.conn; + conn.drop_table_if_exists("dballe_testai"); + conn.exec_no_data("CREATE TABLE dballe_testai (id SERIAL PRIMARY KEY, val INTEGER)"); + auto r1 = conn.exec_one_row("INSERT INTO dballe_testai (id, val) VALUES (DEFAULT, 42) RETURNING id"); + wassert(actual(r1.get_int4(0, 0)) == 1); + auto r2 = conn.exec_one_row("INSERT INTO dballe_testai (id, val) VALUES (DEFAULT, 43) RETURNING id"); + wassert(actual(r2.get_int4(0, 0)) == 2); + }); + + add_method("prepared", [](Fixture& f) { + // Test prepared statements + auto& conn = f.conn; + conn.exec_no_data("INSERT INTO dballe_test VALUES (1)"); + conn.exec_no_data("INSERT INTO dballe_test VALUES (2)"); + + conn.prepare("db_postgresql_internals_9", "SELECT val FROM dballe_test"); + + auto s = conn.exec_prepared("db_postgresql_internals_9"); + wassert(actual(s.rowcount()) == 2); + + int val = 0; + for (unsigned row = 0; row < 2; ++row) + val += s.get_int4(row, 0); + + wassert(actual(val) == 3); + }); + + add_method("prepared_int", [](Fixture& f) { + // Test prepared statements with int arguments + auto& conn = f.conn; + conn.exec_no_data("INSERT INTO dballe_test VALUES (1)"); + conn.exec_no_data("INSERT INTO dballe_test VALUES (2)"); + conn.prepare("db_postgresql_internals_10", "SELECT val FROM dballe_test WHERE val=$1::int4"); + + auto res = conn.exec_prepared("db_postgresql_internals_10", 1); + wassert(actual(res.rowcount()) == 1); + wassert(actual(res.get_int4(0, 0)) == 1); + }); + + add_method("prepared_string", [](Fixture& f) { + // Test prepared statements with string arguments + auto& conn = f.conn; + conn.drop_table_if_exists("db_postgresql_internals_11"); + conn.exec_no_data("CREATE TABLE db_postgresql_internals_11 (val TEXT)"); + conn.exec_no_data("INSERT INTO db_postgresql_internals_11 VALUES ('foo')"); + conn.exec_no_data("INSERT INTO db_postgresql_internals_11 VALUES ('bar')"); + conn.prepare("db_postgresql_internals_11_select", "SELECT val FROM db_postgresql_internals_11 WHERE val=$1::text"); + + auto res1 = conn.exec_prepared("db_postgresql_internals_11_select", "foo"); + wassert(actual(res1.rowcount()) == 1); + wassert(actual(res1.get_string(0, 0)) == "foo"); + + auto res2 = conn.exec_prepared("db_postgresql_internals_11_select", string("foo")); + wassert(actual(res2.rowcount()) == 1); + wassert(actual(res2.get_string(0, 0)) == "foo"); + }); + + add_method("prepared_datetime", [](Fixture& f) { + // Test prepared statements with datetime arguments + auto& conn = f.conn; + conn.drop_table_if_exists("db_postgresql_internals_12"); + conn.exec_no_data("CREATE TABLE db_postgresql_internals_12 (val TIMESTAMP)"); + conn.exec_no_data("INSERT INTO db_postgresql_internals_12 VALUES ('2015-04-01 12:30:45')"); + conn.exec_no_data("INSERT INTO db_postgresql_internals_12 VALUES ('1945-04-25 08:10:20')"); + conn.prepare("db_postgresql_internals_12_select", "SELECT val FROM db_postgresql_internals_12 WHERE val=$1::timestamp"); + + auto res1 = conn.exec("SELECT val FROM db_postgresql_internals_12 WHERE val=TIMESTAMP '2015-04-01 12:30:45'"); + wassert(actual(res1.rowcount()) == 1); + wassert(actual(res1.get_timestamp(0, 0)) == Datetime(2015, 4, 1, 12, 30, 45)); + + auto res2 = conn.exec_prepared("db_postgresql_internals_12_select", Datetime(2015, 4, 1, 12, 30, 45)); + wassert(actual(res2.rowcount()) == 1); + wassert(actual(res2.get_timestamp(0, 0)) == Datetime(2015, 4, 1, 12, 30, 45)); + + auto res3 = conn.exec("SELECT val FROM db_postgresql_internals_12 WHERE val=TIMESTAMP '1945-04-25 08:10:20'"); + wassert(actual(res3.rowcount()) == 1); + wassert(actual(res3.get_timestamp(0, 0)) == Datetime(1945, 4, 25, 8, 10, 20)); + + auto res4 = conn.exec_prepared("db_postgresql_internals_12_select", Datetime(1945, 4, 25, 8, 10, 20)); + wassert(actual(res4.rowcount()) == 1); + wassert(actual(res4.get_timestamp(0, 0)) == Datetime(1945, 4, 25, 8, 10, 20)); + }); + } +} test("db_postgresql_internals"); + +} diff --git a/dballe/db/postgresql/internals-tut.cc b/dballe/db/postgresql/internals-tut.cc deleted file mode 100644 index 2a1fe2e7f..000000000 --- a/dballe/db/postgresql/internals-tut.cc +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Copyright (C) 2014 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ -#include "db/tests.h" -#include "internals.h" - -using namespace dballe; -using namespace dballe::db; -using namespace wreport; -using namespace wibble::tests; -using namespace std; - -namespace tut { - -struct db_postgresql_internals_shar -{ - PostgreSQLConnection conn; - - db_postgresql_internals_shar() - { - conn.open_test(); - } - - ~db_postgresql_internals_shar() - { - } - - void reset() - { - conn.drop_table_if_exists("dballe_test"); - conn.exec_no_data("CREATE TABLE dballe_test (val INTEGER NOT NULL)"); - } -}; -TESTGRP(db_postgresql_internals); - -// Test querying int values -template<> template<> -void to::test<1>() -{ - reset(); - - conn.exec_no_data("INSERT INTO dballe_test VALUES (1)"); - conn.exec_no_data("INSERT INTO dballe_test VALUES (2)"); - - auto s = conn.exec("SELECT val FROM dballe_test"); - wassert(actual(s.rowcount()) == 2); - - int val = 0; - for (unsigned row = 0; row < 2; ++row) - val += s.get_int4(row, 0); - - wassert(actual(val) == 3); -} - -// Test querying int values, with potential NULLs -template<> template<> -void to::test<2>() -{ - reset(); - - conn.drop_table_if_exists("dballe_testnull"); - conn.exec_no_data("CREATE TABLE dballe_testnull (val INTEGER)"); - conn.exec_no_data("INSERT INTO dballe_testnull VALUES (NULL)"); - conn.exec_no_data("INSERT INTO dballe_testnull VALUES (42)"); - - auto s = conn.exec("SELECT val FROM dballe_testnull"); - wassert(actual(s.rowcount()) == 2); - - int val = 0; - unsigned countnulls = 0; - for (unsigned row = 0; row < 2; ++row) - { - if (s.is_null(row, 0)) - ++countnulls; - else - val += s.get_int4(row, 0); - } - - wassert(actual(val) == 42); - wassert(actual(countnulls) == 1); -} - -// Test querying unsigned values -template<> template<> -void to::test<3>() -{ - reset(); - - conn.drop_table_if_exists("dballe_testbig"); - conn.exec_no_data("CREATE TABLE dballe_testbig (val BIGINT)"); - conn.exec_no_data("INSERT INTO dballe_testbig VALUES (x'FFFFFFFE'::bigint)"); - - auto s = conn.exec("SELECT val FROM dballe_testbig"); - wassert(actual(s.rowcount()) == 1); - - unsigned val = 0; - for (unsigned row = 0; row < 1; ++row) - val += s.get_int8(row, 0); - wassert(actual(val) == 0xFFFFFFFE); -} - -// Test querying unsigned short values -template<> template<> -void to::test<5>() -{ - reset(); - - conn.drop_table_if_exists("dballe_testshort"); - conn.exec_no_data("CREATE TABLE dballe_testshort (val SMALLINT)"); - conn.exec_no_data("INSERT INTO dballe_testshort VALUES (123)"); - - auto s = conn.exec("SELECT val FROM dballe_testshort"); - wassert(actual(s.rowcount()) == 1); - - Varcode val = 0; - for (unsigned row = 0; row < 1; ++row) - val += s.get_int2(row, 0); - wassert(actual(val) == 123); -} - -// Test has_tables -template<> template<> -void to::test<6>() -{ - reset(); - - wassert(actual(conn.has_table("this_should_not_exist")).isfalse()); - wassert(actual(conn.has_table("dballe_test")).istrue()); -} - -// Test settings -template<> template<> -void to::test<7>() -{ - conn.drop_table_if_exists("dballe_settings"); - wassert(actual(conn.has_table("dballe_settings")).isfalse()); - - wassert(actual(conn.get_setting("test_key")) == ""); - - conn.set_setting("test_key", "42"); - wassert(actual(conn.has_table("dballe_settings")).istrue()); - - wassert(actual(conn.get_setting("test_key")) == "42"); - wassert(actual(conn.get_setting("test_key1")) == ""); -} - -// Test auto_increment -template<> template<> -void to::test<8>() -{ - conn.drop_table_if_exists("dballe_testai"); - conn.exec_no_data("CREATE TABLE dballe_testai (id SERIAL PRIMARY KEY, val INTEGER)"); - auto r1 = conn.exec_one_row("INSERT INTO dballe_testai (id, val) VALUES (DEFAULT, 42) RETURNING id"); - wassert(actual(r1.get_int4(0, 0)) == 1); - auto r2 = conn.exec_one_row("INSERT INTO dballe_testai (id, val) VALUES (DEFAULT, 43) RETURNING id"); - wassert(actual(r2.get_int4(0, 0)) == 2); -} - -// Test prepared statements -template<> template<> -void to::test<9>() -{ - reset(); - conn.exec_no_data("INSERT INTO dballe_test VALUES (1)"); - conn.exec_no_data("INSERT INTO dballe_test VALUES (2)"); - - conn.prepare("db_postgresql_internals_9", "SELECT val FROM dballe_test"); - - auto s = conn.exec_prepared("db_postgresql_internals_9"); - wassert(actual(s.rowcount()) == 2); - - int val = 0; - for (unsigned row = 0; row < 2; ++row) - val += s.get_int4(row, 0); - - wassert(actual(val) == 3); -} - -// Test prepared statements with int arguments -template<> template<> -void to::test<10>() -{ - reset(); - conn.exec_no_data("INSERT INTO dballe_test VALUES (1)"); - conn.exec_no_data("INSERT INTO dballe_test VALUES (2)"); - conn.prepare("db_postgresql_internals_10", "SELECT val FROM dballe_test WHERE val=$1::int4"); - - auto res = conn.exec_prepared("db_postgresql_internals_10", 1); - wassert(actual(res.rowcount()) == 1); - wassert(actual(res.get_int4(0, 0)) == 1); -} - -// Test prepared statements with string arguments -template<> template<> -void to::test<11>() -{ - reset(); - conn.drop_table_if_exists("db_postgresql_internals_11"); - conn.exec_no_data("CREATE TABLE db_postgresql_internals_11 (val TEXT)"); - conn.exec_no_data("INSERT INTO db_postgresql_internals_11 VALUES ('foo')"); - conn.exec_no_data("INSERT INTO db_postgresql_internals_11 VALUES ('bar')"); - conn.prepare("db_postgresql_internals_11_select", "SELECT val FROM db_postgresql_internals_11 WHERE val=$1::text"); - - auto res1 = conn.exec_prepared("db_postgresql_internals_11_select", "foo"); - wassert(actual(res1.rowcount()) == 1); - wassert(actual(res1.get_string(0, 0)) == "foo"); - - auto res2 = conn.exec_prepared("db_postgresql_internals_11_select", string("foo")); - wassert(actual(res2.rowcount()) == 1); - wassert(actual(res2.get_string(0, 0)) == "foo"); -} - -// Test prepared statements with datetime arguments -template<> template<> -void to::test<12>() -{ - reset(); - conn.drop_table_if_exists("db_postgresql_internals_12"); - conn.exec_no_data("CREATE TABLE db_postgresql_internals_12 (val TIMESTAMP)"); - conn.exec_no_data("INSERT INTO db_postgresql_internals_12 VALUES ('2015-04-01 12:30:45')"); - conn.exec_no_data("INSERT INTO db_postgresql_internals_12 VALUES ('1945-04-25 08:10:20')"); - conn.prepare("db_postgresql_internals_12_select", "SELECT val FROM db_postgresql_internals_12 WHERE val=$1::timestamp"); - - auto res1 = conn.exec("SELECT val FROM db_postgresql_internals_12 WHERE val=TIMESTAMP '2015-04-01 12:30:45'"); - wassert(actual(res1.rowcount()) == 1); - wassert(actual(res1.get_timestamp(0, 0)) == Datetime(2015, 4, 1, 12, 30, 45)); - - auto res2 = conn.exec_prepared("db_postgresql_internals_12_select", Datetime(2015, 4, 1, 12, 30, 45)); - wassert(actual(res2.rowcount()) == 1); - wassert(actual(res2.get_timestamp(0, 0)) == Datetime(2015, 4, 1, 12, 30, 45)); - - auto res3 = conn.exec("SELECT val FROM db_postgresql_internals_12 WHERE val=TIMESTAMP '1945-04-25 08:10:20'"); - wassert(actual(res3.rowcount()) == 1); - wassert(actual(res3.get_timestamp(0, 0)) == Datetime(1945, 4, 25, 8, 10, 20)); - - auto res4 = conn.exec_prepared("db_postgresql_internals_12_select", Datetime(1945, 4, 25, 8, 10, 20)); - wassert(actual(res4.rowcount()) == 1); - wassert(actual(res4.get_timestamp(0, 0)) == Datetime(1945, 4, 25, 8, 10, 20)); -} - - -} diff --git a/dballe/db/querybuf-test.cc b/dballe/db/querybuf-test.cc new file mode 100644 index 000000000..1cdaa94f2 --- /dev/null +++ b/dballe/db/querybuf-test.cc @@ -0,0 +1,56 @@ +#include "db/tests.h" +#include "db/querybuf.h" + +using namespace wreport; +using namespace dballe; +using namespace dballe::tests; +using namespace std; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("append", []() { + Querybuf buf(10); + + // A new querybuf contains the empty string + wassert(actual(buf) == string()); + + // Clearing should still be empty + buf.clear(); + wassert(actual(buf) == string()); + + buf.append("ciao"); + wassert(actual(buf) == "ciao"); + wassert(actual(buf.size()) == 4); + + buf.appendf("%d %s", 42, "--"); + wassert(actual(buf) == "ciao42 --"); + wassert(actual(buf.size()) == 9); + + buf.clear(); + wassert(actual(buf) == string()); + + buf.append("123456789"); + wassert(actual(buf) == "123456789"); + + buf.clear(); + buf.start_list(", "); + buf.append_list("1"); + buf.append_list("2"); + buf.append_listf("%d", 3); + wassert(actual(buf) == "1, 2, 3"); + }); + add_method("varlist", []() { + Querybuf buf(50); + buf.append_varlist("B12101,B12103,block"); + wassert(actual((string)buf) == "3173,3175,257"); + }); + } +} test("db_querybuf"); + +} diff --git a/dballe/db/querybuf-tut.cc b/dballe/db/querybuf-tut.cc deleted file mode 100644 index f9dd1d928..000000000 --- a/dballe/db/querybuf-tut.cc +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2005--2015 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "db/tests.h" -#include "db/querybuf.h" - -using namespace wibble::tests; -using namespace wreport; -using namespace dballe; -using namespace std; - -namespace { - -typedef dballe::tests::test_group<> test_group; -typedef test_group::Test Test; -typedef test_group::Fixture Fixture; - -std::vector tests { - Test("append", [](Fixture& f) { - Querybuf buf(10); - - // A new querybuf contains the empty string - ensure_equals(buf, string()); - - // Clearing should still be empty - buf.clear(); - ensure_equals(buf, string()); - - buf.append("ciao"); - ensure_equals(buf, "ciao"); - ensure_equals(buf.size(), 4); - - buf.appendf("%d %s", 42, "--"); - ensure_equals(buf, "ciao42 --"); - ensure_equals(buf.size(), 9); - - buf.clear(); - ensure_equals(buf, string()); - - buf.append("123456789"); - ensure_equals(buf, "123456789"); - - buf.clear(); - buf.start_list(", "); - buf.append_list("1"); - buf.append_list("2"); - buf.append_listf("%d", 3); - ensure_equals(buf, "1, 2, 3"); - }), - Test("varlist", [](Fixture& f) { - Querybuf buf(50); - buf.append_varlist("B12101,B12103,block"); - wassert(actual((string)buf) == "3173,3175,257"); - }), -}; - -test_group tg("db_querybuf", tests); - -} diff --git a/dballe/db/sql/attrv6-test.cc b/dballe/db/sql/attrv6-test.cc new file mode 100644 index 000000000..51ad3dd00 --- /dev/null +++ b/dballe/db/sql/attrv6-test.cc @@ -0,0 +1,216 @@ +#include "db/tests.h" +#include "db/v6/db.h" +#include "db/sql.h" +#include "db/sql/repinfo.h" +#include "db/sql/station.h" +#include "db/sql/levtr.h" +#include "db/sql/datav6.h" +#include "db/sql/attrv6.h" +#include "config.h" + +using namespace dballe; +using namespace dballe::tests; +using namespace wreport; +using namespace std; + +namespace { + +struct Fixture : DriverFixture +{ + using DriverFixture::DriverFixture; + + unique_ptr attr; + + void reset_attr() + { + using namespace dballe::db::sql; + + auto st = driver->create_stationv6(); + auto lt = driver->create_levtrv6(); + auto da = driver->create_datav6(); + + int added, deleted, updated; + driver->create_repinfov6()->update(nullptr, &added, &deleted, &updated); + + // Insert a mobile station + wassert(actual(st->obtain_id(4500000, 1100000, "ciao")) == 1); + + // Insert a fixed station + wassert(actual(st->obtain_id(4600000, 1200000)) == 2); + + // Insert a lev_tr + wassert(actual(lt->obtain_id(Level(1, 2, 0, 3), Trange(4, 5, 6))) == 1); + + // Insert another lev_tr + wassert(actual(lt->obtain_id(Level(2, 3, 1, 4), Trange(5, 6, 7))) == 2); + + auto t = conn->transaction(); + // Insert a datum + { + bulk::InsertV6 vars; + vars.id_station = 1; + vars.id_report = 1; + vars.datetime = Datetime(2001, 2, 3, 4, 5, 6); + Var var(varinfo(WR_VAR(0, 1, 2)), 123); + vars.add(&var, 1); + da->insert(*t, vars, DataV6::ERROR); + } + + // Insert another datum + { + bulk::InsertV6 vars; + vars.id_station = 2; + vars.id_report = 2; + vars.datetime = Datetime(2002, 3, 4, 5, 6, 7); + Var var(varinfo(WR_VAR(0, 1, 2)), 234); + vars.add(&var, 2); + da->insert(*t, vars, DataV6::ERROR); + } + t->commit(); + } + + void test_setup() override + { + DriverFixture::test_setup(); + attr = driver->create_attrv6(); + reset_attr(); + } + + Var query(int id_data, unsigned expected_attr_count) + { + Var res(varinfo(WR_VAR(0, 12, 101))); + unsigned count = 0; + attr->read(id_data, [&](unique_ptr attr) { res.seta(auto_ptr(attr.release())); ++count; }); + wassert(actual(count) == expected_attr_count); + return res; + } +}; + +class Tests : public FixtureTestCase +{ + using FixtureTestCase::FixtureTestCase; + + void register_tests() override + { + add_method("insert", [](Fixture& f) { + using namespace dballe::db::sql; + auto& at = *f.attr; + + auto t = f.conn->transaction(); + + Var var1(varinfo(WR_VAR(0, 12, 101)), 280.0); + var1.seta(newvar(WR_VAR(0, 33, 7), 50)); + + Var var2(varinfo(WR_VAR(0, 12, 101)), 280.0); + var2.seta(newvar(WR_VAR(0, 33, 7), 75)); + + // Insert two attributes + { + bulk::InsertAttrsV6 attrs; + attrs.add_all(var1, 1); + at.insert(*t, attrs, AttrV6::ERROR); + wassert(actual(attrs.size()) == 1); + wassert(actual(attrs[0].needs_insert()).isfalse()); + wassert(actual(attrs[0].inserted()).istrue()); + wassert(actual(attrs[0].needs_update()).isfalse()); + wassert(actual(attrs[0].updated()).isfalse()); + } + { + bulk::InsertAttrsV6 attrs; + attrs.add_all(var2, 2); + at.insert(*t, attrs, AttrV6::ERROR); + wassert(actual(attrs.size()) == 1); + wassert(actual(attrs[0].needs_insert()).isfalse()); + wassert(actual(attrs[0].inserted()).istrue()); + wassert(actual(attrs[0].needs_update()).isfalse()); + wassert(actual(attrs[0].updated()).isfalse()); + } + + // Reinsert the first attribute: it should work, doing no insert/update queries + { + bulk::InsertAttrsV6 attrs; + attrs.add_all(var1, 1); + at.insert(*t, attrs, AttrV6::IGNORE); + wassert(actual(attrs.size()) == 1); + wassert(actual(attrs[0].needs_insert()).isfalse()); + wassert(actual(attrs[0].inserted()).isfalse()); + wassert(actual(attrs[0].needs_update()).isfalse()); + wassert(actual(attrs[0].updated()).isfalse()); + } + + // Reinsert the second attribute: it should work, doing no insert/update queries + { + bulk::InsertAttrsV6 attrs; + attrs.add_all(var2, 2); + at.insert(*t, attrs, AttrV6::UPDATE); + wassert(actual(attrs.size()) == 1); + wassert(actual(attrs[0].needs_insert()).isfalse()); + wassert(actual(attrs[0].inserted()).isfalse()); + wassert(actual(attrs[0].needs_update()).isfalse()); + wassert(actual(attrs[0].updated()).isfalse()); + } + + // Load the attributes for the first variable + { + Var var(f.query(1, 1)); + wassert(actual(var.next_attr()->code()) == WR_VAR(0, 33, 7)); + wassert(actual(*var.next_attr()) == 50); + } + + // Load the attributes for the second variable + { + Var var(f.query(2, 1)); + wassert(actual(var.next_attr()->code()) == WR_VAR(0, 33, 7)); + wassert(actual(*var.next_attr()) == 75); + } + + // Update both values + { + bulk::InsertAttrsV6 attrs; + attrs.add_all(var2, 1); + at.insert(*t, attrs, AttrV6::UPDATE); + wassert(actual(attrs.size()) == 1); + wassert(actual(attrs[0].needs_insert()).isfalse()); + wassert(actual(attrs[0].inserted()).isfalse()); + wassert(actual(attrs[0].needs_update()).isfalse()); + wassert(actual(attrs[0].updated()).istrue()); + } + { + bulk::InsertAttrsV6 attrs; + attrs.add_all(var1, 2); + at.insert(*t, attrs, AttrV6::UPDATE); + wassert(actual(attrs.size()) == 1); + wassert(actual(attrs[0].needs_insert()).isfalse()); + wassert(actual(attrs[0].inserted()).isfalse()); + wassert(actual(attrs[0].needs_update()).isfalse()); + wassert(actual(attrs[0].updated()).istrue()); + } + // Load the attributes again to verify that they changed + { + Var var(f.query(1, 1)); + wassert(actual(var.next_attr()->code()) == WR_VAR(0, 33, 7)); + wassert(actual(*var.next_attr()) == 75); + } + { + Var var(f.query(2, 1)); + wassert(actual(var.next_attr()->code()) == WR_VAR(0, 33, 7)); + wassert(actual(*var.next_attr()) == 50); + } + + // TODO: test a mix of update and insert + }); + } +}; + +Tests tg1("db_sql_attr_v6_sqlite", "SQLITE", db::V6); +#ifdef HAVE_ODBC +Tests tg2("db_sql_attr_v6_odbc", "ODBC", db::V6); +#endif +#ifdef HAVE_LIBPQ +Tests tg3("db_sql_attr_v6_postgresql", "POSTGRESQL", db::V6); +#endif +#ifdef HAVE_MYSQL +Tests tg4("db_sql_attr_v6_mysql", "MYSQL", db::V6); +#endif + +} diff --git a/dballe/db/sql/attrv6-tut.cc b/dballe/db/sql/attrv6-tut.cc deleted file mode 100644 index bb662ccde..000000000 --- a/dballe/db/sql/attrv6-tut.cc +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright (C) 2005--2015 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "db/tests.h" -#include "db/v6/db.h" -#include "db/sql.h" -#include "db/sql/repinfo.h" -#include "db/sql/station.h" -#include "db/sql/levtr.h" -#include "db/sql/datav6.h" -#include "db/sql/attrv6.h" -#include "config.h" - -using namespace dballe; -using namespace dballe::tests; -using namespace wreport; -using namespace wibble::tests; -using namespace std; - -namespace { - -struct Fixture : dballe::tests::DriverFixture -{ - unique_ptr attr; - - Fixture() - { - using namespace dballe::db::sql; - reset_attr(); - - auto st = driver->create_stationv6(); - auto lt = driver->create_levtrv6(); - auto da = driver->create_datav6(); - - int added, deleted, updated; - driver->create_repinfov6()->update(nullptr, &added, &deleted, &updated); - - // Insert a mobile station - wassert(actual(st->obtain_id(4500000, 1100000, "ciao")) == 1); - - // Insert a fixed station - wassert(actual(st->obtain_id(4600000, 1200000)) == 2); - - // Insert a lev_tr - wassert(actual(lt->obtain_id(Level(1, 2, 0, 3), Trange(4, 5, 6))) == 1); - - // Insert another lev_tr - wassert(actual(lt->obtain_id(Level(2, 3, 1, 4), Trange(5, 6, 7))) == 2); - - auto t = conn->transaction(); - // Insert a datum - { - bulk::InsertV6 vars; - vars.id_station = 1; - vars.id_report = 1; - vars.datetime = Datetime(2001, 2, 3, 4, 5, 6); - Var var(varinfo(WR_VAR(0, 1, 2)), 123); - vars.add(&var, 1); - da->insert(*t, vars, DataV6::ERROR); - } - - // Insert another datum - { - bulk::InsertV6 vars; - vars.id_station = 2; - vars.id_report = 2; - vars.datetime = Datetime(2002, 3, 4, 5, 6, 7); - Var var(varinfo(WR_VAR(0, 1, 2)), 234); - vars.add(&var, 2); - da->insert(*t, vars, DataV6::ERROR); - } - t->commit(); - } - - void reset_attr() - { - driver->exec_no_data("DELETE FROM attr"); - attr = driver->create_attrv6(); - } - - void reset() - { - dballe::tests::DriverFixture::reset(); - reset_attr(); - } - - Var query(int id_data, unsigned expected_attr_count) - { - Var res(varinfo(WR_VAR(0, 12, 101))); - unsigned count = 0; - attr->read(id_data, [&](unique_ptr attr) { res.seta(auto_ptr(attr.release())); ++count; }); - wassert(actual(count) == expected_attr_count); - return res; - } -}; - -typedef dballe::tests::driver_test_group test_group; -typedef test_group::Test Test; - -std::vector tests { - Test("insert", [](Fixture& f) { - using namespace dballe::db::sql; - auto& at = *f.attr; - - auto t = f.conn->transaction(); - - Var var1(varinfo(WR_VAR(0, 12, 101)), 280.0); - var1.seta(newvar(WR_VAR(0, 33, 7), 50)); - - Var var2(varinfo(WR_VAR(0, 12, 101)), 280.0); - var2.seta(newvar(WR_VAR(0, 33, 7), 75)); - - // Insert two attributes - { - bulk::InsertAttrsV6 attrs; - attrs.add_all(var1, 1); - at.insert(*t, attrs, AttrV6::ERROR); - wassert(actual(attrs.size()) == 1); - wassert(actual(attrs[0].needs_insert()).isfalse()); - wassert(actual(attrs[0].inserted()).istrue()); - wassert(actual(attrs[0].needs_update()).isfalse()); - wassert(actual(attrs[0].updated()).isfalse()); - } - { - bulk::InsertAttrsV6 attrs; - attrs.add_all(var2, 2); - at.insert(*t, attrs, AttrV6::ERROR); - wassert(actual(attrs.size()) == 1); - wassert(actual(attrs[0].needs_insert()).isfalse()); - wassert(actual(attrs[0].inserted()).istrue()); - wassert(actual(attrs[0].needs_update()).isfalse()); - wassert(actual(attrs[0].updated()).isfalse()); - } - - // Reinsert the first attribute: it should work, doing no insert/update queries - { - bulk::InsertAttrsV6 attrs; - attrs.add_all(var1, 1); - at.insert(*t, attrs, AttrV6::IGNORE); - wassert(actual(attrs.size()) == 1); - wassert(actual(attrs[0].needs_insert()).isfalse()); - wassert(actual(attrs[0].inserted()).isfalse()); - wassert(actual(attrs[0].needs_update()).isfalse()); - wassert(actual(attrs[0].updated()).isfalse()); - } - - // Reinsert the second attribute: it should work, doing no insert/update queries - { - bulk::InsertAttrsV6 attrs; - attrs.add_all(var2, 2); - at.insert(*t, attrs, AttrV6::UPDATE); - wassert(actual(attrs.size()) == 1); - wassert(actual(attrs[0].needs_insert()).isfalse()); - wassert(actual(attrs[0].inserted()).isfalse()); - wassert(actual(attrs[0].needs_update()).isfalse()); - wassert(actual(attrs[0].updated()).isfalse()); - } - - // Load the attributes for the first variable - { - Var var(f.query(1, 1)); - wassert(actual(var.next_attr()->code()) == WR_VAR(0, 33, 7)); - wassert(actual(*var.next_attr()) == 50); - } - - // Load the attributes for the second variable - { - Var var(f.query(2, 1)); - wassert(actual(var.next_attr()->code()) == WR_VAR(0, 33, 7)); - wassert(actual(*var.next_attr()) == 75); - } - - // Update both values - { - bulk::InsertAttrsV6 attrs; - attrs.add_all(var2, 1); - at.insert(*t, attrs, AttrV6::UPDATE); - wassert(actual(attrs.size()) == 1); - wassert(actual(attrs[0].needs_insert()).isfalse()); - wassert(actual(attrs[0].inserted()).isfalse()); - wassert(actual(attrs[0].needs_update()).isfalse()); - wassert(actual(attrs[0].updated()).istrue()); - } - { - bulk::InsertAttrsV6 attrs; - attrs.add_all(var1, 2); - at.insert(*t, attrs, AttrV6::UPDATE); - wassert(actual(attrs.size()) == 1); - wassert(actual(attrs[0].needs_insert()).isfalse()); - wassert(actual(attrs[0].inserted()).isfalse()); - wassert(actual(attrs[0].needs_update()).isfalse()); - wassert(actual(attrs[0].updated()).istrue()); - } - // Load the attributes again to verify that they changed - { - Var var(f.query(1, 1)); - wassert(actual(var.next_attr()->code()) == WR_VAR(0, 33, 7)); - wassert(actual(*var.next_attr()) == 75); - } - { - Var var(f.query(2, 1)); - wassert(actual(var.next_attr()->code()) == WR_VAR(0, 33, 7)); - wassert(actual(*var.next_attr()) == 50); - } - - // TODO: test a mix of update and insert - }), -}; - -test_group tg1("db_sql_attr_v6_sqlite", "SQLITE", db::V6, tests); -#ifdef HAVE_ODBC -test_group tg2("db_sql_attr_v6_odbc", "ODBC", db::V6, tests); -#endif -#ifdef HAVE_LIBPQ -test_group tg3("db_sql_attr_v6_postgresql", "POSTGRESQL", db::V6, tests); -#endif -#ifdef HAVE_MYSQL -test_group tg4("db_sql_attr_v6_mysql", "MYSQL", db::V6, tests); -#endif - -} diff --git a/dballe/db/sql/datav6-test.cc b/dballe/db/sql/datav6-test.cc new file mode 100644 index 000000000..aab363464 --- /dev/null +++ b/dballe/db/sql/datav6-test.cc @@ -0,0 +1,179 @@ +#include "db/tests.h" +#include "db/v6/db.h" +#include "db/sql.h" +#include "db/sql/repinfo.h" +#include "db/sql/station.h" +#include "db/sql/levtr.h" +#include "db/sql/datav6.h" +#include "config.h" + +using namespace dballe; +using namespace dballe::tests; +using namespace wreport; +using namespace std; + +namespace { + +struct Fixture : DriverFixture +{ + using DriverFixture::DriverFixture; + + unique_ptr data; + + void reset_data() + { + auto st = driver->create_stationv6(); + auto lt = driver->create_levtrv6(); + + int added, deleted, updated; + driver->create_repinfov6()->update(nullptr, &added, &deleted, &updated); + + // Insert a mobile station + wassert(actual(st->obtain_id(4500000, 1100000, "ciao")) == 1); + + // Insert a fixed station + wassert(actual(st->obtain_id(4600000, 1200000)) == 2); + + // Insert a lev_tr + wassert(actual(lt->obtain_id(Level(1, 2, 0, 3), Trange(4, 5, 6))) == 1); + + // Insert another lev_tr + wassert(actual(lt->obtain_id(Level(2, 3, 1, 4), Trange(5, 6, 7))) == 2); + } + + void test_setup() override + { + DriverFixture::test_setup(); + data = driver->create_datav6(); + reset_data(); + } +}; + + +class Tests : public FixtureTestCase +{ + using FixtureTestCase::FixtureTestCase; + + void register_tests() override + { + add_method("insert", [](Fixture& f) { + using namespace dballe::db::sql; + auto& da = *f.data; + + auto t = f.conn->transaction(); + + Var var(varinfo(WR_VAR(0, 1, 2))); + + auto insert_sample1 = [&](bulk::InsertV6& vars, int value, DataV6::UpdateMode update) { + vars.id_station = 1; + vars.id_report = 1; + vars.datetime = Datetime(2001, 2, 3, 4, 5, 6); + var.seti(value); + vars.add(&var, 1); + da.insert(*t, vars, update); + }; + + // Insert a datum + { + bulk::InsertV6 vars; + insert_sample1(vars, 123, DataV6::ERROR); + wassert(actual(vars[0].id_data) == 1); + wassert(actual(vars[0].needs_insert()).isfalse()); + wassert(actual(vars[0].inserted()).istrue()); + wassert(actual(vars[0].needs_update()).isfalse()); + wassert(actual(vars[0].updated()).isfalse()); + } + + // Insert another datum + { + bulk::InsertV6 vars; + vars.id_station = 2; + vars.id_report = 2; + vars.datetime = Datetime(2002, 3, 4, 5, 6, 7); + Var var(varinfo(WR_VAR(0, 1, 2)), 234); + vars.add(&var, 2); + da.insert(*t, vars, DataV6::ERROR); + wassert(actual(vars[0].id_data) == 2); + wassert(actual(vars[0].needs_insert()).isfalse()); + wassert(actual(vars[0].inserted()).istrue()); + wassert(actual(vars[0].needs_update()).isfalse()); + wassert(actual(vars[0].updated()).isfalse()); + } + + // Reinsert the first datum: it should find its ID and do nothing + { + bulk::InsertV6 vars; + insert_sample1(vars, 123, DataV6::ERROR); + wassert(actual(vars[0].id_data) == 1); + wassert(actual(vars[0].needs_insert()).isfalse()); + wassert(actual(vars[0].inserted()).isfalse()); + wassert(actual(vars[0].needs_update()).isfalse()); + wassert(actual(vars[0].updated()).isfalse()); + } + + // Reinsert the first datum, with a different value and ignore + // overwrite: it should find its ID and do nothing + { + bulk::InsertV6 vars; + insert_sample1(vars, 125, DataV6::IGNORE); + wassert(actual(vars[0].id_data) == 1); + wassert(actual(vars[0].needs_insert()).isfalse()); + wassert(actual(vars[0].inserted()).isfalse()); + wassert(actual(vars[0].needs_update()).istrue()); + wassert(actual(vars[0].updated()).isfalse()); + } + + // Reinsert the first datum, with a different value and overwrite: + // it should find its ID and update it + { + bulk::InsertV6 vars; + insert_sample1(vars, 125, DataV6::UPDATE); + wassert(actual(vars[0].id_data) == 1); + wassert(actual(vars[0].needs_insert()).isfalse()); + wassert(actual(vars[0].inserted()).isfalse()); + wassert(actual(vars[0].needs_update()).isfalse()); + wassert(actual(vars[0].updated()).istrue()); + } + + // Reinsert the first datum, with the same value and error on + // overwrite: it should find its ID and do nothing, because the value + // does not change. + { + bulk::InsertV6 vars; + insert_sample1(vars, 125, DataV6::ERROR); + wassert(actual(vars[0].id_data) == 1); + wassert(actual(vars[0].needs_insert()).isfalse()); + wassert(actual(vars[0].inserted()).isfalse()); + wassert(actual(vars[0].needs_update()).isfalse()); + wassert(actual(vars[0].updated()).isfalse()); + } + + // Reinsert the first datum, with a different value and error on + // overwrite: it should throw an error + { + bulk::InsertV6 vars; + try { + insert_sample1(vars, 126, DataV6::IGNORE); + wassert(actual(false).isfalse()); + } catch (std::exception& e) { + wassert(actual(e.what()).contains("refusing to overwrite existing data")); + } + } + + t->commit(); + }); + } +}; + +Tests tg1("db_sql_data_v6_sqlite", "SQLITE", db::V6); +#ifdef HAVE_ODBC +Tests tg2("db_sql_data_v6_odbc", "ODBC", db::V6); +#endif +#ifdef HAVE_LIBPQ +Tests tg3("db_sql_data_v6_postgresql", "POSTGRESQL", db::V6); +#endif +#ifdef HAVE_MYSQL +Tests tg4("db_sql_data_v6_mysql", "MYSQL", db::V6); +#endif + +} diff --git a/dballe/db/sql/datav6-tut.cc b/dballe/db/sql/datav6-tut.cc deleted file mode 100644 index e81a6a170..000000000 --- a/dballe/db/sql/datav6-tut.cc +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright (C) 2005--2015 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "db/tests.h" -#include "db/v6/db.h" -#include "db/sql.h" -#include "db/sql/repinfo.h" -#include "db/sql/station.h" -#include "db/sql/levtr.h" -#include "db/sql/datav6.h" -#include "config.h" - -using namespace dballe; -using namespace dballe::tests; -using namespace wreport; -using namespace wibble::tests; -using namespace std; - -namespace { - -struct Fixture : dballe::tests::DriverFixture -{ - unique_ptr data; - - Fixture() - { - reset_data(); - - auto st = driver->create_stationv6(); - auto lt = driver->create_levtrv6(); - - int added, deleted, updated; - driver->create_repinfov6()->update(nullptr, &added, &deleted, &updated); - - // Insert a mobile station - wassert(actual(st->obtain_id(4500000, 1100000, "ciao")) == 1); - - // Insert a fixed station - wassert(actual(st->obtain_id(4600000, 1200000)) == 2); - - // Insert a lev_tr - wassert(actual(lt->obtain_id(Level(1, 2, 0, 3), Trange(4, 5, 6))) == 1); - - // Insert another lev_tr - wassert(actual(lt->obtain_id(Level(2, 3, 1, 4), Trange(5, 6, 7))) == 2); - } - - void reset_data() - { - driver->exec_no_data("DELETE FROM data"); - data = driver->create_datav6(); - } - - void reset() - { - dballe::tests::DriverFixture::reset(); - reset_data(); - } -}; - -typedef dballe::tests::driver_test_group test_group; -typedef test_group::Test Test; - -std::vector tests { - Test("insert", [](Fixture& f) { - using namespace dballe::db::sql; - auto& da = *f.data; - - auto t = f.conn->transaction(); - - auto insert_sample1 = [&](bulk::InsertV6& vars, int value, DataV6::UpdateMode update) { - vars.id_station = 1; - vars.id_report = 1; - vars.datetime = Datetime(2001, 2, 3, 4, 5, 6); - Var var(varinfo(WR_VAR(0, 1, 2)), value); - vars.add(&var, 1); - da.insert(*t, vars, update); - }; - - // Insert a datum - { - bulk::InsertV6 vars; - insert_sample1(vars, 123, DataV6::ERROR); - wassert(actual(vars[0].id_data) == 1); - wassert(actual(vars[0].needs_insert()).isfalse()); - wassert(actual(vars[0].inserted()).istrue()); - wassert(actual(vars[0].needs_update()).isfalse()); - wassert(actual(vars[0].updated()).isfalse()); - } - - // Insert another datum - { - bulk::InsertV6 vars; - vars.id_station = 2; - vars.id_report = 2; - vars.datetime = Datetime(2002, 3, 4, 5, 6, 7); - Var var(varinfo(WR_VAR(0, 1, 2)), 234); - vars.add(&var, 2); - da.insert(*t, vars, DataV6::ERROR); - wassert(actual(vars[0].id_data) == 2); - wassert(actual(vars[0].needs_insert()).isfalse()); - wassert(actual(vars[0].inserted()).istrue()); - wassert(actual(vars[0].needs_update()).isfalse()); - wassert(actual(vars[0].updated()).isfalse()); - } - - // Reinsert the first datum: it should find its ID and do nothing - { - bulk::InsertV6 vars; - insert_sample1(vars, 123, DataV6::ERROR); - wassert(actual(vars[0].id_data) == 1); - wassert(actual(vars[0].needs_insert()).isfalse()); - wassert(actual(vars[0].inserted()).isfalse()); - wassert(actual(vars[0].needs_update()).isfalse()); - wassert(actual(vars[0].updated()).isfalse()); - } - - // Reinsert the first datum, with a different value and ignore - // overwrite: it should find its ID and do nothing - { - bulk::InsertV6 vars; - insert_sample1(vars, 125, DataV6::IGNORE); - wassert(actual(vars[0].id_data) == 1); - wassert(actual(vars[0].needs_insert()).isfalse()); - wassert(actual(vars[0].inserted()).isfalse()); - wassert(actual(vars[0].needs_update()).istrue()); - wassert(actual(vars[0].updated()).isfalse()); - } - - // Reinsert the first datum, with a different value and overwrite: - // it should find its ID and update it - { - bulk::InsertV6 vars; - insert_sample1(vars, 125, DataV6::UPDATE); - wassert(actual(vars[0].id_data) == 1); - wassert(actual(vars[0].needs_insert()).isfalse()); - wassert(actual(vars[0].inserted()).isfalse()); - wassert(actual(vars[0].needs_update()).isfalse()); - wassert(actual(vars[0].updated()).istrue()); - } - - // Reinsert the first datum, with the same value and error on - // overwrite: it should find its ID and do nothing, because the value - // does not change. - { - bulk::InsertV6 vars; - insert_sample1(vars, 125, DataV6::ERROR); - wassert(actual(vars[0].id_data) == 1); - wassert(actual(vars[0].needs_insert()).isfalse()); - wassert(actual(vars[0].inserted()).isfalse()); - wassert(actual(vars[0].needs_update()).isfalse()); - wassert(actual(vars[0].updated()).isfalse()); - } - - // Reinsert the first datum, with a different value and error on - // overwrite: it should throw an error - { - bulk::InsertV6 vars; - try { - insert_sample1(vars, 126, DataV6::IGNORE); - wassert(actual(false).isfalse()); - } catch (std::exception& e) { - wassert(actual(e.what()).contains("refusing to overwrite existing data")); - } - } - - t->commit(); - }), -}; - -test_group tg1("db_sql_data_v6_sqlite", "SQLITE", db::V6, tests); -#ifdef HAVE_ODBC -test_group tg2("db_sql_data_v6_odbc", "ODBC", db::V6, tests); -#endif -#ifdef HAVE_LIBPQ -test_group tg3("db_sql_data_v6_postgresql", "POSTGRESQL", db::V6, tests); -#endif -#ifdef HAVE_MYSQL -test_group tg4("db_sql_data_v6_mysql", "MYSQL", db::V6, tests); -#endif - -} diff --git a/dballe/db/sql/levtr-test.cc b/dballe/db/sql/levtr-test.cc new file mode 100644 index 000000000..7fdac9d5e --- /dev/null +++ b/dballe/db/sql/levtr-test.cc @@ -0,0 +1,63 @@ +#include "db/tests.h" +#include "db/v6/db.h" +#include "db/sql.h" +#include "db/sql/levtr.h" +#include "config.h" + +using namespace dballe; +using namespace dballe::tests; +using namespace wreport; +using namespace std; + +namespace { + +struct Fixture : DriverFixture +{ + using DriverFixture::DriverFixture; + + unique_ptr levtr; + + void reset_levtr() + { + if (conn->has_table("levtr")) + driver->exec_no_data("DELETE FROM levtr"); + levtr = driver->create_levtrv6(); + } + + void test_setup() override + { + DriverFixture::test_setup(); + reset_levtr(); + } +}; + +class Tests : public FixtureTestCase +{ + using FixtureTestCase::FixtureTestCase; + + void register_tests() override + { + add_method("insert", [](Fixture& f) { + auto& lt = *f.levtr; + + // Insert a lev_tr + wassert(actual(lt.obtain_id(Level(1, 2, 0, 3), Trange(4, 5, 6))) == 1); + + // Insert another lev_tr + wassert(actual(lt.obtain_id(Level(2, 3, 1, 4), Trange(5, 6, 7))) == 2); + }); + } +}; + +Tests test_sqlite("db_sql_levtr_v6_sqlite", "SQLITE", db::V6); +#ifdef HAVE_ODBC +Tests test_odbc("db_sql_levtr_v6_odbc", "ODBC", db::V6); +#endif +#ifdef HAVE_LIBPQ +Tests test_psql("db_sql_levtr_v6_postgresql", "POSTGRESQL", db::V6); +#endif +#ifdef HAVE_MYSQL +Tests test_mysql("db_sql_levtr_v6_mysql", "MYSQL", db::V6); +#endif + +} diff --git a/dballe/db/sql/levtr-tut.cc b/dballe/db/sql/levtr-tut.cc deleted file mode 100644 index c386a93c8..000000000 --- a/dballe/db/sql/levtr-tut.cc +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2005--2015 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "db/tests.h" -#include "db/v6/db.h" -#include "db/sql.h" -#include "db/sql/levtr.h" -#include "config.h" - -using namespace dballe; -using namespace dballe::tests; -using namespace wibble::tests; -using namespace wreport; -using namespace std; - -namespace { - -struct Fixture : dballe::tests::DriverFixture -{ - unique_ptr levtr; - - Fixture() - { - reset_levtr(); - } - - void reset_levtr() - { - if (conn->has_table("levtr")) - driver->exec_no_data("DELETE FROM levtr"); - levtr = driver->create_levtrv6(); - } - - void reset() - { - dballe::tests::DriverFixture::reset(); - reset_levtr(); - } -}; - -typedef dballe::tests::driver_test_group test_group; -typedef test_group::Test Test; - -std::vector tests { - Test("insert", [](Fixture& f) { - auto& lt = *f.levtr; - - // Insert a lev_tr - wassert(actual(lt.obtain_id(Level(1, 2, 0, 3), Trange(4, 5, 6))) == 1); - - // Insert another lev_tr - wassert(actual(lt.obtain_id(Level(2, 3, 1, 4), Trange(5, 6, 7))) == 2); - }), -}; - -test_group tg1("db_sql_levtr_v6_sqlite", "SQLITE", db::V6, tests); -#ifdef HAVE_ODBC -test_group tg2("db_sql_levtr_v6_odbc", "ODBC", db::V6, tests); -#endif -#ifdef HAVE_LIBPQ -test_group tg3("db_sql_levtr_v6_postgresql", "POSTGRESQL", db::V6, tests); -#endif -#ifdef HAVE_MYSQL -test_group tg4("db_sql_levtr_v6_mysql", "MYSQL", db::V6, tests); -#endif - -} diff --git a/dballe/db/sql/repinfo-test.cc b/dballe/db/sql/repinfo-test.cc new file mode 100644 index 000000000..5903e3cab --- /dev/null +++ b/dballe/db/sql/repinfo-test.cc @@ -0,0 +1,136 @@ +#include "db/tests.h" +#include "db/v6/db.h" +#include "db/sql.h" +#include "db/sql/driver.h" +#include "db/sql/repinfo.h" +#include "config.h" + +using namespace dballe; +using namespace dballe::db; +using namespace dballe::tests; +using namespace std; +using namespace wreport; + +namespace { + +struct Fixture : DriverFixture +{ + using DriverFixture::DriverFixture; + + unique_ptr repinfo; + + void reset_repinfo() + { + if (conn->has_table("repinfo")) + driver->exec_no_data("DELETE FROM repinfo"); + + switch (format) + { + case V5: throw error_unimplemented("v5 db is not supported"); + case V6: + repinfo = driver->create_repinfov6(); + break; + default: + throw error_consistency("cannot test repinfo on the current DB format"); + } + int added, deleted, updated; + repinfo->update(nullptr, &added, &deleted, &updated); + } + + void test_setup() override + { + DriverFixture::test_setup(); + reset_repinfo(); + } +}; + +class Tests : public FixtureTestCase +{ + using FixtureTestCase::FixtureTestCase; + + void register_tests() override + { + // Test simple queries + add_method("query", [](Fixture& f) { + auto& ri = *f.repinfo; + wassert(actual(ri.get_id("synop")) == 1); + wassert(actual(ri.get_id("generic")) == 255); + wassert(actual(ri.get_rep_memo(1)) == "synop"); + wassert(actual(ri.get_priority(199)) == INT_MAX); + }); + // Test update + add_method("update", [](Fixture& f) { + auto& ri = *f.repinfo; + + wassert(actual(ri.get_id("synop")) == 1); + + int added, deleted, updated; + ri.update(NULL, &added, &deleted, &updated); + + wassert(actual(added) == 0); + wassert(actual(deleted) == 0); + wassert(actual(updated) == 13); + + wassert(actual(ri.get_id("synop")) == 1); + }); + // Test update from a file that was known to fail + add_method("fail", [](Fixture& f) { + auto& ri = *f.repinfo; + + wassert(actual(ri.get_id("synop")) == 1); + + int added, deleted, updated; + ri.update((string(getenv("DBA_TESTDATA")) + "/test-repinfo1.csv").c_str(), &added, &deleted, &updated); + + wassert(actual(added) == 3); + wassert(actual(deleted) == 11); + wassert(actual(updated) == 2); + + wassert(actual(ri.get_id("synop")) == 1); + wassert(actual(ri.get_id("FIXspnpo")) == 201); + }); + // Test update from a file with a negative priority + add_method("fail", [](Fixture& f) { + auto& ri = *f.repinfo; + + int id = ri.get_id("generic"); + wassert(actual(ri.get_priority(id)) == 1000); + + int added, deleted, updated; + ri.update((string(getenv("DBA_TESTDATA")) + "/test-repinfo2.csv").c_str(), &added, &deleted, &updated); + + wassert(actual(added) == 3); + wassert(actual(deleted) == 11); + wassert(actual(updated) == 2); + + wassert(actual(ri.get_priority(id)) == -5); + }); + // Test automatic repinfo creation + add_method("fail", [](Fixture& f) { + auto& ri = *f.repinfo; + + int id = ri.obtain_id("foobar"); + wassert(actual(id) > 0); + wassert(actual(ri.get_rep_memo(id)) == "foobar"); + wassert(actual(ri.get_priority(id)) == 1001); + + id = ri.obtain_id("barbaz"); + wassert(actual(id) > 0); + wassert(actual(ri.get_rep_memo(id)) == "barbaz"); + wassert(actual(ri.get_priority(id)) == 1002); + }); + } +}; + +Tests test_sqlite("db_sql_repinfo_v6_sqlite", "SQLITE", db::V6); +#ifdef HAVE_ODBC +Tests test_odbc("db_sql_repinfo_v6_odbc", "ODBC", db::V6); +#endif +#ifdef HAVE_LIBPQ +Tests test_psql("db_sql_repinfo_v6_postgresql", "POSTGRESQL", db::V6); +#endif +#ifdef HAVE_MYSQL +Tests test_mysql("db_sql_repinfo_v6_mysql", "MYSQL", db::V6); +#endif + +} diff --git a/dballe/db/sql/repinfo-tut.cc b/dballe/db/sql/repinfo-tut.cc deleted file mode 100644 index 7b7c85682..000000000 --- a/dballe/db/sql/repinfo-tut.cc +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (C) 2005--2015 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "config.h" -#include "db/tests.h" -#include "db/v6/db.h" -#include "db/sql.h" -#include "db/sql/driver.h" -#include "db/sql/repinfo.h" - -using namespace dballe; -using namespace dballe::db; -using namespace dballe::tests; -using namespace std; -using namespace wreport; -using namespace wibble::tests; - -namespace { - -struct Fixture : dballe::tests::DriverFixture -{ - unique_ptr repinfo; - - Fixture() - { - reset_repinfo(); - } - - void reset_repinfo() - { - if (conn->has_table("repinfo")) - driver->exec_no_data("DELETE FROM repinfo"); - - switch (format) - { - case V5: throw error_unimplemented("v5 db is not supported"); - case V6: - repinfo = driver->create_repinfov6(); - break; - default: - throw error_consistency("cannot test repinfo on the current DB format"); - } - int added, deleted, updated; - repinfo->update(nullptr, &added, &deleted, &updated); - } - - void reset() - { - dballe::tests::DriverFixture::reset(); - reset_repinfo(); - } - -}; - -typedef dballe::tests::driver_test_group test_group; -typedef test_group::Test Test; - -std::vector tests { - // Test simple queries - Test("query", [](Fixture& f) { - auto& ri = *f.repinfo; - wassert(actual(ri.get_id("synop")) == 1); - wassert(actual(ri.get_id("generic")) == 255); - wassert(actual(ri.get_rep_memo(1)) == "synop"); - wassert(actual(ri.get_priority(199)) == INT_MAX); - }), - // Test update - Test("update", [](Fixture& f) { - auto& ri = *f.repinfo; - - wassert(actual(ri.get_id("synop")) == 1); - - int added, deleted, updated; - ri.update(NULL, &added, &deleted, &updated); - - wassert(actual(added) == 0); - wassert(actual(deleted) == 0); - wassert(actual(updated) == 13); - - wassert(actual(ri.get_id("synop")) == 1); - }), - // Test update from a file that was known to fail - Test("fail", [](Fixture& f) { - auto& ri = *f.repinfo; - - wassert(actual(ri.get_id("synop")) == 1); - - int added, deleted, updated; - ri.update((string(getenv("DBA_TESTDATA")) + "/test-repinfo1.csv").c_str(), &added, &deleted, &updated); - - wassert(actual(added) == 3); - wassert(actual(deleted) == 11); - wassert(actual(updated) == 2); - - wassert(actual(ri.get_id("synop")) == 1); - wassert(actual(ri.get_id("FIXspnpo")) == 201); - }), - // Test update from a file with a negative priority - Test("fail", [](Fixture& f) { - auto& ri = *f.repinfo; - - int id = ri.get_id("generic"); - wassert(actual(ri.get_priority(id)) == 1000); - - int added, deleted, updated; - ri.update((string(getenv("DBA_TESTDATA")) + "/test-repinfo2.csv").c_str(), &added, &deleted, &updated); - - wassert(actual(added) == 3); - wassert(actual(deleted) == 11); - wassert(actual(updated) == 2); - - wassert(actual(ri.get_priority(id)) == -5); - }), - // Test automatic repinfo creation - Test("fail", [](Fixture& f) { - auto& ri = *f.repinfo; - - int id = ri.obtain_id("foobar"); - wassert(actual(id) > 0); - wassert(actual(ri.get_rep_memo(id)) == "foobar"); - wassert(actual(ri.get_priority(id)) == 1001); - - id = ri.obtain_id("barbaz"); - wassert(actual(id) > 0); - wassert(actual(ri.get_rep_memo(id)) == "barbaz"); - wassert(actual(ri.get_priority(id)) == 1002); - }), -}; - -test_group tg2("db_sql_repinfo_v6_sqlite", "SQLITE", db::V6, tests); -#ifdef HAVE_ODBC -test_group tg4("db_sql_repinfo_v6_odbc", "ODBC", db::V6, tests); -#endif -#ifdef HAVE_LIBPQ -test_group tg6("db_sql_repinfo_v6_postgresql", "POSTGRESQL", db::V6, tests); -#endif -#ifdef HAVE_MYSQL -test_group tg8("db_sql_repinfo_v6_mysql", "MYSQL", db::V6, tests); -#endif - -} diff --git a/dballe/db/sql/station-test.cc b/dballe/db/sql/station-test.cc new file mode 100644 index 000000000..b439a13ae --- /dev/null +++ b/dballe/db/sql/station-test.cc @@ -0,0 +1,86 @@ +#include "db/tests.h" +#include "db/v6/db.h" +#include "db/sql.h" +#include "db/sql/station.h" +#include "config.h" + +using namespace dballe; +using namespace dballe::tests; +using namespace wreport; +using namespace std; + +namespace { + +struct Fixture : DriverFixture +{ + using DriverFixture::DriverFixture; + + unique_ptr station; + + void reset_station() + { + if (conn->has_table("station")) + driver->exec_no_data("DELETE FROM station"); + + switch (format) + { + case db::V5: throw error_unimplemented("v5 db is not supported"); + case db::V6: + station = driver->create_stationv6(); + break; + default: + throw error_consistency("cannot test station on the current DB format"); + } + } + + void test_setup() override + { + DriverFixture::test_setup(); + reset_station(); + } +}; + +class Tests : public FixtureTestCase +{ + using FixtureTestCase::FixtureTestCase; + + void register_tests() override + { + add_method("insert", [](Fixture& f) { + // Insert some values and try to read them again + auto& st = *f.station; + bool inserted; + + // Insert a mobile station + wassert(actual(st.obtain_id(4500000, 1100000, "ciao", &inserted)) == 1); + wassert(actual(inserted).istrue()); + wassert(actual(st.obtain_id(4500000, 1100000, "ciao", &inserted)) == 1); + wassert(actual(inserted).isfalse()); + + // Insert a fixed station + wassert(actual(st.obtain_id(4600000, 1200000, NULL, &inserted)) == 2); + wassert(actual(inserted).istrue()); + wassert(actual(st.obtain_id(4600000, 1200000, NULL, &inserted)) == 2); + wassert(actual(inserted).isfalse()); + + // Get the ID of the first station + wassert(actual(st.get_id(4500000, 1100000, "ciao")) == 1); + + // Get the ID of the second station + wassert(actual(st.get_id(4600000, 1200000)) == 2); + }); + } +}; + +Tests test_sqlite("db_sql_station_v6_sqlite", "SQLITE", db::V6); +#ifdef HAVE_ODBC +Tests test_odbc("db_sql_station_v6_odbc", "ODBC", db::V6); +#endif +#ifdef HAVE_LIBPQ +Tests test_psql("db_sql_station_v6_postgresql", "POSTGRESQL", db::V6); +#endif +#ifdef HAVE_MYSQL +Tests test_mysql("db_sql_station_v6_mysql", "MYSQL", db::V6); +#endif + +} diff --git a/dballe/db/sql/station-tut.cc b/dballe/db/sql/station-tut.cc deleted file mode 100644 index c457a5b72..000000000 --- a/dballe/db/sql/station-tut.cc +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2005--2015 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "db/tests.h" -#include "db/v6/db.h" -#include "db/sql.h" -#include "db/sql/station.h" -#include "config.h" - -using namespace dballe; -using namespace dballe::tests; -using namespace wreport; -using namespace wibble::tests; -using namespace std; - -namespace { - -struct Fixture : dballe::tests::DriverFixture -{ - unique_ptr station; - - Fixture() - { - reset_station(); - } - - void reset_station() - { - if (conn->has_table("station")) - driver->exec_no_data("DELETE FROM station"); - - switch (format) - { - case db::V5: throw error_unimplemented("v5 db is not supported"); - case db::V6: - station = driver->create_stationv6(); - break; - default: - throw error_consistency("cannot test station on the current DB format"); - } - } - - void reset() - { - dballe::tests::DriverFixture::reset(); - reset_station(); - } -}; - -typedef dballe::tests::driver_test_group test_group; -typedef test_group::Test Test; - -std::vector tests { - Test("insert", [](Fixture& f) { - // Insert some values and try to read them again - auto& st = *f.station; - bool inserted; - - // Insert a mobile station - wassert(actual(st.obtain_id(4500000, 1100000, "ciao", &inserted)) == 1); - wassert(actual(inserted).istrue()); - wassert(actual(st.obtain_id(4500000, 1100000, "ciao", &inserted)) == 1); - wassert(actual(inserted).isfalse()); - - // Insert a fixed station - wassert(actual(st.obtain_id(4600000, 1200000, NULL, &inserted)) == 2); - wassert(actual(inserted).istrue()); - wassert(actual(st.obtain_id(4600000, 1200000, NULL, &inserted)) == 2); - wassert(actual(inserted).isfalse()); - - // Get the ID of the first station - wassert(actual(st.get_id(4500000, 1100000, "ciao")) == 1); - - // Get the ID of the second station - wassert(actual(st.get_id(4600000, 1200000)) == 2); - }), -}; - -test_group tg2("db_sql_station_v6_sqlite", "SQLITE", db::V6, tests); -#ifdef HAVE_ODBC -test_group tg4("db_sql_station_v6_odbc", "ODBC", db::V6, tests); -#endif -#ifdef HAVE_LIBPQ -test_group tg6("db_sql_station_v6_postgresql", "POSTGRESQL", db::V6, tests); -#endif -#ifdef HAVE_MYSQL -test_group tg8("db_sql_station_v6_mysql", "MYSQL", db::V6, tests); -#endif - -} diff --git a/dballe/db/sqlite/internals-test.cc b/dballe/db/sqlite/internals-test.cc new file mode 100644 index 000000000..4ac8b89a4 --- /dev/null +++ b/dballe/db/sqlite/internals-test.cc @@ -0,0 +1,136 @@ +#include "core/tests.h" +#include "internals.h" + +using namespace std; +using namespace dballe; +using namespace dballe::db; +using namespace dballe::tests; +using namespace wreport; + +namespace { + +struct SQLiteFixture : Fixture +{ + SQLiteConnection conn; + + SQLiteFixture() + { + conn.open_memory(); + } + + void test_setup() override + { + Fixture::test_setup(); + conn.drop_table_if_exists("dballe_test"); + conn.exec("CREATE TABLE dballe_test (val INTEGER NOT NULL)"); + } +}; + +class Tests : public FixtureTestCase +{ + using FixtureTestCase::FixtureTestCase; + + void register_tests() override + { + add_method("query_int", [](Fixture& f) { + // Test querying int values + f.conn.exec("INSERT INTO dballe_test VALUES (1)"); + f.conn.exec("INSERT INTO dballe_test VALUES (2)"); + + auto s = f.conn.sqlitestatement("SELECT val FROM dballe_test"); + + int val = 0; + unsigned count = 0; + s->execute([&]() { + val += s->column_int(0); + ++count; + }); + wassert(actual(count) == 2); + wassert(actual(val) == 3); + }); + add_method("query_int_null", [](Fixture& f) { + // Test querying int values, with potential NULLs + f.conn.exec("CREATE TABLE dballe_testnull (val INTEGER)"); + f.conn.exec("INSERT INTO dballe_testnull VALUES (NULL)"); + f.conn.exec("INSERT INTO dballe_testnull VALUES (42)"); + + auto s = f.conn.sqlitestatement("SELECT val FROM dballe_testnull"); + + int val = 0; + unsigned count = 0; + unsigned countnulls = 0; + s->execute([&]() { + if (s->column_isnull(0)) + ++countnulls; + else + val += s->column_int(0); + ++count; + }); + + wassert(actual(val) == 42); + wassert(actual(count) == 2); + wassert(actual(countnulls) == 1); + }); + add_method("query_unsigned", [](Fixture& f) { + // Test querying unsigned values + f.conn.exec("INSERT INTO dballe_test VALUES (0xFFFFFFFE)"); + + auto s = f.conn.sqlitestatement("SELECT val FROM dballe_test"); + + unsigned val = 0; + unsigned count = 0; + s->execute([&]() { + val += s->column_int64(0); + ++count; + }); + wassert(actual(count) == 1); + wassert(actual(val) == 0xFFFFFFFE); + }); + add_method("query_unsigned_short", [](Fixture& f) { + // Test querying unsigned short values + char buf[200]; + snprintf(buf, 200, "INSERT INTO dballe_test VALUES (%d)", (int)WR_VAR(3, 1, 12)); + f.conn.exec(buf); + + auto s = f.conn.sqlitestatement("SELECT val FROM dballe_test"); + + Varcode val = 0; + unsigned count = 0; + s->execute([&]() { + val = (Varcode)s->column_int(0); + ++count; + }); + wassert(actual(count) == 1); + wassert(actual(val) == WR_VAR(3, 1, 12)); + }); + add_method("query_has_tables", [](Fixture& f) { + // Test has_tables + wassert(actual(f.conn.has_table("this_should_not_exist")).isfalse()); + wassert(actual(f.conn.has_table("dballe_test")).istrue()); + }); + add_method("query_settings", [](Fixture& f) { + // Test settings + f.conn.drop_table_if_exists("dballe_settings"); + wassert(actual(f.conn.has_table("dballe_settings")).isfalse()); + + wassert(actual(f.conn.get_setting("test_key")) == ""); + + f.conn.set_setting("test_key", "42"); + wassert(actual(f.conn.has_table("dballe_settings")).istrue()); + + wassert(actual(f.conn.get_setting("test_key")) == "42"); + }); + add_method("auto_increment", [](Fixture& f) { + // Test auto_increment + f.conn.exec("CREATE TABLE dballe_testai (id INTEGER PRIMARY KEY, val INTEGER)"); + f.conn.exec("INSERT INTO dballe_testai (val) VALUES (42)"); + wassert(actual(f.conn.get_last_insert_id()) == 1); + f.conn.exec("INSERT INTO dballe_testai (val) VALUES (43)"); + wassert(actual(f.conn.get_last_insert_id()) == 2); + }); + } +}; + +Tests tests("db_internals_sqlite"); + +} diff --git a/dballe/db/sqlite/internals-tut.cc b/dballe/db/sqlite/internals-tut.cc deleted file mode 100644 index aef2ef11c..000000000 --- a/dballe/db/sqlite/internals-tut.cc +++ /dev/null @@ -1,134 +0,0 @@ -#include "core/tests.h" -#include "internals.h" - -using namespace std; -using namespace wibble::tests; -using namespace dballe; -using namespace dballe::db; -using namespace wreport; - -namespace { - -struct Fixture : dballe::tests::Fixture -{ - SQLiteConnection conn; - - Fixture() - { - conn.open_memory(); - reset(); - } - - void reset() - { - dballe::tests::Fixture::reset(); - conn.drop_table_if_exists("dballe_test"); - conn.exec("CREATE TABLE dballe_test (val INTEGER NOT NULL)"); - } -}; - -typedef dballe::tests::test_group test_group; -typedef test_group::Test Test; - -std::vector tests { - Test("query_int", [](Fixture& f) { - // Test querying int values - f.conn.exec("INSERT INTO dballe_test VALUES (1)"); - f.conn.exec("INSERT INTO dballe_test VALUES (2)"); - - auto s = f.conn.sqlitestatement("SELECT val FROM dballe_test"); - - int val = 0; - unsigned count = 0; - s->execute([&]() { - val += s->column_int(0); - ++count; - }); - wassert(actual(count) == 2); - wassert(actual(val) == 3); - }), - Test("query_int_null", [](Fixture& f) { - // Test querying int values, with potential NULLs - f.conn.exec("CREATE TABLE dballe_testnull (val INTEGER)"); - f.conn.exec("INSERT INTO dballe_testnull VALUES (NULL)"); - f.conn.exec("INSERT INTO dballe_testnull VALUES (42)"); - - auto s = f.conn.sqlitestatement("SELECT val FROM dballe_testnull"); - - int val = 0; - unsigned count = 0; - unsigned countnulls = 0; - s->execute([&]() { - if (s->column_isnull(0)) - ++countnulls; - else - val += s->column_int(0); - ++count; - }); - - wassert(actual(val) == 42); - wassert(actual(count) == 2); - wassert(actual(countnulls) == 1); - }), - Test("query_unsigned", [](Fixture& f) { - // Test querying unsigned values - f.conn.exec("INSERT INTO dballe_test VALUES (0xFFFFFFFE)"); - - auto s = f.conn.sqlitestatement("SELECT val FROM dballe_test"); - - unsigned val = 0; - unsigned count = 0; - s->execute([&]() { - val += s->column_int64(0); - ++count; - }); - wassert(actual(count) == 1); - wassert(actual(val) == 0xFFFFFFFE); - }), - Test("query_unsigned_short", [](Fixture& f) { - // Test querying unsigned short values - char buf[200]; - snprintf(buf, 200, "INSERT INTO dballe_test VALUES (%d)", (int)WR_VAR(3, 1, 12)); - f.conn.exec(buf); - - auto s = f.conn.sqlitestatement("SELECT val FROM dballe_test"); - - Varcode val = 0; - unsigned count = 0; - s->execute([&]() { - val = (Varcode)s->column_int(0); - ++count; - }); - wassert(actual(count) == 1); - wassert(actual(val) == WR_VAR(3, 1, 12)); - }), - Test("query_has_tables", [](Fixture& f) { - // Test has_tables - wassert(actual(f.conn.has_table("this_should_not_exist")).isfalse()); - wassert(actual(f.conn.has_table("dballe_test")).istrue()); - }), - Test("query_settings", [](Fixture& f) { - // Test settings - f.conn.drop_table_if_exists("dballe_settings"); - wassert(actual(f.conn.has_table("dballe_settings")).isfalse()); - - wassert(actual(f.conn.get_setting("test_key")) == ""); - - f.conn.set_setting("test_key", "42"); - wassert(actual(f.conn.has_table("dballe_settings")).istrue()); - - wassert(actual(f.conn.get_setting("test_key")) == "42"); - }), - Test("auto_increment", [](Fixture& f) { - // Test auto_increment - f.conn.exec("CREATE TABLE dballe_testai (id INTEGER PRIMARY KEY, val INTEGER)"); - f.conn.exec("INSERT INTO dballe_testai (val) VALUES (42)"); - wassert(actual(f.conn.get_last_insert_id()) == 1); - f.conn.exec("INSERT INTO dballe_testai (val) VALUES (43)"); - wassert(actual(f.conn.get_last_insert_id()) == 2); - }), -}; - -test_group newtg("db_internals_sqlite", tests); - -} diff --git a/dballe/db/summary-test.cc b/dballe/db/summary-test.cc new file mode 100644 index 000000000..8e53c90be --- /dev/null +++ b/dballe/db/summary-test.cc @@ -0,0 +1,128 @@ +#include "db/tests.h" +#include "summary.h" +#include "config.h" + +using namespace dballe; +using namespace dballe::db; +using namespace dballe::tests; +using namespace wreport; +using namespace std; + +namespace { + +class Tests : public FixtureTestCase +{ + using FixtureTestCase::FixtureTestCase; + + void register_tests() override + { + add_method("summary", [](Fixture& f) { + // Test building a summary and checking if it supports queries + wassert(f.populate()); + + core::Query query; + query.query = "details"; + Summary s(query); + wassert(actual(s.is_valid()).isfalse()); + + // Build the whole db summary + auto cur = f.db->query_summary(query); + while (cur->next()) + s.add_summary(*cur, true); + + // Check its contents + wassert(actual(s.is_valid()).istrue()); + if (f.db->format() == V6) + wassert(actual(s.all_stations.size()) == 1); + else + wassert(actual(s.all_stations.size()) == 2); + wassert(actual(s.all_levels.size()) == 1); + wassert(actual(s.all_tranges.size()) == 2); + wassert(actual(s.all_varcodes.size()) == 2); + wassert(actual(s.datetime_min()) == Datetime(1945, 4, 25, 8)); + wassert(actual(s.datetime_max()) == Datetime(1945, 4, 25, 8, 30)); + wassert(actual(s.data_count()) == 4); + + // Check what it can support + + // An existing station is ok: we know we have it + wassert(actual(s.supports(*query_from_string("ana_id=1"))) == summary::Support::EXACT); + + // A non-existing station is also ok: we know we don't have it + wassert(actual(s.supports(*query_from_string("ana_id=2"))) == summary::Support::EXACT); + + wassert(actual(s.supports(*query_from_string("ana_id=1, leveltype1=10"))) == summary::Support::EXACT); + + wassert(actual(s.supports(*query_from_string("ana_id=1, leveltype1=10, pindicator=20"))) == summary::Support::EXACT); + + wassert(actual(s.supports(*query_from_string("ana_id=1, leveltype1=10, pindicator=20"))) == summary::Support::EXACT); + + // Still exact, because the query matches the entire summary + wassert(actual(s.supports(*query_from_string("yearmin=1945"))) == summary::Support::EXACT); + + // Still exact, because although the query partially matches the summary, + // each summary entry is entier included completely or excluded completely + wassert(actual(s.supports(*query_from_string("yearmin=1945, monthmin=4, daymin=25, hourmin=8, yearmax=1945, monthmax=4, daymax=25, hourmax=8, minumax=10"))) == summary::Support::EXACT); + }); + add_method("summary_stack", [](Fixture& f) { + // Test summary::Stack + using namespace summary; + + wassert(f.populate()); + + core::Query query; + query.query = "details"; + Summary s(query); + wassert(actual(s.is_valid()).isfalse()); + + Stack stack; + + // Build the whole db summary + Summary& general = stack.push(core::Query()); + auto cur = f.db->query_summary(query); + while (cur->next()) + general.add_summary(*cur, true); + + wassert(actual(stack.size()) == 1); + wassert(actual(stack.top().data_count()) == 4); + + // Query the stack + query.clear(); query.rep_memo = "synop"; + Support res = stack.query(query, true, [](const Entry& e) { return e.rep_memo == "synop"; }); + wassert(actual(res) == EXACT); + + wassert(actual(stack.size()) == 2); + wassert(actual(stack.top().data_count()) == 2); + + // Query further + query.clear(); query.rep_memo = "synop"; query.varcodes.insert(WR_VAR(0, 1, 11)); + res = stack.query(query, true, [](const Entry& e) { return e.rep_memo == "synop" && e.varcode == WR_VAR(0, 1, 11); }); + wassert(actual(res) == EXACT); + + wassert(actual(stack.size()) == 2); + wassert(actual(stack.top().data_count()) == 1); + + // Query the same var but a different rep_memo + query.clear(); query.rep_memo = "metar"; query.varcodes.insert(WR_VAR(0, 1, 11)); + res = stack.query(query, true, [](const Entry& e) { return e.rep_memo == "metar" && e.varcode == WR_VAR(0, 1, 11); }); + wassert(actual(res) == EXACT); + + wassert(actual(stack.size()) == 2); + wassert(actual(stack.top().data_count()) == 1); + }); + } +}; + +Tests tg1("db_summary_mem", nullptr, db::MEM); +Tests tg2("db_summary_v6_sqlite", "SQLITE", db::V6); +#ifdef HAVE_ODBC +Tests tg4("db_summary_v6_odbc", "ODBC", db::V6); +#endif +#ifdef HAVE_LIBPQ +Tests tg6("db_summary_v6_postgresql", "POSTGRESQL", db::V6); +#endif +#ifdef HAVE_MYSQL +Tests tg8("db_summary_v6_mysql", "MYSQL", db::V6); +#endif + +} diff --git a/dballe/db/summary-tut.cc b/dballe/db/summary-tut.cc deleted file mode 100644 index eb5a8d308..000000000 --- a/dballe/db/summary-tut.cc +++ /dev/null @@ -1,127 +0,0 @@ -#include "db/tests.h" -#include "summary.h" -#include "config.h" - -using namespace dballe; -using namespace dballe::db; -using namespace dballe::tests; -using namespace wreport; -using namespace wibble::tests; -using namespace std; - -namespace { - -typedef dballe::tests::DBFixture Fixture; -typedef dballe::tests::db_test_group test_group; -typedef test_group::Test Test; - -std::vector tests { - Test("summary", [](Fixture& f) { - // Test building a summary and checking if it supports queries - wruntest(f.populate); - - core::Query query; - query.query = "details"; - Summary s(query); - wassert(actual(s.is_valid()).isfalse()); - - // Build the whole db summary - auto cur = f.db->query_summary(query); - while (cur->next()) - s.add_summary(*cur, true); - - // Check its contents - wassert(actual(s.is_valid()).istrue()); - if (f.db->format() == V6) - wassert(actual(s.all_stations.size()) == 1); - else - wassert(actual(s.all_stations.size()) == 2); - wassert(actual(s.all_levels.size()) == 1); - wassert(actual(s.all_tranges.size()) == 2); - wassert(actual(s.all_varcodes.size()) == 2); - wassert(actual(s.datetime_min()) == Datetime(1945, 4, 25, 8)); - wassert(actual(s.datetime_max()) == Datetime(1945, 4, 25, 8, 30)); - wassert(actual(s.data_count()) == 4); - - // Check what it can support - - // An existing station is ok: we know we have it - wassert(actual(s.supports(*query_from_string("ana_id=1"))) == summary::Support::EXACT); - - // A non-existing station is also ok: we know we don't have it - wassert(actual(s.supports(*query_from_string("ana_id=2"))) == summary::Support::EXACT); - - wassert(actual(s.supports(*query_from_string("ana_id=1, leveltype1=10"))) == summary::Support::EXACT); - - wassert(actual(s.supports(*query_from_string("ana_id=1, leveltype1=10, pindicator=20"))) == summary::Support::EXACT); - - wassert(actual(s.supports(*query_from_string("ana_id=1, leveltype1=10, pindicator=20"))) == summary::Support::EXACT); - - // Still exact, because the query matches the entire summary - wassert(actual(s.supports(*query_from_string("yearmin=1945"))) == summary::Support::EXACT); - - // Still exact, because although the query partially matches the summary, - // each summary entry is entier included completely or excluded completely - wassert(actual(s.supports(*query_from_string("yearmin=1945, monthmin=4, daymin=25, hourmin=8, yearmax=1945, monthmax=4, daymax=25, hourmax=8, minumax=10"))) == summary::Support::EXACT); - }), - Test("summary_stack", [](Fixture& f) { - // Test summary::Stack - using namespace summary; - - wruntest(f.populate); - - core::Query query; - query.query = "details"; - Summary s(query); - wassert(actual(s.is_valid()).isfalse()); - - Stack stack; - - // Build the whole db summary - Summary& general = stack.push(core::Query()); - auto cur = f.db->query_summary(query); - while (cur->next()) - general.add_summary(*cur, true); - - wassert(actual(stack.size()) == 1); - wassert(actual(stack.top().data_count()) == 4); - - // Query the stack - query.clear(); query.rep_memo = "synop"; - Support res = stack.query(query, true, [](const Entry& e) { return e.rep_memo == "synop"; }); - wassert(actual(res) == EXACT); - - wassert(actual(stack.size()) == 2); - wassert(actual(stack.top().data_count()) == 2); - - // Query further - query.clear(); query.rep_memo = "synop"; query.varcodes.insert(WR_VAR(0, 1, 11)); - res = stack.query(query, true, [](const Entry& e) { return e.rep_memo == "synop" && e.varcode == WR_VAR(0, 1, 11); }); - wassert(actual(res) == EXACT); - - wassert(actual(stack.size()) == 2); - wassert(actual(stack.top().data_count()) == 1); - - // Query the same var but a different rep_memo - query.clear(); query.rep_memo = "metar"; query.varcodes.insert(WR_VAR(0, 1, 11)); - res = stack.query(query, true, [](const Entry& e) { return e.rep_memo == "metar" && e.varcode == WR_VAR(0, 1, 11); }); - wassert(actual(res) == EXACT); - - wassert(actual(stack.size()) == 2); - wassert(actual(stack.top().data_count()) == 1); - }), -}; - -test_group tg1("db_summary_mem", nullptr, db::MEM, tests); -test_group tg2("db_summary_v6_sqlite", "SQLITE", db::V6, tests); -#ifdef HAVE_ODBC -test_group tg4("db_summary_v6_odbc", "ODBC", db::V6, tests); -#endif -#ifdef HAVE_LIBPQ -test_group tg6("db_summary_v6_postgresql", "POSTGRESQL", db::V6, tests); -#endif -#ifdef HAVE_MYSQL -test_group tg8("db_summary_v6_mysql", "MYSQL", db::V6, tests); -#endif - -} diff --git a/dballe/db/tests.cc b/dballe/db/tests.cc index cb3c657a6..567202394 100644 --- a/dballe/db/tests.cc +++ b/dballe/db/tests.cc @@ -1,21 +1,17 @@ -#include "config.h" #include "tests.h" -#include "dballe/db/v6/db.h" -#include "dballe/db/sql.h" -#include "dballe/db/sql/driver.h" +#include "v6/db.h" +#include "sql.h" +#include "sql/driver.h" #include "dballe/msg/vars.h" #include -#include #include - #include #include #include +#include "config.h" using namespace wreport; using namespace std; -using namespace wibble; -using namespace wibble::tests; namespace dballe { namespace tests { @@ -139,65 +135,65 @@ void TestRecord::set_var(const char* msgvarname, double val, int conf) } #endif -void TestCursorStationKeys::check(WIBBLE_TEST_LOCPRM) const +void ActualCursor::station_keys_match(const Station& expected) { - wassert(actual(cur.get_lat()) == expected.coords.dlat()); - wassert(actual(cur.get_lon()) == expected.coords.dlon()); + wassert(actual(_actual.get_lat()) == expected.coords.dlat()); + wassert(actual(_actual.get_lon()) == expected.coords.dlon()); if (expected.ident.is_missing()) - wassert(actual(cur.get_ident() == nullptr).istrue()); + wassert(actual(_actual.get_ident() == nullptr).istrue()); else - wassert(actual(cur.get_ident()) == expected.ident); + wassert(actual(_actual.get_ident()) == (const char*)expected.ident); #warning Restore report checks once all DBs return rep_memo for station queries //wassert(actual(cur.get_rep_memo()) == expected.report); } -void TestCursorStationVars::check(WIBBLE_TEST_LOCPRM) const +void ActualCursor::station_vars_match(const StationValues& expected) { - wassert(actual(cur).station_keys_match(expected.info)); + wassert(actual(_actual).station_keys_match(expected.info)); auto rec = Record::create(); - cur.to_record(*rec); + _actual.to_record(*rec); wassert(actual(*rec).vars_equal(expected.values)); } -void TestCursorDataContext::check(WIBBLE_TEST_LOCPRM) const +void ActualCursor::data_context_matches(const DataValues& expected) { - db::CursorData* c = dynamic_cast(&cur); - if (!c) wibble_test_location.fail_test("cursor is not an instance of CursorData"); + db::CursorData* c = dynamic_cast(&_actual); + if (!c) throw TestFailed("cursor is not an instance of CursorData"); - wassert(actual(cur).station_keys_match(ds.info)); - wassert(actual(c->get_rep_memo()) == ds.info.report); - wassert(actual(c->get_level()) == ds.info.level); - wassert(actual(c->get_trange()) == ds.info.trange); - wassert(actual(c->get_datetime()) == ds.info.datetime); + wassert(actual(_actual).station_keys_match(expected.info)); + wassert(actual(c->get_rep_memo()) == expected.info.report); + wassert(actual(c->get_level()) == expected.info.level); + wassert(actual(c->get_trange()) == expected.info.trange); + wassert(actual(c->get_datetime()) == expected.info.datetime); auto rec = Record::create(); - cur.to_record(*rec); - wassert(actual(rec->enq("rep_memo", "")) == ds.info.report); - wassert(actual(rec->enq("leveltype1", MISSING_INT)) == ds.info.level.ltype1); - wassert(actual(rec->enq("l1", MISSING_INT)) == ds.info.level.l1); - wassert(actual(rec->enq("leveltype2", MISSING_INT)) == ds.info.level.ltype2); - wassert(actual(rec->enq("l2", MISSING_INT)) == ds.info.level.l2); - wassert(actual(rec->enq("pindicator", MISSING_INT)) == ds.info.trange.pind); - wassert(actual(rec->enq("p1", MISSING_INT)) == ds.info.trange.p1); - wassert(actual(rec->enq("p2", MISSING_INT)) == ds.info.trange.p2); - wassert(actual(rec->enq("year", MISSING_INT)) == ds.info.datetime.year); - wassert(actual(rec->enq("month", MISSING_INT)) == ds.info.datetime.month); - wassert(actual(rec->enq("day", MISSING_INT)) == ds.info.datetime.day); - wassert(actual(rec->enq("hour", MISSING_INT)) == ds.info.datetime.hour); - wassert(actual(rec->enq("min", MISSING_INT)) == ds.info.datetime.minute); - wassert(actual(rec->enq("sec", MISSING_INT)) == ds.info.datetime.second); + _actual.to_record(*rec); + wassert(actual(rec->enq("rep_memo", "")) == expected.info.report); + wassert(actual(rec->enq("leveltype1", MISSING_INT)) == expected.info.level.ltype1); + wassert(actual(rec->enq("l1", MISSING_INT)) == expected.info.level.l1); + wassert(actual(rec->enq("leveltype2", MISSING_INT)) == expected.info.level.ltype2); + wassert(actual(rec->enq("l2", MISSING_INT)) == expected.info.level.l2); + wassert(actual(rec->enq("pindicator", MISSING_INT)) == expected.info.trange.pind); + wassert(actual(rec->enq("p1", MISSING_INT)) == expected.info.trange.p1); + wassert(actual(rec->enq("p2", MISSING_INT)) == expected.info.trange.p2); + wassert(actual(rec->enq("year", MISSING_INT)) == expected.info.datetime.year); + wassert(actual(rec->enq("month", MISSING_INT)) == expected.info.datetime.month); + wassert(actual(rec->enq("day", MISSING_INT)) == expected.info.datetime.day); + wassert(actual(rec->enq("hour", MISSING_INT)) == expected.info.datetime.hour); + wassert(actual(rec->enq("min", MISSING_INT)) == expected.info.datetime.minute); + wassert(actual(rec->enq("sec", MISSING_INT)) == expected.info.datetime.second); } -void TestCursorDataVar::check(WIBBLE_TEST_LOCPRM) const +void ActualCursor::data_var_matches(const wreport::Var& expected) { - db::CursorValue* c = dynamic_cast(&cur); - if (!c) wibble_test_location.fail_test("cursor is not an instance of CursorValue"); + db::CursorValue* c = dynamic_cast(&_actual); + if (!c) throw TestFailed("cursor is not an instance of CursorValue"); wassert(actual(c->get_varcode()) == expected.code()); wassert(actual(c->get_var()) == expected); auto rec = Record::create(); - wrunchecked(cur.to_record(*rec)); + wassert(_actual.to_record(*rec)); const Var* actvar = nullptr; for (const auto& i: core::Record::downcast(*rec).vars()) if (i->code() == expected.code()) @@ -206,21 +202,23 @@ void TestCursorDataVar::check(WIBBLE_TEST_LOCPRM) const wassert(actual(actvar->value_equals(expected)).istrue()); } -void TestCursorDataMatch::check(WIBBLE_TEST_LOCPRM) const +void ActualCursor::data_matches(const DataValues& ds, wreport::Varcode code) { - wassert(actual(cur).data_context_matches(ds)); - wassert(actual(cur).data_var_matches(ds, code)); + wassert(actual(_actual).data_context_matches(ds)); + wassert(actual(_actual).data_var_matches(ds, code)); } -TestDBTryDataQuery::TestDBTryDataQuery(DB& db, const std::string& query, unsigned expected) - : db(db), expected(expected) +void ActualDB::try_data_query(const std::string& query, unsigned expected) { - this->query.set_from_test_string(query); + core::Query q; + q.set_from_test_string(query); + try_data_query(q, expected); } -void TestDBTryDataQuery::check(WIBBLE_TEST_LOCPRM) const + +void ActualDB::try_data_query(const Query& query, unsigned expected) { // Run the query - unique_ptr cur = db.query_data(query); + unique_ptr cur = _actual.query_data(query); // Check the number of results wassert(actual(cur->remaining()) == expected); @@ -228,10 +226,10 @@ void TestDBTryDataQuery::check(WIBBLE_TEST_LOCPRM) const wassert(actual(count) == expected); } -void TestDBTryStationQuery::check(WIBBLE_TEST_LOCPRM) const +void ActualDB::try_station_query(const std::string& query, unsigned expected) { // Run the query - unique_ptr cur = db.query_stations(core_query_from_string(query)); + unique_ptr cur = _actual.query_stations(core_query_from_string(query)); // Check the number of results wassert(actual(cur->remaining()) == expected); @@ -239,10 +237,10 @@ void TestDBTryStationQuery::check(WIBBLE_TEST_LOCPRM) const wassert(actual(count) == expected); } -void TestDBTrySummaryQuery::check(WIBBLE_TEST_LOCPRM) const +void ActualDB::try_summary_query(const std::string& query, unsigned expected, result_checker check_results) { // Run the query - unique_ptr cur = db.query_summary(core_query_from_string(query)); + unique_ptr cur = _actual.query_summary(core_query_from_string(query)); // Check the number of results // query_summary counts results in advance only optionally @@ -275,14 +273,14 @@ void TestDBTrySummaryQuery::check(WIBBLE_TEST_LOCPRM) const return false; }); - wruntest(check_results, results); + wassert(check_results(results)); } } -std::unique_ptr get_test_connection(const char* backend) +std::unique_ptr get_test_connection(const std::string& backend) { std::string envname = "DBA_DB"; - if (backend) + if (!backend.empty()) { envname = "DBA_DB_"; envname += backend; @@ -293,12 +291,10 @@ std::unique_ptr get_test_connection(const char* backend) return db::Connection::create_from_url(envurl); } -const char* DriverFixture::backend = nullptr; -db::Format DriverFixture::format = db::V6; - -DriverFixture::DriverFixture() +DriverFixture::DriverFixture(const char* backend, db::Format format) + : backend(backend ? backend : ""), format(format) { - conn = get_test_connection(backend).release(); + conn = get_test_connection(this->backend).release(); driver = db::sql::Driver::create(*conn).release(); driver->delete_tables(format); driver->create_tables(format); @@ -312,15 +308,14 @@ DriverFixture::~DriverFixture() delete conn; } -void DriverFixture::reset() +void DriverFixture::test_setup() { + Fixture::test_setup(); driver->remove_all(format); } -const char* DBFixture::backend = nullptr; -db::Format DBFixture::format = db::V6; - -DBFixture::DBFixture() +DBFixture::DBFixture(const char* backend, db::Format format) + : backend(backend ? backend : ""), format(format) { db = create_db().release(); db->reset(); @@ -333,13 +328,6 @@ DBFixture::~DBFixture() delete db; } -void DBFixture::reset() -{ - db->remove_all(); - int added, deleted, updated; - db->update_repinfo(nullptr, &added, &deleted, &updated); -} - std::unique_ptr DBFixture::create_db() { if (format == db::MEM) @@ -352,30 +340,38 @@ std::unique_ptr DBFixture::create_db() } } +void DBFixture::test_setup() +{ + Fixture::test_setup(); + db->remove_all(); + int added, deleted, updated; + db->update_repinfo(nullptr, &added, &deleted, &updated); +} -void DBFixture::populate_database(WIBBLE_TEST_LOCPRM, TestFixture& fixture) +void DBFixture::populate_database(TestDataSet& data_set) { - wruntest(fixture.populate_db, *db); + wassert(data_set.populate_db(*db)); } -void TestFixture::populate_db(WIBBLE_TEST_LOCPRM, DB& db) +void TestDataSet::populate_db(DB& db) { - for (auto& vals: stations) - wrunchecked(db.insert_station_data(vals.second, true, true)); - for (auto& vals: data) - wrunchecked(db.insert_data(vals.second, true, true)); + for (auto& d: stations) + wassert(db.insert_station_data(d.second, true, true)); + for (auto& d: data) + wassert(db.insert_data(d.second, true, true)); } -OldDballeTestFixture::OldDballeTestFixture() +OldDballeTestDataSet::OldDballeTestDataSet() { - stations["synop"].info.coords = Coords(12.34560, 76.54320); stations["synop"].info.report = "synop"; + stations["synop"].info.coords = Coords(12.34560, 76.54320); stations["synop"].values.set("B07030", 42); // Height stations["synop"].values.set("B07031", 234); // Heightbaro stations["synop"].values.set("B01001", 1); // Block stations["synop"].values.set("B01002", 52); // Station stations["synop"].values.set("B01019", "Cippo Lippo"); // Name + stations["metar"] = stations["synop"]; stations["metar"].info.report = "metar"; diff --git a/dballe/db/tests.h b/dballe/db/tests.h index 18c9bc89d..50cef621f 100644 --- a/dballe/db/tests.h +++ b/dballe/db/tests.h @@ -30,6 +30,7 @@ struct OverrideTestDBFormat ~OverrideTestDBFormat(); }; +#if 0 template struct db_tg : public tut::test_group { @@ -53,6 +54,7 @@ struct db_tg : public tut::test_group return tut::test_group::run_test(n); } }; +#endif #if 0 /// Fixture data about a station @@ -104,143 +106,44 @@ struct TestRecord }; #endif -/// Base for all database initialization data fixtures -struct TestFixture +/// Base for datasets used to populate test databases +struct TestDataSet { + /// Arbitrarily named station values std::map stations; + /// Arbitrarily named data values std::map data; - TestFixture() {} - virtual ~TestFixture() {} - - virtual void populate_db(WIBBLE_TEST_LOCPRM, DB& db); + TestDataSet() {} + virtual ~TestDataSet() {} -private: - TestFixture(const TestFixture&); - TestFixture& operator=(const TestFixture&); + virtual void populate_db(DB& db); }; /// Test fixture used by old DB-All.e db tests -struct OldDballeTestFixture : public TestFixture -{ - OldDballeTestFixture(); -}; - -/// Check cursor context after a query_stations -struct TestCursorStationKeys -{ - db::Cursor& cur; - Station expected; - - TestCursorStationKeys(db::Cursor& cur, const Station& expected) : cur(cur), expected(expected) {} - - void check(WIBBLE_TEST_LOCPRM) const; -}; - -/// Check cursor context after a query_stations -struct TestCursorStationVars -{ - db::Cursor& cur; - StationValues expected; - - TestCursorStationVars(db::Cursor& cur, const StationValues& expected) : cur(cur), expected(expected) {} - - void check(WIBBLE_TEST_LOCPRM) const; -}; - -/// Check cursor data context after a query_data -struct TestCursorDataContext -{ - db::Cursor& cur; - const DataValues& ds; - - TestCursorDataContext(db::Cursor& cur, const DataValues& ds) : cur(cur), ds(ds) {} - - void check(WIBBLE_TEST_LOCPRM) const; -}; - -/// Check cursor data variable after a query_data -struct TestCursorDataVar -{ - db::Cursor& cur; - wreport::Var expected; - - TestCursorDataVar(db::Cursor& cur, const StationValues& expected, wreport::Varcode code) : cur(cur), expected(*expected.values[code].var) {} - TestCursorDataVar(db::Cursor& cur, const DataValues& expected, wreport::Varcode code) : cur(cur), expected(*expected.values[code].var) {} - TestCursorDataVar(db::Cursor& cur, const Values& expected, wreport::Varcode code) : cur(cur), expected(*expected[code].var) {} - TestCursorDataVar(db::Cursor& cur, const wreport::Var& expected) : cur(cur), expected(expected) {} - - void check(WIBBLE_TEST_LOCPRM) const; -}; - -/// Check cursor data context anda variable after a query_data -struct TestCursorDataMatch -{ - db::Cursor& cur; - const DataValues& ds; - wreport::Varcode code; - - TestCursorDataMatch(db::Cursor& cur, const DataValues& ds, wreport::Varcode code) : cur(cur), ds(ds), code(code) {} - - void check(WIBBLE_TEST_LOCPRM) const; -}; - -/// Check cursor data context anda variable after a query_data -struct TestDBTryDataQuery -{ - DB& db; - core::Query query; - unsigned expected; - - TestDBTryDataQuery(DB& db, const std::string& query, unsigned expected); - TestDBTryDataQuery(DB& db, const Query& query, unsigned expected) : db(db), query(core::Query::downcast(query)), expected(expected) {} - - void check(WIBBLE_TEST_LOCPRM) const; -}; - -/// Check cursor data context anda variable after a query_data -struct TestDBTryStationQuery +struct OldDballeTestDataSet : public TestDataSet { - DB& db; - std::string query; - unsigned expected; - - TestDBTryStationQuery(DB& db, const std::string& query, unsigned expected) : db(db), query(query), expected(expected) {} - - void check(WIBBLE_TEST_LOCPRM) const; + OldDballeTestDataSet(); }; -/// Check cursor data context anda variable after a query_data -struct TestDBTrySummaryQuery -{ - typedef std::function&)> result_checker; - DB& db; - std::string query; - unsigned expected; - result_checker check_results; - - TestDBTrySummaryQuery(DB& db, const std::string& query, unsigned expected, result_checker check_results=nullptr) - : db(db), query(query), expected(expected), check_results(check_results) {} - - void check(WIBBLE_TEST_LOCPRM) const; -}; - -std::unique_ptr get_test_connection(const char* backend); +std::unique_ptr get_test_connection(const std::string& backend); /// Test fixture for SQL backend drivers -struct DriverFixture +struct DriverFixture : public Fixture { - static const char* backend; - static db::Format format; + std::string backend; + db::Format format; db::Connection* conn = nullptr; db::sql::Driver* driver = nullptr; - DriverFixture(); + DriverFixture(const char* backend, db::Format format); ~DriverFixture(); - void reset(); + + void test_setup() override; }; +#if 0 template struct driver_test_group : public dballe::tests::test_group { @@ -259,31 +162,34 @@ struct driver_test_group : public dballe::tests::test_group return dballe::tests::test_group::create_fixture(); } }; +#endif - -struct DBFixture +struct DBFixture : public Fixture { - static const char* backend; - static db::Format format; + std::string backend; + db::Format format; DB* db = nullptr; - DBFixture(); + DBFixture(const char* backend, db::Format format); ~DBFixture(); - void reset(); + /// Open a new DB with the backend and format specified in this fixture std::unique_ptr create_db(); - template - void populate(WIBBLE_TEST_LOCPRM) + void test_setup() override; + + template + void populate() { - FIXTURE fixture; - wruntest(populate_database, fixture); + DataSet data_set; + wassert(populate_database(data_set)); } - void populate_database(WIBBLE_TEST_LOCPRM, TestFixture& fixture); + void populate_database(TestDataSet& data_set); }; +#if 0 template struct db_test_group : public dballe::tests::test_group { @@ -302,62 +208,86 @@ struct db_test_group : public dballe::tests::test_group return dballe::tests::test_group::create_fixture(); } }; +#endif - -struct ActualCursor : public wibble::tests::Actual +struct ActualCursor : public Actual { - ActualCursor(dballe::db::Cursor& actual) : wibble::tests::Actual(actual) {} - - TestCursorStationKeys station_keys_match(const Station& expected) { return TestCursorStationKeys(this->actual, expected); } - TestCursorStationVars station_vars_match(const StationValues& expected) { return TestCursorStationVars(this->actual, expected); } - TestCursorDataContext data_context_matches(const DataValues& expected) { return TestCursorDataContext(this->actual, expected); } - TestCursorDataVar data_var_matches(const DataValues& expected) { - if (auto c = dynamic_cast(&(this->actual))) - return TestCursorDataVar(this->actual, expected, c->get_varcode()); + using Actual::Actual; + + /// Check cursor context after a query_stations + void station_keys_match(const Station& expected); + + /// Check cursor context after a query_stations + void station_vars_match(const StationValues& expected); + + /// Check cursor data context after a query_data + void data_context_matches(const DataValues& expected); + + /// Check cursor data variable after a query_data + void data_var_matches(const StationValues& expected, wreport::Varcode code) { + data_var_matches(*expected.values[code].var); + } + /// Check cursor data variable after a query_data + void data_var_matches(const DataValues& expected, wreport::Varcode code) { + data_var_matches(*expected.values[code].var); + } + /// Check cursor data variable after a query_data + void data_var_matches(const DataValues& expected) { + if (auto c = dynamic_cast(&_actual)) + data_var_matches(*expected.values[c->get_varcode()].var); else throw wreport::error_consistency("cannot call data_var_matches on this kind of cursor"); } - TestCursorDataVar data_var_matches(const wreport::Var& expected) { return TestCursorDataVar(this->actual, expected); } - template - TestCursorDataVar data_var_matches(const T& expected, wreport::Varcode code) { return TestCursorDataVar(this->actual, expected, code); } - TestCursorDataMatch data_matches(const DataValues& ds) + /// Check cursor data variable after a query_data + void data_var_matches(const Values& expected, wreport::Varcode code) { + data_var_matches(*expected[code].var); + } + /// Check cursor data variable after a query_data + void data_var_matches(const wreport::Var& expected); + + /// Check cursor data context and variable after a query_data + void data_matches(const DataValues& ds) { - if (auto c = dynamic_cast(&(this->actual))) - return TestCursorDataMatch(this->actual, ds, c->get_varcode()); + if (auto c = dynamic_cast(&_actual)) + data_matches(ds, c->get_varcode()); else throw wreport::error_consistency("cannot call data_matches on this kind of cursor"); } - TestCursorDataMatch data_matches(const DataValues& ds, wreport::Varcode code) { return TestCursorDataMatch(this->actual, ds, code); } + /// Check cursor data context and variable after a query_data + void data_matches(const DataValues& ds, wreport::Varcode code); }; -struct ActualDB : public wibble::tests::Actual +typedef std::function&)> result_checker; + +struct ActualDB : public Actual { - ActualDB(dballe::DB& actual) : wibble::tests::Actual(actual) {} + using Actual::Actual; - TestDBTryDataQuery try_data_query(const std::string& query, unsigned expected) { return TestDBTryDataQuery(this->actual, query, expected); } - TestDBTryDataQuery try_data_query(const Query& query, unsigned expected) { return TestDBTryDataQuery(this->actual, query, expected); } - TestDBTryStationQuery try_station_query(const std::string& query, unsigned expected) { return TestDBTryStationQuery(this->actual, query, expected); } - TestDBTrySummaryQuery try_summary_query(const std::string& query, unsigned expected, TestDBTrySummaryQuery::result_checker checker=nullptr) { return TestDBTrySummaryQuery(this->actual, query, expected, checker); } -}; + /// Check cursor data context anda variable after a query_data + void try_data_query(const std::string& query, unsigned expected); -} -} + /// Check cursor data context anda variable after a query_data + void try_data_query(const Query& query, unsigned expected); -namespace wibble { -namespace tests { + /// Check results of a station query + void try_station_query(const std::string& query, unsigned expected); + + /// Check results of a summary query + void try_summary_query(const std::string& query, unsigned expected, result_checker checker=nullptr); +}; -inline dballe::tests::ActualCursor actual(dballe::db::Cursor& actual) { return dballe::tests::ActualCursor(actual); } -inline dballe::tests::ActualCursor actual(dballe::db::CursorStation& actual) { return dballe::tests::ActualCursor(actual); } -inline dballe::tests::ActualCursor actual(dballe::db::CursorStationData& actual) { return dballe::tests::ActualCursor(actual); } -inline dballe::tests::ActualCursor actual(dballe::db::CursorData& actual) { return dballe::tests::ActualCursor(actual); } -inline dballe::tests::ActualCursor actual(dballe::db::CursorSummary& actual) { return dballe::tests::ActualCursor(actual); } -inline dballe::tests::ActualCursor actual(std::unique_ptr& actual) { return dballe::tests::ActualCursor(*actual); } -inline dballe::tests::ActualCursor actual(std::unique_ptr& actual) { return dballe::tests::ActualCursor(*actual); } -inline dballe::tests::ActualCursor actual(std::unique_ptr& actual) { return dballe::tests::ActualCursor(*actual); } -inline dballe::tests::ActualCursor actual(std::unique_ptr& actual) { return dballe::tests::ActualCursor(*actual); } -inline dballe::tests::ActualCursor actual(std::unique_ptr& actual) { return dballe::tests::ActualCursor(*actual); } -inline dballe::tests::ActualDB actual(dballe::DB& actual) { return dballe::tests::ActualDB(actual); } -inline dballe::tests::ActualDB actual(std::unique_ptr& actual) { return dballe::tests::ActualDB(*actual); } +inline ActualCursor actual(dballe::db::Cursor& actual) { return ActualCursor(actual); } +inline ActualCursor actual(dballe::db::CursorStation& actual) { return ActualCursor(actual); } +inline ActualCursor actual(dballe::db::CursorStationData& actual) { return ActualCursor(actual); } +inline ActualCursor actual(dballe::db::CursorData& actual) { return ActualCursor(actual); } +inline ActualCursor actual(dballe::db::CursorSummary& actual) { return ActualCursor(actual); } +inline ActualCursor actual(std::unique_ptr& actual) { return ActualCursor(*actual); } +inline ActualCursor actual(std::unique_ptr& actual) { return ActualCursor(*actual); } +inline ActualCursor actual(std::unique_ptr& actual) { return ActualCursor(*actual); } +inline ActualCursor actual(std::unique_ptr& actual) { return ActualCursor(*actual); } +inline ActualCursor actual(std::unique_ptr& actual) { return ActualCursor(*actual); } +inline ActualDB actual(dballe::DB& actual) { return ActualDB(actual); } +inline ActualDB actual(std::unique_ptr& actual) { return ActualDB(*actual); } } } diff --git a/dballe/db/trace-test.cc b/dballe/db/trace-test.cc new file mode 100644 index 000000000..58e0ad1a2 --- /dev/null +++ b/dballe/db/trace-test.cc @@ -0,0 +1,176 @@ +#include "db/tests.h" + +using namespace std; +using namespace dballe; +using namespace dballe::tests; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { +#if 0 + // Test simple queries + Test("reset", [](Fixture& f) { + // Run twice to see if it is idempotent + auto& db = *f.db; + db.reset(); + db.reset(); + }), + Test("repinfo", [](Fixture& f) { + // Test repinfo-related functions + auto& db = *f.db; + std::map prios = db.get_repinfo_priorities(); + wassert(actual(prios.find("synop") != prios.end()).istrue()); + wassert(actual(prios["synop"]) == 101); + + int added, deleted, updated; + db.update_repinfo((string(getenv("DBA_TESTDATA")) + "/test-repinfo1.csv").c_str(), &added, &deleted, &updated); + + wassert(actual(added) == 3); + wassert(actual(deleted) == 11); + wassert(actual(updated) == 2); + + prios = db.get_repinfo_priorities(); + wassert(actual(prios.find("fixspnpo") != prios.end()).istrue()); + wassert(actual(prios["fixspnpo"]) == 200); + }), + Test("vacuum", [](Fixture& f) { + // Just invoke vacuum + auto& db = *f.db; + db.vacuum(); + }), + Test("simple", [](Fixture& f) { + // Test remove_all + auto& db = *f.db; + db.remove_all(); + Query query; + std::unique_ptr cur = db.query_data(query); + wassert(actual(cur->remaining()) == 0); + + // Check that it is idempotent + db.remove_all(); + cur = db.query_data(query); + wassert(actual(cur->remaining()) == 0); + + // Insert something + wruntest(f.populate); + + cur = db.query_data(query); + wassert(actual(cur->remaining()) == 4); + + db.remove_all(); + + cur = db.query_data(query); + wassert(actual(cur->remaining()) == 0); + }), + Test("stationdata", [](Fixture& f) { + // Test adding station data for different networks + auto& db = *f.db; + db.reset(); + + Record rec; + + rec.set(DBA_KEY_LAT, 12.077); + rec.set(DBA_KEY_LON, 44.600); + + // Insert two values in two networks + rec.set(Level(103, 2000)); + rec.set(Trange::instant()); + rec.set(Datetime(2014, 1, 1, 0, 0, 0)); + rec.set(DBA_KEY_REP_MEMO, "synop"); + rec.set(WR_VAR(0, 12, 101), 273.15); + db.insert(rec, true, true); + rec.set(DBA_KEY_REP_MEMO, "temp"); + rec.set(WR_VAR(0, 12, 101), 274.15); + db.insert(rec, true, true); + + // Insert station names in both networks + rec.set_ana_context(); + rec.set(DBA_KEY_REP_MEMO, "synop"); + rec.set(WR_VAR(0, 1, 19), "Camse"); + db.insert(rec, true, true); + rec.set(DBA_KEY_REP_MEMO, "temp"); + rec.set(WR_VAR(0, 1, 19), "Esmac"); + db.insert(rec, true, true); + + // Query back all the data + Query query; + auto cur = db.query_stations(query); + + // Check results + Record result; + if (dynamic_cast(f.db)) + { + // For mem databases, we get one record per (station, network) + // combination + wassert(actual(cur->next()).istrue()); + wassert(actual(cur->get_station_id()) == 1); + wassert(actual(cur->get_rep_memo()) == "temp"); + cur->to_record(result); + wassert(actual(result.get(WR_VAR(0, 1, 19)).value()) == "Esmac"); + + wassert(actual(cur->next()).istrue()); + wassert(actual(cur->get_station_id()) == 0); + wassert(actual(cur->get_rep_memo()) == "synop"); + cur->to_record(result); + wassert(actual(result.get(WR_VAR(0, 1, 19)).value()) == "Camse"); + } else { + // For normal databases, we only get one record, with the station + // values merged keeping values for the best networks + wassert(actual(cur->next()).istrue()); + wassert(actual(cur->get_station_id()) == 1); + cur->to_record(result); + wassert(actual(result.get(WR_VAR(0, 1, 19)).value()) == "Camse"); + + wassert(actual(cur->next()).isfalse()); + } + + query.clear(); + Msgs msgs; + msg::AcquireMessages amsg(msgs); + db.export_msgs(query, amsg); + wassert(actual(msgs.size()) == 2); + + //msgs.print(stderr); + + wassert(actual(msgs[0]->get_rep_memo_var()->enqc()) == "synop"); + wassert(actual(msgs[0]->get_st_name_var()->enqc()) == "Camse"); + wassert(actual(msgs[0]->get_temp_2m_var()->enqd()) == 273.15); + wassert(actual(msgs[1]->get_rep_memo_var()->enqc()) == "temp"); + wassert(actual(msgs[1]->get_st_name_var()->enqc()) == "Esmac"); + wassert(actual(msgs[1]->get_temp_2m_var()->enqd()) == 274.15); + }), + Test("query_ident", [](Fixture& f) { + // Try querying by ident + auto& db = *f.db; + + // Insert a mobile station + Record rec; + rec.set_from_string("rep_memo=synop"); + rec.set_from_string("lat=44.10"); + rec.set_from_string("lon=11.50"); + rec.set_from_string("ident=foo"); + rec.set(Level(1)); + rec.set(Trange::instant()); + rec.set(Datetime(2015, 4, 25, 12, 30, 45)); + rec.set(WR_VAR(0, 12, 101), 295.1); + db.insert(rec, true, true); + + wassert(actual(db).try_station_query("ident=foo", 1)); + wassert(actual(db).try_station_query("ident=bar", 0)); + wassert(actual(db).try_station_query("mobile=1", 1)); + wassert(actual(db).try_station_query("mobile=0", 0)); + wassert(actual(db).try_data_query("ident=foo", 1)); + wassert(actual(db).try_data_query("ident=bar", 0)); + wassert(actual(db).try_data_query("mobile=1", 1)); + wassert(actual(db).try_data_query("mobile=0", 0)); + }), +#endif + } +} test("db_trace"); + +} diff --git a/dballe/db/trace-tut.cc b/dballe/db/trace-tut.cc deleted file mode 100644 index 3ef2626d3..000000000 --- a/dballe/db/trace-tut.cc +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (C) 2015 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ -#include "db/tests.h" - -namespace { - -typedef dballe::tests::test_group<> test_group; -typedef test_group::Test Test; -typedef test_group::Fixture Fixture; - -std::vector tests { -#if 0 - // Test simple queries - Test("reset", [](Fixture& f) { - // Run twice to see if it is idempotent - auto& db = *f.db; - db.reset(); - db.reset(); - }), - Test("repinfo", [](Fixture& f) { - // Test repinfo-related functions - auto& db = *f.db; - std::map prios = db.get_repinfo_priorities(); - wassert(actual(prios.find("synop") != prios.end()).istrue()); - wassert(actual(prios["synop"]) == 101); - - int added, deleted, updated; - db.update_repinfo((string(getenv("DBA_TESTDATA")) + "/test-repinfo1.csv").c_str(), &added, &deleted, &updated); - - wassert(actual(added) == 3); - wassert(actual(deleted) == 11); - wassert(actual(updated) == 2); - - prios = db.get_repinfo_priorities(); - wassert(actual(prios.find("fixspnpo") != prios.end()).istrue()); - wassert(actual(prios["fixspnpo"]) == 200); - }), - Test("vacuum", [](Fixture& f) { - // Just invoke vacuum - auto& db = *f.db; - db.vacuum(); - }), - Test("simple", [](Fixture& f) { - // Test remove_all - auto& db = *f.db; - db.remove_all(); - Query query; - std::unique_ptr cur = db.query_data(query); - wassert(actual(cur->remaining()) == 0); - - // Check that it is idempotent - db.remove_all(); - cur = db.query_data(query); - wassert(actual(cur->remaining()) == 0); - - // Insert something - wruntest(f.populate); - - cur = db.query_data(query); - wassert(actual(cur->remaining()) == 4); - - db.remove_all(); - - cur = db.query_data(query); - wassert(actual(cur->remaining()) == 0); - }), - Test("stationdata", [](Fixture& f) { - // Test adding station data for different networks - auto& db = *f.db; - db.reset(); - - Record rec; - - rec.set(DBA_KEY_LAT, 12.077); - rec.set(DBA_KEY_LON, 44.600); - - // Insert two values in two networks - rec.set(Level(103, 2000)); - rec.set(Trange::instant()); - rec.set(Datetime(2014, 1, 1, 0, 0, 0)); - rec.set(DBA_KEY_REP_MEMO, "synop"); - rec.set(WR_VAR(0, 12, 101), 273.15); - db.insert(rec, true, true); - rec.set(DBA_KEY_REP_MEMO, "temp"); - rec.set(WR_VAR(0, 12, 101), 274.15); - db.insert(rec, true, true); - - // Insert station names in both networks - rec.set_ana_context(); - rec.set(DBA_KEY_REP_MEMO, "synop"); - rec.set(WR_VAR(0, 1, 19), "Camse"); - db.insert(rec, true, true); - rec.set(DBA_KEY_REP_MEMO, "temp"); - rec.set(WR_VAR(0, 1, 19), "Esmac"); - db.insert(rec, true, true); - - // Query back all the data - Query query; - auto cur = db.query_stations(query); - - // Check results - Record result; - if (dynamic_cast(f.db)) - { - // For mem databases, we get one record per (station, network) - // combination - wassert(actual(cur->next()).istrue()); - wassert(actual(cur->get_station_id()) == 1); - wassert(actual(cur->get_rep_memo()) == "temp"); - cur->to_record(result); - wassert(actual(result.get(WR_VAR(0, 1, 19)).value()) == "Esmac"); - - wassert(actual(cur->next()).istrue()); - wassert(actual(cur->get_station_id()) == 0); - wassert(actual(cur->get_rep_memo()) == "synop"); - cur->to_record(result); - wassert(actual(result.get(WR_VAR(0, 1, 19)).value()) == "Camse"); - } else { - // For normal databases, we only get one record, with the station - // values merged keeping values for the best networks - wassert(actual(cur->next()).istrue()); - wassert(actual(cur->get_station_id()) == 1); - cur->to_record(result); - wassert(actual(result.get(WR_VAR(0, 1, 19)).value()) == "Camse"); - - wassert(actual(cur->next()).isfalse()); - } - - query.clear(); - Msgs msgs; - msg::AcquireMessages amsg(msgs); - db.export_msgs(query, amsg); - wassert(actual(msgs.size()) == 2); - - //msgs.print(stderr); - - wassert(actual(msgs[0]->get_rep_memo_var()->enqc()) == "synop"); - wassert(actual(msgs[0]->get_st_name_var()->enqc()) == "Camse"); - wassert(actual(msgs[0]->get_temp_2m_var()->enqd()) == 273.15); - wassert(actual(msgs[1]->get_rep_memo_var()->enqc()) == "temp"); - wassert(actual(msgs[1]->get_st_name_var()->enqc()) == "Esmac"); - wassert(actual(msgs[1]->get_temp_2m_var()->enqd()) == 274.15); - }), - Test("query_ident", [](Fixture& f) { - // Try querying by ident - auto& db = *f.db; - - // Insert a mobile station - Record rec; - rec.set_from_string("rep_memo=synop"); - rec.set_from_string("lat=44.10"); - rec.set_from_string("lon=11.50"); - rec.set_from_string("ident=foo"); - rec.set(Level(1)); - rec.set(Trange::instant()); - rec.set(Datetime(2015, 4, 25, 12, 30, 45)); - rec.set(WR_VAR(0, 12, 101), 295.1); - db.insert(rec, true, true); - - wassert(actual(db).try_station_query("ident=foo", 1)); - wassert(actual(db).try_station_query("ident=bar", 0)); - wassert(actual(db).try_station_query("mobile=1", 1)); - wassert(actual(db).try_station_query("mobile=0", 0)); - wassert(actual(db).try_data_query("ident=foo", 1)); - wassert(actual(db).try_data_query("ident=bar", 0)); - wassert(actual(db).try_data_query("mobile=1", 1)); - wassert(actual(db).try_data_query("mobile=0", 0)); - }), -#endif -}; - -test_group tg("db_trace", tests); - -} diff --git a/dballe/file-test.cc b/dballe/file-test.cc new file mode 100644 index 000000000..68d20ede9 --- /dev/null +++ b/dballe/file-test.cc @@ -0,0 +1,67 @@ +#include "core/tests.h" +#include "types.h" + +using namespace std; +using namespace wreport::tests; +using namespace dballe; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("binarymessage", []() { + BinaryMessage bm(File::BUFR); + wassert(actual(bm.encoding) == File::BUFR); + wassert(actual(bm.pathname.empty()).istrue()); + wassert(actual(bm.offset) == -1); + wassert(actual(bm.index) == MISSING_INT); + wassert(actual(bm.data.size()) == 0); + wassert(actual(bm.data.empty()).istrue()); + }); + add_method("bufr", []() { + // BUFR Read test + auto file = File::create(File::BUFR, tests::datafile("bufr/bufr1"), "r"); + BinaryMessage msg = wcallchecked(file->read()); + wassert(actual(msg).istrue()); + wassert(actual(msg.data.size()) == 182u); + wassert(actual(msg.index) == 0); + wassert(actual(msg.offset) == 0); + }); + add_method("crex", []() { + // CREX Read test + auto file = File::create(File::CREX, tests::datafile("crex/test-synop0.crex"), "r"); + BinaryMessage msg = wcallchecked(file->read()); + wassert(actual(msg).istrue()); + wassert(actual(msg.data.size()) == 251u); + wassert(actual(msg.index) == 0); + wassert(actual(msg.offset) == 0); + }); + add_method("aof", []() { + // AOF Read test + auto file = File::create(File::AOF, tests::datafile("aof/obs1-11.0.aof"), "r"); + BinaryMessage msg = wcallchecked(file->read()); + wassert(actual(msg).istrue()); + wassert(actual(msg.data.size()) == 140u); + wassert(actual(msg.index) == 0); + wassert(actual(msg.offset) == 140); + }); + Test("parse_encoding", [](Fixture&f) { + // Parse encoding test + wassert(File::parse_encoding("BUFR") == File::BUFR); + wassert(File::parse_encoding("bufr") == File::BUFR); + wassert(File::parse_encoding("Bufr") == File::BUFR); + wassert(File::parse_encoding("CREX") == File::CREX); + wassert(File::parse_encoding("crex") == File::CREX); + wassert(File::parse_encoding("CreX") == File::CREX); + wassert(File::parse_encoding("AOF") == File::AOF); + wassert(File::parse_encoding("aof") == File::AOF); + wassert(File::parse_encoding("AoF") == File::AOF); + }), + } +} test("dballe_file"); + +} diff --git a/dballe/file-tut.cc b/dballe/file-tut.cc deleted file mode 100644 index e302bb5a1..000000000 --- a/dballe/file-tut.cc +++ /dev/null @@ -1,70 +0,0 @@ -#include "core/tests.h" -#include "types.h" - -using namespace std; -using namespace wibble::tests; -using namespace dballe; - -namespace { - -typedef dballe::tests::test_group<> test_group; -typedef test_group::Test Test; -typedef test_group::Fixture Fixture; - -std::vector tests { - Test("binarymessage", [](Fixture& f) { - BinaryMessage bm(File::BUFR); - wassert(actual(bm.encoding) == File::BUFR); - wassert(actual(bm.pathname.empty()).istrue()); - wassert(actual(bm.offset) == -1); - wassert(actual(bm.index) == MISSING_INT); - wassert(actual(bm.data.size()) == 0); - wassert(actual(bm.data.empty()).istrue()); - }), - Test("bufr", [](Fixture& f) { - // BUFR Read test - auto file = File::create(File::BUFR, tests::datafile("bufr/bufr1"), "r"); - BinaryMessage msg(file->encoding()); - wrunchecked(msg = file->read()); - wassert(actual(msg).istrue()); - wassert(actual(msg.data.size()) == 182u); - wassert(actual(msg.index) == 0); - wassert(actual(msg.offset) == 0); - }), - Test("crex", [](Fixture& f) { - // CREX Read test - auto file = File::create(File::CREX, tests::datafile("crex/test-synop0.crex"), "r"); - BinaryMessage msg(file->encoding()); - wrunchecked(msg = file->read()); - wassert(actual(msg).istrue()); - wassert(actual(msg.data.size()) == 251u); - wassert(actual(msg.index) == 0); - wassert(actual(msg.offset) == 0); - }), - Test("aof", [](Fixture& f) { - // AOF Read test - auto file = File::create(File::AOF, tests::datafile("aof/obs1-11.0.aof"), "r"); - BinaryMessage msg(file->encoding()); - wrunchecked(msg = file->read()); - wassert(actual(msg).istrue()); - wassert(actual(msg.data.size()) == 140u); - wassert(actual(msg.index) == 0); - wassert(actual(msg.offset) == 140); - }), - Test("parse_encoding", [](Fixture&f) { - // Parse encoding test - wassert(actual(File::parse_encoding("BUFR")) == File::BUFR); - wassert(actual(File::parse_encoding("bufr")) == File::BUFR); - wassert(actual(File::parse_encoding("Bufr")) == File::BUFR); - wassert(actual(File::parse_encoding("CREX")) == File::CREX); - wassert(actual(File::parse_encoding("crex")) == File::CREX); - wassert(actual(File::parse_encoding("CreX")) == File::CREX); - wassert(actual(File::parse_encoding("AOF")) == File::AOF); - wassert(actual(File::parse_encoding("aof")) == File::AOF); - wassert(actual(File::parse_encoding("AoF")) == File::AOF); - }), -}; - -test_group newtg("dballe_file", tests); - -} diff --git a/dballe/memdb/index-test.cc b/dballe/memdb/index-test.cc new file mode 100644 index 000000000..e7e440d66 --- /dev/null +++ b/dballe/memdb/index-test.cc @@ -0,0 +1,22 @@ +#include "memdb/tests.h" +#include "index.h" + +using namespace dballe; +using namespace dballe::memdb; +using namespace dballe::tests; +using namespace std; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("empty", []() { + }); + } +} test("memdb_index"); + +} diff --git a/dballe/memdb/index-tut.cc b/dballe/memdb/index-tut.cc deleted file mode 100644 index bf24ae8ed..000000000 --- a/dballe/memdb/index-tut.cc +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2013 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "memdb/tests.h" -#include "index.h" - -using namespace dballe; -using namespace dballe::memdb; -using namespace wibble::tests; -using namespace std; - -namespace tut { - -struct memdb_index_shar -{ -}; - -TESTGRP(memdb_index); - -template<> template<> void to::test<1>() -{ -} - -} - - diff --git a/dballe/memdb/levtr-test.cc b/dballe/memdb/levtr-test.cc new file mode 100644 index 000000000..285bc7970 --- /dev/null +++ b/dballe/memdb/levtr-test.cc @@ -0,0 +1,98 @@ +#include "memdb/tests.h" +#include "dballe/core/record.h" +#include "levtr.h" +#include "results.h" + +using namespace dballe; +using namespace dballe::tests; +using namespace dballe::memdb; +using namespace std; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("insert", []() { + LevTrs values; + + // Insert a levtr and check that all data is there + const LevTr& val = *values[values.obtain(Level(1), Trange::instant())]; + wassert(actual(val.level) == Level(1)); + wassert(actual(val.trange) == Trange::instant()); + + // Check that lookup returns the same element + const LevTr& val1 = *values[values.obtain(Level(1), Trange::instant())]; + wassert(actual(&val1) == &val); + + // Check again, looking up records + core::Record rec; + rec.set(Level(1)); + rec.set(Trange::instant()); + const LevTr& val2 = *values[values.obtain(rec)]; + wassert(actual(&val2) == &val); + }); + + add_method("query", []() { + LevTrs values; + values.obtain(Level(1), Trange::instant()); + values.obtain(Level::cloud(2, 3), Trange(1, 2, 3)); + + { + Results res(values); + values.query(core_query_from_string("leveltype1=1"), res); + vector items = get_results(res); + wassert(actual(items.size()) == 1); + wassert(actual(items[0]->level) == Level(1)); + } + + { + Results res(values); + values.query(core_query_from_string("leveltype2=2"), res); + vector items = get_results(res); + wassert(actual(items.size()) == 1); + wassert(actual(items[0]->level) == Level::cloud(2, 3)); + } + + { + Results res(values); + values.query(core_query_from_string("l2=3"), res); + vector items = get_results(res); + wassert(actual(items.size()) == 1); + wassert(actual(items[0]->level) == Level::cloud(2, 3)); + } + + { + Results res(values); + values.query(core_query_from_string("pindicator=1"), res); + vector items = get_results(res); + wassert(actual(items.size()) == 1); + wassert(actual(items[0]->level) == Level::cloud(2, 3)); + } + + { + Results res(values); + values.query(core_query_from_string("p1=2"), res); + vector items = get_results(res); + wassert(actual(items.size()) == 1); + wassert(actual(items[0]->level) == Level::cloud(2, 3)); + } + + { + Results res(values); + values.query(core_query_from_string("p2=3"), res); + vector items = get_results(res); + wassert(actual(items.size()) == 1); + wassert(actual(items[0]->level) == Level::cloud(2, 3)); + } + }); + } +} test("memdb_levtr"); + +} + +#include "results.tcc" + diff --git a/dballe/memdb/levtr-tut.cc b/dballe/memdb/levtr-tut.cc deleted file mode 100644 index 6c466c923..000000000 --- a/dballe/memdb/levtr-tut.cc +++ /dev/null @@ -1,98 +0,0 @@ -#include "memdb/tests.h" -#include "dballe/core/record.h" -#include "levtr.h" -#include "results.h" - -using namespace dballe; -using namespace dballe::tests; -using namespace dballe::memdb; -using namespace wibble::tests; -using namespace std; - -namespace tut { - -struct memdb_levtr_shar -{ -}; - -TESTGRP(memdb_levtr); - -template<> template<> void to::test<1>() -{ - LevTrs values; - - // Insert a levtr and check that all data is there - const LevTr& val = *values[values.obtain(Level(1), Trange::instant())]; - wassert(actual(val.level) == Level(1)); - wassert(actual(val.trange) == Trange::instant()); - - // Check that lookup returns the same element - const LevTr& val1 = *values[values.obtain(Level(1), Trange::instant())]; - wassert(actual(&val1) == &val); - - // Check again, looking up records - core::Record rec; - rec.set(Level(1)); - rec.set(Trange::instant()); - const LevTr& val2 = *values[values.obtain(rec)]; - wassert(actual(&val2) == &val); -} - -template<> template<> void to::test<2>() -{ - LevTrs values; - values.obtain(Level(1), Trange::instant()); - values.obtain(Level::cloud(2, 3), Trange(1, 2, 3)); - - { - Results res(values); - values.query(core_query_from_string("leveltype1=1"), res); - vector items = get_results(res); - wassert(actual(items.size()) == 1); - wassert(actual(items[0]->level) == Level(1)); - } - - { - Results res(values); - values.query(core_query_from_string("leveltype2=2"), res); - vector items = get_results(res); - wassert(actual(items.size()) == 1); - wassert(actual(items[0]->level) == Level::cloud(2, 3)); - } - - { - Results res(values); - values.query(core_query_from_string("l2=3"), res); - vector items = get_results(res); - wassert(actual(items.size()) == 1); - wassert(actual(items[0]->level) == Level::cloud(2, 3)); - } - - { - Results res(values); - values.query(core_query_from_string("pindicator=1"), res); - vector items = get_results(res); - wassert(actual(items.size()) == 1); - wassert(actual(items[0]->level) == Level::cloud(2, 3)); - } - - { - Results res(values); - values.query(core_query_from_string("p1=2"), res); - vector items = get_results(res); - wassert(actual(items.size()) == 1); - wassert(actual(items[0]->level) == Level::cloud(2, 3)); - } - - { - Results res(values); - values.query(core_query_from_string("p2=3"), res); - vector items = get_results(res); - wassert(actual(items.size()) == 1); - wassert(actual(items[0]->level) == Level::cloud(2, 3)); - } -} - -} - -#include "results.tcc" diff --git a/dballe/memdb/match-test.cc b/dballe/memdb/match-test.cc new file mode 100644 index 000000000..18c761c63 --- /dev/null +++ b/dballe/memdb/match-test.cc @@ -0,0 +1,22 @@ +#include "memdb/tests.h" +#include "match.h" + +using namespace dballe; +using namespace dballe::memdb; +using namespace dballe::tests; +using namespace std; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("empty", []() { + }); + } +} test("memdb_match"); + +} diff --git a/dballe/memdb/match-tut.cc b/dballe/memdb/match-tut.cc deleted file mode 100644 index 4770fc313..000000000 --- a/dballe/memdb/match-tut.cc +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2013 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "memdb/tests.h" -#include "match.h" - -using namespace dballe; -using namespace dballe::memdb; -using namespace wibble::tests; -using namespace std; - -namespace tut { - -struct memdb_match_shar -{ - memdb_match_shar() - { - } -}; - -TESTGRP(memdb_match); - -template<> template<> void to::test<1>() -{ -} - -} diff --git a/dballe/memdb/memdb-test.cc b/dballe/memdb/memdb-test.cc new file mode 100644 index 000000000..1d2bbf8b0 --- /dev/null +++ b/dballe/memdb/memdb-test.cc @@ -0,0 +1,24 @@ +#include "memdb/tests.h" +#include "memdb.h" + +using namespace dballe; +using namespace dballe::memdb; +using namespace dballe::tests; +using namespace std; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("empty", []() { + }); + } +} test("memdb"); + +} + +#include "results.tcc" diff --git a/dballe/memdb/memdb-tut.cc b/dballe/memdb/memdb-tut.cc deleted file mode 100644 index 8e9eca82d..000000000 --- a/dballe/memdb/memdb-tut.cc +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2013 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "memdb/tests.h" -#include "memdb.h" - -using namespace dballe; -using namespace dballe::memdb; -using namespace dballe::tests; -using namespace wibble::tests; -using namespace std; - -namespace tut { - -struct memdb_shar -{ -}; - -TESTGRP(memdb); - -template<> template<> void to::test<1>() -{ -} - -} - -#include "results.tcc" diff --git a/dballe/memdb/results-test.cc b/dballe/memdb/results-test.cc new file mode 100644 index 000000000..b47bc99a3 --- /dev/null +++ b/dballe/memdb/results-test.cc @@ -0,0 +1,71 @@ +#include "memdb/tests.h" +#include "results.h" +#include "station.h" + +using namespace std; +using namespace dballe; +using namespace dballe::tests; + +namespace { + +struct Fixture : public dballe::tests::Fixture +{ + memdb::Stations stations; + size_t pos[10]; + + Fixture() + { + // 10 stations in a line from latitude 40.0 to 50.0 + for (unsigned i = 0; i < 10; ++i) + pos[i] = stations.obtain_fixed(Coords(40.0 + i, 11.0), "synop"); + } +}; + +struct Tests : public FixtureTestCase +{ + using FixtureTestCase::FixtureTestCase; + + void register_tests() override + { + add_method("all", [](Fixture& f) { + // Test not performing any selection: all should be selected + memdb::Results res(f.stations); + + wassert(actual(res.is_select_all()).istrue()); + wassert(actual(res.is_empty()).isfalse()); + + vector items; + res.copy_valptrs_to(back_inserter(items)); + wassert(actual(items.size()) == 10); + }); + add_method("none", [](Fixture& f) { + // Test setting to no results + memdb::Results res(f.stations); + res.set_to_empty(); + + wassert(actual(res.is_select_all()).isfalse()); + wassert(actual(res.is_empty()).istrue()); + + vector items; + res.copy_valptrs_to(back_inserter(items)); + wassert(actual(items.size()) == 0); + }); + add_method("single", [](Fixture& f) { + // Test selecting a singleton + memdb::Results res(f.stations); + res.add_singleton(f.pos[0]); + + wassert(actual(res.is_select_all()).isfalse()); + wassert(actual(res.is_empty()).isfalse()); + + vector items; + res.copy_valptrs_to(back_inserter(items)); + wassert(actual(items.size()) == 1); + wassert(actual(items[0]->id) == f.pos[0]); + }); + } +} test("memdb_results"); + +} + +#include "results.tcc" diff --git a/dballe/memdb/results-tut.cc b/dballe/memdb/results-tut.cc deleted file mode 100644 index 904af2517..000000000 --- a/dballe/memdb/results-tut.cc +++ /dev/null @@ -1,70 +0,0 @@ -#include "memdb/tests.h" -#include "results.h" -#include "station.h" - -using namespace std; -using namespace wibble::tests; -using namespace dballe; - -namespace { - -struct Fixture : public dballe::tests::Fixture -{ - memdb::Stations stations; - size_t pos[10]; - - Fixture() - { - // 10 stations in a line from latitude 40.0 to 50.0 - for (unsigned i = 0; i < 10; ++i) - pos[i] = stations.obtain_fixed(Coords(40.0 + i, 11.0), "synop"); - } -}; - -typedef dballe::tests::test_group test_group; -typedef test_group::Test Test; - -std::vector tests { - Test("all", [](Fixture& f) { - // Test not performing any selection: all should be selected - memdb::Results res(f.stations); - - wassert(actual(res.is_select_all()).istrue()); - wassert(actual(res.is_empty()).isfalse()); - - vector items; - res.copy_valptrs_to(back_inserter(items)); - wassert(actual(items.size()) == 10); - }), - Test("none", [](Fixture& f) { - // Test setting to no results - memdb::Results res(f.stations); - res.set_to_empty(); - - wassert(actual(res.is_select_all()).isfalse()); - wassert(actual(res.is_empty()).istrue()); - - vector items; - res.copy_valptrs_to(back_inserter(items)); - wassert(actual(items.size()) == 0); - }), - Test("single", [](Fixture& f) { - // Test selecting a singleton - memdb::Results res(f.stations); - res.add_singleton(f.pos[0]); - - wassert(actual(res.is_select_all()).isfalse()); - wassert(actual(res.is_empty()).isfalse()); - - vector items; - res.copy_valptrs_to(back_inserter(items)); - wassert(actual(items.size()) == 1); - wassert(actual(items[0]->id) == f.pos[0]); - }), -}; - -test_group newtg("memdb_results", tests); - -} - -#include "results.tcc" diff --git a/dballe/memdb/serializer-test.cc b/dballe/memdb/serializer-test.cc new file mode 100644 index 000000000..07ed067c8 --- /dev/null +++ b/dballe/memdb/serializer-test.cc @@ -0,0 +1,158 @@ +#include "memdb/tests.h" +#include "memdb.h" +#include "serializer.h" +#include "core/defs.h" +#include "dballe/var.h" +#include + +using namespace dballe; +using namespace dballe::memdb; +using namespace dballe::tests; +using namespace wreport; +using namespace std; + +namespace { + +struct Fixture : public dballe::tests::Fixture +{ + string testdir = "serializer_test_dir"; + + void test_setup() override + { + if (sys::isdir(testdir)) + sys::rmtree(testdir); + } + +}; + +struct Tests : public FixtureTestCase +{ + using FixtureTestCase::FixtureTestCase; + + + void register_tests() override + { + // Test a simple serialize/deserialize round + add_method("simple", [](Fixture& f) { + Var var_st_1(varinfo(WR_VAR(0, 7, 7)), 100.0); + var_st_1.seta(newvar(WR_VAR(0, 33, 7), 30)); + Var var_st_2(varinfo(WR_VAR(0, 7, 7)), 5000.0); + var_st_2.seta(newvar(WR_VAR(0, 33, 7), 40)); + Var var_1(varinfo(WR_VAR(0, 12, 101)), 274.0); + var_1.seta(newvar(WR_VAR(0, 33, 7), 50)); + Var var_2(varinfo(WR_VAR(0, 12, 101)), 273.0); + var_2.seta(newvar(WR_VAR(0, 33, 7), 60)); + + // Create a memdb and write it out + { + Memdb memdb; + + memdb.insert(Coords(45, 11), string(), "synop", Level(1), Trange::instant(), Datetime(2013, 12, 15), var_1); + memdb.stationvalues.insert(*memdb.stations[0], var_st_1); + memdb.insert(Coords(45, 12), "LH1234", "airep", Level(1), Trange::instant(), Datetime(2013, 12, 16), var_2); + memdb.stationvalues.insert(*memdb.stations[1], var_st_2); + + serialize::CSVWriter serializer(f.testdir); + serializer.write(memdb); + serializer.commit(); + } + + // Read it back + { + Memdb memdb; + serialize::CSVReader reader(f.testdir, memdb); + reader.read(); + wassert(actual(memdb.stations.element_count()) == 2); + wassert(actual(memdb.stations[0]->coords) == Coords(45, 11)); + wassert(actual(memdb.stations[0]->mobile).isfalse()); + wassert(actual(memdb.stations[0]->ident) == ""); + wassert(actual(memdb.stations[0]->report) == "synop"); + wassert(actual(memdb.stations[1]->coords) == Coords(45, 12)); + wassert(actual(memdb.stations[1]->mobile).istrue()); + wassert(actual(memdb.stations[1]->ident) == "LH1234"); + wassert(actual(memdb.stations[1]->report) == "airep"); + + wassert(actual(memdb.stationvalues.element_count()) == 2); + wassert(actual(memdb.stationvalues[0]->station.id) == memdb.stations[0]->id); + wassert(actual(*(memdb.stationvalues[0]->var)) == var_st_1); + wassert(actual(memdb.stationvalues[1]->station.id) == memdb.stations[1]->id); + wassert(actual(*(memdb.stationvalues[1]->var)) == var_st_2); + + wassert(actual(memdb.levtrs.element_count()) == 1); + wassert(actual(memdb.levtrs[0]->level) == Level(1)); + wassert(actual(memdb.levtrs[0]->trange) == Trange::instant()); + + wassert(actual(memdb.values.element_count()) == 2); + wassert(actual(memdb.values[0]->station.id) == memdb.stations[0]->id); + wassert(actual(memdb.values[0]->levtr.level) == Level(1)); + wassert(actual(memdb.values[0]->levtr.trange) == Trange::instant()); + wassert(actual(memdb.values[0]->datetime) == Datetime(2013, 12, 15)); + wassert(actual(*(memdb.values[0]->var)) == var_1); + wassert(actual(memdb.values[1]->station.id) == memdb.stations[1]->id); + wassert(actual(memdb.values[1]->levtr.level) == Level(1)); + wassert(actual(memdb.values[1]->levtr.trange) == Trange::instant()); + wassert(actual(memdb.values[1]->datetime) == Datetime(2013, 12, 16)); + wassert(actual(*(memdb.values[1]->var)) == var_2); + } + }); + + // Test deserializing a nonexisting data dir + add_method("missing_dir", [](Fixture& f) { + Memdb memdb; + serialize::CSVReader reader(f.testdir, memdb); + reader.read(); + wassert(actual(memdb.stations.element_count()) == 0); + wassert(actual(memdb.stationvalues.element_count()) == 0); + wassert(actual(memdb.levtrs.element_count()) == 0); + wassert(actual(memdb.values.element_count()) == 0); + }); + + // Test nasty chars in values + add_method("nasty_chars", [](Fixture& f) { + const char* str_ident = "\"'\n,"; + const char* str_report = "\n\"',"; + Var var_st_1(varinfo(WR_VAR(0, 1, 19)), "'\"\n,"); + var_st_1.seta(newvar(WR_VAR(0, 33, 7), 30)); + Var var_1(varinfo(WR_VAR(0, 12, 101)), 274.0); + var_1.seta(newvar(WR_VAR(0, 1, 212), "'\"\n,")); + + { + Memdb memdb; + memdb.insert(Coords(45, 11), str_ident, str_report, Level(1), Trange::instant(), Datetime(2013, 12, 15), var_1); + memdb.stationvalues.insert(*memdb.stations[0], var_st_1); + + serialize::CSVWriter serializer(f.testdir); + serializer.write(memdb); + serializer.commit(); + } + + { + Memdb memdb; + serialize::CSVReader reader(f.testdir, memdb); + reader.read(); + wassert(actual(memdb.stations.element_count()) == 1); + wassert(actual(memdb.stations[0]->coords) == Coords(45, 11)); + wassert(actual(memdb.stations[0]->mobile).istrue()); + wassert(actual(memdb.stations[0]->ident) == str_ident); + wassert(actual(memdb.stations[0]->report) == str_report); + + wassert(actual(memdb.stationvalues.element_count()) == 1); + wassert(actual(memdb.stationvalues[0]->station.id) == memdb.stations[0]->id); + wassert(actual(*(memdb.stationvalues[0]->var)) == var_st_1); + + wassert(actual(memdb.levtrs.element_count()) == 1); + wassert(actual(memdb.levtrs[0]->level) == Level(1)); + wassert(actual(memdb.levtrs[0]->trange) == Trange::instant()); + + wassert(actual(memdb.values.element_count()) == 1); + wassert(actual(memdb.values[0]->station.id) == memdb.stations[0]->id); + wassert(actual(memdb.values[0]->levtr.level) == Level(1)); + wassert(actual(memdb.values[0]->levtr.trange) == Trange::instant()); + wassert(actual(memdb.values[0]->datetime) == Datetime(2013, 12, 15)); + wassert(actual(*(memdb.values[0]->var)) == var_1); + } + }); + } +} test("memdb_serialize"); + +} diff --git a/dballe/memdb/serializer-tut.cc b/dballe/memdb/serializer-tut.cc deleted file mode 100644 index 9d5d2b302..000000000 --- a/dballe/memdb/serializer-tut.cc +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (C) 2013--2014 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "memdb/tests.h" -#include "memdb.h" -#include "serializer.h" -#include "core/defs.h" -#include "dballe/var.h" -#include - -using namespace dballe; -using namespace dballe::memdb; -using namespace dballe::tests; -using namespace wibble::tests; -using namespace wibble; -using namespace wreport; -using namespace std; - -namespace tut { - -struct memdb_serialize_shar -{ - string testdir; - - memdb_serialize_shar() : testdir("serializer_test_dir") {} - - void reset_test_dir() - { - if (sys::fs::isdir(testdir)) - sys::fs::rmtree(testdir); - } -}; - -TESTGRP(memdb_serialize); - -// Test a simple serialize/deserialize round -template<> template<> void to::test<1>() -{ - reset_test_dir(); - - Var var_st_1(varinfo(WR_VAR(0, 7, 7)), 100.0); - var_st_1.seta(newvar(WR_VAR(0, 33, 7), 30)); - Var var_st_2(varinfo(WR_VAR(0, 7, 7)), 5000.0); - var_st_2.seta(newvar(WR_VAR(0, 33, 7), 40)); - Var var_1(varinfo(WR_VAR(0, 12, 101)), 274.0); - var_1.seta(newvar(WR_VAR(0, 33, 7), 50)); - Var var_2(varinfo(WR_VAR(0, 12, 101)), 273.0); - var_2.seta(newvar(WR_VAR(0, 33, 7), 60)); - - // Create a memdb and write it out - { - Memdb memdb; - - memdb.insert(Coords(45, 11), string(), "synop", Level(1), Trange::instant(), Datetime(2013, 12, 15), var_1); - memdb.stationvalues.insert(*memdb.stations[0], var_st_1); - memdb.insert(Coords(45, 12), "LH1234", "airep", Level(1), Trange::instant(), Datetime(2013, 12, 16), var_2); - memdb.stationvalues.insert(*memdb.stations[1], var_st_2); - - serialize::CSVWriter serializer(testdir); - serializer.write(memdb); - serializer.commit(); - } - - // Read it back - { - Memdb memdb; - serialize::CSVReader reader(testdir, memdb); - reader.read(); - wassert(actual(memdb.stations.element_count()) == 2); - wassert(actual(memdb.stations[0]->coords) == Coords(45, 11)); - wassert(actual(memdb.stations[0]->mobile).isfalse()); - wassert(actual(memdb.stations[0]->ident) == ""); - wassert(actual(memdb.stations[0]->report) == "synop"); - wassert(actual(memdb.stations[1]->coords) == Coords(45, 12)); - wassert(actual(memdb.stations[1]->mobile).istrue()); - wassert(actual(memdb.stations[1]->ident) == "LH1234"); - wassert(actual(memdb.stations[1]->report) == "airep"); - - wassert(actual(memdb.stationvalues.element_count()) == 2); - wassert(actual(memdb.stationvalues[0]->station.id) == memdb.stations[0]->id); - wassert(actual(*(memdb.stationvalues[0]->var)) == var_st_1); - wassert(actual(memdb.stationvalues[1]->station.id) == memdb.stations[1]->id); - wassert(actual(*(memdb.stationvalues[1]->var)) == var_st_2); - - wassert(actual(memdb.levtrs.element_count()) == 1); - wassert(actual(memdb.levtrs[0]->level) == Level(1)); - wassert(actual(memdb.levtrs[0]->trange) == Trange::instant()); - - wassert(actual(memdb.values.element_count()) == 2); - wassert(actual(memdb.values[0]->station.id) == memdb.stations[0]->id); - wassert(actual(memdb.values[0]->levtr.level) == Level(1)); - wassert(actual(memdb.values[0]->levtr.trange) == Trange::instant()); - wassert(actual(memdb.values[0]->datetime) == Datetime(2013, 12, 15)); - wassert(actual(*(memdb.values[0]->var)) == var_1); - wassert(actual(memdb.values[1]->station.id) == memdb.stations[1]->id); - wassert(actual(memdb.values[1]->levtr.level) == Level(1)); - wassert(actual(memdb.values[1]->levtr.trange) == Trange::instant()); - wassert(actual(memdb.values[1]->datetime) == Datetime(2013, 12, 16)); - wassert(actual(*(memdb.values[1]->var)) == var_2); - } -} - -// Test deserializing a nonexisting data dir -template<> template<> void to::test<2>() -{ - reset_test_dir(); - Memdb memdb; - serialize::CSVReader reader(testdir, memdb); - reader.read(); - wassert(actual(memdb.stations.element_count()) == 0); - wassert(actual(memdb.stationvalues.element_count()) == 0); - wassert(actual(memdb.levtrs.element_count()) == 0); - wassert(actual(memdb.values.element_count()) == 0); -} - -// Test nasty chars in values -template<> template<> void to::test<3>() -{ - reset_test_dir(); - - const char* str_ident = "\"'\n,"; - const char* str_report = "\n\"',"; - Var var_st_1(varinfo(WR_VAR(0, 1, 19)), "'\"\n,"); - var_st_1.seta(newvar(WR_VAR(0, 33, 7), 30)); - Var var_1(varinfo(WR_VAR(0, 12, 101)), 274.0); - var_1.seta(newvar(WR_VAR(0, 1, 212), "'\"\n,")); - - { - Memdb memdb; - memdb.insert(Coords(45, 11), str_ident, str_report, Level(1), Trange::instant(), Datetime(2013, 12, 15), var_1); - memdb.stationvalues.insert(*memdb.stations[0], var_st_1); - - serialize::CSVWriter serializer(testdir); - serializer.write(memdb); - serializer.commit(); - } - - { - Memdb memdb; - serialize::CSVReader reader(testdir, memdb); - reader.read(); - wassert(actual(memdb.stations.element_count()) == 1); - wassert(actual(memdb.stations[0]->coords) == Coords(45, 11)); - wassert(actual(memdb.stations[0]->mobile).istrue()); - wassert(actual(memdb.stations[0]->ident) == str_ident); - wassert(actual(memdb.stations[0]->report) == str_report); - - wassert(actual(memdb.stationvalues.element_count()) == 1); - wassert(actual(memdb.stationvalues[0]->station.id) == memdb.stations[0]->id); - wassert(actual(*(memdb.stationvalues[0]->var)) == var_st_1); - - wassert(actual(memdb.levtrs.element_count()) == 1); - wassert(actual(memdb.levtrs[0]->level) == Level(1)); - wassert(actual(memdb.levtrs[0]->trange) == Trange::instant()); - - wassert(actual(memdb.values.element_count()) == 1); - wassert(actual(memdb.values[0]->station.id) == memdb.stations[0]->id); - wassert(actual(memdb.values[0]->levtr.level) == Level(1)); - wassert(actual(memdb.values[0]->levtr.trange) == Trange::instant()); - wassert(actual(memdb.values[0]->datetime) == Datetime(2013, 12, 15)); - wassert(actual(*(memdb.values[0]->var)) == var_1); - } -} - -} - diff --git a/dballe/memdb/station-test.cc b/dballe/memdb/station-test.cc new file mode 100644 index 000000000..944dcb154 --- /dev/null +++ b/dballe/memdb/station-test.cc @@ -0,0 +1,138 @@ +#include "memdb/tests.h" +#include "dballe/core/query.h" +#include "dballe/core/record.h" +#include "station.h" +#include "results.h" + +using namespace dballe; +using namespace dballe::tests; +using namespace std; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("basic", []() { + memdb::Stations stations; + + // Insert a fixed station and check that all data is there + const memdb::Station& stf = *stations[stations.obtain_fixed(Coords(44.0, 11.0), "synop")]; + wassert(actual(stf.coords.dlat()) == 44.0); + wassert(actual(stf.coords.dlon()) == 11.0); + wassert(actual(stf.ident) == ""); + wassert(actual(stf.report) == "synop"); + + // Insert a mobile station and check that all data is there + const memdb::Station& stm = *stations[stations.obtain_mobile(Coords(44.0, 11.0), "LH1234", "airep")]; + wassert(actual(stm.coords.dlat()) == 44.0); + wassert(actual(stm.coords.dlon()) == 11.0); + wassert(actual(stm.ident) == "LH1234"); + wassert(actual(stm.report) == "airep"); + + // Check that lookup returns the same element + const memdb::Station& stf1 = *stations[stations.obtain_fixed(Coords(44.0, 11.0), "synop")]; + wassert(actual(&stf1) == &stf); + const memdb::Station& stm1 = *stations[stations.obtain_mobile(Coords(44.0, 11.0), "LH1234", "airep")]; + wassert(actual(&stm1) == &stm); + + // Check again, looking up records + core::Record sfrec; + sfrec.set("lat", 44.0); + sfrec.set("lon", 11.0); + sfrec.set("rep_memo", "synop"); + const memdb::Station& stf2 = *stations[stations.obtain(sfrec)]; + wassert(actual(&stf2) == &stf); + + core::Record smrec; + smrec.set("lat", 44.0); + smrec.set("lon", 11.0); + smrec.set("ident", "LH1234"); + smrec.set("rep_memo", "airep"); + const memdb::Station& stm2 = *stations[stations.obtain(smrec)]; + wassert(actual(&stm2) == &stm); + }); + add_method("query_ana_id", []() { + // Query by ana_id + memdb::Stations stations; + size_t pos = stations.obtain_fixed(Coords(44.0, 11.0), "synop"); + + core::Query query; + + { + query.ana_id = pos; + memdb::Results res(stations); + stations.query(query, res); + auto items = get_results(res); + wassert(actual(items.size()) == 1); + wassert(actual(items[0]->id) == pos); + } + + { + query.ana_id = 100; + memdb::Results res(stations); + stations.query(query, res); + wassert(actual(res.is_select_all()).isfalse()); + wassert(actual(res.is_empty()).istrue()); + } + + size_t pos1 = stations.obtain_fixed(Coords(45.0, 12.0), "synop"); + + { + query.ana_id = pos; + memdb::Results res(stations); + stations.query(query, res); + auto items = get_results(res); + wassert(actual(items.size()) == 1); + wassert(actual(items[0]->id) == pos); + } + }); + add_method("query_latlon", []() { + // Query by lat,lon + memdb::Stations stations; + size_t pos = stations.obtain_fixed(Coords(44.0, 11.0), "synop"); + stations.obtain_fixed(Coords(45.0, 12.0), "synop"); + + memdb::Results res(stations); + stations.query(tests::core_query_from_string("lat=44.0, lon=11.0"), res); + + auto items = get_results(res); + wassert(actual(items.size()) == 1); + wassert(actual(items[0]->id) == pos); + }); + add_method("query_all", []() { + // Query everything + memdb::Stations stations; + size_t pos1 = stations.obtain_fixed(Coords(44.0, 11.0), "synop"); + size_t pos2 = stations.obtain_fixed(Coords(45.0, 12.0), "synop"); + + memdb::Results res(stations); + stations.query(core::Query(), res); + + wassert(actual(res.is_select_all()).istrue()); + wassert(actual(res.is_empty()).isfalse()); + }); + add_method("query_multi_latitudes", []() { + // Query latitudes matching multiple index entries + memdb::Stations stations; + size_t pos1 = stations.obtain_fixed(Coords(44.0, 11.0), "synop"); + size_t pos2 = stations.obtain_fixed(Coords(45.0, 11.0), "synop"); + size_t pos3 = stations.obtain_fixed(Coords(46.0, 11.0), "synop"); + + memdb::Results res(stations); + stations.query(tests::core_query_from_string("latmin=45.0"), res); + + auto items = get_results(res); + wassert(actual(items.size()) == 2); + wassert(actual(items[0]->id) == pos2); + wassert(actual(items[1]->id) == pos3); + }); + } +} test("memdb_station"); + +} + +#include "results.tcc" diff --git a/dballe/memdb/station-tut.cc b/dballe/memdb/station-tut.cc deleted file mode 100644 index 061efbb61..000000000 --- a/dballe/memdb/station-tut.cc +++ /dev/null @@ -1,138 +0,0 @@ -#include "memdb/tests.h" -#include "dballe/core/query.h" -#include "dballe/core/record.h" -#include "station.h" -#include "results.h" - -using namespace dballe; -using namespace wibble::tests; -using namespace std; - -namespace { - -typedef dballe::tests::test_group<> test_group; -typedef test_group::Test Test; -typedef test_group::Fixture Fixture; - -std::vector tests { - Test("basic", [](Fixture& f) { - memdb::Stations stations; - - // Insert a fixed station and check that all data is there - const memdb::Station& stf = *stations[stations.obtain_fixed(Coords(44.0, 11.0), "synop")]; - wassert(actual(stf.coords.dlat()) == 44.0); - wassert(actual(stf.coords.dlon()) == 11.0); - wassert(actual(stf.ident) == ""); - wassert(actual(stf.report) == "synop"); - - // Insert a mobile station and check that all data is there - const memdb::Station& stm = *stations[stations.obtain_mobile(Coords(44.0, 11.0), "LH1234", "airep")]; - wassert(actual(stm.coords.dlat()) == 44.0); - wassert(actual(stm.coords.dlon()) == 11.0); - wassert(actual(stm.ident) == "LH1234"); - wassert(actual(stm.report) == "airep"); - - // Check that lookup returns the same element - const memdb::Station& stf1 = *stations[stations.obtain_fixed(Coords(44.0, 11.0), "synop")]; - wassert(actual(&stf1) == &stf); - const memdb::Station& stm1 = *stations[stations.obtain_mobile(Coords(44.0, 11.0), "LH1234", "airep")]; - wassert(actual(&stm1) == &stm); - - // Check again, looking up records - core::Record sfrec; - sfrec.set("lat", 44.0); - sfrec.set("lon", 11.0); - sfrec.set("rep_memo", "synop"); - const memdb::Station& stf2 = *stations[stations.obtain(sfrec)]; - wassert(actual(&stf2) == &stf); - - core::Record smrec; - smrec.set("lat", 44.0); - smrec.set("lon", 11.0); - smrec.set("ident", "LH1234"); - smrec.set("rep_memo", "airep"); - const memdb::Station& stm2 = *stations[stations.obtain(smrec)]; - wassert(actual(&stm2) == &stm); - }), - Test("query_ana_id", [](Fixture& f) { - // Query by ana_id - memdb::Stations stations; - size_t pos = stations.obtain_fixed(Coords(44.0, 11.0), "synop"); - - core::Query query; - - { - query.ana_id = pos; - memdb::Results res(stations); - stations.query(query, res); - auto items = get_results(res); - wassert(actual(items.size()) == 1); - wassert(actual(items[0]->id) == pos); - } - - { - query.ana_id = 100; - memdb::Results res(stations); - stations.query(query, res); - wassert(actual(res.is_select_all()).isfalse()); - wassert(actual(res.is_empty()).istrue()); - } - - size_t pos1 = stations.obtain_fixed(Coords(45.0, 12.0), "synop"); - - { - query.ana_id = pos; - memdb::Results res(stations); - stations.query(query, res); - auto items = get_results(res); - wassert(actual(items.size()) == 1); - wassert(actual(items[0]->id) == pos); - } - }), - Test("query_latlon", [](Fixture& f) { - // Query by lat,lon - memdb::Stations stations; - size_t pos = stations.obtain_fixed(Coords(44.0, 11.0), "synop"); - stations.obtain_fixed(Coords(45.0, 12.0), "synop"); - - memdb::Results res(stations); - stations.query(tests::core_query_from_string("lat=44.0, lon=11.0"), res); - - auto items = get_results(res); - wassert(actual(items.size()) == 1); - wassert(actual(items[0]->id) == pos); - }), - Test("query_all", [](Fixture& f) { - // Query everything - memdb::Stations stations; - size_t pos1 = stations.obtain_fixed(Coords(44.0, 11.0), "synop"); - size_t pos2 = stations.obtain_fixed(Coords(45.0, 12.0), "synop"); - - memdb::Results res(stations); - stations.query(core::Query(), res); - - wassert(actual(res.is_select_all()).istrue()); - wassert(actual(res.is_empty()).isfalse()); - }), - Test("query_multi_latitudes", [](Fixture& f) { - // Query latitudes matching multiple index entries - memdb::Stations stations; - size_t pos1 = stations.obtain_fixed(Coords(44.0, 11.0), "synop"); - size_t pos2 = stations.obtain_fixed(Coords(45.0, 11.0), "synop"); - size_t pos3 = stations.obtain_fixed(Coords(46.0, 11.0), "synop"); - - memdb::Results res(stations); - stations.query(tests::core_query_from_string("latmin=45.0"), res); - - auto items = get_results(res); - wassert(actual(items.size()) == 2); - wassert(actual(items[0]->id) == pos2); - wassert(actual(items[1]->id) == pos3); - }), -}; - -test_group newtg("memdb_station", tests); - -} - -#include "results.tcc" diff --git a/dballe/memdb/stationvalue-test.cc b/dballe/memdb/stationvalue-test.cc new file mode 100644 index 000000000..9cad841fe --- /dev/null +++ b/dballe/memdb/stationvalue-test.cc @@ -0,0 +1,39 @@ +#include "dballe/var.h" +#include "memdb/tests.h" +#include "stationvalue.h" +#include "station.h" + +using namespace dballe; +using namespace dballe::tests; +using namespace std; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("basic", []() { + memdb::Stations stations; + const memdb::Station& stf = *stations[stations.obtain_fixed(Coords(44.0, 11.0), "synop")]; + + // Insert a station value and check that all data is there + memdb::StationValues svalues; + const memdb::StationValue& sv = *svalues[svalues.insert(stf, newvar(WR_VAR(0, 12, 101), 28.5))]; + wassert(actual(&sv.station) == &stf); + wassert(actual(sv.var->code()) == WR_VAR(0, 12, 101)); + wassert(actual(sv.var->enqd()) == 28.5); + + // Replacing a value should reuse an existing one + const memdb::StationValue& sv1 = *svalues[svalues.insert(stf, newvar(WR_VAR(0, 12, 101), 29.5))]; + wassert(actual(&sv1) == &sv); + wassert(actual(&sv1.station) == &stf); + wassert(actual(sv1.var->code()) == WR_VAR(0, 12, 101)); + wassert(actual(sv1.var->enqd()) == 29.5); + }); + } +} test("memdb_stationvalue"); + +} diff --git a/dballe/memdb/stationvalue-tut.cc b/dballe/memdb/stationvalue-tut.cc deleted file mode 100644 index 9ad657892..000000000 --- a/dballe/memdb/stationvalue-tut.cc +++ /dev/null @@ -1,39 +0,0 @@ -#include "dballe/var.h" -#include "memdb/tests.h" -#include "stationvalue.h" -#include "station.h" - -using namespace dballe; -using namespace wibble::tests; -using namespace std; - -namespace { - -typedef dballe::tests::test_group<> test_group; -typedef test_group::Test Test; -typedef test_group::Fixture Fixture; - -std::vector tests { - Test("basic", [](Fixture& f) { - memdb::Stations stations; - const memdb::Station& stf = *stations[stations.obtain_fixed(Coords(44.0, 11.0), "synop")]; - - // Insert a station value and check that all data is there - memdb::StationValues svalues; - const memdb::StationValue& sv = *svalues[svalues.insert(stf, newvar(WR_VAR(0, 12, 101), 28.5))]; - wassert(actual(&sv.station) == &stf); - wassert(actual(sv.var->code()) == WR_VAR(0, 12, 101)); - wassert(actual(sv.var->enqd()) == 28.5); - - // Replacing a value should reuse an existing one - const memdb::StationValue& sv1 = *svalues[svalues.insert(stf, newvar(WR_VAR(0, 12, 101), 29.5))]; - wassert(actual(&sv1) == &sv); - wassert(actual(&sv1.station) == &stf); - wassert(actual(sv1.var->code()) == WR_VAR(0, 12, 101)); - wassert(actual(sv1.var->enqd()) == 29.5); - }), -}; - -test_group newtg("memdb_stationvalue", tests); - -} diff --git a/dballe/memdb/tests.cc b/dballe/memdb/tests.cc index c960dd7d1..f2da07143 100644 --- a/dballe/memdb/tests.cc +++ b/dballe/memdb/tests.cc @@ -1,46 +1,11 @@ -/* - * Copyright (C) 2013 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - #include "tests.h" #include "memdb.h" using namespace dballe::memdb; -using namespace wibble; using namespace std; namespace dballe { namespace tests { -/* -std::vector _get_data_results(WIBBLE_TEST_LOCPRM, const Memdb& memdb, const Record& query) -{ - using namespace wibble::tests; - - Results res(memdb.values); - memdb.query_data(query, res); - std::vector items; - res.copy_valptrs_to(std::back_inserter(items)); - return items; } -*/ - } -} - -//#include "query.tcc" diff --git a/dballe/memdb/tests.h b/dballe/memdb/tests.h index 63f02b0c8..91a42ed17 100644 --- a/dballe/memdb/tests.h +++ b/dballe/memdb/tests.h @@ -1,21 +1,3 @@ -/* - * Copyright (C) 2013 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ #ifndef DBA_MEMDB_TESTS_H #define DBA_MEMDB_TESTS_H @@ -34,10 +16,8 @@ struct Value; namespace tests { template -static inline std::vector _get_results(WIBBLE_TEST_LOCPRM, memdb::Results& res) +static inline std::vector get_results(memdb::Results& res) { - using namespace wibble::tests; - wassert(actual(res.is_select_all()).isfalse()); wassert(actual(res.is_empty()).isfalse()); std::vector items; @@ -45,13 +25,6 @@ static inline std::vector _get_results(WIBBLE_TEST_LOCPRM, memdb::Resu return items; } -#define get_results(res) dballe::tests::_get_results(wibble_test_location.nest(wibble_test_location_info, __FILE__, __LINE__, "get_results(" #res ")"), res) - -/* -std::vector _get_data_results(WIBBLE_TEST_LOCPRM, const Memdb& memdb, const Record& query); -#define get_data_results(memdb, query) _get_data_results(wibble_test_location.nest(wibble_test_location_info, __FILE__, __LINE__, "get_data_results(" #memdb ", " #query ")"), memdb, query) -*/ - } } diff --git a/dballe/memdb/value-test.cc b/dballe/memdb/value-test.cc new file mode 100644 index 000000000..a6e0f7b11 --- /dev/null +++ b/dballe/memdb/value-test.cc @@ -0,0 +1,48 @@ +#include "memdb/tests.h" +#include "dballe/var.h" +#include "value.h" +#include "station.h" +#include "levtr.h" + +using namespace dballe; +using namespace dballe::tests; +using namespace std; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("basic", []() { + memdb::Stations stations; + const memdb::Station& stf = *stations[stations.obtain_fixed(Coords(44.0, 11.0), "synop")]; + + memdb::LevTrs levtrs; + const memdb::LevTr& levtr = *levtrs[levtrs.obtain(Level(1), Trange::instant())]; + + Datetime datetime(2013, 10, 30, 23); + + // Insert a station value and check that all data is there + memdb::Values values; + const memdb::Value& v = *values[values.insert(stf, levtr, datetime, newvar(WR_VAR(0, 12, 101), 28.5))]; + wassert(actual(&v.station) == &stf); + wassert(actual(&v.levtr) == &levtr); + wassert(actual(v.datetime) == datetime); + wassert(actual(v.var->code()) == WR_VAR(0, 12, 101)); + wassert(actual(v.var->enqd()) == 28.5); + + // Replacing a value should reuse an existing one + const memdb::Value& v1 = *values[values.insert(stf, levtr, datetime, newvar(WR_VAR(0, 12, 101), 29.5))]; + wassert(actual(&v1.station) == &stf); + wassert(actual(&v1.levtr) == &levtr); + wassert(actual(v1.datetime) == datetime); + wassert(actual(v1.var->code()) == WR_VAR(0, 12, 101)); + wassert(actual(v1.var->enqd()) == 29.5); + }); + } +} test("memdb_value"); + +} diff --git a/dballe/memdb/value-tut.cc b/dballe/memdb/value-tut.cc deleted file mode 100644 index 197c1714c..000000000 --- a/dballe/memdb/value-tut.cc +++ /dev/null @@ -1,48 +0,0 @@ -#include "memdb/tests.h" -#include "dballe/var.h" -#include "value.h" -#include "station.h" -#include "levtr.h" - -using namespace dballe; -using namespace wibble::tests; -using namespace std; - -namespace { - -typedef dballe::tests::test_group<> test_group; -typedef test_group::Test Test; -typedef test_group::Fixture Fixture; - -std::vector tests { - Test("basic", [](Fixture& f) { - memdb::Stations stations; - const memdb::Station& stf = *stations[stations.obtain_fixed(Coords(44.0, 11.0), "synop")]; - - memdb::LevTrs levtrs; - const memdb::LevTr& levtr = *levtrs[levtrs.obtain(Level(1), Trange::instant())]; - - Datetime datetime(2013, 10, 30, 23); - - // Insert a station value and check that all data is there - memdb::Values values; - const memdb::Value& v = *values[values.insert(stf, levtr, datetime, newvar(WR_VAR(0, 12, 101), 28.5))]; - wassert(actual(&v.station) == &stf); - wassert(actual(&v.levtr) == &levtr); - wassert(actual(v.datetime) == datetime); - wassert(actual(v.var->code()) == WR_VAR(0, 12, 101)); - wassert(actual(v.var->enqd()) == 28.5); - - // Replacing a value should reuse an existing one - const memdb::Value& v1 = *values[values.insert(stf, levtr, datetime, newvar(WR_VAR(0, 12, 101), 29.5))]; - wassert(actual(&v1.station) == &stf); - wassert(actual(&v1.levtr) == &levtr); - wassert(actual(v1.datetime) == datetime); - wassert(actual(v1.var->code()) == WR_VAR(0, 12, 101)); - wassert(actual(v1.var->enqd()) == 29.5); - }), -}; - -test_group newtg("memdb_value", tests); - -} diff --git a/dballe/memdb/valuebase-test.cc b/dballe/memdb/valuebase-test.cc new file mode 100644 index 000000000..3fdfd9a8e --- /dev/null +++ b/dballe/memdb/valuebase-test.cc @@ -0,0 +1,22 @@ +#include "memdb/tests.h" +#include "valuebase.h" + +using namespace dballe; +using namespace dballe::memdb; +using namespace dballe::tests; +using namespace std; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("empty", []() { + }); + } +} test("memdb_valuebase"); + +} diff --git a/dballe/memdb/valuebase-tut.cc b/dballe/memdb/valuebase-tut.cc deleted file mode 100644 index 2ee84bf7e..000000000 --- a/dballe/memdb/valuebase-tut.cc +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2014 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "dballe/var.h" -#include "memdb/tests.h" -#include "valuebase.h" - -using namespace dballe; -using namespace dballe::memdb; -using namespace wibble::tests; -using namespace std; - -namespace tut { - -struct memdb_valuebase_shar -{ -}; - -TESTGRP(memdb_valuebase); - -template<> template<> void to::test<1>() -{ -} - -} - - diff --git a/dballe/memdb/valuestorage-test.cc b/dballe/memdb/valuestorage-test.cc new file mode 100644 index 000000000..fd39e517f --- /dev/null +++ b/dballe/memdb/valuestorage-test.cc @@ -0,0 +1,40 @@ +#include "memdb/tests.h" +#include "valuestorage.h" + +using namespace dballe; +using namespace dballe::memdb; +using namespace dballe::tests; +using namespace std; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + // Test Positions + add_method("positions", []() { +#if 0 + Positions pa; + pa.insert(1); + pa.insert(2); + pa.insert(3); + + Positions pb; + pb.insert(1); + pb.insert(3); + pb.insert(4); + + pa.inplace_intersect(pb); + wassert(actual(pa.size()) == 2u); + + wassert(actual(pa.contains(1)).istrue()); + wassert(actual(pa.contains(3)).istrue()); +#endif + }); + } +} test("memdb_core"); + +} diff --git a/dballe/memdb/valuestorage-tut.cc b/dballe/memdb/valuestorage-tut.cc deleted file mode 100644 index 202fd9d6a..000000000 --- a/dballe/memdb/valuestorage-tut.cc +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2013 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "memdb/tests.h" -#include "valuestorage.h" - -using namespace dballe; -using namespace dballe::memdb; -using namespace wibble::tests; -using namespace std; - -namespace tut { - -struct memdb_core_shar -{ -}; - -TESTGRP(memdb_core); - -// Test Positions -template<> template<> void to::test<1>() -{ -#if 0 - Positions pa; - pa.insert(1); - pa.insert(2); - pa.insert(3); - - Positions pb; - pb.insert(1); - pb.insert(3); - pb.insert(4); - - pa.inplace_intersect(pb); - wassert(actual(pa.size()) == 2u); - - wassert(actual(pa.contains(1)).istrue()); - wassert(actual(pa.contains(3)).istrue()); -#endif -} - -} - diff --git a/dballe/message-test.cc b/dballe/message-test.cc new file mode 100644 index 000000000..d8a26fdf3 --- /dev/null +++ b/dballe/message-test.cc @@ -0,0 +1,22 @@ +#include "core/tests.h" +#include "message.h" + +using namespace std; +using namespace dballe; +using namespace dballe::tests; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("empty", []() { + }); + } +} test("dballe_message"); + +} + diff --git a/dballe/message-tut.cc b/dballe/message-tut.cc deleted file mode 100644 index beac4cd74..000000000 --- a/dballe/message-tut.cc +++ /dev/null @@ -1,22 +0,0 @@ -#include "core/tests.h" -#include "message.h" - -using namespace std; -using namespace wibble::tests; -using namespace dballe; - -namespace { - -typedef dballe::tests::test_group<> test_group; -typedef test_group::Test Test; -typedef test_group::Fixture Fixture; - -std::vector tests { - Test("empty", [](Fixture& f) { - }), -}; - -test_group newtg("dballe_message", tests); - -} - diff --git a/dballe/msg/aof_codec-test.cc b/dballe/msg/aof_codec-test.cc new file mode 100644 index 000000000..b10308c76 --- /dev/null +++ b/dballe/msg/aof_codec-test.cc @@ -0,0 +1,451 @@ +#include "tests.h" +#include "aof_codec.h" +#include "msg.h" +#include "context.h" +#include +#include +#include + +using namespace dballe; +using namespace dballe::tests; +using namespace wreport; +using namespace std; + +namespace { + +using dballe::tests::TestCodec; + +msg::AOFImporter importer; + +void strip_attributes(Messages& msgs) +{ + for (auto& i: msgs) + { + Msg& msg = Msg::downcast(i); + for (vector::iterator j = msg.data.begin(); j != msg.data.end(); ++j) + for (vector::iterator k = (*j)->data.begin(); k != (*j)->data.end(); ++k) + (*k)->clear_attrs(); + } +} + +void propagate_if_missing(int varid, const Msg& src, Msg& dst) +{ + const Var* var = src.find_by_id(varid); + if (var == NULL || !var->isset()) return; + dst.set_by_id(*var, varid); +} + +void normalise_encoding_quirks(Messages& amsgs, Messages& bmsgs) +{ + size_t len = amsgs.size(); + if (len == 0) return; + if (bmsgs.size() == 0) return; + + // Message-wide tweaks + if (Msg::downcast(amsgs[0]).type == MSG_PILOT) + { + dballe::tests::tweaks::StripVars stripper; + stripper.codes.push_back(WR_VAR(0, 11, 61)); + stripper.codes.push_back(WR_VAR(0, 11, 62)); + stripper.tweak(amsgs); + stripper.tweak(bmsgs); + } + + if (len > bmsgs.size()) len = bmsgs.size(); + for (size_t msgidx = 0; msgidx < len; ++msgidx) + { + Msg& amsg = Msg::downcast(amsgs[msgidx]); + Msg& bmsg = Msg::downcast(bmsgs[msgidx]); + + for (size_t i = 0; i < bmsg.data.size(); ++i) + { + msg::Context& ctx = *bmsg.data[i]; + for (size_t j = 0; j < ctx.data.size(); ++j) + { + int qc_is_undef = 0; + Var& var = *ctx.data[j]; + + // Recode BUFR attributes to match the AOF 2-bit values + for (const Var* attr = var.next_attr(); attr != NULL; attr = attr->next_attr()) + { + if (attr->code() == WR_VAR(0, 33, 7)) + { + if (!attr->isset()) + { + qc_is_undef = 1; + } + else + { + int val = attr->enqi(); + // Recode val using one of the value in the 4 steps of AOF + if (val > 75) + val = 76; + else if (val > 50) + val = 51; + else if (val > 25) + val = 26; + else + val = 0; + // Cast away const. This whole function is a hack, + // therefore we can. HARRR! + ((Var*)attr)->seti(val); + } + } + } + if (qc_is_undef) + var.unseta(WR_VAR(0, 33, 7)); + + // Propagate Vertical Significances + if (var.code() == WR_VAR(0, 8, 2)) + amsg.set(var, WR_VAR(0, 8, 2), ctx.level, ctx.trange); + } + } + + Var* var; + + if ((var = bmsg.edit_by_id(DBA_MSG_BLOCK)) != NULL) + var->clear_attrs(); + if ((var = bmsg.edit_by_id(DBA_MSG_STATION)) != NULL) + var->clear_attrs(); + if ((var = bmsg.edit_by_id(DBA_MSG_ST_TYPE)) != NULL) + var->clear_attrs(); + if ((var = bmsg.edit_by_id(DBA_MSG_IDENT)) != NULL) + var->clear_attrs(); + if ((var = bmsg.edit_by_id(DBA_MSG_FLIGHT_PHASE)) != NULL) + var->clear_attrs(); + + if ((var = bmsg.edit_by_id(DBA_MSG_CLOUD_CL)) != NULL && + var->enqi() == 62 && amsg.get_cloud_cl_var() == NULL) + amsg.set_cloud_cl_var(*var); + + if ((var = bmsg.edit_by_id(DBA_MSG_CLOUD_CM)) != NULL && + var->enqi() == 61 && amsg.get_cloud_cm_var() == NULL) + amsg.set_cloud_cm_var(*var); + + if ((var = bmsg.edit_by_id(DBA_MSG_CLOUD_CH)) != NULL && + var->enqi() == 60 && amsg.get_cloud_ch_var() == NULL) + amsg.set_cloud_ch_var(*var); + + propagate_if_missing(DBA_MSG_HEIGHT_ANEM, bmsg, amsg); + propagate_if_missing(DBA_MSG_NAVSYS, bmsg, amsg); + + // In AOF, only synops and ships can encode direction and speed + if (amsg.type != MSG_SHIP && amsg.type != MSG_SYNOP) + { + propagate_if_missing(DBA_MSG_ST_DIR, bmsg, amsg); + propagate_if_missing(DBA_MSG_ST_SPEED, bmsg, amsg); + } + + propagate_if_missing(DBA_MSG_PRESS_TEND, bmsg, amsg); + + // AOF AMDAR has pressure indication, BUFR AMDAR has only height + if (amsg.type == MSG_AMDAR) + { + // dba_var p = dba_msg_get_flight_press_var(amsg); + for (size_t i = 0; i < amsg.data.size(); ++i) + { + msg::Context& c = *amsg.data[i]; + if (c.level.ltype1 == 100 && c.trange == Trange::instant()) + if (const Var* var = c.find(WR_VAR(0, 10, 4))) + { + bmsg.set(*var, WR_VAR(0, 10, 4), c.level, c.trange); + break; + } + } +#if 0 + dba_var h = dba_msg_get_height_var(bmsg); + if (p && h) + { + double press, height; + CHECKED(dba_var_enqd(p, &press)); + CHECKED(dba_var_enqd(h, &height)); + dba_msg_level l = dba_msg_find_level(bmsg, 103, (int)height, 0); + if (l) + { + l->ltype = 100; + l->l1 = (int)(press/100); + CHECKED(dba_msg_set_flight_press_var(bmsg, p)); + } + } +#endif + } + + if (amsg.type == MSG_TEMP) + { + propagate_if_missing(DBA_MSG_SONDE_TYPE, bmsg, amsg); + propagate_if_missing(DBA_MSG_SONDE_METHOD, bmsg, amsg); + } + + if (amsg.type == MSG_TEMP_SHIP) + { + propagate_if_missing(DBA_MSG_HEIGHT_STATION, bmsg, amsg); + } + + if (amsg.type == MSG_TEMP || amsg.type == MSG_TEMP_SHIP) + { + propagate_if_missing(DBA_MSG_CLOUD_N, bmsg, amsg); + propagate_if_missing(DBA_MSG_CLOUD_NH, bmsg, amsg); + propagate_if_missing(DBA_MSG_CLOUD_HH, bmsg, amsg); + propagate_if_missing(DBA_MSG_CLOUD_CL, bmsg, amsg); + propagate_if_missing(DBA_MSG_CLOUD_CM, bmsg, amsg); + propagate_if_missing(DBA_MSG_CLOUD_CH, bmsg, amsg); + +#define FIX_GEOPOTENTIAL +#ifdef FIX_GEOPOTENTIAL + // Convert the geopotentials back to heights in a dirty way, but it + // should make the comparison more meaningful. It catches this case: + // - AOF has height, that gets multiplied by 9.80665 + // 25667 becomes 251707 + // - BUFR stores geopotential, without the last digit + // 251707 becomes 251710 + // - However, if we go back to heights, the precision should be + // preserved + // 251710 / 9.80664 becomes 25667 as it was + for (size_t i = 0; i < amsg.data.size(); ++i) + { + msg::Context& c = *amsg.data[i]; + if (Var* var = c.edit(WR_VAR(0, 10, 3))) + var->setd(var->enqd() / 9.80665); + } + for (size_t i = 0; i < bmsg.data.size(); ++i) + { + msg::Context& c = *bmsg.data[i]; + if (Var* var = c.edit(WR_VAR(0, 10, 3))) + var->setd(var->enqd() / 9.80665); + } + + // Decoding BUFR temp messages copies data from the surface level into + // more classical places: compensate by copying the same data in the + // AOF file + propagate_if_missing(DBA_MSG_PRESS, bmsg, amsg); + propagate_if_missing(DBA_MSG_TEMP_2M, bmsg, amsg); + propagate_if_missing(DBA_MSG_DEWPOINT_2M, bmsg, amsg); + propagate_if_missing(DBA_MSG_WIND_DIR, bmsg, amsg); + propagate_if_missing(DBA_MSG_WIND_SPEED, bmsg, amsg); +#endif + } + + // Remove attributes from all vertical sounding significances + for (size_t i = 0; i < bmsg.data.size(); ++i) + { + msg::Context& c = *bmsg.data[i]; + if (Var* var = c.edit(WR_VAR(0, 8, 42))) + { + var->clear_attrs(); + // Remove SIGHUM that is added by ECMWF template conversions + int val = var->enqi(); + val &= ~BUFR08042::SIGHUM; + var->seti(val); + } + } + } +} + + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + // Test simple decoding + add_method("simple", []() { + const char** files = dballe::tests::aof_files; + for (size_t i = 0; files[i] != NULL; i++) + { + try { + BinaryMessage raw = read_rawmsg(files[i], File::AOF); + + /* Parse it */ + Messages msgs = importer.from_binary(raw); + wassert(actual(msgs.empty()).isfalse()); + } catch (std::exception& e) { + throw TestFailed(string("[") + files[i] + "] " + e.what()); + } + } + }); + + // Compare decoding results with BUFR sample data + add_method("compare_bufr", []() { + string files[] = { + "aof/obs1-14.63", // OK + "aof/obs1-21.1", // OK + "aof/obs1-24.2104", // OK + // "aof/obs1-24.34", // Data are in fact slightly different + // "aof/obs2-144.2198", // Data have different precision in BUFR and AOF, but otherwise match + // "aof/obs2-244.0", // BUFR counterpart missing for this message + "aof/obs4-165.2027", // OK + // "aof/obs5-35.61", // Data are in fact slightly different + // "aof/obs5-36.30", // Data are in fact slightly different + "aof/obs6-32.1573", // OK + // "aof/obs6-32.0", // BUFR conterpart missing for this message + "", + }; + + for (size_t i = 0; !files[i].empty(); i++) + { + try { + Messages amsgs = read_msgs((files[i] + ".aof").c_str(), File::AOF); + Messages bmsgs = read_msgs((files[i] + ".bufr").c_str(), File::BUFR); + normalise_encoding_quirks(amsgs, bmsgs); + + // Compare the two dba_msg + notes::Collect c(cerr); + int diffs = amsgs.diff(bmsgs); + if (diffs) + { + dballe::tests::track_different_msgs(amsgs, bmsgs, "aof"); + } + wassert(actual(diffs) == 0); + } catch (std::exception& e) { + throw TestFailed(string(files[i]) + ": " + e.what()); + } + } + }); + + // Reencode to BUFR and compare + add_method("reencode_bufr", []() { + std::unique_ptr exporter(msg::Exporter::create(File::BUFR)); + std::unique_ptr importer(msg::Importer::create(File::BUFR)); + const char** files = dballe::tests::aof_files; + // Note: missingclouds and brokenamdar were not in the original file list before it got unified + for (size_t i = 0; files[i] != NULL; i++) + { + try { + // Read + Messages amsgs = read_msgs(files[i], File::AOF); + + // Reencode to BUFR + BinaryMessage raw(File::BUFR); + raw.data = exporter->to_binary(amsgs); + + // Decode again + Messages bmsgs = importer->from_binary(raw); + + normalise_encoding_quirks(amsgs, bmsgs); + + // Compare the two dba_msg + notes::Collect c(cerr); + int diffs = amsgs.diff(bmsgs); + if (diffs) + { + dballe::tests::track_different_msgs(amsgs, bmsgs, "aof-bufr"); + dballe::tests::dump("aof-bufr", raw, "AOF reencoded to BUFR"); + } + wassert(actual(diffs) == 0); + } catch (std::exception& e) { + throw TestFailed(string(files[i]) + ": " + e.what()); + } + + } + }); + + // Reencode to CREX and compare + add_method("reencode_crex", []() { +#if 0 + const char* files[] = { + "aof/obs1-11.0.aof", + "aof/obs1-14.63.aof", + "aof/obs1-21.1.aof", + "aof/obs1-24.2104.aof", + "aof/obs1-24.34.aof", + "aof/obs2-144.2198.aof", + "aof/obs2-244.0.aof", + "aof/obs2-244.1.aof", + "aof/obs4-165.2027.aof", + "aof/obs5-35.61.aof", + "aof/obs5-36.30.aof", + "aof/obs6-32.1573.aof", + "aof/obs6-32.0.aof", + "aof/aof_27-2-144.aof", + "aof/aof_28-2-144.aof", + "aof/aof_27-2-244.aof", + "aof/aof_28-2-244.aof", + NULL, + }; + + for (size_t i = 0; files[i] != NULL; i++) + { + test_tag(files[i]); + + dba_msgs amsgs = read_test_msg(files[i], AOF); + + dba_rawmsg raw; + CHECKED(dba_marshal_encode(amsgs, CREX, &raw)); + + dba_msgs bmsgs; + CHECKED(dba_marshal_decode(raw, &bmsgs)); + + strip_attributes(amsgs); + normalise_encoding_quirks(amsgs, bmsgs); + roundtemps(amsgs); + roundtemps(bmsgs); + + // Compare the two dba_msg + int diffs = 0; + dba_msgs_diff(amsgs, bmsgs, &diffs, stderr); + if (diffs) track_different_msgs(amsgs, bmsgs, "aof-crex"); + gen_wassert(actual(diffs) == 0); + + dba_msgs_delete(amsgs); + dba_msgs_delete(bmsgs); + dba_rawmsg_delete(raw); + } + test_untag(); +#endif + }); + + // Compare no-dew-point AOF plane reports with those with dew point + add_method("dewpoint", []() { + string prefix = "aof/aof_"; + const char* files[] = { + "-2-144.aof", + "-2-244.aof", + NULL, + }; + + for (size_t i = 0; files[i] != NULL; i++) + { + try { + Messages amsgs1 = read_msgs(string(prefix + "27" + files[i]).c_str(), File::AOF); + Messages amsgs2 = read_msgs(string(prefix + "28" + files[i]).c_str(), File::AOF); + + // Compare the two dba_msg + notes::Collect c(cerr); + int diffs = amsgs1.diff(amsgs2); + if (diffs) dballe::tests::track_different_msgs(amsgs1, amsgs2, "aof-2728"); + wassert(actual(diffs) == 0); + } catch (std::exception& e) { + throw TestFailed(prefix + "2x" + files[i] + ": " + e.what()); + } + } + }); + + // Ensure that missing values in existing synop optional sections are caught + // correctly + add_method("missing", []() { + Messages msgs = read_msgs("aof/missing-cloud-h.aof", File::AOF); + wassert(actual(msgs.size()) == 1); + + const Msg& msg = Msg::downcast(msgs[0]); + wassert(actual(msg.get_cloud_h1_var()).isfalse()); + }); + + // Verify decoding of confidence intervals in optional ship group + add_method("ship_confidence", []() { + Messages msgs = read_msgs("aof/confship.aof", File::AOF); + wassert(actual(msgs.size()) == 1); + + const Msg& msg = Msg::downcast(msgs[0]); + const Var* var = msg.get_st_dir_var(); + wassert(actual(var).istrue()); + + const Var* attr = var->enqa(WR_VAR(0, 33, 7)); + wassert(actual(attr).istrue()); + + wassert(actual(attr->enqi()) == 51); + }); + } +} test("msg_aof_codec"); + +} diff --git a/dballe/msg/aof_codec-tut.cc b/dballe/msg/aof_codec-tut.cc deleted file mode 100644 index ca5240de0..000000000 --- a/dballe/msg/aof_codec-tut.cc +++ /dev/null @@ -1,485 +0,0 @@ -/* - * Copyright (C) 2005--2015 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "tests.h" -#include "aof_codec.h" -#include "msg.h" -#include "context.h" -#include -#include -#include - -using namespace dballe; -using namespace wreport; -using namespace std; - -namespace tut { - -struct aof_codec_shar -{ - msg::AOFImporter importer; - - aof_codec_shar() - { - } - - ~aof_codec_shar() - { - } -}; -TESTGRP(aof_codec); - -// Test simple decoding -template<> template<> -void to::test<1>() -{ - const char** files = dballe::tests::aof_files; - for (size_t i = 0; files[i] != NULL; i++) - { - try { - BinaryMessage raw = read_rawmsg(files[i], File::AOF); - - /* Parse it */ - Messages msgs = importer.from_binary(raw); - ensure(!msgs.empty()); - } catch (std::exception& e) { - throw tut::failure(string("[") + files[i] + "] " + e.what()); - } - } -} - -void strip_attributes(Messages& msgs) -{ - for (auto& i: msgs) - { - Msg& msg = Msg::downcast(i); - for (vector::iterator j = msg.data.begin(); j != msg.data.end(); ++j) - for (vector::iterator k = (*j)->data.begin(); k != (*j)->data.end(); ++k) - (*k)->clear_attrs(); - } -} - -void propagate_if_missing(int varid, const Msg& src, Msg& dst) -{ - const Var* var = src.find_by_id(varid); - if (var == NULL || !var->isset()) return; - dst.set_by_id(*var, varid); -} - -void normalise_encoding_quirks(Messages& amsgs, Messages& bmsgs) -{ - size_t len = amsgs.size(); - if (len == 0) return; - if (bmsgs.size() == 0) return; - - // Message-wide tweaks - if (Msg::downcast(amsgs[0]).type == MSG_PILOT) - { - dballe::tests::tweaks::StripVars stripper; - stripper.codes.push_back(WR_VAR(0, 11, 61)); - stripper.codes.push_back(WR_VAR(0, 11, 62)); - stripper.tweak(amsgs); - stripper.tweak(bmsgs); - } - - if (len > bmsgs.size()) len = bmsgs.size(); - for (size_t msgidx = 0; msgidx < len; ++msgidx) - { - Msg& amsg = Msg::downcast(amsgs[msgidx]); - Msg& bmsg = Msg::downcast(bmsgs[msgidx]); - - for (size_t i = 0; i < bmsg.data.size(); ++i) - { - msg::Context& ctx = *bmsg.data[i]; - for (size_t j = 0; j < ctx.data.size(); ++j) - { - int qc_is_undef = 0; - Var& var = *ctx.data[j]; - - // Recode BUFR attributes to match the AOF 2-bit values - for (const Var* attr = var.next_attr(); attr != NULL; attr = attr->next_attr()) - { - if (attr->code() == WR_VAR(0, 33, 7)) - { - if (!attr->isset()) - { - qc_is_undef = 1; - } - else - { - int val = attr->enqi(); - // Recode val using one of the value in the 4 steps of AOF - if (val > 75) - val = 76; - else if (val > 50) - val = 51; - else if (val > 25) - val = 26; - else - val = 0; - // Cast away const. This whole function is a hack, - // therefore we can. HARRR! - ((Var*)attr)->seti(val); - } - } - } - if (qc_is_undef) - var.unseta(WR_VAR(0, 33, 7)); - - // Propagate Vertical Significances - if (var.code() == WR_VAR(0, 8, 2)) - amsg.set(var, WR_VAR(0, 8, 2), ctx.level, ctx.trange); - } - } - - Var* var; - - if ((var = bmsg.edit_by_id(DBA_MSG_BLOCK)) != NULL) - var->clear_attrs(); - if ((var = bmsg.edit_by_id(DBA_MSG_STATION)) != NULL) - var->clear_attrs(); - if ((var = bmsg.edit_by_id(DBA_MSG_ST_TYPE)) != NULL) - var->clear_attrs(); - if ((var = bmsg.edit_by_id(DBA_MSG_IDENT)) != NULL) - var->clear_attrs(); - if ((var = bmsg.edit_by_id(DBA_MSG_FLIGHT_PHASE)) != NULL) - var->clear_attrs(); - - if ((var = bmsg.edit_by_id(DBA_MSG_CLOUD_CL)) != NULL && - var->enqi() == 62 && amsg.get_cloud_cl_var() == NULL) - amsg.set_cloud_cl_var(*var); - - if ((var = bmsg.edit_by_id(DBA_MSG_CLOUD_CM)) != NULL && - var->enqi() == 61 && amsg.get_cloud_cm_var() == NULL) - amsg.set_cloud_cm_var(*var); - - if ((var = bmsg.edit_by_id(DBA_MSG_CLOUD_CH)) != NULL && - var->enqi() == 60 && amsg.get_cloud_ch_var() == NULL) - amsg.set_cloud_ch_var(*var); - - propagate_if_missing(DBA_MSG_HEIGHT_ANEM, bmsg, amsg); - propagate_if_missing(DBA_MSG_NAVSYS, bmsg, amsg); - - // In AOF, only synops and ships can encode direction and speed - if (amsg.type != MSG_SHIP && amsg.type != MSG_SYNOP) - { - propagate_if_missing(DBA_MSG_ST_DIR, bmsg, amsg); - propagate_if_missing(DBA_MSG_ST_SPEED, bmsg, amsg); - } - - propagate_if_missing(DBA_MSG_PRESS_TEND, bmsg, amsg); - - // AOF AMDAR has pressure indication, BUFR AMDAR has only height - if (amsg.type == MSG_AMDAR) - { - // dba_var p = dba_msg_get_flight_press_var(amsg); - for (size_t i = 0; i < amsg.data.size(); ++i) - { - msg::Context& c = *amsg.data[i]; - if (c.level.ltype1 == 100 && c.trange == Trange::instant()) - if (const Var* var = c.find(WR_VAR(0, 10, 4))) - { - bmsg.set(*var, WR_VAR(0, 10, 4), c.level, c.trange); - break; - } - } -#if 0 - dba_var h = dba_msg_get_height_var(bmsg); - if (p && h) - { - double press, height; - CHECKED(dba_var_enqd(p, &press)); - CHECKED(dba_var_enqd(h, &height)); - dba_msg_level l = dba_msg_find_level(bmsg, 103, (int)height, 0); - if (l) - { - l->ltype = 100; - l->l1 = (int)(press/100); - CHECKED(dba_msg_set_flight_press_var(bmsg, p)); - } - } -#endif - } - - if (amsg.type == MSG_TEMP) - { - propagate_if_missing(DBA_MSG_SONDE_TYPE, bmsg, amsg); - propagate_if_missing(DBA_MSG_SONDE_METHOD, bmsg, amsg); - } - - if (amsg.type == MSG_TEMP_SHIP) - { - propagate_if_missing(DBA_MSG_HEIGHT_STATION, bmsg, amsg); - } - - if (amsg.type == MSG_TEMP || amsg.type == MSG_TEMP_SHIP) - { - propagate_if_missing(DBA_MSG_CLOUD_N, bmsg, amsg); - propagate_if_missing(DBA_MSG_CLOUD_NH, bmsg, amsg); - propagate_if_missing(DBA_MSG_CLOUD_HH, bmsg, amsg); - propagate_if_missing(DBA_MSG_CLOUD_CL, bmsg, amsg); - propagate_if_missing(DBA_MSG_CLOUD_CM, bmsg, amsg); - propagate_if_missing(DBA_MSG_CLOUD_CH, bmsg, amsg); - -#define FIX_GEOPOTENTIAL -#ifdef FIX_GEOPOTENTIAL - // Convert the geopotentials back to heights in a dirty way, but it - // should make the comparison more meaningful. It catches this case: - // - AOF has height, that gets multiplied by 9.80665 - // 25667 becomes 251707 - // - BUFR stores geopotential, without the last digit - // 251707 becomes 251710 - // - However, if we go back to heights, the precision should be - // preserved - // 251710 / 9.80664 becomes 25667 as it was - for (size_t i = 0; i < amsg.data.size(); ++i) - { - msg::Context& c = *amsg.data[i]; - if (Var* var = c.edit(WR_VAR(0, 10, 3))) - var->setd(var->enqd() / 9.80665); - } - for (size_t i = 0; i < bmsg.data.size(); ++i) - { - msg::Context& c = *bmsg.data[i]; - if (Var* var = c.edit(WR_VAR(0, 10, 3))) - var->setd(var->enqd() / 9.80665); - } - - // Decoding BUFR temp messages copies data from the surface level into - // more classical places: compensate by copying the same data in the - // AOF file - propagate_if_missing(DBA_MSG_PRESS, bmsg, amsg); - propagate_if_missing(DBA_MSG_TEMP_2M, bmsg, amsg); - propagate_if_missing(DBA_MSG_DEWPOINT_2M, bmsg, amsg); - propagate_if_missing(DBA_MSG_WIND_DIR, bmsg, amsg); - propagate_if_missing(DBA_MSG_WIND_SPEED, bmsg, amsg); -#endif - } - - // Remove attributes from all vertical sounding significances - for (size_t i = 0; i < bmsg.data.size(); ++i) - { - msg::Context& c = *bmsg.data[i]; - if (Var* var = c.edit(WR_VAR(0, 8, 42))) - { - var->clear_attrs(); - // Remove SIGHUM that is added by ECMWF template conversions - int val = var->enqi(); - val &= ~BUFR08042::SIGHUM; - var->seti(val); - } - } - } -} - -// Compare decoding results with BUFR sample data -template<> template<> -void to::test<2>() -{ - string files[] = { - "aof/obs1-14.63", // OK - "aof/obs1-21.1", // OK - "aof/obs1-24.2104", // OK -// "aof/obs1-24.34", // Data are in fact slightly different -// "aof/obs2-144.2198", // Data have different precision in BUFR and AOF, but otherwise match -// "aof/obs2-244.0", // BUFR counterpart missing for this message - "aof/obs4-165.2027", // OK -// "aof/obs5-35.61", // Data are in fact slightly different -// "aof/obs5-36.30", // Data are in fact slightly different - "aof/obs6-32.1573", // OK -// "aof/obs6-32.0", // BUFR conterpart missing for this message - "", - }; - - for (size_t i = 0; !files[i].empty(); i++) - { - try { - Messages amsgs = read_msgs((files[i] + ".aof").c_str(), File::AOF); - Messages bmsgs = read_msgs((files[i] + ".bufr").c_str(), File::BUFR); - normalise_encoding_quirks(amsgs, bmsgs); - - // Compare the two dba_msg - notes::Collect c(cerr); - int diffs = amsgs.diff(bmsgs); - if (diffs) - { - dballe::tests::track_different_msgs(amsgs, bmsgs, "aof"); - } - ensure_equals(diffs, 0); - } catch (std::exception& e) { - throw tut::failure(string(files[i]) + ": " + e.what()); - } - } -} - -// Reencode to BUFR and compare -template<> template<> -void to::test<3>() -{ - std::unique_ptr exporter(msg::Exporter::create(File::BUFR)); - std::unique_ptr importer(msg::Importer::create(File::BUFR)); - const char** files = dballe::tests::aof_files; - // Note: missingclouds and brokenamdar were not in the original file list before it got unified - for (size_t i = 0; files[i] != NULL; i++) - { - try { - // Read - Messages amsgs = read_msgs(files[i], File::AOF); - - // Reencode to BUFR - BinaryMessage raw(File::BUFR); - raw.data = exporter->to_binary(amsgs); - - // Decode again - Messages bmsgs = importer->from_binary(raw); - - normalise_encoding_quirks(amsgs, bmsgs); - - // Compare the two dba_msg - notes::Collect c(cerr); - int diffs = amsgs.diff(bmsgs); - if (diffs) - { - dballe::tests::track_different_msgs(amsgs, bmsgs, "aof-bufr"); - dballe::tests::dump("aof-bufr", raw, "AOF reencoded to BUFR"); - } - ensure_equals(diffs, 0); - } catch (std::exception& e) { - throw tut::failure(string(files[i]) + ": " + e.what()); - } - - } -} - -// Reencode to CREX and compare -template<> template<> -void to::test<4>() -{ -#if 0 - const char* files[] = { - "aof/obs1-11.0.aof", - "aof/obs1-14.63.aof", - "aof/obs1-21.1.aof", - "aof/obs1-24.2104.aof", - "aof/obs1-24.34.aof", - "aof/obs2-144.2198.aof", - "aof/obs2-244.0.aof", - "aof/obs2-244.1.aof", - "aof/obs4-165.2027.aof", - "aof/obs5-35.61.aof", - "aof/obs5-36.30.aof", - "aof/obs6-32.1573.aof", - "aof/obs6-32.0.aof", - "aof/aof_27-2-144.aof", - "aof/aof_28-2-144.aof", - "aof/aof_27-2-244.aof", - "aof/aof_28-2-244.aof", - NULL, - }; - - for (size_t i = 0; files[i] != NULL; i++) - { - test_tag(files[i]); - - dba_msgs amsgs = read_test_msg(files[i], AOF); - - dba_rawmsg raw; - CHECKED(dba_marshal_encode(amsgs, CREX, &raw)); - - dba_msgs bmsgs; - CHECKED(dba_marshal_decode(raw, &bmsgs)); - - strip_attributes(amsgs); - normalise_encoding_quirks(amsgs, bmsgs); - roundtemps(amsgs); - roundtemps(bmsgs); - - // Compare the two dba_msg - int diffs = 0; - dba_msgs_diff(amsgs, bmsgs, &diffs, stderr); - if (diffs) track_different_msgs(amsgs, bmsgs, "aof-crex"); - gen_ensure_equals(diffs, 0); - - dba_msgs_delete(amsgs); - dba_msgs_delete(bmsgs); - dba_rawmsg_delete(raw); - } - test_untag(); -#endif -} - - -// Compare no-dew-point AOF plane reports with those with dew point -template<> template<> -void to::test<5>() -{ - string prefix = "aof/aof_"; - const char* files[] = { - "-2-144.aof", - "-2-244.aof", - NULL, - }; - - for (size_t i = 0; files[i] != NULL; i++) - { - try { - Messages amsgs1 = read_msgs(string(prefix + "27" + files[i]).c_str(), File::AOF); - Messages amsgs2 = read_msgs(string(prefix + "28" + files[i]).c_str(), File::AOF); - - // Compare the two dba_msg - notes::Collect c(cerr); - int diffs = amsgs1.diff(amsgs2); - if (diffs) dballe::tests::track_different_msgs(amsgs1, amsgs2, "aof-2728"); - ensure_equals(diffs, 0); - } catch (std::exception& e) { - throw tut::failure(prefix + "2x" + files[i] + ": " + e.what()); - } - } -} - -// Ensure that missing values in existing synop optional sections are caught -// correctly -template<> template<> -void to::test<6>() -{ - Messages msgs = read_msgs("aof/missing-cloud-h.aof", File::AOF); - ensure_equals(msgs.size(), 1); - - const Msg& msg = Msg::downcast(msgs[0]); - ensure(msg.get_cloud_h1_var() == NULL); -} - -// Verify decoding of confidence intervals in optional ship group -template<> template<> -void to::test<7>() -{ - Messages msgs = read_msgs("aof/confship.aof", File::AOF); - ensure_equals(msgs.size(), 1); - - const Msg& msg = Msg::downcast(msgs[0]); - const Var* var = msg.get_st_dir_var(); - ensure(var != NULL); - - const Var* attr = var->enqa(WR_VAR(0, 33, 7)); - ensure(attr != NULL); - - ensure_equals(attr->enqi(), 51); -} - -} diff --git a/dballe/msg/codec-test.cc b/dballe/msg/codec-test.cc new file mode 100644 index 000000000..e79f676a9 --- /dev/null +++ b/dballe/msg/codec-test.cc @@ -0,0 +1,31 @@ +#include "msg/tests.h" +#include "msg/codec.h" + +using namespace wreport; +using namespace dballe; +using namespace dballe::tests; +using namespace std; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("options", []() { + using namespace dballe::msg; + + Importer::Options simplified; + Importer::Options accurate; + accurate.simplified = false; + + wassert(actual(Importer::Options::from_string("") == simplified).istrue()); + wassert(actual(Importer::Options::from_string("simplified") == simplified).istrue()); + wassert(actual(Importer::Options::from_string("accurate") == accurate).istrue()); + }); + } +} test("msg_codec"); + +} diff --git a/dballe/msg/codec-tut.cc b/dballe/msg/codec-tut.cc deleted file mode 100644 index 78fc04bcb..000000000 --- a/dballe/msg/codec-tut.cc +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2014 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "msg/tests.h" -#include "msg/codec.h" - -using namespace dballe; -using namespace wreport; -using namespace wibble::tests; -using namespace std; - -namespace tut { - -struct msg_codec_shar -{ -}; -TESTGRP(msg_codec); - -template<> template<> -void to::test<1>() -{ - using namespace dballe::msg; - - Importer::Options simplified; - Importer::Options accurate; - accurate.simplified = false; - - wassert(actual(Importer::Options::from_string("") == simplified).istrue()); - wassert(actual(Importer::Options::from_string("simplified") == simplified).istrue()); - wassert(actual(Importer::Options::from_string("accurate") == accurate).istrue()); -} - -} diff --git a/dballe/msg/context-test.cc b/dballe/msg/context-test.cc new file mode 100644 index 000000000..37f8a9505 --- /dev/null +++ b/dballe/msg/context-test.cc @@ -0,0 +1,106 @@ +#include "msg/tests.h" +#include "msg/context.h" +#include + +using namespace wreport; +using namespace dballe; +using namespace dballe::tests; +using namespace std; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("compare", []() { + Level lev(9, 8, 7, 6); + unique_ptr c1(new msg::Context(lev, Trange(1, 2, 3))); + unique_ptr c2(new msg::Context(lev, Trange(1, 3, 2))); + + wassert(actual(c1->data.size()) == 0); + wassert(actual(c1->level) == lev); + wassert(actual(c1->trange) == Trange(1, 2, 3)); + wassert(actual(c2->data.size()) == 0); + wassert(actual(c2->level) == lev); + wassert(actual(c2->trange) == Trange(1, 3, 2)); + + c1->set(var(WR_VAR(0, 1, 1))); + c2->set(var(WR_VAR(0, 1, 1))); + + wassert(actual(c1->compare(*c2)) < 0); + wassert(actual(c2->compare(*c1)) > 0); + wassert(actual(c1->compare(*c1)) == 0); + wassert(actual(c2->compare(*c2)) == 0); + + wassert(actual(c1->compare(lev, Trange(1, 2, 4))) < 0); + wassert(actual(c1->compare(lev, Trange(1, 2, 2))) > 0); + wassert(actual(c1->compare(lev, Trange(1, 3, 3))) < 0); + wassert(actual(c1->compare(lev, Trange(1, 1, 3))) > 0); + wassert(actual(c1->compare(lev, Trange(2, 2, 3))) < 0); + wassert(actual(c1->compare(lev, Trange(0, 2, 3))) > 0); + wassert(actual(c1->compare(Level(9, 8, 7, 7), Trange(1, 2, 3))) < 0); + wassert(actual(c1->compare(Level(9, 8, 7, 5), Trange(1, 2, 3))) > 0); + wassert(actual(c1->compare(lev, Trange(1, 2, 3))) == 0); + }); + + // Test Context external ordering + add_method("compare_external", []() { + Trange tr(1, 2, 3); + unique_ptr c1(new msg::Context(Level(1, 2, 3, 4), tr)); + unique_ptr c2(new msg::Context(Level(2, 1, 4, 3), tr)); + + wassert(actual(c1->data.size()) == 0); + wassert(actual(c1->level) == Level(1, 2, 3, 4)); + wassert(actual(c2->data.size()) == 0); + wassert(actual(c2->level) == Level(2, 1, 4, 3)); + + wassert(actual(c1->compare(*c2)) < 0); + wassert(actual(c2->compare(*c1)) > 0); + wassert(actual(c1->compare(*c1)) == 0); + wassert(actual(c2->compare(*c2)) == 0); + + wassert(actual(c1->compare(Level(1, 2, 4, 4), tr)) < 0); + wassert(actual(c1->compare(Level(1, 2, 2, 4), tr)) > 0); + wassert(actual(c1->compare(Level(1, 3, 3, 4), tr)) < 0); + wassert(actual(c1->compare(Level(1, 1, 3, 4), tr)) > 0); + wassert(actual(c1->compare(Level(2, 2, 3, 4), tr)) < 0); + wassert(actual(c1->compare(Level(0, 2, 3, 4), tr)) > 0); + wassert(actual(c1->compare(Level(1, 2, 3, 4), tr)) == 0); + }); + + // Test msg::Context internal ordering + add_method("compare_internal", []() { + unique_ptr c(new msg::Context(Level(1, 2, 3, 4), Trange::instant())); + + c->set(var(WR_VAR(0, 1, 1))); + wassert(actual(c->data.size()) == 1); + c->set(var(WR_VAR(0, 1, 7))); + wassert(actual(c->data.size()) == 2); + c->set(var(WR_VAR(0, 1, 2))); + wassert(actual(c->data.size()) == 3); + // Variables with same code must get substituded and not added + c->set(var(WR_VAR(0, 1, 1))); + wassert(actual(c->data.size()) == 3); + + // Check that the datum vector inside the context is in strict ascending order + for (unsigned i = 0; i < c->data.size() - 1; ++i) + wassert(actual_varcode(c->data[i]->code()) < c->data[i + 1]->code()); + + wassert(actual(c->find(WR_VAR(0, 1, 1))).istrue()); + wassert(actual_varcode(c->find(WR_VAR(0, 1, 1))->code()) == WR_VAR(0, 1, 1)); + + wassert(actual(c->find(WR_VAR(0, 1, 2))).istrue()); + wassert(actual_varcode(c->find(WR_VAR(0, 1, 2))->code()) == WR_VAR(0, 1, 2)); + + wassert(actual(c->find(WR_VAR(0, 1, 7))).istrue()); + wassert(actual_varcode(c->find(WR_VAR(0, 1, 7))->code()) == WR_VAR(0, 1, 7)); + + wassert(actual(c->find(WR_VAR(0, 1, 8))) == (const Var*)0); + }); + } +} test("msg_context"); + +} diff --git a/dballe/msg/context-tut.cc b/dballe/msg/context-tut.cc deleted file mode 100644 index c19c08223..000000000 --- a/dballe/msg/context-tut.cc +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (C) 2005--2010 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "msg/tests.h" -#include "msg/context.h" -#include - -using namespace wreport; -using namespace dballe; -using namespace std; - -namespace tut { - -struct context_shar -{ - context_shar() - { - } - - ~context_shar() - { - } -}; -TESTGRP(context); - -// Ensure that the datum vector inside the context is in strict ascending order -void _ensure_context_is_sorted(const wibble::tests::Location& loc, const msg::Context& ctx) -{ - if (ctx.data.size() < 2) - return; - for (int i = 0; i < ctx.data.size() - 1; ++i) - inner_ensure(ctx.data[i]->code() < ctx.data[i + 1]->code()); -} -#define ensure_context_is_sorted(x) _ensure_context_is_sorted(wibble::tests::Location(__FILE__, __LINE__, "context is sorted in " #x), (x)) - - -/* Test msg::Context */ -template<> template<> -void to::test<1>() -{ - Level lev(9, 8, 7, 6); - unique_ptr c1(new msg::Context(lev, Trange(1, 2, 3))); - unique_ptr c2(new msg::Context(lev, Trange(1, 3, 2))); - - ensure_equals(c1->data.size(), 0); - ensure_equals(c1->level, lev); - ensure_equals(c1->trange, Trange(1, 2, 3)); - ensure_equals(c2->data.size(), 0); - ensure_equals(c2->level, lev); - ensure_equals(c2->trange, Trange(1, 3, 2)); - - c1->set(var(WR_VAR(0, 1, 1))); - c2->set(var(WR_VAR(0, 1, 1))); - - ensure(c1->compare(*c2) < 0); - ensure(c2->compare(*c1) > 0); - ensure_equals(c1->compare(*c1), 0); - ensure_equals(c2->compare(*c2), 0); - - ensure(c1->compare(lev, Trange(1, 2, 4)) < 0); - ensure(c1->compare(lev, Trange(1, 2, 2)) > 0); - ensure(c1->compare(lev, Trange(1, 3, 3)) < 0); - ensure(c1->compare(lev, Trange(1, 1, 3)) > 0); - ensure(c1->compare(lev, Trange(2, 2, 3)) < 0); - ensure(c1->compare(lev, Trange(0, 2, 3)) > 0); - ensure(c1->compare(Level(9, 8, 7, 7), Trange(1, 2, 3)) < 0); - ensure(c1->compare(Level(9, 8, 7, 5), Trange(1, 2, 3)) > 0); - ensure_equals(c1->compare(lev, Trange(1, 2, 3)), 0); -} - -/* Test Context external ordering */ -template<> template<> -void to::test<2>() -{ - Trange tr(1, 2, 3); - unique_ptr c1(new msg::Context(Level(1, 2, 3, 4), tr)); - unique_ptr c2(new msg::Context(Level(2, 1, 4, 3), tr)); - - ensure_equals(c1->data.size(), 0); - ensure_equals(c1->level, Level(1, 2, 3, 4)); - ensure_equals(c2->data.size(), 0); - ensure_equals(c2->level, Level(2, 1, 4, 3)); - - ensure(c1->compare(*c2) < 0); - ensure(c2->compare(*c1) > 0); - ensure_equals(c1->compare(*c1), 0); - ensure_equals(c2->compare(*c2), 0); - - ensure(c1->compare(Level(1, 2, 4, 4), tr) < 0); - ensure(c1->compare(Level(1, 2, 2, 4), tr) > 0); - ensure(c1->compare(Level(1, 3, 3, 4), tr) < 0); - ensure(c1->compare(Level(1, 1, 3, 4), tr) > 0); - ensure(c1->compare(Level(2, 2, 3, 4), tr) < 0); - ensure(c1->compare(Level(0, 2, 3, 4), tr) > 0); - ensure_equals(c1->compare(Level(1, 2, 3, 4), tr), 0); -} - -/* Test msg::Context internal ordering */ -template<> template<> -void to::test<3>() -{ - unique_ptr c(new msg::Context(Level(1, 2, 3, 4), Trange::instant())); - - c->set(var(WR_VAR(0, 1, 1))); - ensure_equals(c->data.size(), 1); - c->set(var(WR_VAR(0, 1, 7))); - ensure_equals(c->data.size(), 2); - c->set(var(WR_VAR(0, 1, 2))); - ensure_equals(c->data.size(), 3); - // Variables with same code must get substituded and not added - c->set(var(WR_VAR(0, 1, 1))); - ensure_equals(c->data.size(), 3); - - ensure_context_is_sorted(*c); - - ensure(c->find(WR_VAR(0, 1, 1)) != NULL); - ensure_varcode_equals(c->find(WR_VAR(0, 1, 1))->code(), WR_VAR(0, 1, 1)); - - ensure(c->find(WR_VAR(0, 1, 2)) != NULL); - ensure_varcode_equals(c->find(WR_VAR(0, 1, 2))->code(), WR_VAR(0, 1, 2)); - - ensure(c->find(WR_VAR(0, 1, 7)) != NULL); - ensure_varcode_equals(c->find(WR_VAR(0, 1, 7))->code(), WR_VAR(0, 1, 7)); - - ensure_equals(c->find(WR_VAR(0, 1, 8)), (const Var*)0); -} - -} - -/* vim:set ts=4 sw=4: */ diff --git a/dballe/msg/lua-test.cc b/dballe/msg/lua-test.cc new file mode 100644 index 000000000..cde476b6f --- /dev/null +++ b/dballe/msg/lua-test.cc @@ -0,0 +1,78 @@ +#include "tests.h" +#include "tests-lua.h" +#include "msg.h" + +using namespace dballe; +using namespace dballe::tests; +using namespace wreport; +using namespace std; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("message", []() { + // Test message access + + // Get a test message + Messages msgs = read_msgs("bufr/obs0-1.22.bufr", File::BUFR); + wassert(actual(msgs.size()) == 1u); + Msg& msg = Msg::downcast(msgs[0]); + + dballe::tests::Lua test( + "function test() \n" + " if msg:type() ~= 'synop' then return 'type is '..msg:type()..' instead of synop' end \n" + " if msg:size() ~= 17 then return 'size is '..msg:size()..' instead of 17' end \n" + + " count = 0\n" + " msg:foreach(function(x) count = count + 1 end)\n" + " if count ~= 17 then return 'count is '..count..' instead of 17' end\n" + + " count = 0\n" + " msg:foreach(function(x) count = count + x:size() end)\n" + " if count ~= 43 then return 'count is '..count..' instead of 43' end\n" + + " count = 0\n" + " msg:foreach(function(x) x:foreach(function(y) count = count + 1 end) end)\n" + " if count ~= 43 then return 'count is '..count..' instead of 43' end\n" + + " context = nil\n" + " msg:foreach(function(x) context=x end)\n" + " if context.ltype1 == nil then return 'context.ltype1 is nil' end\n" + " if context.l1 == nil then return 'context.l1 is nil' end\n" + " if context.ltype2 == nil then return 'context.ltype2 is nil' end\n" + " if context.l2 == nil then return 'context.l2 is nil' end\n" + " if context.pind == nil then return 'context.pind is nil' end\n" + " if context.p1 == nil then return 'context.p1 is nil' end\n" + " if context.p2 == nil then return 'context.p2 is nil' end\n" + + " var = msg:find('temp_2m')\n" + " if var == nil then return 'temp_2m is nil' end\n" + " if var:enqd() ~= 289.2 then return 'temp_2m is '..var:enqd()..' instead of 289.2' end\n" + + " var = msg:find('B12101', 103, 2000, nil, nil, 254, 0, 0)\n" + " if var == nil then return 'B12101 is nil' end\n" + " if var:enqd() ~= 289.2 then return 'B12101 is '..var:enqd()..' instead of 289.2' end\n" + "end \n" + ); + + // Push the variable as a global + msg.lua_push(test.L); + lua_setglobal(test.L, "msg"); + + // Check that we can retrieve it + lua_getglobal(test.L, "msg"); + Msg* msg1 = Msg::lua_check(test.L, 1); + lua_pop(test.L, 1); + wassert(actual(&msg == msg1).istrue()); + + wassert(actual(test.run()) == ""); + }); + } +} test("lua"); + +} diff --git a/dballe/msg/lua-tut.cc b/dballe/msg/lua-tut.cc deleted file mode 100644 index 15e644bfd..000000000 --- a/dballe/msg/lua-tut.cc +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2010 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "tests.h" -#include "tests-lua.h" -#include "msg.h" - -using namespace dballe; -using namespace wreport; -using namespace std; - -namespace tut { - -struct lua_shar -{ - lua_shar() - { - } - - ~lua_shar() - { - } -}; -TESTGRP(lua); - - -// Test message access -template<> template<> -void to::test<1>() -{ - // Get a test message - Messages msgs = read_msgs("bufr/obs0-1.22.bufr", File::BUFR); - ensure_equals(msgs.size(), 1u); - Msg& msg = Msg::downcast(msgs[0]); - - dballe::tests::Lua test( - "function test() \n" - " if msg:type() ~= 'synop' then return 'type is '..msg:type()..' instead of synop' end \n" - " if msg:size() ~= 17 then return 'size is '..msg:size()..' instead of 17' end \n" - - " count = 0\n" - " msg:foreach(function(x) count = count + 1 end)\n" - " if count ~= 17 then return 'count is '..count..' instead of 17' end\n" - - " count = 0\n" - " msg:foreach(function(x) count = count + x:size() end)\n" - " if count ~= 43 then return 'count is '..count..' instead of 43' end\n" - - " count = 0\n" - " msg:foreach(function(x) x:foreach(function(y) count = count + 1 end) end)\n" - " if count ~= 43 then return 'count is '..count..' instead of 43' end\n" - - " context = nil\n" - " msg:foreach(function(x) context=x end)\n" - " if context.ltype1 == nil then return 'context.ltype1 is nil' end\n" - " if context.l1 == nil then return 'context.l1 is nil' end\n" - " if context.ltype2 == nil then return 'context.ltype2 is nil' end\n" - " if context.l2 == nil then return 'context.l2 is nil' end\n" - " if context.pind == nil then return 'context.pind is nil' end\n" - " if context.p1 == nil then return 'context.p1 is nil' end\n" - " if context.p2 == nil then return 'context.p2 is nil' end\n" - - " var = msg:find('temp_2m')\n" - " if var == nil then return 'temp_2m is nil' end\n" - " if var:enqd() ~= 289.2 then return 'temp_2m is '..var:enqd()..' instead of 289.2' end\n" - - " var = msg:find('B12101', 103, 2000, nil, nil, 254, 0, 0)\n" - " if var == nil then return 'B12101 is nil' end\n" - " if var:enqd() ~= 289.2 then return 'B12101 is '..var:enqd()..' instead of 289.2' end\n" - "end \n" - ); - - // Push the variable as a global - msg.lua_push(test.L); - lua_setglobal(test.L, "msg"); - - // Check that we can retrieve it - lua_getglobal(test.L, "msg"); - Msg* msg1 = Msg::lua_check(test.L, 1); - lua_pop(test.L, 1); - ensure(&msg == msg1); - - ensure_equals(test.run(), ""); -} - -} - -/* vim:set ts=4 sw=4: */ diff --git a/dballe/msg/msg-test.cc b/dballe/msg/msg-test.cc new file mode 100644 index 000000000..20335d47c --- /dev/null +++ b/dballe/msg/msg-test.cc @@ -0,0 +1,613 @@ +#include "tests.h" +#include "msg.h" +#include "context.h" +#include "core/csv.h" +#include + +using namespace std; +using namespace wreport; +using namespace dballe; +using namespace dballe::tests; + +namespace { + +std::unique_ptr get_matcher(const char* q) +{ + return Matcher::create(dballe::tests::core_query_from_string(q)); +} + +void init(Messages& m) +{ + unique_ptr msg(new Msg); + m.append(move(msg)); +} + +// Ensure that the context vector inside the message is in strict ascending order +void msg_is_sorted(const Msg& msg) +{ + if (msg.data.size() < 2) + return; + for (unsigned i = 0; i < msg.data.size() - 1; ++i) + wassert(actual(msg.data[i]->compare(*msg.data[i + 1])) < 0); +} + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("ordering", []() { + // Test dba_msg internal ordering + Msg msg; + Level lev1(1, 1, 1, 1); + Level lev2(2, 2, 2, 2); + Trange tr1(1, 1, 1); + Trange tr2(2, 2, 2); + unique_ptr av1 = newvar(WR_VAR(0, 1, 1)); + unique_ptr av2 = newvar(WR_VAR(0, 1, 1)); + unique_ptr av3 = newvar(WR_VAR(0, 1, 1)); + unique_ptr av4 = newvar(WR_VAR(0, 1, 1)); + Var* v1 = av1.get(); + Var* v2 = av2.get(); + Var* v3 = av3.get(); + Var* v4 = av4.get(); + + msg.set(move(av1), lev2, tr1); wassert(actual(msg.data.size()) == 1); + msg.set(move(av2), lev2, tr1); wassert(actual(msg.data.size()) == 1); + msg.set(move(av3), lev1, tr1); wassert(actual(msg.data.size()) == 2); + msg.set(move(av4), lev1, tr2); wassert(actual(msg.data.size()) == 3); + + wassert(msg_is_sorted(msg)); + + wassert(actual(msg.get(WR_VAR(0, 1, 1), lev1, tr1)).istrue()); + wassert(actual(msg.get(WR_VAR(0, 1, 1), lev1, tr1)) == v3); + + wassert(actual(msg.get(WR_VAR(0, 1, 1), lev1, tr2)).istrue()); + wassert(actual(msg.get(WR_VAR(0, 1, 1), lev1, tr2)) == v4); + + wassert(actual(msg.get(WR_VAR(0, 1, 1), lev2, tr1)).istrue()); + wassert(actual(msg.get(WR_VAR(0, 1, 1), lev2, tr1)) == v2); + + wassert(actual(msg.get(WR_VAR(0, 1, 2), lev1, tr2)) == (Var*)0); + wassert(actual(msg.get(WR_VAR(0, 1, 1), Level(0, 0, 0, 0), tr1)) == (Var*)0); + wassert(actual(msg.get(WR_VAR(0, 1, 1), Level(3, 3, 3, 3), tr1)) == (Var*)0); + wassert(actual(msg.get(WR_VAR(0, 1, 1), lev1, Trange(3, 3, 3))) == (Var*)0); + }); + add_method("compose", []() { + // Try to write a generic message from scratch + Msg msg; + msg.type = MSG_GENERIC; + //msg->type = MSG_SYNOP; + + // Fill in the dba_msg + msg.seti(WR_VAR(0, 4, 1), 2008, -1, Level(), Trange()); + msg.seti(WR_VAR(0, 4, 2), 5, -1, Level(), Trange()); + msg.seti(WR_VAR(0, 4, 3), 7, -1, Level(), Trange()); + // ... + msg.setd(WR_VAR(0, 5, 1), 45.0, -1, Level(), Trange()); + msg.setd(WR_VAR(0, 6, 1), 11.0, -1, Level(), Trange()); + // ... + msg.setd(WR_VAR(0,12, 101), 273.0, 75, Level(102, 2000), Trange::instant()); + + // Append the dba_msg to a dba_msgs + Messages msgs; + msgs.append(msg); + }); + add_method("repmemo", []() { + // Test repmemo handling + wassert(actual(Msg::type_from_repmemo(Msg::repmemo_from_type(MSG_SYNOP))) == MSG_SYNOP); + wassert(actual(Msg::type_from_repmemo(Msg::repmemo_from_type(MSG_METAR))) == MSG_METAR); + wassert(actual(Msg::type_from_repmemo(Msg::repmemo_from_type(MSG_SHIP))) == MSG_SHIP); + wassert(actual(Msg::type_from_repmemo(Msg::repmemo_from_type(MSG_BUOY))) == MSG_BUOY); + wassert(actual(Msg::type_from_repmemo(Msg::repmemo_from_type(MSG_AIREP))) == MSG_AIREP); + wassert(actual(Msg::type_from_repmemo(Msg::repmemo_from_type(MSG_AMDAR))) == MSG_AMDAR); + wassert(actual(Msg::type_from_repmemo(Msg::repmemo_from_type(MSG_ACARS))) == MSG_ACARS); + wassert(actual(Msg::type_from_repmemo(Msg::repmemo_from_type(MSG_PILOT))) == MSG_PILOT); + wassert(actual(Msg::type_from_repmemo(Msg::repmemo_from_type(MSG_TEMP))) == MSG_TEMP); + wassert(actual(Msg::type_from_repmemo(Msg::repmemo_from_type(MSG_TEMP_SHIP))) == MSG_TEMP_SHIP); + wassert(actual(Msg::type_from_repmemo(Msg::repmemo_from_type(MSG_SAT))) == MSG_SAT); + wassert(actual(Msg::type_from_repmemo(Msg::repmemo_from_type(MSG_POLLUTION))) == MSG_POLLUTION); + wassert(actual(Msg::type_from_repmemo(Msg::repmemo_from_type(MSG_GENERIC))) == MSG_GENERIC); + + wassert(actual(Msg::type_from_repmemo("synop")) == MSG_SYNOP); + wassert(actual(Msg::type_from_repmemo("SYNOP")) == MSG_SYNOP); // Case insensitive + wassert(actual(Msg::type_from_repmemo("metar")) == MSG_METAR); + wassert(actual(Msg::type_from_repmemo("ship")) == MSG_SHIP); + wassert(actual(Msg::type_from_repmemo("buoy")) == MSG_BUOY); + wassert(actual(Msg::type_from_repmemo("airep")) == MSG_AIREP); + wassert(actual(Msg::type_from_repmemo("amdar")) == MSG_AMDAR); + wassert(actual(Msg::type_from_repmemo("acars")) == MSG_ACARS); + wassert(actual(Msg::type_from_repmemo("pilot")) == MSG_PILOT); + wassert(actual(Msg::type_from_repmemo("temp")) == MSG_TEMP); + wassert(actual(Msg::type_from_repmemo("tempship")) == MSG_TEMP_SHIP); + wassert(actual(Msg::type_from_repmemo("satellite")) == MSG_SAT); + wassert(actual(Msg::type_from_repmemo("pollution")) == MSG_POLLUTION); + wassert(actual(Msg::type_from_repmemo("generic")) == MSG_GENERIC); + wassert(actual(Msg::type_from_repmemo("antani")) == MSG_GENERIC); + wassert(actual(Msg::type_from_repmemo("")) == MSG_GENERIC); + wassert(actual(Msg::type_from_repmemo(NULL)) == MSG_GENERIC); + }); + add_method("msg_match_varid", []() { + // Test var_id matcher + auto m = get_matcher("context_id=1"); + + Msg matched; + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_NO); + + matched.set(newvar(WR_VAR(0, 12, 101), 21.5), Level(1), Trange::instant()); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_NO); + + std::unique_ptr var = newvar(WR_VAR(0, 12, 103), 18.5); + var->seta(newvar(WR_VAR(0, 33, 195), 1)); + matched.set(move(var), Level(1), Trange::instant()); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_YES); + }); + add_method("msg_match_stationid", []() { + // Test station_id matcher + auto m = get_matcher("ana_id=1"); + + Msg matched; + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_NO); + + matched.seti(WR_VAR(0, 1, 192), 2, -1, Level(), Trange()); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_NO); + + matched.seti(WR_VAR(0, 1, 192), 1, -1, Level(), Trange()); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_YES); + }); + add_method("msg_match_blockstation", []() { + // Test station WMO matcher + { + auto m = get_matcher("block=11"); + + Msg matched; + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_NO); + + matched.set_block(1); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_NO); + + matched.set_block(11); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_YES); + + matched.set_station(222); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_YES); + } + + { + auto m = get_matcher("block=11, station=222"); + + Msg matched; + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_NO); + + matched.set_block(1); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_NO); + + matched.set_block(11); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_NO); + + matched.set_station(22); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_NO); + + matched.set_station(222); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_YES); + + matched.set_block(1); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_NO); + } + }); + add_method("msg_match_date", []() { + // Test date matcher + { + auto m = get_matcher("yearmin=2000"); + + Msg matched; + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_NO); + + matched.set_datetime(Datetime(1999)); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_NO); + + matched.set_datetime(Datetime(2000)); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_YES); + } + { + auto m = get_matcher("yearmax=2000"); + + Msg matched; + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_NO); + + matched.set_datetime(Datetime(2001)); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_NO); + + matched.set_datetime(Datetime(2000)); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_YES); + } + { + auto m = get_matcher("yearmin=2000, yearmax=2010"); + + Msg matched; + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_NO); + + matched.set_datetime(Datetime(1999)); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_NO); + + matched.set_datetime(Datetime(2011)); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_NO); + + matched.set_datetime(Datetime(2000)); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_YES); + + matched.set_datetime(Datetime(2005)); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_YES); + + matched.set_datetime(Datetime(2010)); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_YES); + } + }); + add_method("msg_match_coords", []() { + // Test coordinates matcher + { + auto m = get_matcher("latmin=45.00"); + + Msg matched; + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_NO); + + matched.set_latitude(43.0); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_NO); + + matched.set_latitude(45.0); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_YES); + matched.set_latitude(46.0); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_YES); + } + { + auto m = get_matcher("latmax=45.00"); + + Msg matched; + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_NO); + + matched.set_latitude(46.0); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_NO); + + matched.set_latitude(45.0); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_YES); + matched.set_latitude(44.0); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_YES); + } + { + auto m = get_matcher("lonmin=45.00, lonmax=180.0"); + + Msg matched; + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_NO); + + matched.set_longitude(43.0); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_NO); + + matched.set_longitude(45.0); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_YES); + matched.set_longitude(45.0); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_YES); + } + { + auto m = get_matcher("lonmin=-180, lonmax=45.0"); + + Msg matched; + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_NO); + + matched.set_longitude(46.0); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_NO); + + matched.set_longitude(45.0); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_YES); + matched.set_longitude(44.0); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_YES); + } + { + auto m = get_matcher("latmin=45.0, latmax=46.0, lonmin=10.0, lonmax=12.0"); + + Msg matched; + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_NO); + + matched.set_latitude(45.5); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_NO); + + matched.set_longitude(13.0); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_NO); + + matched.set_longitude(11.0); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_YES); + } + }); + add_method("msg_match_repmemo", []() { + // Test rep_memo matcher + auto m = get_matcher("rep_memo=synop"); + + Msg matched; + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_NO); + + matched.set_rep_memo("temp"); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_NO); + + matched.set_rep_memo("synop"); + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_YES); + }); + add_method("msg_match_empty", []() { + // Test empty matcher + std::unique_ptr m = Matcher::create(core::Query()); + + Msg matched; + wassert(actual_matcher_result(m->match(MatchedMsg(matched))) == matcher::MATCH_YES); + }); + add_method("msg_csv", []() { + // Test CSV encoding/decoding + Msg msg; + msg.type = MSG_TEMP; + //msg->type = MSG_SYNOP; + + // Fill in the dba_msg + msg.set_datetime(Datetime(2011, 5, 3, 12, 30, 45)); + msg.set_latitude(45.0); + msg.set_longitude(11.0); + msg.set_temp_2m(273.0, 75); + msg.set_height_station(1230.0); + msg.set_st_name("antani"); + msg.set_rep_memo("temp"); + + stringstream str; + msg.to_csv(str); + + Msg msg1; + str.seekg(0); + CSVReader in(str); + wassert(actual(in.next()).istrue()); + msg1.from_csv(in); + + notes::Collect c(cerr); + wassert(actual(msg.diff(msg1)) == 0u); + }); + add_method("msgs_match_varid", []() { + // Test var_id matcher + auto m = get_matcher("context_id=1"); + + Messages matched; init(matched); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_NO); + + Msg::downcast(matched[0]).set(newvar(WR_VAR(0, 12, 101), 21.5), Level(1), Trange::instant()); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_NO); + + std::unique_ptr var = newvar(WR_VAR(0, 12, 103), 18.5); + var->seta(newvar(WR_VAR(0, 33, 195), 1)); + Msg::downcast(matched[0]).set(move(var), Level(1), Trange::instant()); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_YES); + }); + add_method("msgs_match_stationid", []() { + // Test station_id matcher + auto m = get_matcher("ana_id=1"); + + Messages matched; init(matched); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_NO); + + Msg::downcast(matched[0]).seti(WR_VAR(0, 1, 192), 2, -1, Level(), Trange()); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_NO); + + Msg::downcast(matched[0]).seti(WR_VAR(0, 1, 192), 1, -1, Level(), Trange()); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_YES); + }); + add_method("msgs_match_blockstation", []() { + // Test station WMO matcher + { + auto m = get_matcher("block=11"); + + Messages matched; init(matched); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_NO); + + Msg::downcast(matched[0]).set_block(1); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_NO); + + Msg::downcast(matched[0]).set_block(11); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_YES); + + Msg::downcast(matched[0]).set_station(222); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_YES); + } + + { + auto m = get_matcher("block=11, station=222"); + + Messages matched; init(matched); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_NO); + + Msg::downcast(matched[0]).set_block(1); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_NO); + + Msg::downcast(matched[0]).set_block(11); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_NO); + + Msg::downcast(matched[0]).set_station(22); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_NO); + + Msg::downcast(matched[0]).set_station(222); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_YES); + + Msg::downcast(matched[0]).set_block(1); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_NO); + } + }); + add_method("msgs_match_date", []() { + // Test date matcher + { + auto m = get_matcher("yearmin=2000"); + + Messages matched; init(matched); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_NO); + + Msg::downcast(matched[0]).set_datetime(Datetime(1999)); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_NO); + + Msg::downcast(matched[0]).set_datetime(Datetime(2000)); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_YES); + } + { + auto m = get_matcher("yearmax=2000"); + + Messages matched; init(matched); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_NO); + + Msg::downcast(matched[0]).set_datetime(Datetime(2001)); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_NO); + + Msg::downcast(matched[0]).set_datetime(Datetime(2000)); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_YES); + } + { + auto m = get_matcher("yearmin=2000, yearmax=2010"); + + Messages matched; init(matched); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_NO); + + Msg::downcast(matched[0]).set_datetime(Datetime(1999)); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_NO); + + Msg::downcast(matched[0]).set_datetime(Datetime(2011)); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_NO); + + Msg::downcast(matched[0]).set_datetime(Datetime(2000)); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_YES); + + Msg::downcast(matched[0]).set_datetime(Datetime(2005)); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_YES); + + Msg::downcast(matched[0]).set_datetime(Datetime(2010)); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_YES); + } + }); + add_method("msgs_match_coords", []() { + // Test coordinates matcher + { + auto m = get_matcher("latmin=45.00"); + + Messages matched; init(matched); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_NO); + + Msg::downcast(matched[0]).set_latitude(43.0); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_NO); + + Msg::downcast(matched[0]).set_latitude(45.0); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_YES); + Msg::downcast(matched[0]).set_latitude(46.0); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_YES); + } + { + auto m = get_matcher("latmax=45.00"); + + Messages matched; init(matched); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_NO); + + Msg::downcast(matched[0]).set_latitude(46.0); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_NO); + + Msg::downcast(matched[0]).set_latitude(45.0); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_YES); + Msg::downcast(matched[0]).set_latitude(44.0); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_YES); + } + { + auto m = get_matcher("lonmin=45.00, lonmax=180.0"); + + Messages matched; init(matched); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_NO); + + Msg::downcast(matched[0]).set_longitude(43.0); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_NO); + + Msg::downcast(matched[0]).set_longitude(45.0); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_YES); + Msg::downcast(matched[0]).set_longitude(45.0); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_YES); + } + { + auto m = get_matcher("lonmin=-180, lonmax=45.0"); + + Messages matched; init(matched); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_NO); + + Msg::downcast(matched[0]).set_longitude(46.0); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_NO); + + Msg::downcast(matched[0]).set_longitude(45.0); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_YES); + Msg::downcast(matched[0]).set_longitude(44.0); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_YES); + } + { + auto m = get_matcher("latmin=45.0, latmax=46.0, lonmin=10.0, lonmax=12.0"); + + Messages matched; init(matched); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_NO); + + Msg::downcast(matched[0]).set_latitude(45.5); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_NO); + + Msg::downcast(matched[0]).set_longitude(13.0); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_NO); + + Msg::downcast(matched[0]).set_longitude(11.0); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_YES); + } + }); + add_method("msgs_match_repmemo", []() { + // Test rep_memo matcher + auto m = get_matcher("rep_memo=synop"); + + Messages matched; init(matched); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_NO); + + Msg::downcast(matched[0]).set_rep_memo("temp"); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_NO); + + Msg::downcast(matched[0]).set_rep_memo("synop"); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_YES); + }); + add_method("msgs_match_empty", []() { + // Test empty matcher + std::unique_ptr m = Matcher::create(core::Query()); + + Messages matched; init(matched); + wassert(actual_matcher_result(m->match(MatchedMessages(matched))) == matcher::MATCH_YES); + }); + add_method("msgs_csv", []() { + // Test CSV encoding/decoding + Messages msgs = read_msgs("bufr/synop-evapo.bufr", File::BUFR); + + // Serialise to CSV + stringstream str; + msg::messages_to_csv(msgs, str); + + // Read back + str.seekg(0); + CSVReader in(str); + wassert(actual(in.next()).istrue()); + Messages msgs1 = msg::messages_from_csv(in); + + // Normalise before compare + for (auto& i: msgs) + { + Msg& m = Msg::downcast(i); + m.set_rep_memo("synop"); + //m.set_second(0); + } + + notes::Collect c(cerr); + wassert(actual(msgs.diff(msgs1)) == 0u); + }); + add_method("msgs_copy", []() { + Messages msgs = read_msgs("bufr/synop-evapo.bufr", File::BUFR); + Messages msgs1(msgs); + Messages msgs2; + msgs2 = msgs; + msgs1 = msgs1; + }); + } +} test("msg_msg"); + +} diff --git a/dballe/msg/msg-tut.cc b/dballe/msg/msg-tut.cc deleted file mode 100644 index f6d99dbaa..000000000 --- a/dballe/msg/msg-tut.cc +++ /dev/null @@ -1,618 +0,0 @@ -#include "tests.h" -#include "msg.h" -#include "context.h" -#include "core/csv.h" -#include - -using namespace dballe; -using namespace wreport; -using namespace std; - -using namespace std; -using namespace wibble::tests; -using namespace dballe; - -namespace { - -std::unique_ptr get_matcher(const char* q) -{ - return Matcher::create(dballe::tests::core_query_from_string(q)); -} - -void init(Messages& m) -{ - unique_ptr msg(new Msg); - m.append(move(msg)); -} - -// Ensure that the context vector inside the message is in strict ascending order -void _ensure_msg_is_sorted(const wibble::tests::Location& loc, const Msg& msg) -{ - if (msg.data.size() < 2) - return; - for (unsigned i = 0; i < msg.data.size() - 1; ++i) - inner_ensure(msg.data[i]->compare(*msg.data[i + 1]) < 0); -} -#define ensure_msg_is_sorted(x) _ensure_msg_is_sorted(wibble::tests::Location(__FILE__, __LINE__, "msg is sorted in " #x), (x)) - - -typedef dballe::tests::test_group<> test_group; -typedef test_group::Test Test; -typedef test_group::Fixture Fixture; - -std::vector tests { - Test("ordering", [](Fixture& f) { - // Test dba_msg internal ordering - Msg msg; - Level lev1(1, 1, 1, 1); - Level lev2(2, 2, 2, 2); - Trange tr1(1, 1, 1); - Trange tr2(2, 2, 2); - unique_ptr av1 = newvar(WR_VAR(0, 1, 1)); - unique_ptr av2 = newvar(WR_VAR(0, 1, 1)); - unique_ptr av3 = newvar(WR_VAR(0, 1, 1)); - unique_ptr av4 = newvar(WR_VAR(0, 1, 1)); - Var* v1 = av1.get(); - Var* v2 = av2.get(); - Var* v3 = av3.get(); - Var* v4 = av4.get(); - - msg.set(move(av1), lev2, tr1); ensure_equals(msg.data.size(), 1); - msg.set(move(av2), lev2, tr1); ensure_equals(msg.data.size(), 1); - msg.set(move(av3), lev1, tr1); ensure_equals(msg.data.size(), 2); - msg.set(move(av4), lev1, tr2); ensure_equals(msg.data.size(), 3); - - ensure_msg_is_sorted(msg); - - ensure(msg.get(WR_VAR(0, 1, 1), lev1, tr1) != NULL); - ensure_equals(msg.get(WR_VAR(0, 1, 1), lev1, tr1), v3); - - ensure(msg.get(WR_VAR(0, 1, 1), lev1, tr2) != NULL); - ensure_equals(msg.get(WR_VAR(0, 1, 1), lev1, tr2), v4); - - ensure(msg.get(WR_VAR(0, 1, 1), lev2, tr1) != NULL); - ensure_equals(msg.get(WR_VAR(0, 1, 1), lev2, tr1), v2); - - ensure_equals(msg.get(WR_VAR(0, 1, 2), lev1, tr2), (Var*)0); - ensure_equals(msg.get(WR_VAR(0, 1, 1), Level(0, 0, 0, 0), tr1), (Var*)0); - ensure_equals(msg.get(WR_VAR(0, 1, 1), Level(3, 3, 3, 3), tr1), (Var*)0); - ensure_equals(msg.get(WR_VAR(0, 1, 1), lev1, Trange(3, 3, 3)), (Var*)0); - }), - Test("compose", [](Fixture& f) { - // Try to write a generic message from scratch - Msg msg; - msg.type = MSG_GENERIC; - //msg->type = MSG_SYNOP; - - // Fill in the dba_msg - msg.seti(WR_VAR(0, 4, 1), 2008, -1, Level(), Trange()); - msg.seti(WR_VAR(0, 4, 2), 5, -1, Level(), Trange()); - msg.seti(WR_VAR(0, 4, 3), 7, -1, Level(), Trange()); - // ... - msg.setd(WR_VAR(0, 5, 1), 45.0, -1, Level(), Trange()); - msg.setd(WR_VAR(0, 6, 1), 11.0, -1, Level(), Trange()); - // ... - msg.setd(WR_VAR(0,12, 101), 273.0, 75, Level(102, 2000), Trange::instant()); - - // Append the dba_msg to a dba_msgs - Messages msgs; - msgs.append(msg); - }), - Test("repmemo", [](Fixture& f) { - // Test repmemo handling - ensure_equals(Msg::type_from_repmemo(Msg::repmemo_from_type(MSG_SYNOP)), MSG_SYNOP); - ensure_equals(Msg::type_from_repmemo(Msg::repmemo_from_type(MSG_METAR)), MSG_METAR); - ensure_equals(Msg::type_from_repmemo(Msg::repmemo_from_type(MSG_SHIP)), MSG_SHIP); - ensure_equals(Msg::type_from_repmemo(Msg::repmemo_from_type(MSG_BUOY)), MSG_BUOY); - ensure_equals(Msg::type_from_repmemo(Msg::repmemo_from_type(MSG_AIREP)), MSG_AIREP); - ensure_equals(Msg::type_from_repmemo(Msg::repmemo_from_type(MSG_AMDAR)), MSG_AMDAR); - ensure_equals(Msg::type_from_repmemo(Msg::repmemo_from_type(MSG_ACARS)), MSG_ACARS); - ensure_equals(Msg::type_from_repmemo(Msg::repmemo_from_type(MSG_PILOT)), MSG_PILOT); - ensure_equals(Msg::type_from_repmemo(Msg::repmemo_from_type(MSG_TEMP)), MSG_TEMP); - ensure_equals(Msg::type_from_repmemo(Msg::repmemo_from_type(MSG_TEMP_SHIP)), MSG_TEMP_SHIP); - ensure_equals(Msg::type_from_repmemo(Msg::repmemo_from_type(MSG_SAT)), MSG_SAT); - ensure_equals(Msg::type_from_repmemo(Msg::repmemo_from_type(MSG_POLLUTION)), MSG_POLLUTION); - ensure_equals(Msg::type_from_repmemo(Msg::repmemo_from_type(MSG_GENERIC)), MSG_GENERIC); - - ensure_equals(Msg::type_from_repmemo("synop"), MSG_SYNOP); - ensure_equals(Msg::type_from_repmemo("SYNOP"), MSG_SYNOP); // Case insensitive - ensure_equals(Msg::type_from_repmemo("metar"), MSG_METAR); - ensure_equals(Msg::type_from_repmemo("ship"), MSG_SHIP); - ensure_equals(Msg::type_from_repmemo("buoy"), MSG_BUOY); - ensure_equals(Msg::type_from_repmemo("airep"), MSG_AIREP); - ensure_equals(Msg::type_from_repmemo("amdar"), MSG_AMDAR); - ensure_equals(Msg::type_from_repmemo("acars"), MSG_ACARS); - ensure_equals(Msg::type_from_repmemo("pilot"), MSG_PILOT); - ensure_equals(Msg::type_from_repmemo("temp"), MSG_TEMP); - ensure_equals(Msg::type_from_repmemo("tempship"), MSG_TEMP_SHIP); - ensure_equals(Msg::type_from_repmemo("satellite"), MSG_SAT); - ensure_equals(Msg::type_from_repmemo("pollution"), MSG_POLLUTION); - ensure_equals(Msg::type_from_repmemo("generic"), MSG_GENERIC); - ensure_equals(Msg::type_from_repmemo("antani"), MSG_GENERIC); - ensure_equals(Msg::type_from_repmemo(""), MSG_GENERIC); - ensure_equals(Msg::type_from_repmemo(NULL), MSG_GENERIC); - }), - Test("msg_match_varid", [](Fixture& f) { - // Test var_id matcher - auto m = get_matcher("context_id=1"); - - Msg matched; - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_NO); - - matched.set(newvar(WR_VAR(0, 12, 101), 21.5), Level(1), Trange::instant()); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_NO); - - std::unique_ptr var = newvar(WR_VAR(0, 12, 103), 18.5); - var->seta(newvar(WR_VAR(0, 33, 195), 1)); - matched.set(move(var), Level(1), Trange::instant()); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_YES); - }), - Test("msg_match_stationid", [](Fixture& f) { - // Test station_id matcher - auto m = get_matcher("ana_id=1"); - - Msg matched; - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_NO); - - matched.seti(WR_VAR(0, 1, 192), 2, -1, Level(), Trange()); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_NO); - - matched.seti(WR_VAR(0, 1, 192), 1, -1, Level(), Trange()); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_YES); - }), - Test("msg_match_blockstation", [](Fixture& f) { - // Test station WMO matcher - { - auto m = get_matcher("block=11"); - - Msg matched; - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_NO); - - matched.set_block(1); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_NO); - - matched.set_block(11); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_YES); - - matched.set_station(222); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_YES); - } - - { - auto m = get_matcher("block=11, station=222"); - - Msg matched; - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_NO); - - matched.set_block(1); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_NO); - - matched.set_block(11); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_NO); - - matched.set_station(22); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_NO); - - matched.set_station(222); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_YES); - - matched.set_block(1); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_NO); - } - }), - Test("msg_match_date", [](Fixture& f) { - // Test date matcher - { - auto m = get_matcher("yearmin=2000"); - - Msg matched; - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_NO); - - matched.set_datetime(Datetime(1999)); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_NO); - - matched.set_datetime(Datetime(2000)); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_YES); - } - { - auto m = get_matcher("yearmax=2000"); - - Msg matched; - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_NO); - - matched.set_datetime(Datetime(2001)); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_NO); - - matched.set_datetime(Datetime(2000)); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_YES); - } - { - auto m = get_matcher("yearmin=2000, yearmax=2010"); - - Msg matched; - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_NO); - - matched.set_datetime(Datetime(1999)); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_NO); - - matched.set_datetime(Datetime(2011)); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_NO); - - matched.set_datetime(Datetime(2000)); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_YES); - - matched.set_datetime(Datetime(2005)); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_YES); - - matched.set_datetime(Datetime(2010)); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_YES); - } - }), - Test("msg_match_coords", [](Fixture& f) { - // Test coordinates matcher - { - auto m = get_matcher("latmin=45.00"); - - Msg matched; - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_NO); - - matched.set_latitude(43.0); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_NO); - - matched.set_latitude(45.0); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_YES); - matched.set_latitude(46.0); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_YES); - } - { - auto m = get_matcher("latmax=45.00"); - - Msg matched; - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_NO); - - matched.set_latitude(46.0); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_NO); - - matched.set_latitude(45.0); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_YES); - matched.set_latitude(44.0); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_YES); - } - { - auto m = get_matcher("lonmin=45.00, lonmax=180.0"); - - Msg matched; - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_NO); - - matched.set_longitude(43.0); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_NO); - - matched.set_longitude(45.0); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_YES); - matched.set_longitude(45.0); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_YES); - } - { - auto m = get_matcher("lonmin=-180, lonmax=45.0"); - - Msg matched; - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_NO); - - matched.set_longitude(46.0); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_NO); - - matched.set_longitude(45.0); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_YES); - matched.set_longitude(44.0); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_YES); - } - { - auto m = get_matcher("latmin=45.0, latmax=46.0, lonmin=10.0, lonmax=12.0"); - - Msg matched; - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_NO); - - matched.set_latitude(45.5); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_NO); - - matched.set_longitude(13.0); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_NO); - - matched.set_longitude(11.0); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_YES); - } - }), - Test("msg_match_repmemo", [](Fixture& f) { - // Test rep_memo matcher - auto m = get_matcher("rep_memo=synop"); - - Msg matched; - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_NO); - - matched.set_rep_memo("temp"); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_NO); - - matched.set_rep_memo("synop"); - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_YES); - }), - Test("msg_match_empty", [](Fixture& f) { - // Test empty matcher - std::unique_ptr m = Matcher::create(core::Query()); - - Msg matched; - ensure(m->match(MatchedMsg(matched)) == matcher::MATCH_YES); - }), - Test("msg_csv", [](Fixture& f) { - // Test CSV encoding/decoding - Msg msg; - msg.type = MSG_TEMP; - //msg->type = MSG_SYNOP; - - // Fill in the dba_msg - msg.set_datetime(Datetime(2011, 5, 3, 12, 30, 45)); - msg.set_latitude(45.0); - msg.set_longitude(11.0); - msg.set_temp_2m(273.0, 75); - msg.set_height_station(1230.0); - msg.set_st_name("antani"); - msg.set_rep_memo("temp"); - - stringstream str; - msg.to_csv(str); - - Msg msg1; - str.seekg(0); - CSVReader in(str); - ensure(in.next()); - msg1.from_csv(in); - - notes::Collect c(cerr); - ensure_equals(msg.diff(msg1), 0u); - }), - Test("msgs_match_varid", [](Fixture& f) { - // Test var_id matcher - auto m = get_matcher("context_id=1"); - - Messages matched; init(matched); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_NO); - - Msg::downcast(matched[0]).set(newvar(WR_VAR(0, 12, 101), 21.5), Level(1), Trange::instant()); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_NO); - - std::unique_ptr var = newvar(WR_VAR(0, 12, 103), 18.5); - var->seta(newvar(WR_VAR(0, 33, 195), 1)); - Msg::downcast(matched[0]).set(move(var), Level(1), Trange::instant()); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_YES); - }), - Test("msgs_match_stationid", [](Fixture& f) { - // Test station_id matcher - auto m = get_matcher("ana_id=1"); - - Messages matched; init(matched); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_NO); - - Msg::downcast(matched[0]).seti(WR_VAR(0, 1, 192), 2, -1, Level(), Trange()); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_NO); - - Msg::downcast(matched[0]).seti(WR_VAR(0, 1, 192), 1, -1, Level(), Trange()); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_YES); - }), - Test("msgs_match_blockstation", [](Fixture& f) { - // Test station WMO matcher - { - auto m = get_matcher("block=11"); - - Messages matched; init(matched); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_NO); - - Msg::downcast(matched[0]).set_block(1); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_NO); - - Msg::downcast(matched[0]).set_block(11); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_YES); - - Msg::downcast(matched[0]).set_station(222); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_YES); - } - - { - auto m = get_matcher("block=11, station=222"); - - Messages matched; init(matched); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_NO); - - Msg::downcast(matched[0]).set_block(1); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_NO); - - Msg::downcast(matched[0]).set_block(11); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_NO); - - Msg::downcast(matched[0]).set_station(22); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_NO); - - Msg::downcast(matched[0]).set_station(222); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_YES); - - Msg::downcast(matched[0]).set_block(1); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_NO); - } - }), - Test("msgs_match_date", [](Fixture& f) { - // Test date matcher - { - auto m = get_matcher("yearmin=2000"); - - Messages matched; init(matched); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_NO); - - Msg::downcast(matched[0]).set_datetime(Datetime(1999)); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_NO); - - Msg::downcast(matched[0]).set_datetime(Datetime(2000)); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_YES); - } - { - auto m = get_matcher("yearmax=2000"); - - Messages matched; init(matched); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_NO); - - Msg::downcast(matched[0]).set_datetime(Datetime(2001)); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_NO); - - Msg::downcast(matched[0]).set_datetime(Datetime(2000)); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_YES); - } - { - auto m = get_matcher("yearmin=2000, yearmax=2010"); - - Messages matched; init(matched); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_NO); - - Msg::downcast(matched[0]).set_datetime(Datetime(1999)); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_NO); - - Msg::downcast(matched[0]).set_datetime(Datetime(2011)); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_NO); - - Msg::downcast(matched[0]).set_datetime(Datetime(2000)); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_YES); - - Msg::downcast(matched[0]).set_datetime(Datetime(2005)); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_YES); - - Msg::downcast(matched[0]).set_datetime(Datetime(2010)); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_YES); - } - }), - Test("msgs_match_coords", [](Fixture& f) { - // Test coordinates matcher - { - auto m = get_matcher("latmin=45.00"); - - Messages matched; init(matched); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_NO); - - Msg::downcast(matched[0]).set_latitude(43.0); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_NO); - - Msg::downcast(matched[0]).set_latitude(45.0); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_YES); - Msg::downcast(matched[0]).set_latitude(46.0); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_YES); - } - { - auto m = get_matcher("latmax=45.00"); - - Messages matched; init(matched); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_NO); - - Msg::downcast(matched[0]).set_latitude(46.0); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_NO); - - Msg::downcast(matched[0]).set_latitude(45.0); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_YES); - Msg::downcast(matched[0]).set_latitude(44.0); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_YES); - } - { - auto m = get_matcher("lonmin=45.00, lonmax=180.0"); - - Messages matched; init(matched); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_NO); - - Msg::downcast(matched[0]).set_longitude(43.0); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_NO); - - Msg::downcast(matched[0]).set_longitude(45.0); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_YES); - Msg::downcast(matched[0]).set_longitude(45.0); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_YES); - } - { - auto m = get_matcher("lonmin=-180, lonmax=45.0"); - - Messages matched; init(matched); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_NO); - - Msg::downcast(matched[0]).set_longitude(46.0); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_NO); - - Msg::downcast(matched[0]).set_longitude(45.0); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_YES); - Msg::downcast(matched[0]).set_longitude(44.0); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_YES); - } - { - auto m = get_matcher("latmin=45.0, latmax=46.0, lonmin=10.0, lonmax=12.0"); - - Messages matched; init(matched); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_NO); - - Msg::downcast(matched[0]).set_latitude(45.5); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_NO); - - Msg::downcast(matched[0]).set_longitude(13.0); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_NO); - - Msg::downcast(matched[0]).set_longitude(11.0); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_YES); - } - }), - Test("msgs_match_repmemo", [](Fixture& f) { - // Test rep_memo matcher - auto m = get_matcher("rep_memo=synop"); - - Messages matched; init(matched); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_NO); - - Msg::downcast(matched[0]).set_rep_memo("temp"); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_NO); - - Msg::downcast(matched[0]).set_rep_memo("synop"); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_YES); - }), - Test("msgs_match_empty", [](Fixture& f) { - // Test empty matcher - std::unique_ptr m = Matcher::create(core::Query()); - - Messages matched; init(matched); - ensure(m->match(MatchedMessages(matched)) == matcher::MATCH_YES); - }), - Test("msgs_csv", [](Fixture& f) { - // Test CSV encoding/decoding - Messages msgs = read_msgs("bufr/synop-evapo.bufr", File::BUFR); - - // Serialise to CSV - stringstream str; - msg::messages_to_csv(msgs, str); - - // Read back - str.seekg(0); - CSVReader in(str); - ensure(in.next()); - Messages msgs1 = msg::messages_from_csv(in); - - // Normalise before compare - for (auto& i: msgs) - { - Msg& m = Msg::downcast(i); - m.set_rep_memo("synop"); - //m.set_second(0); - } - - notes::Collect c(cerr); - ensure_equals(msgs.diff(msgs1), 0u); - }), - Test("msgs_copy", [](Fixture& f) { - Messages msgs = read_msgs("bufr/synop-evapo.bufr", File::BUFR); - Messages msgs1(msgs); - Messages msgs2; - msgs2 = msgs; - msgs1 = msgs1; - }), -}; - -test_group newtg("msg_msg", tests); - -} diff --git a/dballe/msg/tests.cc b/dballe/msg/tests.cc index 50036208b..598ae8310 100644 --- a/dballe/msg/tests.cc +++ b/dballe/msg/tests.cc @@ -1,22 +1,3 @@ -/* - * Copyright (C) 2005--2010 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - #include "tests.h" #include "codec.h" #include "wr_codec.h" @@ -28,8 +9,6 @@ #include #include #include -#include - #include #include #include @@ -37,8 +16,6 @@ #include #include -using namespace wibble; -using namespace wibble::tests; using namespace wreport; using namespace std; @@ -134,18 +111,14 @@ const char* aof_files[] = { NULL, }; -Messages _read_msgs(const wibble::tests::Location& loc, const char* filename, File::Encoding type, const msg::Importer::Options& opts) +Messages read_msgs(const char* filename, File::Encoding type, const msg::Importer::Options& opts) { - try { - BinaryMessage raw = read_rawmsg(filename, type); - std::unique_ptr importer = msg::Importer::create(type, opts); - return importer->from_binary(raw); - } catch (std::exception& e) { - throw tut::failure(loc.msg(string("cannot read ") + filename + ": " + e.what())); - } + BinaryMessage raw = wcallchecked(read_rawmsg(filename, type)); + std::unique_ptr importer = msg::Importer::create(type, opts); + return importer->from_binary(raw); } -Messages _read_msgs_csv(const Location& loc, const char* filename) +Messages read_msgs_csv(const char* filename) { std::string fname = datafile(filename); CSVReader reader(fname); @@ -155,12 +128,12 @@ Messages _read_msgs_csv(const Location& loc, const char* filename) { std::stringstream ss; ss << "cannot find the start of CSV message in " << fname; - throw tut::failure(loc.msg(ss.str())); + throw TestFailed(ss.str()); } return msgs; } -unique_ptr export_msgs(WIBBLE_TEST_LOCPRM, File::Encoding enctype, const Messages& in, const std::string& tag, const dballe::msg::Exporter::Options& opts) +unique_ptr export_msgs(File::Encoding enctype, const Messages& in, const std::string& tag, const dballe::msg::Exporter::Options& opts) { try { std::unique_ptr exporter(msg::Exporter::create(enctype, opts)); @@ -168,7 +141,7 @@ unique_ptr export_msgs(WIBBLE_TEST_LOCPRM, File::Encoding enctype, con } catch (std::exception& e) { dballe::tests::dump("bul-" + tag, in); //dballe::tests::dump("msg-" + tag, out); - wibble_test_location.fail_test("cannot export to bulletin (" + tag + "): " + e.what()); + throw TestFailed("cannot export to bulletin (" + tag + "): " + e.what()); } } @@ -191,34 +164,32 @@ void track_different_msgs(const Messages& msgs1, const Messages& msgs2, const st dump(prefix + "2", msgs2, "second message"); } -void _ensure_msg_undef(const wibble::tests::Location& loc, const Message& msg, int shortcut) +void ActualMessage::is_undef(int shortcut) const { - const Var* var = Msg::downcast(msg).find_by_id(shortcut); - if (var && var->isset()) - { - std::stringstream ss; - ss << "value is " << var->enqc() << " instead of being undefined"; - throw tut::failure(loc.msg(ss.str())); - } + const Var* var = Msg::downcast(_actual).find_by_id(shortcut); + if (!var || !var->isset()) return; + std::stringstream ss; + ss << "value is " << var->enqc() << " instead of being undefined"; + throw TestFailed(ss.str()); } -const Var& _want_var(const Location& loc, const Message& msg, int shortcut) +const Var& want_var(const Message& msg, int shortcut) { const Var* var = Msg::downcast(msg).find_by_id(shortcut); if (!var) - throw tut::failure(loc.msg("value is missing")); + throw TestFailed("value is missing"); if (!var->isset()) - throw tut::failure(loc.msg("value is present but undefined")); + throw TestFailed("value is present but undefined"); return *var; } -const Var& _want_var(const Location& loc, const Message& msg, wreport::Varcode code, const dballe::Level& lev, const dballe::Trange& tr) +const Var& want_var(const Message& msg, wreport::Varcode code, const dballe::Level& lev, const dballe::Trange& tr) { const Var* var = msg.get(code, lev, tr); if (!var) - throw tut::failure(loc.msg("value is missing")); + throw TestFailed("value is missing"); if (!var->isset()) - throw tut::failure(loc.msg("value is present but undefined")); + throw TestFailed("value is present but undefined"); return *var; } @@ -687,7 +658,7 @@ void TestCodec::configure_ecmwf_to_wmo_tweaks() after_convert_import.add(new tweaks::StripDatetimeVars); } -void TestCodec::do_compare(WIBBLE_TEST_LOCPRM, const TestMessage& msg1, const TestMessage& msg2) +void TestCodec::do_compare(const TestMessage& msg1, const TestMessage& msg2) { // Compare msgs { @@ -699,16 +670,26 @@ void TestCodec::do_compare(WIBBLE_TEST_LOCPRM, const TestMessage& msg1, const Te msg1.dump(); msg2.dump(); dballe::tests::dump("diffs", str.str(), "details of differences"); - throw tut::failure(wibble_test_location.msg(str::fmtf("found %d differences", diffs))); + std::stringstream ss; + ss << "found " << diffs << " differences"; + throw TestFailed(ss.str()); } } // Compare bulletins try { if (msg2.bulletin->subsets.size() != (unsigned)expected_subsets) - throw tut::failure(wibble_test_location.msg(str::fmtf("Number of subsets differ from expected: %zd != %d\n", msg2.bulletin->subsets.size(), expected_subsets))); + { + std::stringstream ss; + ss << "Number of subsets differ from expected: " << msg2.bulletin->subsets.size() << " != " << expected_subsets; + throw TestFailed(ss.str()); + } if (msg2.bulletin->subsets[0].size() < (unsigned)expected_min_vars) - throw tut::failure(wibble_test_location.msg(str::fmtf("Number of items in first subset is too small: %zd < %d\n", msg2.bulletin->subsets[0].size(), expected_min_vars))); + { + std::stringstream ss; + ss << "Number of items in first subset is too small: " << msg2.bulletin->subsets[0].size() << " < " << expected_min_vars; + throw TestFailed(ss.str()); + } } catch (...) { msg1.dump(); msg2.dump(); @@ -716,17 +697,15 @@ void TestCodec::do_compare(WIBBLE_TEST_LOCPRM, const TestMessage& msg1, const Te } } -void TestCodec::run_reimport(WIBBLE_TEST_LOCPRM) +void TestCodec::run_reimport() { - if (verbose) cerr << "Running test " << wibble_test_location.locstr() << endl; - // Import if (verbose) cerr << "Importing " << fname << " with options '" << input_opts.to_string() << "'" << endl; TestMessage orig(type, "orig"); try { orig.read_from_file(fname, input_opts); } catch (std::exception& e) { - throw tut::failure(wibble_test_location.msg(string("cannot decode ") + fname + ": " + e.what())); + throw TestFailed(string("cannot decode ") + fname + ": " + e.what()); } wassert(actual(orig.msgs.size()) > 0); @@ -745,43 +724,41 @@ void TestCodec::run_reimport(WIBBLE_TEST_LOCPRM) auto tpl = exp->infer_template(orig.msgs); wassert(actual(tpl->name()) == expected_template); } - wrunchecked(exported.read_from_msgs(orig.msgs, output_opts)); + wassert(exported.read_from_msgs(orig.msgs, output_opts)); // Import again if (verbose) cerr << "Reimporting with options '" << input_opts.to_string() << "'" << endl; TestMessage final(type, "final"); - wrunchecked(final.read_from_raw(exported.raw, input_opts)); + wassert(final.read_from_raw(exported.raw, input_opts)); // Run tweaks after_reimport_reimport.apply(orig.msgs); after_reimport_reimport.apply(final.msgs); try { - wruntest(do_compare, orig, final); + wassert(do_compare(orig, final)); } catch (...) { dballe::tests::dump("reexported", exported.raw); throw; } - if (expected_type != MISSING_INT) - wassert(actual(final.bulletin->data_category) == expected_type); - if (expected_subtype != MISSING_INT) - wassert(actual(final.bulletin->data_subcategory) == expected_subtype); - if (expected_localsubtype != MISSING_INT) - wassert(actual(final.bulletin->data_subcategory_local) == expected_localsubtype); + if (expected_data_category != MISSING_INT) + wassert(actual(final.bulletin->data_category) == expected_data_category); + if (expected_data_subcategory != MISSING_INT) + wassert(actual(final.bulletin->data_subcategory) == expected_data_subcategory); + if (expected_data_subcategory_local != MISSING_INT) + wassert(actual(final.bulletin->data_subcategory_local) == expected_data_subcategory_local); } -void TestCodec::run_convert(WIBBLE_TEST_LOCPRM, const std::string& tplname) +void TestCodec::run_convert(const std::string& tplname) { - if (verbose) cerr << "Running test " << wibble_test_location.locstr() << endl; - // Import if (verbose) cerr << "Importing " << fname << " with options " << input_opts.to_string() << endl; TestMessage orig(type, "orig"); try { orig.read_from_file(fname, input_opts); } catch (std::exception& e) { - throw tut::failure(wibble_test_location.msg(string("cannot decode ") + fname + ": " + e.what())); + throw TestFailed(string("cannot decode ") + fname + ": " + e.what()); } wassert(actual(orig.msgs.size()) > 0); @@ -799,7 +776,7 @@ void TestCodec::run_convert(WIBBLE_TEST_LOCPRM, const std::string& tplname) } catch (std::exception& e) { //dballe::tests::dump("bul1", *exported); //balle::tests::dump("msg1", *msgs1); - throw tut::failure(wibble_test_location.msg(string("cannot export: ") + e.what())); + throw TestFailed(string("cannot export: ") + e.what()); } // Import again @@ -809,7 +786,7 @@ void TestCodec::run_convert(WIBBLE_TEST_LOCPRM, const std::string& tplname) } catch (std::exception& e) { //dballe::tests::dump("msg1", *msgs1); //dballe::tests::dump("msg", rawmsg); - throw tut::failure(wibble_test_location.msg(string("importing from exported rawmsg: ") + e.what())); + throw TestFailed(string("importing from exported rawmsg: ") + e.what()); } // Run tweaks @@ -817,7 +794,7 @@ void TestCodec::run_convert(WIBBLE_TEST_LOCPRM, const std::string& tplname) after_convert_reimport.apply(reimported.msgs); try { - wruntest(do_compare, orig, reimported); + wassert(do_compare(orig, reimported)); } catch (...) { dballe::tests::dump("converted", exported.raw); throw; diff --git a/dballe/msg/tests.h b/dballe/msg/tests.h index 826f1e14e..fc4eaebe3 100644 --- a/dballe/msg/tests.h +++ b/dballe/msg/tests.h @@ -1,33 +1,6 @@ -/* - * Copyright (C) 2005--2012 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - #include #include #include -#if 0 -#include -#include -#include - -#include -#include -#endif #include namespace wreport { @@ -37,21 +10,20 @@ struct Vartable; namespace dballe { namespace tests { -typedef wibble::tests::Location Location; +Messages read_msgs(const char* filename, File::Encoding type, const dballe::msg::Importer::Options& opts=dballe::msg::Importer::Options()); +Messages read_msgs_csv(const char* filename); -Messages _read_msgs(const Location& loc, const char* filename, File::Encoding type, const dballe::msg::Importer::Options& opts=dballe::msg::Importer::Options()); -#define read_msgs(filename, type) dballe::tests::_read_msgs(wibble::tests::Location(__FILE__, __LINE__, "load " #filename " " #type), (filename), (type)) -#define inner_read_msgs(filename, type) dballe::tests::_read_msgs(wibble::tests::Location(loc, __FILE__, __LINE__, "load " #filename " " #type), (filename), (type)) -#define read_msgs_opts(filename, type, opts) dballe::tests::_read_msgs(wibble::tests::Location(__FILE__, __LINE__, "load " #filename " " #type), (filename), (type), (opts)) -#define inner_read_msgs_opts(filename, type, opts) dballe::tests::_read_msgs(wibble::tests::Location(loc, __FILE__, __LINE__, "load " #filename " " #type), (filename), (type), (opts)) +struct ActualMessage : public Actual +{ + using Actual::Actual; -Messages _read_msgs_csv(const Location& loc, const char* filename); -#define read_msgs_csv(filename) dballe::tests::_read_msgs_csv(wibble::tests::Location(__FILE__, __LINE__, "load csv " #filename), (filename)) -#define inner_read_msgs_csv(filename) dballe::tests::_read_msgs_csv(wibble::tests::Location(loc, __FILE__, __LINE__, "load csv " #filename), (filename)) + void is_undef(int shortcut) const; +}; -std::unique_ptr export_msgs(WIBBLE_TEST_LOCPRM, File::Encoding enctype, const Messages& in, const std::string& tag, const dballe::msg::Exporter::Options& opts=dballe::msg::Exporter::Options()); -#define test_export_msgs(...) dballe::tests::export_msgs(wibble_test_location.nest(wibble_test_location_info, __FILE__, __LINE__, "export_msgs("#__VA_ARGS__")"), __VA_ARGS__) +inline ActualMessage actual(const Message& message) { return ActualMessage(message); } +std::unique_ptr export_msgs(File::Encoding enctype, const Messages& in, const std::string& tag, const dballe::msg::Exporter::Options& opts=dballe::msg::Exporter::Options()); +#define test_export_msgs(...) wcallchecked(export_msgs(__VA_ARGS__)) void track_different_msgs(const Message& msg1, const Message& msg2, const std::string& prefix); void track_different_msgs(const Messages& msgs1, const Messages& msgs2, const std::string& prefix); @@ -60,13 +32,11 @@ extern const char* bufr_files[]; extern const char* crex_files[]; extern const char* aof_files[]; -void _ensure_msg_undef(const Location& loc, const Message& msg, int shortcut); -#define ensure_msg_undef(msg, id) dballe::tests::_ensure_msg_undef(wibble::tests::Location(__FILE__, __LINE__, #msg " has undefined " #id), (msg), (id)) -#define inner_ensure_msg_undef(msg, id) dballe::tests::_ensure_msg_undef(wibble::tests::Location(loc, __FILE__, __LINE__, #msg " has undefined " #id), (msg), (id)) +const wreport::Var& want_var(const Message& msg, int shortcut); +const wreport::Var& want_var(const Message& msg, wreport::Varcode code, const dballe::Level& lev, const dballe::Trange& tr); -const wreport::Var& _want_var(const Location& loc, const Message& msg, int shortcut); -const wreport::Var& _want_var(const Location& loc, const Message& msg, wreport::Varcode code, const dballe::Level& lev, const dballe::Trange& tr); -#define want_var(msg, ...) dballe::tests::_want_var(wibble::tests::Location(__FILE__, __LINE__, #msg " needs to have var " #__VA_ARGS__), (msg), __VA_ARGS__) +inline ActualVar actual(const Message& message, int shortcut) { return ActualVar(want_var(message, shortcut)); } +inline ActualVar actual(const Message& message, wreport::Varcode code, const dballe::Level& lev, const dballe::Trange& tr) { return ActualVar(want_var(message, code, lev, tr)); } void dump(const std::string& tag, const Message& msg, const std::string& desc="message"); void dump(const std::string& tag, const Messages& msgs, const std::string& desc="message"); @@ -251,30 +221,27 @@ struct TestCodec std::string expected_template; int expected_subsets = 1; int expected_min_vars = 1; - int expected_type = MISSING_INT; - int expected_subtype = MISSING_INT; - int expected_localsubtype = MISSING_INT; + int expected_data_category = MISSING_INT; + int expected_data_subcategory = MISSING_INT; + int expected_data_subcategory_local = MISSING_INT; MessageTweakers after_reimport_import; MessageTweakers after_reimport_reimport; MessageTweakers after_convert_import; MessageTweakers after_convert_reimport; - void do_compare(WIBBLE_TEST_LOCPRM, const TestMessage& msg1, const TestMessage& msg2); + void do_compare(const TestMessage& msg1, const TestMessage& msg2); TestCodec(const std::string& fname, File::Encoding type=File::BUFR); void configure_ecmwf_to_wmo_tweaks(); // "import, export, import again, compare" test - void run_reimport(WIBBLE_TEST_LOCPRM); + void run_reimport(); // "import, export as different template, import again, compare" test - void run_convert(WIBBLE_TEST_LOCPRM, const std::string& tplname); + void run_convert(const std::string& tplname); }; -#define TEST_reimport(obj) obj.run_reimport(wibble::tests::Location(__FILE__, __LINE__, #obj ".run_reimport")) -#define TEST_convert(obj, tpl) obj.run_convert(wibble::tests::Location(__FILE__, __LINE__, #obj ".run_convert " tpl), tpl) - #if 0 diff --git a/dballe/msg/vars-test.cc b/dballe/msg/vars-test.cc new file mode 100644 index 000000000..b7441c3b2 --- /dev/null +++ b/dballe/msg/vars-test.cc @@ -0,0 +1,34 @@ +#include "msg/tests.h" +#include "core/defs.h" +#include "msg/vars.h" + +using namespace dballe; +using namespace dballe::tests; +using namespace std; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + // Test variable alias resolution + add_method("aliases", []() { + // First + wassert(actual(resolve_var("block")) == DBA_MSG_BLOCK); + wassert(actual(resolve_var_substring("blocks", 5)) == DBA_MSG_BLOCK); + + // Last + wassert(actual(resolve_var("tot_prec1")) == DBA_MSG_TOT_PREC1); + + // Inbetween + wassert(actual(resolve_var("cloud_h4")) == DBA_MSG_CLOUD_H4); + wassert(actual(resolve_var("st_type")) == DBA_MSG_ST_TYPE); + wassert(actual(resolve_var("tot_snow")) == DBA_MSG_TOT_SNOW); + }); + } +} test("msg_vars"); + +} diff --git a/dballe/msg/vars-tut.cc b/dballe/msg/vars-tut.cc deleted file mode 100644 index c05222260..000000000 --- a/dballe/msg/vars-tut.cc +++ /dev/null @@ -1,55 +0,0 @@ -/* - * DB-ALLe - Archive for punctual meteorological data - * - * Copyright (C) 2005--2010 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "msg/tests.h" -#include "core/defs.h" -#include "msg/vars.h" - -using namespace dballe; -using namespace std; - -namespace tut { - -struct msg_vars_shar -{ -}; -TESTGRP(msg_vars); - -// Test variable alias resolution -template<> template<> -void to::test<1>() -{ - // First - ensure_equals(resolve_var("block"), DBA_MSG_BLOCK); - ensure_equals(resolve_var_substring("blocks", 5), DBA_MSG_BLOCK); - - // Last - ensure_equals(resolve_var("tot_prec1"), DBA_MSG_TOT_PREC1); - - // Inbetween - ensure_equals(resolve_var("cloud_h4"), DBA_MSG_CLOUD_H4); - ensure_equals(resolve_var("st_type"), DBA_MSG_ST_TYPE); - ensure_equals(resolve_var("tot_snow"), DBA_MSG_TOT_SNOW); -} - -} - -/* vim:set ts=4 sw=4: */ diff --git a/dballe/msg/wr_codec-test.cc b/dballe/msg/wr_codec-test.cc new file mode 100644 index 000000000..c130faa2f --- /dev/null +++ b/dballe/msg/wr_codec-test.cc @@ -0,0 +1,22 @@ +#include "tests.h" +#include "wr_codec.h" +#include + +using namespace std; +using namespace dballe; +using namespace dballe::tests; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("empty", []() { + }); + } +} test("msg_wr_codec"); + +} diff --git a/dballe/msg/wr_codec-tut.cc b/dballe/msg/wr_codec-tut.cc deleted file mode 100644 index f013a7268..000000000 --- a/dballe/msg/wr_codec-tut.cc +++ /dev/null @@ -1,23 +0,0 @@ -#include "tests.h" -#include "wr_codec.h" -#include - -using namespace std; -using namespace wibble::tests; -using namespace dballe; - -namespace { - -typedef dballe::tests::test_group<> test_group; -typedef test_group::Test Test; -typedef test_group::Fixture Fixture; - -std::vector tests { - Test("empty", [](Fixture& f) { - }), -}; - -test_group newtg("msg_wr_codec", tests); - -} - diff --git a/dballe/msg/wr_codec_generic-test.cc b/dballe/msg/wr_codec_generic-test.cc new file mode 100644 index 000000000..79b9042dd --- /dev/null +++ b/dballe/msg/wr_codec_generic-test.cc @@ -0,0 +1,220 @@ +#include "tests.h" +#include "wr_codec.h" +#include "msg.h" +#include +#include + +using namespace std; +using namespace wreport; +using namespace dballe; +using namespace dballe::tests; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("empty", []() { + // Try encoding and decoding an empty generic message + unique_ptr importer = msg::Importer::create(File::BUFR); + unique_ptr exporter = msg::Exporter::create(File::BUFR); + + Messages msgs; + msgs.append(unique_ptr(new Msg)); + + // Export msg as a generic message + BinaryMessage raw(File::BUFR); + raw.data = wcallchecked(exporter->to_binary(msgs)); + + // Parse it back + Messages msgs1 = wcallchecked(importer->from_binary(raw)); + + // Check that the data are the same + notes::Collect c(cerr); + int diffs = msgs.diff(msgs1); + if (diffs) dballe::tests::track_different_msgs(msgs, msgs1, "genericempty"); + wassert(actual(diffs) == 0); + }); + add_method("known", []() { + // Try encoding and decoding a generic message + unique_ptr importer = msg::Importer::create(File::BUFR); + unique_ptr exporter = msg::Exporter::create(File::BUFR); + + unique_ptr msg(new Msg); + + /* Fill up msg */ + msg->set_press( 15, 45); + msg->set_height_anem( 15, 45); + msg->set_tot_snow( 15, 45); + msg->set_visibility( 15, 45); + msg->set_pres_wtr( 5, 45); + msg->set_metar_wtr( 5, 45); + msg->set_water_temp( 15, 45); + msg->set_past_wtr1_3h( 2, 45); + msg->set_past_wtr2_3h( 2, 45); + msg->set_press_tend( 5, 45); + msg->set_tot_prec24( 15, 45); + msg->set_press_3h( 15, 45); + msg->set_press_msl( 15, 45); + msg->set_qnh( 15, 45); + msg->set_temp_2m( 15, 45); + msg->set_wet_temp_2m( 15, 45); + msg->set_dewpoint_2m( 15, 45); + msg->set_humidity( 15, 45); + msg->set_wind_dir( 15, 45); + msg->set_wind_speed( 15, 45); + msg->set_ex_ccw_wind( 15, 45); + msg->set_ex_cw_wind( 15, 45); + msg->set_wind_gust_max_speed( 15, 45); + msg->set_cloud_n( 3, 45); + msg->set_cloud_nh( 10, 45); + msg->set_cloud_hh( 3, 45); + msg->set_cloud_cl( 3, 45); + msg->set_cloud_cm( 3, 45); + msg->set_cloud_ch( 3, 45); + msg->set_cloud_n1( 3, 45); + msg->set_cloud_c1( 3, 45); + msg->set_cloud_h1( 3, 45); + msg->set_cloud_n2( 3, 45); + msg->set_cloud_c2( 3, 45); + msg->set_cloud_h2( 3, 45); + msg->set_cloud_n3( 3, 45); + msg->set_cloud_c3( 3, 45); + msg->set_cloud_h3( 3, 45); + msg->set_cloud_n4( 3, 45); + msg->set_cloud_c4( 3, 45); + msg->set_cloud_h4( 3, 45); + msg->set_block( 3, 45); + msg->set_station( 3, 45); + msg->set_flight_reg_no( "pippo", 45); + msg->set_ident( "cippo", 45); + msg->set_st_dir( 3, 45); + msg->set_st_speed( 3, 45); + msg->set_st_name( "ciop", 45); + msg->set_st_name_icao( "cip", 45); + msg->set_st_type( 1, 45); + msg->set_wind_inst( 3, 45); + msg->set_temp_precision( 1.23, 45); + msg->set_sonde_type( 3, 45); + msg->set_sonde_method( 3, 45); + msg->set_navsys( 3, 45); + msg->set_data_relay( 3, 45); + msg->set_flight_roll( 3, 45); + msg->set_latlon_spec( 3, 45); + msg->set_datetime(Datetime(3, 3, 3, 3, 3, 0)); + msg->seti(WR_VAR(0, 4, 1), 3, 45, Level(), Trange()); + msg->seti(WR_VAR(0, 4, 2), 3, 45, Level(), Trange()); + msg->seti(WR_VAR(0, 4, 3), 3, 45, Level(), Trange()); + msg->seti(WR_VAR(0, 4, 4), 3, 45, Level(), Trange()); + msg->seti(WR_VAR(0, 4, 5), 3, 45, Level(), Trange()); + msg->set_latitude( 3, 45); + msg->set_longitude( 3, 45); + msg->set_height_station(3, 45); + msg->set_height_baro( 3, 45); + msg->set_flight_phase( 3, 45); + msg->set_timesig( 3, 45); + //CHECKED(dba_msg_set_flight_press( msg, 3, 45)); + + Messages msgs; + msgs.append(move(msg)); + + /* Export msg as a generic message */ + BinaryMessage raw(File::BUFR); + raw.data = wcallchecked(exporter->to_binary(msgs)); + + //FILE* out = fopen("/tmp/zaza.bufr", "wb"); + //fwrite(raw.data.data(), raw.data.size(), 1, out); + //fclose(out); + + /* Parse it back */ + Messages msgs1 = wcallchecked(importer->from_binary(raw)); + + /* Check that the data are the same */ + notes::Collect c(cerr); + int diffs = msgs.diff(msgs1); + if (diffs) dballe::tests::track_different_msgs(msgs, msgs1, "generic2"); + wassert(actual(diffs) == 0); + }); + add_method("attrs", []() { + // Check that attributes are properly exported + unique_ptr importer = msg::Importer::create(File::BUFR); + unique_ptr exporter = msg::Exporter::create(File::BUFR); + + /* Create a new message */ + unique_ptr msg(new Msg); + msg->type = MSG_GENERIC; + + // Set some metadata + msg->set_datetime(Datetime(2006, 1, 19, 14, 50)); + msg->set_latitude(50.0); + msg->set_longitude(12.0); + + /* Create a variable to add to the message */ + unique_ptr var = newvar(WR_VAR(0, 12, 101), 270.15); + + /* Add some attributes to the variable */ + var->seta(newvar(WR_VAR(0, 33, 2), 1)); + var->seta(newvar(WR_VAR(0, 33, 3), 2)); + var->seta(newvar(WR_VAR(0, 33, 5), 3)); + + /* Add the variable to the message */ + msg->set(move(var), Level(1), Trange::instant()); + + /* Create a second variable to add to the message */ + var = newvar(WR_VAR(0, 12, 102), 272.0); + + /* Add some attributes to the variable */ + var->seta(newvar(WR_VAR(0, 33, 3), 1)); + var->seta(newvar(WR_VAR(0, 33, 5), 2)); + + /* Add the variable to the message */ + msg->set(move(var), Level(1), Trange::instant()); + + Messages msgs; + msgs.append(move(msg)); + + // Encode the message + BinaryMessage raw(File::BUFR); + raw.data = wcallchecked(exporter->to_binary(msgs)); + + // Decode the message + Messages msgs1 = wcallchecked(importer->from_binary(raw)); + + // Check that the data are the same + notes::Collect c(cerr); + int diffs = msgs.diff(msgs1); + if (diffs) dballe::tests::track_different_msgs(msgs, msgs1, "genericattr"); + wassert(actual(diffs) == 0); + }); + add_method("doublememo", []() { + // Test a bug in which B01194 ([SIM] Report mnemonic) appears twice + + // Import a synop message + Messages msgs = read_msgs("bufr/obs0-1.22.bufr", File::BUFR); + wassert(actual(msgs.size()) > 0); + + // Convert it to generic, with a 'ship' rep_memo + Msg::downcast(msgs[0]).type = MSG_GENERIC; + Msg::downcast(msgs[0]).set_rep_memo("ship"); + + // Export it + unique_ptr exporter = msg::Exporter::create(File::BUFR); + unique_ptr bulletin = exporter->to_bulletin(msgs); + + // Ensure that B01194 only appears once + wassert(actual(bulletin->subsets.size()) == 1u); + unsigned count = 0; + for (std::vector::const_iterator i = bulletin->subsets[0].begin(); i != bulletin->subsets[0].end(); ++i) + { + if (i->code() == WR_VAR(0, 1, 194)) + ++count; + } + wassert(actual(count) == 1u); + }); + } +} test("msg_wr_codec_generic"); + +} diff --git a/dballe/msg/wr_codec_generic-tut.cc b/dballe/msg/wr_codec_generic-tut.cc deleted file mode 100644 index c65cf2cc0..000000000 --- a/dballe/msg/wr_codec_generic-tut.cc +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (C) 2005--2015 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "tests.h" -#include "wr_codec.h" -#include "msg.h" -#include -#include - -using namespace dballe; -using namespace wreport; -using namespace wibble::tests; -using namespace std; - -namespace { - -typedef dballe::tests::test_group<> test_group; -typedef test_group::Test Test; -typedef test_group::Fixture Fixture; - -std::vector tests { - Test("empty", [](Fixture& f) { - // Try encoding and decoding an empty generic message - unique_ptr importer = msg::Importer::create(File::BUFR); - unique_ptr exporter = msg::Exporter::create(File::BUFR); - - Messages msgs; - msgs.append(unique_ptr(new Msg)); - - // Export msg as a generic message - BinaryMessage raw(File::BUFR); - raw.data = wcallchecked(exporter->to_binary(msgs)); - - // Parse it back - Messages msgs1 = wcallchecked(importer->from_binary(raw)); - - // Check that the data are the same - notes::Collect c(cerr); - int diffs = msgs.diff(msgs1); - if (diffs) dballe::tests::track_different_msgs(msgs, msgs1, "genericempty"); - ensure_equals(diffs, 0); - }), - Test("known", [](Fixture& f) { - // Try encoding and decoding a generic message - unique_ptr importer = msg::Importer::create(File::BUFR); - unique_ptr exporter = msg::Exporter::create(File::BUFR); - - unique_ptr msg(new Msg); - - /* Fill up msg */ - msg->set_press( 15, 45); - msg->set_height_anem( 15, 45); - msg->set_tot_snow( 15, 45); - msg->set_visibility( 15, 45); - msg->set_pres_wtr( 5, 45); - msg->set_metar_wtr( 5, 45); - msg->set_water_temp( 15, 45); - msg->set_past_wtr1_3h( 2, 45); - msg->set_past_wtr2_3h( 2, 45); - msg->set_press_tend( 5, 45); - msg->set_tot_prec24( 15, 45); - msg->set_press_3h( 15, 45); - msg->set_press_msl( 15, 45); - msg->set_qnh( 15, 45); - msg->set_temp_2m( 15, 45); - msg->set_wet_temp_2m( 15, 45); - msg->set_dewpoint_2m( 15, 45); - msg->set_humidity( 15, 45); - msg->set_wind_dir( 15, 45); - msg->set_wind_speed( 15, 45); - msg->set_ex_ccw_wind( 15, 45); - msg->set_ex_cw_wind( 15, 45); - msg->set_wind_gust_max_speed( 15, 45); - msg->set_cloud_n( 3, 45); - msg->set_cloud_nh( 10, 45); - msg->set_cloud_hh( 3, 45); - msg->set_cloud_cl( 3, 45); - msg->set_cloud_cm( 3, 45); - msg->set_cloud_ch( 3, 45); - msg->set_cloud_n1( 3, 45); - msg->set_cloud_c1( 3, 45); - msg->set_cloud_h1( 3, 45); - msg->set_cloud_n2( 3, 45); - msg->set_cloud_c2( 3, 45); - msg->set_cloud_h2( 3, 45); - msg->set_cloud_n3( 3, 45); - msg->set_cloud_c3( 3, 45); - msg->set_cloud_h3( 3, 45); - msg->set_cloud_n4( 3, 45); - msg->set_cloud_c4( 3, 45); - msg->set_cloud_h4( 3, 45); - msg->set_block( 3, 45); - msg->set_station( 3, 45); - msg->set_flight_reg_no( "pippo", 45); - msg->set_ident( "cippo", 45); - msg->set_st_dir( 3, 45); - msg->set_st_speed( 3, 45); - msg->set_st_name( "ciop", 45); - msg->set_st_name_icao( "cip", 45); - msg->set_st_type( 1, 45); - msg->set_wind_inst( 3, 45); - msg->set_temp_precision( 1.23, 45); - msg->set_sonde_type( 3, 45); - msg->set_sonde_method( 3, 45); - msg->set_navsys( 3, 45); - msg->set_data_relay( 3, 45); - msg->set_flight_roll( 3, 45); - msg->set_latlon_spec( 3, 45); - msg->set_datetime(Datetime(3, 3, 3, 3, 3, 0)); - msg->seti(WR_VAR(0, 4, 1), 3, 45, Level(), Trange()); - msg->seti(WR_VAR(0, 4, 2), 3, 45, Level(), Trange()); - msg->seti(WR_VAR(0, 4, 3), 3, 45, Level(), Trange()); - msg->seti(WR_VAR(0, 4, 4), 3, 45, Level(), Trange()); - msg->seti(WR_VAR(0, 4, 5), 3, 45, Level(), Trange()); - msg->set_latitude( 3, 45); - msg->set_longitude( 3, 45); - msg->set_height_station(3, 45); - msg->set_height_baro( 3, 45); - msg->set_flight_phase( 3, 45); - msg->set_timesig( 3, 45); - //CHECKED(dba_msg_set_flight_press( msg, 3, 45)); - - Messages msgs; - msgs.append(move(msg)); - - /* Export msg as a generic message */ - BinaryMessage raw(File::BUFR); - raw.data = wcallchecked(exporter->to_binary(msgs)); - - //FILE* out = fopen("/tmp/zaza.bufr", "wb"); - //fwrite(raw.data.data(), raw.data.size(), 1, out); - //fclose(out); - - /* Parse it back */ - Messages msgs1 = wcallchecked(importer->from_binary(raw)); - - /* Check that the data are the same */ - notes::Collect c(cerr); - int diffs = msgs.diff(msgs1); - if (diffs) dballe::tests::track_different_msgs(msgs, msgs1, "generic2"); - wassert(actual(diffs) == 0); - }), - Test("attrs", [](Fixture& f) { - // Check that attributes are properly exported - unique_ptr importer = msg::Importer::create(File::BUFR); - unique_ptr exporter = msg::Exporter::create(File::BUFR); - - /* Create a new message */ - unique_ptr msg(new Msg); - msg->type = MSG_GENERIC; - - // Set some metadata - msg->set_datetime(Datetime(2006, 1, 19, 14, 50)); - msg->set_latitude(50.0); - msg->set_longitude(12.0); - - /* Create a variable to add to the message */ - unique_ptr var = newvar(WR_VAR(0, 12, 101), 270.15); - - /* Add some attributes to the variable */ - var->seta(newvar(WR_VAR(0, 33, 2), 1)); - var->seta(newvar(WR_VAR(0, 33, 3), 2)); - var->seta(newvar(WR_VAR(0, 33, 5), 3)); - - /* Add the variable to the message */ - msg->set(move(var), Level(1), Trange::instant()); - - /* Create a second variable to add to the message */ - var = newvar(WR_VAR(0, 12, 102), 272.0); - - /* Add some attributes to the variable */ - var->seta(newvar(WR_VAR(0, 33, 3), 1)); - var->seta(newvar(WR_VAR(0, 33, 5), 2)); - - /* Add the variable to the message */ - msg->set(move(var), Level(1), Trange::instant()); - - Messages msgs; - msgs.append(move(msg)); - - // Encode the message - BinaryMessage raw(File::BUFR); - raw.data = wcallchecked(exporter->to_binary(msgs)); - - // Decode the message - Messages msgs1 = wcallchecked(importer->from_binary(raw)); - - // Check that the data are the same - notes::Collect c(cerr); - int diffs = msgs.diff(msgs1); - if (diffs) dballe::tests::track_different_msgs(msgs, msgs1, "genericattr"); - ensure_equals(diffs, 0); - }), - Test("doublememo", [](Fixture& f) { - // Test a bug in which B01194 ([SIM] Report mnemonic) appears twice - - // Import a synop message - Messages msgs = read_msgs("bufr/obs0-1.22.bufr", File::BUFR); - ensure(msgs.size() > 0); - - // Convert it to generic, with a 'ship' rep_memo - Msg::downcast(msgs[0]).type = MSG_GENERIC; - Msg::downcast(msgs[0]).set_rep_memo("ship"); - - // Export it - unique_ptr exporter = msg::Exporter::create(File::BUFR); - unique_ptr bulletin = exporter->to_bulletin(msgs); - - // Ensure that B01194 only appears once - ensure_equals(bulletin->subsets.size(), 1u); - unsigned count = 0; - for (std::vector::const_iterator i = bulletin->subsets[0].begin(); i != bulletin->subsets[0].end(); ++i) - { - if (i->code() == WR_VAR(0, 1, 194)) - ++count; - } - ensure_equals(count, 1u); - }), -}; - -test_group newtg("msg_wr_codec_generic", tests); - -} diff --git a/dballe/msg/wr_export-test.cc b/dballe/msg/wr_export-test.cc new file mode 100644 index 000000000..bdc7cb813 --- /dev/null +++ b/dballe/msg/wr_export-test.cc @@ -0,0 +1,754 @@ +#include "tests.h" +#include "wr_codec.h" +#include "msg.h" +#include "context.h" +#include +#include +#include +#include +#include +#include + +using namespace wreport; +using namespace std; +using namespace dballe; +using namespace dballe::tests; +using namespace dballe::tests::tweaks; + +namespace { + +using dballe::tests::TestCodec; + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void add_testcodec(const char* fname, std::function method) + { + add_method(fname, [=]() { + string pathname = "bufr/"; + pathname += fname; + dballe::tests::TestCodec test(pathname); + method(test); + }); + } + + void register_tests() override + { + add_method("all_bufr", []() { + // Test that plain re-export of all our BUFR test files is possible + // note: These were blacklisted: + // "bufr/obs3-3.1.bufr", + // "bufr/obs3-56.2.bufr", + // "bufr/test-buoy1.bufr", + // "bufr/test-soil1.bufr", + set bl_crex; + // CREX tables do not contain the entries needed to encode pollution + // messages + bl_crex.insert("bufr/ed4.bufr"); + + const char** files = dballe::tests::bufr_files; + vector fails; + int i; + for (i = 0; files[i] != NULL; i++) + { + try { + Messages msgs = read_msgs(files[i], File::BUFR); + wassert(actual(msgs.size()) > 0); + + std::unique_ptr exporter; + + exporter = msg::Exporter::create(File::BUFR/*, const Options& opts=Options()*/); + unique_ptr bbulletin = exporter->to_bulletin(msgs); + + if (bl_crex.find(files[i]) == bl_crex.end()) + { + exporter = msg::Exporter::create(File::CREX/*, const Options& opts=Options()*/); + unique_ptr cbulletin = exporter->to_bulletin(msgs); + } + } catch (std::exception& e) { + fails.push_back(string(files[i]) + ": " + e.what()); + } + } + if (!fails.empty()) + { + std::stringstream ss; + ss << fails.size() << "/" << i << " errors:" << endl; + ss << str::join("\n", fails.begin(), fails.end()); + throw TestFailed(ss.str()); + } + }); + add_method("all_crex", []() { + // Test that plain re-export of all our CREX test files is possible + const char** files = dballe::tests::crex_files; + + vector fails; + int i; + for (i = 0; files[i] != NULL; i++) + { + try { + Messages msgs = read_msgs(files[i], File::CREX); + wassert(actual(msgs.size()) > 0); + + std::unique_ptr exporter = msg::Exporter::create(File::BUFR/*, const Options& opts=Options()*/); + unique_ptr bbulletin = exporter->to_bulletin(msgs); + + exporter = msg::Exporter::create(File::CREX/*, const Options& opts=Options()*/); + unique_ptr cbulletin = exporter->to_bulletin(msgs); + } catch (std::exception& e) { + fails.push_back(string(files[i]) + ": " + e.what()); + } + } + if (!fails.empty()) + { + std::stringstream ss; + ss << fails.size() << "/" << i << " errors:" << endl; + ss << str::join("\n", fails.begin(), fails.end()); + throw TestFailed(ss.str()); + } + }); + add_method("reproduce_temp", []() { + // Export a well known TEMP which used to fail + Messages msgs = read_msgs_csv("csv/temp1.csv"); + wassert(actual(msgs.size()) > 0); + + // Replace with packed levels because comparison later happens against + // packed levels + { + unique_ptr msg(new Msg); + msg->sounding_pack_levels(Msg::downcast(msgs[0])); + msgs.clear(); + msgs.append(move(msg)); + } + + // Export to BUFR + std::unique_ptr bufr_exporter(msg::Exporter::create(File::BUFR/*, const Options& opts=Options()*/)); + unique_ptr bbulletin = bufr_exporter->to_bulletin(msgs); + + // Import and check the differences + { + std::unique_ptr bufr_importer(msg::Importer::create(File::BUFR/*, const Options& opts=Options()*/)); + Messages msgs1 = wcallchecked(bufr_importer->from_bulletin(*bbulletin)); + notes::Collect c(cerr); + wassert(actual(msgs.diff(msgs1)) == 0); + } + + // Export to CREX + std::unique_ptr crex_exporter(msg::Exporter::create(File::CREX/*, const Options& opts=Options()*/)); + unique_ptr cbulletin = crex_exporter->to_bulletin(msgs); + + // Import and check the differences + { + std::unique_ptr crex_importer(msg::Importer::create(File::CREX/*, const Options& opts=Options()*/)); + Messages msgs1 = wcallchecked(crex_importer->from_bulletin(*cbulletin)); + notes::Collect c(cerr); + wassert(actual(msgs.diff(msgs1)) == 0); + } + }); + + // Re-export tests for old style synops + add_testcodec("obs0-1.22.bufr", [](TestCodec& test) { + test.expected_min_vars = 34; + test.configure_ecmwf_to_wmo_tweaks(); + + wassert(test.run_reimport()); + wassert(test.run_convert("synop-wmo")); + + test.after_convert_reimport.add(new StripDatetimeVars()); + wassert(test.run_convert("synop")); + }); + add_testcodec("obs0-1.11188.bufr", [](TestCodec& test) { + test.expected_subsets = 1; + test.expected_min_vars = 10; + wassert(test.run_reimport()); + wassert(test.run_convert("synop-ecmwf")); + }); + add_testcodec("obs0-3.504.bufr", [](TestCodec& test) { + test.expected_subsets = 1; + test.expected_min_vars = 10; + wassert(test.run_reimport()); + wassert(test.run_convert("synop-ecmwf")); + }); + // Re-export test for new style synops + add_testcodec("synop-cloudbelow.bufr", [](TestCodec& test) { + test.expected_subsets = 22; + test.expected_min_vars = 10; + test.expected_data_category = 0; + test.expected_data_subcategory = 1; + test.expected_data_subcategory_local = 255; + wassert(test.run_reimport()); + wassert(test.run_convert("synop-wmo")); + }); + add_testcodec("synop-evapo.bufr", [](TestCodec& test) { + test.expected_subsets = 14; + test.expected_min_vars = 10; + test.expected_data_category = 0; + test.expected_data_subcategory = 2; + test.expected_data_subcategory_local = 255; + wassert(test.run_reimport()); + wassert(test.run_convert("synop-wmo")); + }); + add_testcodec("synop-groundtemp.bufr", [](TestCodec& test) { + test.expected_subsets = 26; + test.expected_min_vars = 10; + test.expected_data_category = 0; + test.expected_data_subcategory = 1; + test.expected_data_subcategory_local = 255; + wassert(test.run_reimport()); + wassert(test.run_convert("synop-wmo")); + }); + add_testcodec("synop-longname.bufr", [](TestCodec& test) { + test.after_reimport_reimport.add(new TruncStName()); + test.after_convert_reimport.add(new TruncStName()); + test.expected_subsets = 7; + test.expected_min_vars = 10; + test.expected_data_category = 0; + test.expected_data_subcategory = 1; + test.expected_data_subcategory_local = 255; + wassert(test.run_reimport()); + wassert(test.run_convert("synop-wmo")); + }); + add_testcodec("synop-oddgust.bufr", [](TestCodec& test) { + test.expected_subsets = 26; + test.expected_min_vars = 10; + test.expected_data_category = 0; + test.expected_data_subcategory = 0; + test.expected_data_subcategory_local = 255; + wassert(test.run_reimport()); + wassert(test.run_convert("synop-wmo")); + }); + add_testcodec("synop-oddprec.bufr", [](TestCodec& test) { + test.expected_subsets = 1; + test.expected_min_vars = 10; + test.expected_data_category = 0; + test.expected_data_subcategory = 0; + test.expected_data_subcategory_local = 255; + wassert(test.run_reimport()); + wassert(test.run_convert("synop-wmo")); + }); + add_testcodec("synop-strayvs.bufr", [](TestCodec& test) { + test.expected_subsets = 1; + test.expected_min_vars = 10; + test.expected_data_category = 0; + test.expected_data_subcategory = 1; + test.expected_data_subcategory_local = 255; + wassert(test.run_reimport()); + wassert(test.run_convert("synop-wmo")); + }); + add_testcodec("synop-sunshine.bufr", [](TestCodec& test) { + test.expected_subsets = 26; + test.expected_min_vars = 10; + test.expected_data_category = 0; + test.expected_data_subcategory = 1; + test.expected_data_subcategory_local = 255; + wassert(test.run_reimport()); + wassert(test.run_convert("synop-wmo")); + }); + add_testcodec("synop-gtscosmo.bufr", [](TestCodec& test) { + test.expected_subsets = 1; + test.expected_min_vars = 10; + test.expected_data_category = 0; + test.expected_data_subcategory = 2; + test.expected_data_subcategory_local = 255; + wassert(test.run_reimport()); + wassert(test.run_convert("synop-wmo")); + }); + add_testcodec("synop-rad1.bufr", [](TestCodec& test) { + // Test import/export of GTS synop with radiation information + test.expected_subsets = 25; + test.expected_min_vars = 50; + + wassert(test.run_reimport()); + wassert(test.run_convert("synop-wmo")); + }); + add_testcodec("synop-rad2.bufr", [](TestCodec& test) { + // Test import/export of GTS synop without pressure of standard level + test.expected_template = "synop-wmo"; + test.expected_min_vars = 50; + test.verbose = true; + + wassert(test.run_reimport()); + wassert(test.run_convert("synop-wmo")); + }); + add_testcodec("synop-tchange.bufr", [](TestCodec& test) { + // Test import/export of GTS synop with temperature change information + test.expected_min_vars = 50; + + wassert(test.run_reimport()); + wassert(test.run_convert("synop-wmo")); + }); + add_testcodec("temp-timesig18.bufr", [](TestCodec& test) { + // FIXME: temp message with data category 0: what do we do with it? + test.expected_min_vars = 50; + test.expected_template = "temp-wmo"; + + wassert(test.run_reimport()); + wassert(test.run_convert("temp-wmo")); + }); + add_testcodec("obs2-101.16.bufr", [](TestCodec& test) { + // Re-export test for old style temps + wassert(test.run_reimport()); + wassert(test.run_convert("temp-ecmwf")); + }); + add_testcodec("obs2-102.1.bufr", [](TestCodec& test) { + wassert(test.run_reimport()); + wassert(test.run_convert("temp-ecmwf")); + }); + add_testcodec("obs2-91.2.bufr", [](TestCodec& test) { + wassert(test.run_reimport()); + wassert(test.run_convert("pilot-ecmwf")); + }); + add_testcodec("temp-bad3.bufr", [](TestCodec& test) { + wassert(test.run_reimport()); + wassert(test.run_convert("temp")); + }); + add_testcodec("temp-bad5.bufr", [](TestCodec& test) { + // This has some sounding groups with undefined VSS + wassert(test.run_reimport()); + wassert(test.run_convert("temp-ecmwf")); + }); + add_testcodec("test-temp1.bufr", [](TestCodec& test) { + // This has some sounding groups with undefined VSS, and an unusual template + wassert(test.run_reimport()); + wassert(test.run_convert("temp-ecmwf")); + }); + add_testcodec("C23000.bufr", [](TestCodec& test) { + // This has an unusual template + test.after_reimport_import.add(new StripSubstituteAttrs()); + test.after_convert_import.add(new StripSubstituteAttrs()); + wassert(test.run_reimport()); + wassert(test.run_convert("temp-ecmwf")); + }); + add_testcodec("temp-bad1.bufr", [](TestCodec& test) { + test.after_reimport_reimport.add(new StripQCAttrs()); + test.after_reimport_reimport.add(new StripSubstituteAttrs()); + test.after_reimport_reimport.add(new StripDatetimeVars()); + test.after_convert_import.add(new StripQCAttrs()); + test.after_convert_import.add(new StripSubstituteAttrs()); + test.after_convert_import.add(new StripDatetimeVars()); + test.expected_subsets = 1; + test.expected_min_vars = 10; + test.expected_data_category = 2; + test.expected_data_subcategory = 255; + test.expected_data_subcategory_local = 101; + wassert(test.run_reimport()); + wassert(test.run_convert("temp-ecmwf")); + }); + add_testcodec("temp-bad2.bufr", [](TestCodec& test) { + test.after_reimport_reimport.add(new StripQCAttrs()); + test.after_reimport_reimport.add(new StripSubstituteAttrs()); + test.after_reimport_reimport.add(new StripDatetimeVars()); + test.after_convert_import.add(new StripQCAttrs()); + test.after_convert_import.add(new StripSubstituteAttrs()); + test.after_convert_import.add(new StripDatetimeVars()); + test.expected_subsets = 1; + test.expected_min_vars = 10; + test.expected_data_category = 2; + test.expected_data_subcategory = 255; + test.expected_data_subcategory_local = 101; + wassert(test.run_reimport()); + wassert(test.run_convert("temp-ecmwf")); + }); + add_testcodec("temp-gts1.bufr", [](TestCodec& test) { + // Re-export test for new style temps + test.expected_subsets = 1; + test.expected_min_vars = 10; + test.expected_data_category = 2; + test.expected_data_subcategory = 4; + test.expected_data_subcategory_local = 255; + wassert(test.run_reimport()); + wassert(test.run_convert("temp-wmo")); + }); + add_testcodec("temp-gts2.bufr", [](TestCodec& test) { + test.expected_subsets = 6; + test.expected_min_vars = 10; + test.expected_data_category = 2; + test.expected_data_subcategory = 4; + test.expected_data_subcategory_local = 255; + wassert(test.run_reimport()); + wassert(test.run_convert("temp-wmo")); + }); + add_testcodec("temp-gts3.bufr", [](TestCodec& test) { + test.expected_subsets = 1; + test.expected_min_vars = 10; + test.expected_data_category = 2; + test.expected_data_subcategory = 4; + test.expected_data_subcategory_local = 255; + wassert(test.run_reimport()); + wassert(test.run_convert("temp-wmo")); + }); + add_testcodec("temp-gtscosmo.bufr", [](TestCodec& test) { + test.expected_subsets = 1; + test.expected_min_vars = 10; + test.expected_data_category = 2; + test.expected_data_subcategory = 4; + test.expected_data_subcategory_local = 255; + wassert(test.run_reimport()); + wassert(test.run_convert("temp-wmo")); + }); +#if 0 + Test("new_temp5", [](Fixture& f) { +#warning This is importer with height above ground levels, but exported with pressure levels + // Another weird template + BufrReimportTest test("bufr/temp-2-255.bufr"); + run_test(test, do_wmo, "temp"); + //test.output_opts.template_name = "temp-wmo"; + //run_test(test, do_test, "auto"); + //test.output_opts.template_name = "temp-ecmwf"; + //test.clear_tweaks(); + //run_test(test, do_test, "old"); + }); +#endif + add_testcodec("temp-bad4.bufr", [](TestCodec& test) { + test.after_reimport_reimport.add(new RoundLegacyVars()); + test.expected_subsets = 1; + test.expected_min_vars = 10; + test.expected_data_category = 2; + test.expected_data_subcategory = 255; + test.expected_data_subcategory_local = 101; + wassert(test.run_reimport()); + wassert(test.run_convert("temp-wmo")); + }); +#if 0 + Test("new_temp8", [](Fixture& f) { +#warning There is no template that can export these forecast TEMPs except generic + // generic temp with forecast info and hybrid levels (?) + BufrReimportTest test("bufr/tempforecast.bufr"); + run_test(test, do_test, "temp-wmo"); + test.clear_tweaks(); + test.tweaks.push_back(new StripQCAttrs()); + run_test(test, do_test, "temp-ecmwf", "temp-wmo"); + }); +#endif + add_testcodec("vad.bufr", [](TestCodec& test) { + // Test that temp vad subtype is set correctly + test.expected_subsets = 1; + test.expected_min_vars = 10; + test.expected_data_category = 6; + test.expected_data_subcategory = 1; + test.expected_data_subcategory_local = 255; + wassert(test.run_reimport()); + wassert(test.run_convert("temp")); + }); + add_testcodec("temp-huge.bufr", [](TestCodec& test) { + // Test correct import/export of a temp with thousands of levels + test.expected_min_vars = 30000; + wassert(test.run_reimport()); + wassert(test.run_convert("temp-wmo")); + }); + // New style ACARS +#if 0 + Test("new_acars1", [](Fixture& f) { +#warning no documentation on new stile ACARS available yet + //BufrReimportTest test("bufr/gts-acars1.bufr"); + //run_test(test, do_test, "acars-wmo"); + }); + Test("new_acars2", [](Fixture& f) { +#warning no documentation on new stile ACARS available yet + //BufrReimportTest test("bufr/gts-acars2.bufr"); + //run_test(test, do_test, "acars-wmo"); + }); + Test("new_acars3", [](Fixture& f) { +#warning no documentation on new stile ACARS available yet + //BufrReimportTest test("bufr/gts-acars-uk1.bufr"); + //run_test(test, do_test, "acars-wmo"); + }); + Test("new_acars4", [](Fixture& f) { +#warning no documentation on new stile ACARS available yet + //BufrReimportTest test("bufr/gts-acars-us1.bufr"); + //run_test(test, do_test, "acars-wmo"); + }); +#endif + // New style AMDAR + add_testcodec("gts-amdar1.bufr", [](TestCodec& test) { + wassert(test.run_reimport()); + wassert(test.run_convert("amdar-wmo")); + }); + add_testcodec("gts-amdar2.bufr", [](TestCodec& test) { + wassert(test.run_reimport()); + wassert(test.run_convert("amdar-wmo")); + }); + // Re-export to BUFR (simplified, full template autodetect) and see the differences + add_method("all_simplified", []() { + const char** files = dballe::tests::bufr_files; + // Uncomment to single out one failing file + //const char* files[] = { "bufr/temp-2-255.bufr", NULL }; + set blacklist; + // Generics, would need a template override to avoid the per-rete + // autodetected export + blacklist.insert("bufr/tempforecast.bufr"); + blacklist.insert("bufr/obs255-255.0.bufr"); + // Nonstandard messages that need tweaks, tested individually above + blacklist.insert("bufr/test-temp1.bufr"); + blacklist.insert("bufr/C23000.bufr"); + blacklist.insert("bufr/temp-2-255.bufr"); + blacklist.insert("bufr/synop-longname.bufr"); + blacklist.insert("bufr/temp-bad4.bufr"); + + vector fails; + int i; + std::unique_ptr exporter; + exporter = msg::Exporter::create(File::BUFR/*, const Options& opts=Options()*/); + std::unique_ptr importer = msg::Importer::create(File::BUFR/*, opts*/); + + for (i = 0; files[i] != NULL; i++) + { + if (blacklist.find(files[i]) != blacklist.end()) continue; + try { + // Import + Messages msgs = read_msgs(files[i], File::BUFR); + wassert(actual(msgs.size()) > 0); + + // Export + unique_ptr bbulletin = exporter->to_bulletin(msgs); + + // Import again + Messages msgs1 = importer->from_bulletin(*bbulletin); + + // Compare + notes::Collect c(cerr); + int diffs = msgs.diff(msgs1); + if (diffs) + { + FILE* out1 = fopen("/tmp/msg1.txt", "w"); + FILE* out2 = fopen("/tmp/msg2.txt", "w"); + msgs.print(out1); + msgs1.print(out2); + fclose(out1); + fclose(out2); + } + wassert(actual(diffs) == 0); + } catch (std::exception& e) { + fails.push_back(string(files[i]) + ": " + e.what()); + } + } + if (!fails.empty()) + { + std::stringstream ss; + ss << fails.size() << "/" << i << " errors:" << endl; + ss << str::join("\n", fails.begin(), fails.end()); + throw TestFailed(ss.str()); + } + }); + add_method("all_precise", []() { + // Re-export to BUFR (not simplified, full template autodetect) and see the differences + const char** files = dballe::tests::bufr_files; + // Uncomment to single out one failing file + //const char* files[] = { "bufr/obs0-1.22.bufr", NULL }; + set blacklist; + // Generics, would need a template override to avoid the per-rete + // autodetected export + blacklist.insert("bufr/tempforecast.bufr"); + blacklist.insert("bufr/obs255-255.0.bufr"); + // Nonstandard messages that need tweaks, tested individually above + blacklist.insert("bufr/test-temp1.bufr"); + blacklist.insert("bufr/C23000.bufr"); + blacklist.insert("bufr/temp-2-255.bufr"); + blacklist.insert("bufr/synop-longname.bufr"); + blacklist.insert("bufr/temp-bad4.bufr"); + + vector fails; + int i; + std::unique_ptr exporter; + exporter = msg::Exporter::create(File::BUFR/*, const Options& opts=Options()*/); + msg::Importer::Options import_opts; + import_opts.simplified = false; + std::unique_ptr importer = msg::Importer::create(File::BUFR, import_opts); + + for (i = 0; files[i] != NULL; i++) + { + if (blacklist.find(files[i]) != blacklist.end()) continue; + try { + // Import + Messages msgs = read_msgs(files[i], File::BUFR, import_opts); + wassert(actual(msgs.size()) > 0); + + // Export + unique_ptr bbulletin = exporter->to_bulletin(msgs); + + // Import again + Messages msgs1 = importer->from_bulletin(*bbulletin); + + // Compare + stringstream str; + notes::Collect c(str); + int diffs = msgs.diff(msgs1); + if (diffs) + { + string tag = str::basename(files[i]); + dballe::tests::dump("dballe-orig-" + tag, msgs, "original message"); + dballe::tests::dump("dballe-reenc-" + tag, *bbulletin, "reencoded message"); + dballe::tests::dump("dballe-reenc-" + tag, msgs1, "decoded reencoded message"); + dballe::tests::dump("dballe-diffs-" + tag, str.str(), "differences"); + } + wassert(actual(diffs) == 0); + } catch (std::exception& e) { + fails.push_back(string(files[i]) + ": " + e.what()); + } + } + if (!fails.empty()) + { + std::stringstream ss; + ss << fails.size() << "/" << i << " errors:" << endl; + ss << str::join("\n", fails.begin(), fails.end()); + throw TestFailed(ss.str()); + } + }); + // Old PILOT + add_method("old_pilot1", []() { + // Test that pilot subtype is set correctly + Messages msgs = read_msgs("bufr/obs2-91.2.bufr", File::BUFR); + msg::Exporter::Options opts; + opts.template_name = "pilot-wmo"; + unique_ptr bulletin = test_export_msgs(File::BUFR, msgs, "pilotwmo", opts); + wassert(actual(bulletin->data_category) == 2); + wassert(actual(bulletin->data_subcategory) == 1); + wassert(actual(bulletin->data_subcategory_local) == 255); + }); + add_testcodec("pilot-gts3.bufr", [](TestCodec& test) { + // Test import/export of ECMWF pilot with pressure levels + test.expected_min_vars = 50; + test.configure_ecmwf_to_wmo_tweaks(); + + wassert(test.run_reimport()); + wassert(test.run_convert("pilot-wmo")); + }); + add_testcodec("pilot-gts4.bufr", [](TestCodec& test) { + // Test import/export of ECMWF pilot with geopotential levels + test.expected_min_vars = 50; + test.configure_ecmwf_to_wmo_tweaks(); + test.after_convert_reimport.add(new dballe::tests::tweaks::HeightToGeopotential); + test.after_convert_reimport.add(new dballe::tests::tweaks::RemoveContext( + Level(100, 70000), Trange::instant())); + test.after_convert_reimport.add(new dballe::tests::tweaks::RemoveContext( + Level(100, 85000), Trange::instant())); + + wassert(test.run_reimport()); + wassert(test.run_convert("pilot-wmo")); + }); + // New style PILOT + add_testcodec("pilot-gts1.bufr", [](TestCodec& test) { + test.expected_data_category = 2; + test.expected_data_subcategory = 2; + test.expected_data_subcategory_local = 255; + + wassert(test.run_reimport()); + wassert(test.run_convert("pilot-wmo")); + }); + add_testcodec("pilot-ecmwf-geopotential.bufr", [](TestCodec& test) { + test.configure_ecmwf_to_wmo_tweaks(); + test.after_reimport_reimport.add(new StripVars({ WR_VAR(0, 10, 8) })); + test.after_convert_reimport.add(new StripVars({ WR_VAR(0, 10, 8) })); + test.expected_min_vars = 30; + wassert(test.run_reimport()); + wassert(test.run_convert("pilot-wmo")); + }); + add_method("new_pilot4", []() { + // Test for a bug where geopotential levels became pressure levels + Messages msgs1 = read_msgs("bufr/pilot-ecmwf-geopotential.bufr", File::BUFR); + wassert(actual(msgs1.size()) == 1); + Msg& msg1 = Msg::downcast(msgs1[0]); + + // Geopotential levels are converted to height above msl + const msg::Context* c = msg1.find_context(Level(102, 900), Trange(254, 0, 0)); + wassert(actual(c).istrue()); + + // Convert to WMO template + msg::Exporter::Options output_opts; + output_opts.template_name = "pilot-wmo"; + //if (verbose) cerr << "Exporting " << output_opts.to_string() << endl; + std::unique_ptr bulletin = test_export_msgs(File::BUFR, msgs1, "towmo", output_opts); + + // Import again + std::unique_ptr imp = msg::Importer::create(File::BUFR); + Messages msgs2 = imp->from_bulletin(*bulletin); + wassert(actual(msgs2.size()) == 1); + Msg& msg2 = Msg::downcast(msgs2[0]); + + // Ensure we didn't get pressure levels + wassert(actual(msg2.find_context(Level(100, 900), Trange(254, 0, 0))).isfalse()); + }); + add_method("new_pilot5", []() { + // Test for a range error in one specific BUFR + Messages msgs1 = read_msgs("bufr/temp-2-255.bufr", File::BUFR); + wassert(actual(msgs1.size()) == 1); + + // Convert to CREX + msg::Exporter::Options output_opts; + output_opts.template_name = "temp-wmo"; + std::unique_ptr bulletin = test_export_msgs(File::CREX, msgs1, "tocrex", output_opts); + + // Import again + std::unique_ptr imp = msg::Importer::create(File::BUFR); + Messages msgs2 = wcallchecked(imp->from_bulletin(*bulletin)); + wassert(actual(msgs2.size()) == 1); + }); + // Old SHIP + add_method("old_ship1", []() { + // Test that temp ship subtype is set correctly + Messages msgs = read_msgs("bufr/obs2-102.1.bufr", File::BUFR); + msg::Exporter::Options opts; + opts.template_name = "temp-wmo"; + unique_ptr bulletin = test_export_msgs(File::BUFR, msgs, "tempship", opts); + wassert(actual(bulletin->data_category) == 2); + wassert(actual(bulletin->data_subcategory) == 5); + wassert(actual(bulletin->data_subcategory_local) == 255); + }); + add_testcodec("ecmwf-ship-1-11.bufr", [](TestCodec& test) { + // Test import/export of ECMWF synop ship + test.expected_min_vars = 34; + test.configure_ecmwf_to_wmo_tweaks(); + + wassert(test.run_reimport()); + wassert(test.run_convert("ship-wmo")); + }); + add_testcodec("ecmwf-ship-1-12.bufr", [](TestCodec& test) { + // Test import/export of ECMWF synop ship record 2 + test.expected_min_vars = 21; + test.configure_ecmwf_to_wmo_tweaks(); + + wassert(test.run_reimport()); + wassert(test.run_convert("ship-wmo")); + }); + add_testcodec("ecmwf-ship-1-13.bufr", [](TestCodec& test) { + // Test import/export of ECMWF synop ship (auto) + test.expected_min_vars = 30; + test.configure_ecmwf_to_wmo_tweaks(); + + wassert(test.run_reimport()); + wassert(test.run_convert("ship-wmo")); + }); + add_testcodec("ecmwf-ship-1-14.bufr", [](TestCodec& test) { + // Test import/export of ECMWF synop ship (auto) record 2 + test.expected_min_vars = 28; + test.configure_ecmwf_to_wmo_tweaks(); + + wassert(test.run_reimport()); + wassert(test.run_convert("ship-wmo")); + }); + // New SHIP + add_testcodec("wmo-ship-1.bufr", [](TestCodec& test) { + // Test import/export of WMO synop ship + test.expected_min_vars = 50; + + wassert(test.run_reimport()); + wassert(test.run_convert("ship-wmo")); + }); + // ECMWF <-> WMO + add_testcodec("ecmwf-amdar1.bufr", [](TestCodec& test) { + test.expected_min_vars = 21; + + wassert(test.run_reimport()); + wassert(test.run_convert("amdar")); + }); + add_testcodec("ecmwf-acars1.bufr", [](TestCodec& test) { + test.expected_min_vars = 21; + + wassert(test.run_reimport()); + wassert(test.run_convert("acars")); + }); + } +} test("msg_wr_export"); + +} + diff --git a/dballe/msg/wr_export-tut.cc b/dballe/msg/wr_export-tut.cc deleted file mode 100644 index 477160ed4..000000000 --- a/dballe/msg/wr_export-tut.cc +++ /dev/null @@ -1,1046 +0,0 @@ -/* - * Copyright (C) 2005--2015 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "tests.h" -#include "wr_codec.h" -#include "msg.h" -#include "context.h" -#include -#include -#include -#include -#include -#include - -using namespace dballe; -using namespace wreport; -using namespace wibble; -using namespace std; -using namespace dballe::tests::tweaks; - -namespace { - -typedef dballe::tests::test_group<> test_group; -typedef test_group::Test Test; -typedef test_group::Fixture Fixture; - -// Common machinery for import -> export -> reimport tests -template -struct ReimportTest -{ - typedef dballe::tests::MessageTweaker Tweaker; - - string fname; - File::Encoding type; - Messages msgs1; - Messages msgs2; - unique_ptr exported; - msg::Importer::Options input_opts; - msg::Exporter::Options output_opts; - vector tweaks; - vector ecmwf_tweaks; - vector wmo_tweaks; - bool do_ecmwf_tweaks; - bool do_wmo_tweaks; - bool do_ignore_context_attrs; - bool do_round_geopotential; - bool verbose; - - void clear_tweaks() - { - for (typename vector::iterator i = tweaks.begin(); - i != tweaks.end(); ++i) - delete *i; - tweaks.clear(); - for (typename vector::iterator i = ecmwf_tweaks.begin(); - i != ecmwf_tweaks.end(); ++i) - delete *i; - ecmwf_tweaks.clear(); - for (typename vector::iterator i = wmo_tweaks.begin(); - i != wmo_tweaks.end(); ++i) - delete *i; - wmo_tweaks.clear(); - } - - ReimportTest(const std::string& fname, File::Encoding type=File::BUFR) - : fname(fname), type(type), do_ecmwf_tweaks(false), do_wmo_tweaks(false), - do_ignore_context_attrs(false), do_round_geopotential(false), verbose(false) - { - ecmwf_tweaks.push_back(new StripQCAttrs()); - ecmwf_tweaks.push_back(new StripDatetimeVars()); - wmo_tweaks.push_back(new RoundLegacyVars()); - } - ~ReimportTest() - { - clear_tweaks(); - } - - void do_test(const dballe::tests::Location& loc, const char* tname1, const char* tname2=NULL) - { - if (verbose) cerr << "Running test " << loc.locstr() << endl; - - std::unique_ptr importer(msg::Importer::create(type, input_opts)); - - // Import - if (verbose) cerr << "Importing " << fname << " " << input_opts.to_string() << endl; - msgs1 = inner_read_msgs_opts(fname.c_str(), type, input_opts); - inner_ensure(msgs1.size() > 0); - - // Run tweaks - for (typename vector::iterator i = tweaks.begin(); i != tweaks.end(); ++i) - { - if (verbose) cerr << "Running tweak " << (*i)->desc() << endl; - (*i)->tweak(msgs1); - } - if (do_ecmwf_tweaks) - for (typename vector::iterator i = ecmwf_tweaks.begin(); i != ecmwf_tweaks.end(); ++i) - { - if (verbose) cerr << "Running ecmwf tweak " << (*i)->desc() << endl; - (*i)->tweak(msgs1); - } - if (do_wmo_tweaks) - for (typename vector::iterator i = wmo_tweaks.begin(); i != wmo_tweaks.end(); ++i) - { - if (verbose) cerr << "Running wmo tweak " << (*i)->desc() << endl; - (*i)->tweak(msgs1); - } - - // Export - exported.reset(nullptr); - try { - if (tname1 != NULL) - output_opts.template_name = tname1; - else - output_opts.template_name.clear(); - if (verbose) cerr << "Exporting " << output_opts.to_string() << endl; - std::unique_ptr exporter(msg::Exporter::create(type, output_opts)); - exported = exporter->to_bulletin(msgs1); - } catch (std::exception& e) { - // dballe::tests::dump("bul1", *exported); - dballe::tests::dump("msg1", msgs1); - throw tut::failure(loc.msg(string("exporting to bulletin (first template): ") + e.what())); - } - - // Encode - BinaryMessage rawmsg(type); - try { - rawmsg.data = exported->encode(); - //exporter->to_rawmsg(*msgs1, rawmsg); - } catch (std::exception& e) { - dballe::tests::dump("bul1", *exported); - dballe::tests::dump("msg1", msgs1); - throw tut::failure(loc.msg(string("encoding to rawmsg (first template): ") + e.what())); - } - - // Import again - if (verbose) cerr << "Reimporting " << input_opts.to_string() << endl; - msgs2.clear(); - try { - msgs2 = importer->from_binary(rawmsg); - } catch (std::exception& e) { - dballe::tests::dump("msg1", msgs1); - dballe::tests::dump("msg", rawmsg); - throw tut::failure(loc.msg(string("importing from rawmsg (first template): ") + e.what())); - } - - Messages msgs3; - if (tname2) - { - // Export - unique_ptr bulletin; - try { - output_opts.template_name = tname2; - if (verbose) cerr << "Reexporting " << output_opts.to_string() << endl; - std::unique_ptr exporter(msg::Exporter::create(type, output_opts)); - bulletin = exporter->to_bulletin(msgs2); - } catch (std::exception& e) { - //dballe::tests::dump("bul2", *bulletin); - dballe::tests::dump("msg2", msgs1); - throw tut::failure(loc.msg(string("exporting to bulletin (second template): ") + e.what())); - } - - // Encode - rawmsg = BinaryMessage(type); - try { - rawmsg.data = bulletin->encode(); - //exporter->to_rawmsg(*msgs1, rawmsg); - } catch (std::exception& e) { - dballe::tests::dump("bul2", *bulletin); - dballe::tests::dump("msg2", msgs1); - throw tut::failure(loc.msg(string("encoding to rawmsg (second template): ") + e.what())); - } - - // Import again - try { - if (verbose) cerr << "Reimporting " << input_opts.to_string() << endl; - msgs3 = importer->from_binary(rawmsg); - } catch (std::exception& e) { - dballe::tests::dump("msg2", msgs2); - dballe::tests::dump("raw2", rawmsg); - throw tut::failure(loc.msg(string("importing from rawmsg (first template): ") + e.what())); - } - } else - msgs3 = move(msgs2); - -#if 0 - // Run tweaks - for (typename vector::iterator i = tweaks.begin(); i != tweaks.end(); ++i) - (*i)->clean_second(*msgs3); -#endif - if (do_ignore_context_attrs) - { - StripContextAttrs sca; - sca.tweak(msgs1); - } - if (do_round_geopotential) - { - RoundGeopotential rg; - rg.tweak(msgs1); - rg.tweak(msgs3); - } - - // Compare - stringstream str; - notes::Collect c(str); - int diffs = msgs1.diff(msgs3); - if (diffs) - { - dballe::tests::dump("msg1", msgs1); - //if (msgs2.get()) - //dballe::tests::dump("msg2", msgs2); - dballe::tests::dump("msg3", msgs3); - dballe::tests::dump("msg", rawmsg); - dballe::tests::dump("diffs", str.str(), "details of differences"); - throw tut::failure(loc.msg(str::fmtf("found %d differences", diffs))); - } - } - -#define inner_do_test(name, ...) do_test(wibble::tests::Location(loc, __FILE__, __LINE__, name), __VA_ARGS__) - void do_ecmwf(const dballe::tests::Location& loc, const char* template_type="synop") - { - string ecmwf_template_name = string(template_type) + "-ecmwf"; - string wmo_template_name = string(template_type) + "-wmo"; - do_wmo_tweaks = false; - - input_opts.simplified = true; - do_round_geopotential = false; - - do_ecmwf_tweaks = false; - inner_do_test("simp-ecmwf-through-auto", NULL); - inner_do_test("simp-ecmwf-through-ecmwf", ecmwf_template_name.c_str()); - - do_ecmwf_tweaks = true; - do_round_geopotential = true; - inner_do_test("simp-ecmwf-through-wmo", wmo_template_name.c_str()); - - input_opts.simplified = false; - do_round_geopotential = false; - - do_ecmwf_tweaks = false; - inner_do_test("real-ecmwf-through-auto", NULL); - inner_do_test("real-ecmwf-through-ecmwf", ecmwf_template_name.c_str()); - - do_ecmwf_tweaks = true; - do_round_geopotential = true; - inner_do_test("real-ecmwf-through-wmo", wmo_template_name.c_str()); - } - void do_wmo(const dballe::tests::Location& loc, const char* template_type="synop") - { - string ecmwf_template_name = string(template_type) + "-ecmwf"; - string wmo_template_name = string(template_type) + "-wmo"; - do_ecmwf_tweaks = false; - - input_opts.simplified = true; - do_round_geopotential = false; - - do_wmo_tweaks = false; - inner_do_test("simp-wmo-through-auto", NULL); - inner_do_test("simp-wmo-through-wmo", wmo_template_name.c_str()); - - do_wmo_tweaks = true; - do_ignore_context_attrs = true; - do_round_geopotential = true; - inner_do_test("simp-wmo-through-ecmwf", ecmwf_template_name.c_str()); - do_ignore_context_attrs = false; - - input_opts.simplified = false; - do_round_geopotential = false; - - do_wmo_tweaks = false; - inner_do_test("real-wmo-through-auto", NULL); - inner_do_test("real-wmo-through-wmo", wmo_template_name.c_str()); - - // There doesn't seem much sense testing this at the moment - //do_tweaks = true; - //inner_do_test("real-wmo-through-ecmwf", ecmwf_template_name.c_str()); - } -#undef inner_do_test -}; -typedef ReimportTest BufrReimportTest; -typedef ReimportTest CrexReimportTest; -#define run_test(obj, meth, ...) obj.meth(wibble::tests::Location(__FILE__, __LINE__, (obj.fname + " " #__VA_ARGS__).c_str()), __VA_ARGS__) - -std::vector tests { - Test("all_bufr", [](Fixture& f) { - // Test that plain re-export of all our BUFR test files is possible - // note: These were blacklisted: - // "bufr/obs3-3.1.bufr", - // "bufr/obs3-56.2.bufr", - // "bufr/test-buoy1.bufr", - // "bufr/test-soil1.bufr", - set bl_crex; - // CREX tables do not contain the entries needed to encode pollution - // messages - bl_crex.insert("bufr/ed4.bufr"); - - const char** files = dballe::tests::bufr_files; - vector fails; - int i; - for (i = 0; files[i] != NULL; i++) - { - try { - Messages msgs = read_msgs(files[i], File::BUFR); - ensure(msgs.size() > 0); - - std::unique_ptr exporter; - - exporter = msg::Exporter::create(File::BUFR/*, const Options& opts=Options()*/); - unique_ptr bbulletin = exporter->to_bulletin(msgs); - - if (bl_crex.find(files[i]) == bl_crex.end()) - { - exporter = msg::Exporter::create(File::CREX/*, const Options& opts=Options()*/); - unique_ptr cbulletin = exporter->to_bulletin(msgs); - } - } catch (std::exception& e) { - fails.push_back(string(files[i]) + ": " + e.what()); - } - } - if (!fails.empty()) - throw tut::failure(str::fmtf("%zd/%d errors:\n", fails.size(), i) + str::join(fails.begin(), fails.end(), "\n")); - }), - Test("all_crex", [](Fixture& f) { - // Test that plain re-export of all our CREX test files is possible - const char** files = dballe::tests::crex_files; - - vector fails; - int i; - for (i = 0; files[i] != NULL; i++) - { - try { - Messages msgs = read_msgs(files[i], File::CREX); - ensure(msgs.size() > 0); - - std::unique_ptr exporter = msg::Exporter::create(File::BUFR/*, const Options& opts=Options()*/); - unique_ptr bbulletin = exporter->to_bulletin(msgs); - - exporter = msg::Exporter::create(File::CREX/*, const Options& opts=Options()*/); - unique_ptr cbulletin = exporter->to_bulletin(msgs); - } catch (std::exception& e) { - fails.push_back(string(files[i]) + ": " + e.what()); - } - } - if (!fails.empty()) - throw tut::failure(str::fmtf("%zd/%d errors:\n", fails.size(), i) + str::join(fails.begin(), fails.end(), "\n")); - }), - Test("reproduce_temp", [](Fixture& f) { - // Export a well known TEMP which used to fail - Messages msgs = read_msgs_csv("csv/temp1.csv"); - ensure(msgs.size() > 0); - - // Replace with packed levels because comparison later happens against - // packed levels - { - unique_ptr msg(new Msg); - msg->sounding_pack_levels(Msg::downcast(msgs[0])); - msgs.clear(); - msgs.append(move(msg)); - } - - // Export to BUFR - std::unique_ptr bufr_exporter(msg::Exporter::create(File::BUFR/*, const Options& opts=Options()*/)); - unique_ptr bbulletin = bufr_exporter->to_bulletin(msgs); - - // Import and check the differences - { - std::unique_ptr bufr_importer(msg::Importer::create(File::BUFR/*, const Options& opts=Options()*/)); - Messages msgs1 = wcallchecked(bufr_importer->from_bulletin(*bbulletin)); - notes::Collect c(cerr); - ensure_equals(msgs.diff(msgs1), 0); - } - - // Export to CREX - std::unique_ptr crex_exporter(msg::Exporter::create(File::CREX/*, const Options& opts=Options()*/)); - unique_ptr cbulletin = crex_exporter->to_bulletin(msgs); - - // Import and check the differences - { - std::unique_ptr crex_importer(msg::Importer::create(File::CREX/*, const Options& opts=Options()*/)); - Messages msgs1 = wcallchecked(crex_importer->from_bulletin(*cbulletin)); - notes::Collect c(cerr); - ensure_equals(msgs.diff(msgs1), 0); - } - }), - // Re-export tests for old style synops - Test("old_synop1", [](Fixture& f) { - dballe::tests::TestCodec test("bufr/obs0-1.22.bufr"); - test.expected_min_vars = 34; - test.configure_ecmwf_to_wmo_tweaks(); - - TEST_reimport(test); - TEST_convert(test, "synop-wmo"); - - test.after_convert_reimport.add(new StripDatetimeVars()); - TEST_convert(test, "synop"); - }), - Test("old_synop2", [](Fixture& f) { - dballe::tests::TestCodec test("bufr/obs0-1.11188.bufr"); - test.expected_subsets = 1; - test.expected_min_vars = 10; - TEST_reimport(test); - TEST_convert(test, "synop-ecmwf"); - }), - Test("old_synop3", [](Fixture& f) { - dballe::tests::TestCodec test("bufr/obs0-3.504.bufr"); - test.expected_subsets = 1; - test.expected_min_vars = 10; - TEST_reimport(test); - TEST_convert(test, "synop-ecmwf"); - }), - // Re-export test for new style synops - Test("new_synop1", [](Fixture& f) { - dballe::tests::TestCodec test("bufr/synop-cloudbelow.bufr"); - test.expected_subsets = 22; - test.expected_min_vars = 10; - test.expected_type = 0; - test.expected_subtype = 1; - test.expected_localsubtype = 255; - TEST_reimport(test); - TEST_convert(test, "synop-wmo"); - }), - Test("new_synop2", [](Fixture& f) { - dballe::tests::TestCodec test("bufr/synop-evapo.bufr"); - test.expected_subsets = 14; - test.expected_min_vars = 10; - test.expected_type = 0; - test.expected_subtype = 2; - test.expected_localsubtype = 255; - TEST_reimport(test); - TEST_convert(test, "synop-wmo"); - }), - Test("new_synop3", [](Fixture& f) { - dballe::tests::TestCodec test("bufr/synop-groundtemp.bufr"); - test.expected_subsets = 26; - test.expected_min_vars = 10; - test.expected_type = 0; - test.expected_subtype = 1; - test.expected_localsubtype = 255; - TEST_reimport(test); - TEST_convert(test, "synop-wmo"); - }), - Test("new_synop4", [](Fixture& f) { - dballe::tests::TestCodec test("bufr/synop-longname.bufr"); - test.after_reimport_reimport.add(new TruncStName()); - test.after_convert_reimport.add(new TruncStName()); - test.expected_subsets = 7; - test.expected_min_vars = 10; - test.expected_type = 0; - test.expected_subtype = 1; - test.expected_localsubtype = 255; - TEST_reimport(test); - TEST_convert(test, "synop-wmo"); - }), - Test("new_synop5", [](Fixture& f) { - dballe::tests::TestCodec test("bufr/synop-oddgust.bufr"); - test.expected_subsets = 26; - test.expected_min_vars = 10; - test.expected_type = 0; - test.expected_subtype = 0; - test.expected_localsubtype = 255; - TEST_reimport(test); - TEST_convert(test, "synop-wmo"); - }), - Test("new_synop6", [](Fixture& f) { - dballe::tests::TestCodec test("bufr/synop-oddprec.bufr"); - test.expected_subsets = 1; - test.expected_min_vars = 10; - test.expected_type = 0; - test.expected_subtype = 0; - test.expected_localsubtype = 255; - TEST_reimport(test); - TEST_convert(test, "synop-wmo"); - }), - Test("new_synop7", [](Fixture& f) { - dballe::tests::TestCodec test("bufr/synop-strayvs.bufr"); - test.expected_subsets = 1; - test.expected_min_vars = 10; - test.expected_type = 0; - test.expected_subtype = 1; - test.expected_localsubtype = 255; - TEST_reimport(test); - TEST_convert(test, "synop-wmo"); - }), - Test("new_synop8", [](Fixture& f) { - dballe::tests::TestCodec test("bufr/synop-sunshine.bufr"); - test.expected_subsets = 26; - test.expected_min_vars = 10; - test.expected_type = 0; - test.expected_subtype = 1; - test.expected_localsubtype = 255; - TEST_reimport(test); - TEST_convert(test, "synop-wmo"); - }), - Test("new_synop9", [](Fixture& f) { - dballe::tests::TestCodec test("bufr/synop-gtscosmo.bufr"); - test.expected_subsets = 1; - test.expected_min_vars = 10; - test.expected_type = 0; - test.expected_subtype = 2; - test.expected_localsubtype = 255; - TEST_reimport(test); - TEST_convert(test, "synop-wmo"); - }), - Test("new_synop10", [](Fixture& f) { - // Test import/export of GTS synop with radiation information - dballe::tests::TestCodec test("bufr/synop-rad1.bufr"); - test.expected_subsets = 25; - test.expected_min_vars = 50; - - TEST_reimport(test); - TEST_convert(test, "synop-wmo"); - }), - Test("new_synop11", [](Fixture& f) { - // Test import/export of GTS synop without pressure of standard level - dballe::tests::TestCodec test("bufr/synop-rad2.bufr"); - test.expected_template = "synop-wmo"; - test.expected_min_vars = 50; - test.verbose = true; - - wruntest(test.run_reimport); - wruntest(test.run_convert, "synop-wmo"); - }), - Test("new_synop12", [](Fixture& f) { - // Test import/export of GTS synop with temperature change information - dballe::tests::TestCodec test("bufr/synop-tchange.bufr"); - test.expected_min_vars = 50; - - wruntest(test.run_reimport); - TEST_convert(test, "synop-wmo"); - }), - Test("new_synop13", [](Fixture& f) { - // FIXME: temp message with data category 0: what do we do with it? - dballe::tests::TestCodec test("bufr/temp-timesig18.bufr"); - test.expected_min_vars = 50; - test.expected_template = "temp-wmo"; - - wruntest(test.run_reimport); - TEST_convert(test, "temp-wmo"); - }), - // Re-export test for old style temps - Test("old_temp1", [](Fixture& f) { - dballe::tests::TestCodec test("bufr/obs2-101.16.bufr"); - wruntest(test.run_reimport); - wruntest(test.run_convert, "temp-ecmwf"); - }), - Test("old_temp2", [](Fixture& f) { - dballe::tests::TestCodec test("bufr/obs2-102.1.bufr"); - wruntest(test.run_reimport); - wruntest(test.run_convert, "temp-ecmwf"); - }), - Test("old_temp3", [](Fixture& f) { - dballe::tests::TestCodec test("bufr/obs2-91.2.bufr"); - wruntest(test.run_reimport); - wruntest(test.run_convert, "pilot-ecmwf"); - }), - Test("old_temp4", [](Fixture& f) { - dballe::tests::TestCodec test("bufr/temp-bad3.bufr"); - wruntest(test.run_reimport); - wruntest(test.run_convert, "temp"); - }), - Test("old_temp5", [](Fixture& f) { - // This has some sounding groups with undefined VSS - dballe::tests::TestCodec test("bufr/temp-bad5.bufr"); - wruntest(test.run_reimport); - wruntest(test.run_convert, "temp-ecmwf"); - }), - Test("old_temp6", [](Fixture& f) { - // This has some sounding groups with undefined VSS, and an unusual template - dballe::tests::TestCodec test("bufr/test-temp1.bufr"); - wruntest(test.run_reimport); - wruntest(test.run_convert, "temp-ecmwf"); - }), - Test("old_temp7", [](Fixture& f) { - // This has an unusual template - dballe::tests::TestCodec test("bufr/C23000.bufr"); - test.after_reimport_import.add(new StripSubstituteAttrs()); - test.after_convert_import.add(new StripSubstituteAttrs()); - wruntest(test.run_reimport); - wruntest(test.run_convert, "temp-ecmwf"); - }), - Test("old_temp8", [](Fixture& f) { - dballe::tests::TestCodec test("bufr/temp-bad1.bufr"); - test.after_reimport_reimport.add(new StripQCAttrs()); - test.after_reimport_reimport.add(new StripSubstituteAttrs()); - test.after_reimport_reimport.add(new StripDatetimeVars()); - test.after_convert_import.add(new StripQCAttrs()); - test.after_convert_import.add(new StripSubstituteAttrs()); - test.after_convert_import.add(new StripDatetimeVars()); - test.expected_subsets = 1; - test.expected_min_vars = 10; - test.expected_type = 2; - test.expected_subtype = 255; - test.expected_localsubtype = 101; - TEST_reimport(test); - TEST_convert(test, "temp-ecmwf"); - }), - Test("old_temp9", [](Fixture& f) { - dballe::tests::TestCodec test("bufr/temp-bad2.bufr"); - test.after_reimport_reimport.add(new StripQCAttrs()); - test.after_reimport_reimport.add(new StripSubstituteAttrs()); - test.after_reimport_reimport.add(new StripDatetimeVars()); - test.after_convert_import.add(new StripQCAttrs()); - test.after_convert_import.add(new StripSubstituteAttrs()); - test.after_convert_import.add(new StripDatetimeVars()); - test.expected_subsets = 1; - test.expected_min_vars = 10; - test.expected_type = 2; - test.expected_subtype = 255; - test.expected_localsubtype = 101; - TEST_reimport(test); - TEST_convert(test, "temp-ecmwf"); - }), - // Re-export test for new style temps - Test("new_temp1", [](Fixture& f) { - dballe::tests::TestCodec test("bufr/temp-gts1.bufr"); - test.expected_subsets = 1; - test.expected_min_vars = 10; - test.expected_type = 2; - test.expected_subtype = 4; - test.expected_localsubtype = 255; - TEST_reimport(test); - TEST_convert(test, "temp-wmo"); - }), - Test("new_temp2", [](Fixture& f) { - dballe::tests::TestCodec test("bufr/temp-gts2.bufr"); - test.expected_subsets = 6; - test.expected_min_vars = 10; - test.expected_type = 2; - test.expected_subtype = 4; - test.expected_localsubtype = 255; - TEST_reimport(test); - TEST_convert(test, "temp-wmo"); - }), - Test("new_temp3", [](Fixture& f) { - dballe::tests::TestCodec test("bufr/temp-gts3.bufr"); - test.expected_subsets = 1; - test.expected_min_vars = 10; - test.expected_type = 2; - test.expected_subtype = 4; - test.expected_localsubtype = 255; - TEST_reimport(test); - TEST_convert(test, "temp-wmo"); - }), - Test("new_temp4", [](Fixture& f) { - dballe::tests::TestCodec test("bufr/temp-gtscosmo.bufr"); - test.expected_subsets = 1; - test.expected_min_vars = 10; - test.expected_type = 2; - test.expected_subtype = 4; - test.expected_localsubtype = 255; - TEST_reimport(test); - TEST_convert(test, "temp-wmo"); - }), - Test("new_temp5", [](Fixture& f) { -#warning This is importer with height above ground levels, but exported with pressure levels -#if 0 - // Another weird template - BufrReimportTest test("bufr/temp-2-255.bufr"); - run_test(test, do_wmo, "temp"); - //test.output_opts.template_name = "temp-wmo"; - //run_test(test, do_test, "auto"); - //test.output_opts.template_name = "temp-ecmwf"; - //test.clear_tweaks(); - //run_test(test, do_test, "old"); -#endif - }), - Test("new_temp7", [](Fixture& f) { - dballe::tests::TestCodec test("bufr/temp-bad4.bufr"); - test.after_reimport_reimport.add(new RoundLegacyVars()); - test.expected_subsets = 1; - test.expected_min_vars = 10; - test.expected_type = 2; - test.expected_subtype = 255; - test.expected_localsubtype = 101; - TEST_reimport(test); - TEST_convert(test, "temp-wmo"); - }), - Test("new_temp8", [](Fixture& f) { -#warning There is no template that can export these forecast TEMPs except generic -#if 0 - // generic temp with forecast info and hybrid levels (?) - BufrReimportTest test("bufr/tempforecast.bufr"); - run_test(test, do_test, "temp-wmo"); - test.clear_tweaks(); - test.tweaks.push_back(new StripQCAttrs()); - run_test(test, do_test, "temp-ecmwf", "temp-wmo"); -#endif - }), - Test("new_temp9", [](Fixture& f) { - // Test that temp vad subtype is set correctly - dballe::tests::TestCodec test("bufr/vad.bufr"); - test.expected_subsets = 1; - test.expected_min_vars = 10; - test.expected_type = 6; - test.expected_subtype = 1; - test.expected_localsubtype = 255; - TEST_reimport(test); - TEST_convert(test, "temp"); - }), - Test("new_temp10", [](Fixture& f) { - // Test correct import/export of a temp with thousands of levels - dballe::tests::TestCodec test("bufr/temp-huge.bufr"); - test.expected_min_vars = 30000; - TEST_reimport(test); - TEST_convert(test, "temp-wmo"); - }), - // New style ACARS - Test("new_acars1", [](Fixture& f) { -#warning no documentation on new stile ACARS available yet - //BufrReimportTest test("bufr/gts-acars1.bufr"); - //run_test(test, do_test, "acars-wmo"); - }), - Test("new_acars2", [](Fixture& f) { -#warning no documentation on new stile ACARS available yet - //BufrReimportTest test("bufr/gts-acars2.bufr"); - //run_test(test, do_test, "acars-wmo"); - }), - Test("new_acars3", [](Fixture& f) { -#warning no documentation on new stile ACARS available yet - //BufrReimportTest test("bufr/gts-acars-uk1.bufr"); - //run_test(test, do_test, "acars-wmo"); - }), - Test("new_acars4", [](Fixture& f) { -#warning no documentation on new stile ACARS available yet - //BufrReimportTest test("bufr/gts-acars-us1.bufr"); - //run_test(test, do_test, "acars-wmo"); - }), - // New style AMDAR - Test("new_amdar1", [](Fixture& f) { - BufrReimportTest test("bufr/gts-amdar1.bufr"); - run_test(test, do_test, "amdar-wmo"); - }), - Test("new_amdar2", [](Fixture& f) { - BufrReimportTest test("bufr/gts-amdar2.bufr"); - run_test(test, do_test, "amdar-wmo"); - }), - // Re-export to BUFR (simplified, full template autodetect) and see the differences - Test("all_simplified", [](Fixture& f) { - const char** files = dballe::tests::bufr_files; - // Uncomment to single out one failing file - //const char* files[] = { "bufr/temp-2-255.bufr", NULL }; - set blacklist; - // Generics, would need a template override to avoid the per-rete - // autodetected export - blacklist.insert("bufr/tempforecast.bufr"); - blacklist.insert("bufr/obs255-255.0.bufr"); - // Nonstandard messages that need tweaks, tested individually above - blacklist.insert("bufr/test-temp1.bufr"); - blacklist.insert("bufr/C23000.bufr"); - blacklist.insert("bufr/temp-2-255.bufr"); - blacklist.insert("bufr/synop-longname.bufr"); - blacklist.insert("bufr/temp-bad4.bufr"); - - vector fails; - int i; - std::unique_ptr exporter; - exporter = msg::Exporter::create(File::BUFR/*, const Options& opts=Options()*/); - std::unique_ptr importer = msg::Importer::create(File::BUFR/*, opts*/); - - for (i = 0; files[i] != NULL; i++) - { - if (blacklist.find(files[i]) != blacklist.end()) continue; - try { - // Import - Messages msgs = read_msgs(files[i], File::BUFR); - ensure(msgs.size() > 0); - - // Export - unique_ptr bbulletin = exporter->to_bulletin(msgs); - - // Import again - Messages msgs1 = importer->from_bulletin(*bbulletin); - - // Compare - notes::Collect c(cerr); - int diffs = msgs.diff(msgs1); - if (diffs) - { - FILE* out1 = fopen("/tmp/msg1.txt", "w"); - FILE* out2 = fopen("/tmp/msg2.txt", "w"); - msgs.print(out1); - msgs1.print(out2); - fclose(out1); - fclose(out2); - } - ensure_equals(diffs, 0); - } catch (std::exception& e) { - fails.push_back(string(files[i]) + ": " + e.what()); - } - } - if (!fails.empty()) - throw tut::failure(str::fmtf("%zd/%d errors:\n", fails.size(), i) + str::join(fails.begin(), fails.end(), "\n")); - }), - Test("all_precise", [](Fixture& f) { - // Re-export to BUFR (not simplified, full template autodetect) and see the differences - const char** files = dballe::tests::bufr_files; - // Uncomment to single out one failing file - //const char* files[] = { "bufr/obs0-1.22.bufr", NULL }; - set blacklist; - // Generics, would need a template override to avoid the per-rete - // autodetected export - blacklist.insert("bufr/tempforecast.bufr"); - blacklist.insert("bufr/obs255-255.0.bufr"); - // Nonstandard messages that need tweaks, tested individually above - blacklist.insert("bufr/test-temp1.bufr"); - blacklist.insert("bufr/C23000.bufr"); - blacklist.insert("bufr/temp-2-255.bufr"); - blacklist.insert("bufr/synop-longname.bufr"); - blacklist.insert("bufr/temp-bad4.bufr"); - - vector fails; - int i; - std::unique_ptr exporter; - exporter = msg::Exporter::create(File::BUFR/*, const Options& opts=Options()*/); - msg::Importer::Options import_opts; - import_opts.simplified = false; - std::unique_ptr importer = msg::Importer::create(File::BUFR, import_opts); - - for (i = 0; files[i] != NULL; i++) - { - if (blacklist.find(files[i]) != blacklist.end()) continue; - try { - // Import - Messages msgs = read_msgs_opts(files[i], File::BUFR, import_opts); - ensure(msgs.size() > 0); - - // Export - unique_ptr bbulletin = exporter->to_bulletin(msgs); - - // Import again - Messages msgs1 = importer->from_bulletin(*bbulletin); - - // Compare - stringstream str; - notes::Collect c(str); - int diffs = msgs.diff(msgs1); - if (diffs) - { - string tag = str::basename(files[i]); - dballe::tests::dump("dballe-orig-" + tag, msgs, "original message"); - dballe::tests::dump("dballe-reenc-" + tag, *bbulletin, "reencoded message"); - dballe::tests::dump("dballe-reenc-" + tag, msgs1, "decoded reencoded message"); - dballe::tests::dump("dballe-diffs-" + tag, str.str(), "differences"); - } - ensure_equals(diffs, 0); - } catch (std::exception& e) { - fails.push_back(string(files[i]) + ": " + e.what()); - } - } - if (!fails.empty()) - throw tut::failure(str::fmtf("%zd/%d errors:\n", fails.size(), i) + str::join(fails.begin(), fails.end(), "\n")); - }), - // Old PILOT - Test("old_pilot1", [](Fixture& f) { - // Test that pilot subtype is set correctly - Messages msgs = read_msgs("bufr/obs2-91.2.bufr", File::BUFR); - msg::Exporter::Options opts; - opts.template_name = "pilot-wmo"; - unique_ptr bulletin = test_export_msgs(File::BUFR, msgs, "pilotwmo", opts); - ensure_equals(bulletin->data_category, 2); - ensure_equals(bulletin->data_subcategory, 1); - ensure_equals(bulletin->data_subcategory_local, 255); - }), - Test("old_pilot2", [](Fixture& f) { - // Test import/export of ECMWF pilot with pressure levels - dballe::tests::TestCodec test("bufr/pilot-gts3.bufr"); - test.expected_min_vars = 50; - test.configure_ecmwf_to_wmo_tweaks(); - - TEST_reimport(test); - TEST_convert(test, "pilot-wmo"); - }), - Test("old_pilot3", [](Fixture& f) { - // Test import/export of ECMWF pilot with geopotential levels - dballe::tests::TestCodec test("bufr/pilot-gts4.bufr"); - test.expected_min_vars = 50; - test.configure_ecmwf_to_wmo_tweaks(); - test.after_convert_reimport.add(new dballe::tests::tweaks::HeightToGeopotential); - test.after_convert_reimport.add(new dballe::tests::tweaks::RemoveContext( - Level(100, 70000), Trange::instant())); - test.after_convert_reimport.add(new dballe::tests::tweaks::RemoveContext( - Level(100, 85000), Trange::instant())); - - TEST_reimport(test); - TEST_convert(test, "pilot-wmo"); - }), - // New style PILOT - Test("new_pilot1", [](Fixture& f) { - BufrReimportTest test("bufr/pilot-gts1.bufr"); - run_test(test, do_test, "pilot-wmo"); - - ensure_equals(test.exported->data_category, 2); - ensure_equals(test.exported->data_subcategory, 1); - ensure_equals(test.exported->data_subcategory_local, 255); - }), - Test("new_pilot2", [](Fixture& f) { - BufrReimportTest test("bufr/pilot-gts1.bufr"); - run_test(test, do_test, "pilot-wmo"); - - ensure_equals(test.exported->data_category, 2); - ensure_equals(test.exported->data_subcategory, 1); - ensure_equals(test.exported->data_subcategory_local, 255); - }), - Test("new_pilot3", [](Fixture& f) { - dballe::tests::TestCodec test("bufr/pilot-ecmwf-geopotential.bufr"); - test.configure_ecmwf_to_wmo_tweaks(); - test.after_reimport_reimport.add(new StripVars({ WR_VAR(0, 10, 8) })); - test.after_convert_reimport.add(new StripVars({ WR_VAR(0, 10, 8) })); - test.expected_min_vars = 30; - TEST_reimport(test); - TEST_convert(test, "pilot-wmo"); - }), - Test("new_pilot4", [](Fixture& f) { - // Test for a bug where geopotential levels became pressure levels - Messages msgs1 = read_msgs("bufr/pilot-ecmwf-geopotential.bufr", File::BUFR); - ensure_equals(msgs1.size(), 1); - Msg& msg1 = Msg::downcast(msgs1[0]); - - // Geopotential levels are converted to height above msl - const msg::Context* c = msg1.find_context(Level(102, 900), Trange(254, 0, 0)); - ensure(c != NULL); - - // Convert to WMO template - msg::Exporter::Options output_opts; - output_opts.template_name = "pilot-wmo"; - //if (verbose) cerr << "Exporting " << output_opts.to_string() << endl; - std::unique_ptr bulletin = test_export_msgs(File::BUFR, msgs1, "towmo", output_opts); - - // Import again - std::unique_ptr imp = msg::Importer::create(File::BUFR); - Messages msgs2 = imp->from_bulletin(*bulletin); - ensure_equals(msgs2.size(), 1); - Msg& msg2 = Msg::downcast(msgs2[0]); - - // Ensure we didn't get pressure levels - ensure(msg2.find_context(Level(100, 900), Trange(254, 0, 0)) == NULL); - }), - Test("new_pilot5", [](Fixture& f) { - // Test for a range error in one specific BUFR - Messages msgs1 = read_msgs("bufr/temp-2-255.bufr", File::BUFR); - ensure_equals(msgs1.size(), 1); - Msg& msg1 = Msg::downcast(msgs1[0]); - - // Convert to CREX - msg::Exporter::Options output_opts; - output_opts.template_name = "temp-wmo"; - std::unique_ptr bulletin = test_export_msgs(File::CREX, msgs1, "tocrex", output_opts); - - // Import again - std::unique_ptr imp = msg::Importer::create(File::BUFR); - Messages msgs2 = wcallchecked(imp->from_bulletin(*bulletin)); - ensure_equals(msgs2.size(), 1); - Msg& msg2 = Msg::downcast(msgs2[0]); - }), - // Old SHIP - Test("old_ship1", [](Fixture& f) { - // Test that temp ship subtype is set correctly - Messages msgs = read_msgs("bufr/obs2-102.1.bufr", File::BUFR); - msg::Exporter::Options opts; - opts.template_name = "temp-wmo"; - unique_ptr bulletin = test_export_msgs(File::BUFR, msgs, "tempship", opts); - ensure_equals(bulletin->data_category, 2); - ensure_equals(bulletin->data_subcategory, 5); - ensure_equals(bulletin->data_subcategory_local, 255); - }), - Test("old_ship2", [](Fixture& f) { - // Test import/export of ECMWF synop ship - dballe::tests::TestCodec test("bufr/ecmwf-ship-1-11.bufr"); - test.expected_min_vars = 34; - test.configure_ecmwf_to_wmo_tweaks(); - - TEST_reimport(test); - TEST_convert(test, "ship-wmo"); - }), - Test("old_ship3", [](Fixture& f) { - // Test import/export of ECMWF synop ship record 2 - dballe::tests::TestCodec test("bufr/ecmwf-ship-1-12.bufr"); - test.expected_min_vars = 21; - test.configure_ecmwf_to_wmo_tweaks(); - - TEST_reimport(test); - TEST_convert(test, "ship-wmo"); - }), - Test("old_ship4", [](Fixture& f) { - // Test import/export of ECMWF synop ship (auto) - dballe::tests::TestCodec test("bufr/ecmwf-ship-1-13.bufr"); - test.expected_min_vars = 30; - test.configure_ecmwf_to_wmo_tweaks(); - - TEST_reimport(test); - TEST_convert(test, "ship-wmo"); - }), - Test("old_ship5", [](Fixture& f) { - // Test import/export of ECMWF synop ship (auto) record 2 - dballe::tests::TestCodec test("bufr/ecmwf-ship-1-14.bufr"); - test.expected_min_vars = 28; - test.configure_ecmwf_to_wmo_tweaks(); - - TEST_reimport(test); - TEST_convert(test, "ship-wmo"); - }), - // New SHIP - Test("new_ship1", [](Fixture& f) { - // Test import/export of WMO synop ship - dballe::tests::TestCodec test("bufr/wmo-ship-1.bufr"); - test.expected_min_vars = 50; - - TEST_reimport(test); - TEST_convert(test, "ship-wmo"); - }), - // ECMWF <-> WMO - Test("ecmwf_wmo1", [](Fixture& f) { - dballe::tests::TestCodec test("bufr/ecmwf-amdar1.bufr"); - test.expected_min_vars = 21; - - TEST_reimport(test); - TEST_convert(test, "amdar"); - }), - Test("ecmwf_wmo2", [](Fixture& f) { - dballe::tests::TestCodec test("bufr/ecmwf-acars1.bufr"); - test.expected_min_vars = 21; - - TEST_reimport(test); - TEST_convert(test, "acars"); - }), -}; - -test_group tg("msg_wr_export", tests); - -} - diff --git a/dballe/msg/wr_exporters/temp.cc b/dballe/msg/wr_exporters/temp.cc index 34c7c103f..17c76ac44 100644 --- a/dballe/msg/wr_exporters/temp.cc +++ b/dballe/msg/wr_exporters/temp.cc @@ -815,7 +815,7 @@ struct PilotEcmwf : public TempBase /* Add vertical sounding significance */ { - Var nvar(subset.tables->btable->query(WR_VAR(0, 8, 1)), convert_BUFR08042_to_BUFR08001(vss->enqi())); + Var nvar(subset.tables->btable->query(WR_VAR(0, 8, 1)), (int)convert_BUFR08042_to_BUFR08001(vss->enqi())); nvar.setattrs(*vss); subset.store_variable(WR_VAR(0, 8, 1), nvar); } diff --git a/dballe/msg/wr_import-test.cc b/dballe/msg/wr_import-test.cc new file mode 100644 index 000000000..0dc172a40 --- /dev/null +++ b/dballe/msg/wr_import-test.cc @@ -0,0 +1,425 @@ +#include "tests.h" +#include "wr_codec.h" +#include "msg.h" +#include "context.h" +#include +#include +#include + +using namespace wreport; +using namespace dballe; +using namespace dballe::tests; +using namespace std; + +namespace { + +#define IS(field, val) do { \ + WREPORT_TEST_INFO(locinfo); \ + locinfo() << #field; \ + const Var* var = msg.get_##field##_var(); \ + wassert(actual(var).istrue()); \ + wassert(actual(*var) == val); \ + } while (0) +#define IS2(code, lev, tr, val) do { \ + WREPORT_TEST_INFO(locinfo); \ + locinfo() << #code #lev #tr; \ + const Var* var = msg.get(code, lev, tr); \ + wassert(actual(var).istrue()); \ + wassert(actual(*var) == val); \ + } while (0) +#define UN(field) do { \ + const Var* var = msg.get_##field##_var(); \ + if (var != 0) \ + wassert(actual(*var).isunset()); \ + } while (0) + + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void add_bufr_method(const std::string& fname, std::function m) + { + add_method(fname, [=]() { + std::string pathname = "bufr/" + fname; + msg::Importer::Options opts; + opts.simplified = false; + Messages msgs = wcallchecked(read_msgs(pathname.c_str(), File::BUFR, opts)); + m(msgs); + }); + } + + void add_bufr_simplified_method(const std::string& fname, std::function m) + { + add_method(fname, [=]() { + std::string pathname = "bufr/" + fname; + msg::Importer::Options opts; + opts.simplified = true; + Messages msgs = read_msgs(pathname.c_str(), File::BUFR, opts); + m(msgs); + }); + } + + void add_crex_method(const std::string& fname, std::function m) + { + add_method(fname, [=]() { + std::string pathname = "crex/" + fname; + Messages msgs = read_msgs(pathname.c_str(), File::CREX); + m(msgs); + }); + } + + void register_tests() override + { + // Test plain import of all our BUFR test files + add_method("all_bufr", []() { + // note: These were blacklisted: + // "bufr/obs3-3.1.bufr", + // "bufr/obs3-56.2.bufr", + // "bufr/test-buoy1.bufr", + // "bufr/test-soil1.bufr", + const char** files = dballe::tests::bufr_files; + + for (int i = 0; files[i] != NULL; i++) + { + try { + Messages msgs = read_msgs(files[i], File::BUFR); + wassert(actual(msgs.size()) > 0); + } catch (std::exception& e) { + cerr << "Failing bulletin:"; + try { + BinaryMessage raw = read_rawmsg(files[i], File::BUFR); + unique_ptr bulletin(BufrBulletin::decode(raw.data)); + bulletin->print(stderr); + } catch (std::exception& e1) { + cerr << "Cannot display failing bulletin: " << e1.what() << endl; + } + throw TestFailed(string("[") + files[i] + "] " + e.what()); + } + } + }); + + // Test plain import of all our CREX test files + add_method("all_crex", []() { + const char** files = dballe::tests::crex_files; + + for (int i = 0; files[i] != NULL; i++) + { + try { + Messages msgs = read_msgs(files[i], File::CREX); + wassert(actual(msgs.size()) > 0); + } catch (std::exception& e) { + cerr << "Failing bulletin:"; + try { + BinaryMessage raw = read_rawmsg(files[i], File::CREX); + unique_ptr bulletin(CrexBulletin::decode(raw.data)); + bulletin->print(stderr); + } catch (std::exception& e1) { + cerr << "Cannot display failing bulletin: " << e1.what() << endl; + } + throw TestFailed(string("[") + files[i] + "] " + e.what()); + } + } + }); + + add_crex_method("test-synop0.crex", [](const Messages& msgs) { + const Msg& msg = Msg::downcast(msgs[0]); + wassert(actual(msg.type) == MSG_SYNOP); + + IS(block, 10); IS(station, 837); IS(st_type, 1); + wassert(actual(msg.get_datetime()) == Datetime(2004, 11, 30, 12, 0)); + IS(latitude, 48.22); IS(longitude, 9.92); + IS(height_station, 550.0); UN(height_baro); + IS(press, 94340.0); IS(press_msl, 100940.0); IS(press_tend, 7.0); + IS(wind_dir, 80.0); IS(wind_speed, 6.0); + IS(temp_2m, 276.15); IS(dewpoint_2m, 273.85); UN(humidity); + IS(visibility, 5000.0); IS(pres_wtr, 10); IS(past_wtr1_6h, 2); IS(past_wtr2_6h, 2); + IS(cloud_n, 100); IS(cloud_nh, 8); IS(cloud_hh, 450.0); + IS(cloud_cl, 35); IS(cloud_cm, 61); IS(cloud_ch, 60); + IS(cloud_n1, 8); IS(cloud_c1, 6); IS(cloud_h1, 350.0); + UN(cloud_n2); UN(cloud_c2); UN(cloud_h2); + UN(cloud_n3); UN(cloud_c3); UN(cloud_h3); + UN(cloud_n4); UN(cloud_c4); UN(cloud_h4); + UN(tot_prec24); UN(tot_snow); + }); + + add_bufr_simplified_method("obs0-1.22.bufr", [](const Messages& msgs) { + const Msg& msg = Msg::downcast(msgs[0]); + wassert(actual(msg.type) == MSG_SYNOP); + + IS(block, 60); IS(station, 150); IS(st_type, 1); + wassert(actual(msg.get_datetime()) == Datetime(2004, 11, 30, 12, 0)); + IS(latitude, 33.88); IS(longitude, -5.53); + IS(height_station, 560.0); UN(height_baro); + IS(press, 94190.0); IS(press_msl, 100540.0); IS(press_3h, -180.0); IS(press_tend, 8.0); + IS(wind_dir, 80.0); IS(wind_speed, 4.0); + IS(temp_2m, 289.2); IS(dewpoint_2m, 285.7); UN(humidity); + IS(visibility, 8000.0); IS(pres_wtr, 2); IS(past_wtr1_6h, 6); IS(past_wtr2_6h, 2); + IS(cloud_n, 100); IS(cloud_nh, 8); IS(cloud_hh, 250.0); + IS(cloud_cl, 39); IS(cloud_cm, 61); IS(cloud_ch, 60); + IS(cloud_n1, 2); IS(cloud_c1, 8); IS(cloud_h1, 320.0); + IS(cloud_n2, 5); IS(cloud_c2, 8); IS(cloud_h2, 620.0); + IS(cloud_n3, 2); IS(cloud_c3, 9); IS(cloud_h3, 920.0); + UN(cloud_n4); UN(cloud_c4); UN(cloud_h4); + IS(tot_prec12, 0.5); UN(tot_snow); + }); + + add_bufr_simplified_method("synop-cloudbelow.bufr", [](const Messages& msgs) { + const Msg& msg = Msg::downcast(msgs[0]); + wassert(actual(msg.type) == MSG_SYNOP); + + // msg.print(stderr); + + IS(block, 11); IS(station, 406); IS(st_type, 1); + wassert(actual(msg.get_datetime()) == Datetime(2009, 12, 3, 15, 0)); + IS(latitude, 50.07361); IS(longitude, 12.40333); + IS(height_station, 483.0); IS(height_baro, 490.0); + IS(press, 95090.0); IS(press_msl, 101060.0); IS(press_3h, -110.0); IS(press_tend, 6.0); + IS(wind_dir, 0.0); IS(wind_speed, 1.0); + IS(temp_2m, 273.05); IS(dewpoint_2m, 271.35); IS(humidity, 88.0); + IS(visibility, 14000.0); IS(pres_wtr, 508); + IS2(WR_VAR(0, 20, 4), Level(1), Trange(205, 0, 10800), 10); // past_wtr1 + IS2(WR_VAR(0, 20, 5), Level(1), Trange(205, 0, 10800), 10); // past_wtr2 + IS(cloud_n, 38); IS(cloud_nh, 0); IS(cloud_hh, 6000.0); + IS(cloud_cl, 30); IS(cloud_cm, 20); IS(cloud_ch, 12); + IS(cloud_n1, 3); IS(cloud_c1, 0); IS(cloud_h1, 6000.0); + UN(cloud_n2); UN(cloud_c2); UN(cloud_h2); + UN(cloud_n3); UN(cloud_c3); UN(cloud_h3); + UN(cloud_n4); UN(cloud_c4); UN(cloud_h4); + UN(tot_prec24); UN(tot_snow); + }); + + add_bufr_method("synop-cloudbelow.bufr", [](const Messages& msgs) { + const Msg& msg = Msg::downcast(msgs[0]); + wassert(actual(msg.type) == MSG_SYNOP); + + // msg.print(stderr); + + IS(block, 11); IS(station, 406); IS(st_type, 1); + wassert(actual(msg.get_datetime()) == Datetime(2009, 12, 3, 15, 0)); + IS(latitude, 50.07361); IS(longitude, 12.40333); + IS(height_station, 483.0); IS(height_baro, 490.0); + IS2(WR_VAR(0, 10, 4), Level(102, 490000), Trange::instant(), 95090.0); // press + IS2(WR_VAR(0, 10, 51), Level(102, 490000), Trange::instant(), 101060.0); // press_msl + IS2(WR_VAR(0, 10, 63), Level(102, 490000), Trange(205, 0,10800), 6.0); // press_tend + IS2(WR_VAR(0, 10, 60), Level(102, 490000), Trange(4, 0, 10800), -110.0); // press_3h + IS2(WR_VAR(0, 11, 1), Level(103, 10000), Trange(200, 0, 600), 0.0); // wind_dir + IS2(WR_VAR(0, 11, 2), Level(103, 10000), Trange(200, 0, 600), 1.0); // wind_speed + IS2(WR_VAR(0, 12, 101), Level(103, 2050), Trange::instant(), 273.05); // temp_2m + IS2(WR_VAR(0, 12, 103), Level(103, 2050), Trange::instant(), 271.35); // dewpoint_2m + IS2(WR_VAR(0, 13, 3), Level(103, 2050), Trange::instant(), 88.0); // humidity + IS2(WR_VAR(0, 20, 1), Level(103, 8000), Trange::instant(), 14000.0); // visibility + IS(pres_wtr, 508); + IS2(WR_VAR(0, 20, 4), Level(1), Trange(205, 0, 10800), 10); // past_wtr1 + IS2(WR_VAR(0, 20, 5), Level(1), Trange(205, 0, 10800), 10); // past_wtr2 + IS(cloud_n, 38); IS(cloud_nh, 0); IS(cloud_hh, 6000.0); + IS(cloud_cl, 30); IS(cloud_cm, 20); IS(cloud_ch, 12); + IS(cloud_n1, 3); IS(cloud_c1, 0); IS(cloud_h1, 6000.0); + UN(cloud_n2); UN(cloud_c2); UN(cloud_h2); + UN(cloud_n3); UN(cloud_c3); UN(cloud_h3); + UN(cloud_n4); UN(cloud_c4); UN(cloud_h4); + UN(tot_prec24); UN(tot_snow); + }); + + add_bufr_simplified_method("temp-2-255.bufr", [](const Messages& msgs) { + const Msg& msg = Msg::downcast(msgs[0]); + wassert(actual(msg.type) == MSG_TEMP); + + // No negative pressure layers please + wassert(actual(msg.find_context(Level(100, -1), Trange::instant())).isfalse()); + }); + + add_bufr_simplified_method("synop-longname.bufr", [](const Messages& msgs) { + wassert(actual(msgs.size()) == 7u); + const Msg& msg = Msg::downcast(msgs[2]); + wassert(actual(msg.type) == MSG_SYNOP); + + // Check that the long station name has been correctly truncated on import + const Var* var = msg.get_st_name_var(); + wassert(actual(var).istrue()); + wassert(actual(string(var->enqc())) == "Budapest Pestszentlorinc-kulteru"); + }); + + add_bufr_simplified_method("temp-bad1.bufr", [](const Messages& msgs) { + wassert(actual(msgs.size()) == 1u); + const Msg& msg = Msg::downcast(msgs[0]); + wassert(actual(msg.type) == MSG_TEMP); + }); + + add_bufr_simplified_method("temp-bad2.bufr", [](const Messages& msgs) { + wassert(actual(msgs.size()) == 1u); + const Msg& msg = Msg::downcast(msgs[0]); + wassert(actual(msg.type) == MSG_TEMP); + }); + + add_bufr_simplified_method("temp-bad3.bufr", [](const Messages& msgs) { + wassert(actual(msgs.size()) == 1u); + const Msg& msg = Msg::downcast(msgs[0]); + wassert(actual(msg.type) == MSG_TEMP); + }); + + add_bufr_simplified_method("temp-bad4.bufr", [](const Messages& msgs) { + wassert(actual(msgs.size()) == 1u); + const Msg& msg = Msg::downcast(msgs[0]); + wassert(actual(msg.type) == MSG_TEMP); + }); + + // ECWMF AIREP + add_bufr_simplified_method("obs4-142.1.bufr", [](const Messages& msgs) { + wassert(actual(msgs.size()) == 1u); + const Msg& msg = Msg::downcast(msgs[0]); + wassert(actual(msg.type) == MSG_AIREP); + IS(ident, "ACA872"); + }); + + // ECWMF AMDAR + add_bufr_simplified_method("obs4-144.4.bufr", [](const Messages& msgs) { + wassert(actual(msgs.size()) == 1u); + const Msg& msg = Msg::downcast(msgs[0]); + wassert(actual(msg.type) == MSG_AMDAR); + IS(ident, "EU4444"); + }); + + // ECWMF ACARS + add_bufr_simplified_method("obs4-145.4.bufr", [](const Messages& msgs) { + wassert(actual(msgs.size()) == 1u); + const Msg& msg = Msg::downcast(msgs[0]); + wassert(actual(msg.type) == MSG_ACARS); + IS(ident, "JBNYR3RA"); + }); + + // WMO ACARS + add_bufr_simplified_method("gts-acars1.bufr", [](const Messages& msgs) { + wassert(actual(msgs.size()) == 1u); + const Msg& msg = Msg::downcast(msgs[0]); + wassert(actual(msg.type) == MSG_ACARS); + IS(ident, "EU5331"); + }); + + // WMO ACARS + add_bufr_simplified_method("gts-acars2.bufr", [](const Messages& msgs) { + wassert(actual(msgs.size()) == 1u); + const Msg& msg = Msg::downcast(msgs[0]); + wassert(actual(msg.type) == MSG_ACARS); + IS(ident, "FJCYR4RA"); + }); + + // WMO ACARS UK + add_bufr_simplified_method("gts-acars-uk1.bufr", [](const Messages& msgs) { + wassert(actual(msgs.size()) == 1u); + const Msg& msg = Msg::downcast(msgs[0]); + // This contains the same data as an AMDAR and has undefined subtype and + // localsubtype, so it gets identified as an AMDAR + wassert(actual(msg.type) == MSG_AMDAR); + IS(ident, "EU3375"); + }); + + // WMO ACARS US + add_bufr_simplified_method("gts-acars-us1.bufr", [](const Messages& msgs) { + wassert(actual(msgs.size()) == 1u); + const Msg& msg = Msg::downcast(msgs[0]); + wassert(actual(msg.type) == MSG_ACARS); + IS(ident, "FJCYR4RA"); + }); + + // WMO AMDAR + add_bufr_simplified_method("gts-amdar1.bufr", [](const Messages& msgs) { + wassert(actual(msgs.size()) == 1u); + const Msg& msg = Msg::downcast(msgs[0]); + wassert(actual(msg.type) == MSG_AMDAR); + IS(ident, "EU0274"); + }); + + // WMO AMDAR + add_bufr_simplified_method("gts-amdar2.bufr", [](const Messages& msgs) { + wassert(actual(msgs.size()) == 1u); + const Msg& msg = Msg::downcast(msgs[0]); + wassert(actual(msg.type) == MSG_AMDAR); + IS(ident, "EU7866"); + }); + + // BUFR that has a variable that goes out of range when converted to local B + // table + add_method("outofrange", []() { + try + { + // Read and interpretate the message + BinaryMessage raw = read_rawmsg("bufr/interpreted-range.bufr", File::BUFR); + std::unique_ptr importer = msg::Importer::create(File::BUFR); + Messages msgs = importer->from_binary(raw); + throw TestFailed("error_domain was not thrown"); + } catch (wreport::error_domain& e) { + //cerr << e.code() << "--" << e.what() << endl; + } + + { + wreport::options::LocalOverride o(wreport::options::var_silent_domain_errors, true); + Messages msgs = read_msgs("bufr/interpreted-range.bufr", File::BUFR); + wassert(actual(msgs.size()) == 1u); + const Msg& msg = Msg::downcast(msgs[0]); + wassert(actual(msg.type) == MSG_SHIP); + IS(ident, "DBBC"); + } + }); + + // WMO PILOT, with geopotential levels + add_bufr_simplified_method("pilot-gts1.bufr", [](const Messages& msgs) { + wassert(actual(msgs.size()) == 1u); + const Msg& msg = Msg::downcast(msgs[0]); + wassert(actual(msg.type) == MSG_PILOT); + }); + + // WMO PILOT, with pressure levels + add_bufr_simplified_method("pilot-gts2.bufr", [](const Messages& msgs) { + wassert(actual(msgs.size()) == 1u); + const Msg& msg = Msg::downcast(msgs[0]); + wassert(actual(msg.type) == MSG_PILOT); + }); + + // WMO PILOT, with pressure levels + add_bufr_simplified_method("temp-tsig-2.bufr", [](const Messages& msgs) { + // FIXME: this still fails + wassert(actual(msgs.size()) == 1u); + const Msg& msg = Msg::downcast(msgs[0]); + wassert(actual(msg.type) == MSG_TEMP); + }); + + // WMO pilot pressure + add_bufr_simplified_method("pilot-gts3.bufr", [](const Messages& msgs) { + wassert(actual(msgs.size()) == 1u); + const Msg& msg = Msg::downcast(msgs[0]); + wassert(actual(msg.type) == MSG_PILOT); + }); + + // WMO pilot geopotential + add_bufr_simplified_method("pilot-gts4.bufr", [](const Messages& msgs) { + wassert(actual(msgs.size()) == 1u); + const Msg& msg = Msg::downcast(msgs[0]); + wassert(actual(msg.type) == MSG_PILOT); + }); + + add_bufr_simplified_method("vad.bufr", [](const Messages& msgs) { + wassert(actual(msgs.size()) == 1u); + const Msg& msg = Msg::downcast(msgs[0]); + wassert(actual(msg.type) == MSG_TEMP); + }); + + // Wind profiler + add_bufr_simplified_method("temp-windprof1.bufr", [](const Messages& msgs) { + wassert(actual(msgs.size()) == 1u); + const Msg& msg = Msg::downcast(msgs[0]); + wassert(actual(msg.type) == MSG_TEMP); + }); + + // Precise import + add_bufr_method("gts-synop-linate.bufr", [](const Messages& msgs) { + wassert(actual(msgs.size()) == 1u); + const Msg& msg = Msg::downcast(msgs[0]); + const Var* v = msg.get(WR_VAR(0, 12, 101), Level(103, 2000), Trange(3, 0, 43200)); + wassert(actual(v).istrue()); + wassert(actual(v->enqd()) == 284.75); + }); + } +} test("msg_wr_import"); + +} diff --git a/dballe/msg/wr_import-tut.cc b/dballe/msg/wr_import-tut.cc deleted file mode 100644 index ba34397a3..000000000 --- a/dballe/msg/wr_import-tut.cc +++ /dev/null @@ -1,505 +0,0 @@ -/* - * Copyright (C) 2005--2015 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "tests.h" -#include "wr_codec.h" -#include "msg.h" -#include "context.h" -#include -#include -#include - -using namespace dballe; -using namespace wreport; -using namespace wibble::tests; -using namespace std; - -namespace tut { - -struct wr_import_shar -{ - wr_import_shar() - { - } - - ~wr_import_shar() - { - } -}; -TESTGRP(wr_import); - -#define IS(field, val) do { \ - const Var* var = msg.get_##field##_var(); \ - ensure(((void)#field, var != 0)); \ - wassert(actual(*var) == val); \ - } while (0) -#define IS2(code, lev, tr, val) do { \ - const Var* var = msg.get(code, lev, tr); \ - ensure(((void)#code #lev #tr, var != 0)); \ - wassert(actual(*var) == val); \ - } while (0) -#define UN(field) do { \ - const Var* var = msg.get_##field##_var(); \ - if (var != 0) \ - wassert(actual(*var).is_undef()); \ - } while (0) - -// Test plain import of all our BUFR test files -template<> template<> -void to::test<1>() -{ - // note: These were blacklisted: - // "bufr/obs3-3.1.bufr", - // "bufr/obs3-56.2.bufr", - // "bufr/test-buoy1.bufr", - // "bufr/test-soil1.bufr", - const char** files = dballe::tests::bufr_files; - - for (int i = 0; files[i] != NULL; i++) - { - try { - Messages msgs = read_msgs(files[i], File::BUFR); - ensure(msgs.size() > 0); - } catch (std::exception& e) { - cerr << "Failing bulletin:"; - try { - BinaryMessage raw = read_rawmsg(files[i], File::BUFR); - unique_ptr bulletin(BufrBulletin::decode(raw.data)); - bulletin->print(stderr); - } catch (std::exception& e1) { - cerr << "Cannot display failing bulletin: " << e1.what() << endl; - } - throw tut::failure(string("[") + files[i] + "] " + e.what()); - } - } -} - -// Test plain import of all our CREX test files -template<> template<> -void to::test<2>() -{ - const char** files = dballe::tests::crex_files; - - for (int i = 0; files[i] != NULL; i++) - { - try { - Messages msgs = read_msgs(files[i], File::CREX); - ensure(msgs.size() > 0); - } catch (std::exception& e) { - cerr << "Failing bulletin:"; - try { - BinaryMessage raw = read_rawmsg(files[i], File::CREX); - unique_ptr bulletin(CrexBulletin::decode(raw.data)); - bulletin->print(stderr); - } catch (std::exception& e1) { - cerr << "Cannot display failing bulletin: " << e1.what() << endl; - } - throw tut::failure(string("[") + files[i] + "] " + e.what()); - } - } -} - -template<> template<> -void to::test<3>() -{ - Messages msgs = read_msgs("crex/test-synop0.crex", File::CREX); - const Msg& msg = Msg::downcast(msgs[0]); - ensure_equals(msg.type, MSG_SYNOP); - - IS(block, 10); IS(station, 837); IS(st_type, 1); - wassert(actual(msg.get_datetime()) == Datetime(2004, 11, 30, 12, 0)); - IS(latitude, 48.22); IS(longitude, 9.92); - IS(height_station, 550.0); UN(height_baro); - IS(press, 94340.0); IS(press_msl, 100940.0); IS(press_tend, 7.0); - IS(wind_dir, 80.0); IS(wind_speed, 6.0); - IS(temp_2m, 276.15); IS(dewpoint_2m, 273.85); UN(humidity); - IS(visibility, 5000.0); IS(pres_wtr, 10); IS(past_wtr1_6h, 2); IS(past_wtr2_6h, 2); - IS(cloud_n, 100); IS(cloud_nh, 8); IS(cloud_hh, 450.0); - IS(cloud_cl, 35); IS(cloud_cm, 61); IS(cloud_ch, 60); - IS(cloud_n1, 8); IS(cloud_c1, 6); IS(cloud_h1, 350.0); - UN(cloud_n2); UN(cloud_c2); UN(cloud_h2); - UN(cloud_n3); UN(cloud_c3); UN(cloud_h3); - UN(cloud_n4); UN(cloud_c4); UN(cloud_h4); - UN(tot_prec24); UN(tot_snow); -} - -template<> template<> -void to::test<4>() -{ - Messages msgs = read_msgs("bufr/obs0-1.22.bufr", File::BUFR); - const Msg& msg = Msg::downcast(msgs[0]); - ensure_equals(msg.type, MSG_SYNOP); - - IS(block, 60); IS(station, 150); IS(st_type, 1); - wassert(actual(msg.get_datetime()) == Datetime(2004, 11, 30, 12, 0)); - IS(latitude, 33.88); IS(longitude, -5.53); - IS(height_station, 560.0); UN(height_baro); - IS(press, 94190.0); IS(press_msl, 100540.0); IS(press_3h, -180.0); IS(press_tend, 8.0); - IS(wind_dir, 80.0); IS(wind_speed, 4.0); - IS(temp_2m, 289.2); IS(dewpoint_2m, 285.7); UN(humidity); - IS(visibility, 8000.0); IS(pres_wtr, 2); IS(past_wtr1_6h, 6); IS(past_wtr2_6h, 2); - IS(cloud_n, 100); IS(cloud_nh, 8); IS(cloud_hh, 250.0); - IS(cloud_cl, 39); IS(cloud_cm, 61); IS(cloud_ch, 60); - IS(cloud_n1, 2); IS(cloud_c1, 8); IS(cloud_h1, 320.0); - IS(cloud_n2, 5); IS(cloud_c2, 8); IS(cloud_h2, 620.0); - IS(cloud_n3, 2); IS(cloud_c3, 9); IS(cloud_h3, 920.0); - UN(cloud_n4); UN(cloud_c4); UN(cloud_h4); - IS(tot_prec12, 0.5); UN(tot_snow); -} - -template<> template<> -void to::test<5>() -{ - msg::Importer::Options opts; - opts.simplified = true; - Messages msgs = read_msgs_opts("bufr/synop-cloudbelow.bufr", File::BUFR, opts); - const Msg& msg = Msg::downcast(msgs[0]); - ensure_equals(msg.type, MSG_SYNOP); - - // msg.print(stderr); - - IS(block, 11); IS(station, 406); IS(st_type, 1); - wassert(actual(msg.get_datetime()) == Datetime(2009, 12, 3, 15, 0)); - IS(latitude, 50.07361); IS(longitude, 12.40333); - IS(height_station, 483.0); IS(height_baro, 490.0); - IS(press, 95090.0); IS(press_msl, 101060.0); IS(press_3h, -110.0); IS(press_tend, 6.0); - IS(wind_dir, 0.0); IS(wind_speed, 1.0); - IS(temp_2m, 273.05); IS(dewpoint_2m, 271.35); IS(humidity, 88.0); - IS(visibility, 14000.0); IS(pres_wtr, 508); - IS2(WR_VAR(0, 20, 4), Level(1), Trange(205, 0, 10800), 10); // past_wtr1 - IS2(WR_VAR(0, 20, 5), Level(1), Trange(205, 0, 10800), 10); // past_wtr2 - IS(cloud_n, 38); IS(cloud_nh, 0); IS(cloud_hh, 6000.0); - IS(cloud_cl, 30); IS(cloud_cm, 20); IS(cloud_ch, 12); - IS(cloud_n1, 3); IS(cloud_c1, 0); IS(cloud_h1, 6000.0); - UN(cloud_n2); UN(cloud_c2); UN(cloud_h2); - UN(cloud_n3); UN(cloud_c3); UN(cloud_h3); - UN(cloud_n4); UN(cloud_c4); UN(cloud_h4); - UN(tot_prec24); UN(tot_snow); -} - -template<> template<> -void to::test<6>() -{ - msg::Importer::Options opts; - opts.simplified = false; - Messages msgs = read_msgs_opts("bufr/synop-cloudbelow.bufr", File::BUFR, opts); - const Msg& msg = Msg::downcast(msgs[0]); - ensure_equals(msg.type, MSG_SYNOP); - - // msg.print(stderr); - - IS(block, 11); IS(station, 406); IS(st_type, 1); - wassert(actual(msg.get_datetime()) == Datetime(2009, 12, 3, 15, 0)); - IS(latitude, 50.07361); IS(longitude, 12.40333); - IS(height_station, 483.0); IS(height_baro, 490.0); - IS2(WR_VAR(0, 10, 4), Level(102, 490000), Trange::instant(), 95090.0); // press - IS2(WR_VAR(0, 10, 51), Level(102, 490000), Trange::instant(), 101060.0); // press_msl - IS2(WR_VAR(0, 10, 63), Level(102, 490000), Trange(205, 0,10800), 6.0); // press_tend - IS2(WR_VAR(0, 10, 60), Level(102, 490000), Trange(4, 0, 10800), -110.0); // press_3h - IS2(WR_VAR(0, 11, 1), Level(103, 10000), Trange(200, 0, 600), 0.0); // wind_dir - IS2(WR_VAR(0, 11, 2), Level(103, 10000), Trange(200, 0, 600), 1.0); // wind_speed - IS2(WR_VAR(0, 12, 101), Level(103, 2050), Trange::instant(), 273.05); // temp_2m - IS2(WR_VAR(0, 12, 103), Level(103, 2050), Trange::instant(), 271.35); // dewpoint_2m - IS2(WR_VAR(0, 13, 3), Level(103, 2050), Trange::instant(), 88.0); // humidity - IS2(WR_VAR(0, 20, 1), Level(103, 8000), Trange::instant(), 14000.0); // visibility - IS(pres_wtr, 508); - IS2(WR_VAR(0, 20, 4), Level(1), Trange(205, 0, 10800), 10); // past_wtr1 - IS2(WR_VAR(0, 20, 5), Level(1), Trange(205, 0, 10800), 10); // past_wtr2 - IS(cloud_n, 38); IS(cloud_nh, 0); IS(cloud_hh, 6000.0); - IS(cloud_cl, 30); IS(cloud_cm, 20); IS(cloud_ch, 12); - IS(cloud_n1, 3); IS(cloud_c1, 0); IS(cloud_h1, 6000.0); - UN(cloud_n2); UN(cloud_c2); UN(cloud_h2); - UN(cloud_n3); UN(cloud_c3); UN(cloud_h3); - UN(cloud_n4); UN(cloud_c4); UN(cloud_h4); - UN(tot_prec24); UN(tot_snow); -} - -template<> template<> -void to::test<7>() -{ - Messages msgs = read_msgs("bufr/temp-2-255.bufr", File::BUFR); - const Msg& msg = Msg::downcast(msgs[0]); - ensure_equals(msg.type, MSG_TEMP); - - // No negative pressure layers please - ensure(msg.find_context(Level(100, -1), Trange::instant()) == 0); -} - -template<> template<> -void to::test<8>() -{ - Messages msgs = read_msgs("bufr/synop-longname.bufr", File::BUFR); - ensure_equals(msgs.size(), 7u); - const Msg& msg = Msg::downcast(msgs[2]); - ensure_equals(msg.type, MSG_SYNOP); - - // Check that the long station name has been correctly truncated on import - const Var* var = msg.get_st_name_var(); - ensure(var != NULL); - ensure_equals(string(var->enqc()), "Budapest Pestszentlorinc-kulteru"); -} - -template<> template<> -void to::test<9>() -{ - Messages msgs = read_msgs("bufr/temp-bad1.bufr", File::BUFR); - ensure_equals(msgs.size(), 1u); - const Msg& msg = Msg::downcast(msgs[0]); - ensure_equals(msg.type, MSG_TEMP); -} - -template<> template<> -void to::test<10>() -{ - Messages msgs = read_msgs("bufr/temp-bad2.bufr", File::BUFR); - ensure_equals(msgs.size(), 1u); - const Msg& msg = Msg::downcast(msgs[0]); - ensure_equals(msg.type, MSG_TEMP); -} - -template<> template<> -void to::test<11>() -{ - Messages msgs = read_msgs("bufr/temp-bad3.bufr", File::BUFR); - ensure_equals(msgs.size(), 1u); - const Msg& msg = Msg::downcast(msgs[0]); - ensure_equals(msg.type, MSG_TEMP); -} - -template<> template<> -void to::test<12>() -{ - Messages msgs = read_msgs("bufr/temp-bad4.bufr", File::BUFR); - ensure_equals(msgs.size(), 1u); - const Msg& msg = Msg::downcast(msgs[0]); - ensure_equals(msg.type, MSG_TEMP); -} - -// ECWMF AIREP -template<> template<> -void to::test<13>() -{ - Messages msgs = read_msgs("bufr/obs4-142.1.bufr", File::BUFR); - ensure_equals(msgs.size(), 1u); - const Msg& msg = Msg::downcast(msgs[0]); - ensure_equals(msg.type, MSG_AIREP); - IS(ident, "ACA872"); -} - -// ECWMF AMDAR -template<> template<> -void to::test<14>() -{ - Messages msgs = read_msgs("bufr/obs4-144.4.bufr", File::BUFR); - ensure_equals(msgs.size(), 1u); - const Msg& msg = Msg::downcast(msgs[0]); - ensure_equals(msg.type, MSG_AMDAR); - IS(ident, "EU4444"); -} - -// ECWMF ACARS -template<> template<> -void to::test<15>() -{ - Messages msgs = read_msgs("bufr/obs4-145.4.bufr", File::BUFR); - ensure_equals(msgs.size(), 1u); - const Msg& msg = Msg::downcast(msgs[0]); - ensure_equals(msg.type, MSG_ACARS); - IS(ident, "JBNYR3RA"); -} - -// WMO ACARS -template<> template<> -void to::test<16>() -{ - Messages msgs = read_msgs("bufr/gts-acars1.bufr", File::BUFR); - ensure_equals(msgs.size(), 1u); - const Msg& msg = Msg::downcast(msgs[0]); - ensure_equals(msg.type, MSG_ACARS); - IS(ident, "EU5331"); -} - -// WMO ACARS -template<> template<> -void to::test<17>() -{ - Messages msgs = read_msgs("bufr/gts-acars2.bufr", File::BUFR); - ensure_equals(msgs.size(), 1u); - const Msg& msg = Msg::downcast(msgs[0]); - ensure_equals(msg.type, MSG_ACARS); - IS(ident, "FJCYR4RA"); -} - -// WMO ACARS UK -template<> template<> -void to::test<18>() -{ - Messages msgs = read_msgs("bufr/gts-acars-uk1.bufr", File::BUFR); - ensure_equals(msgs.size(), 1u); - const Msg& msg = Msg::downcast(msgs[0]); - // This contains the same data as an AMDAR and has undefined subtype and - // localsubtype, so it gets identified as an AMDAR - ensure_equals(msg.type, MSG_AMDAR); - IS(ident, "EU3375"); -} - -// WMO ACARS US -template<> template<> -void to::test<19>() -{ - Messages msgs = read_msgs("bufr/gts-acars-us1.bufr", File::BUFR); - ensure_equals(msgs.size(), 1u); - const Msg& msg = Msg::downcast(msgs[0]); - ensure_equals(msg.type, MSG_ACARS); - IS(ident, "FJCYR4RA"); -} - -// WMO AMDAR -template<> template<> -void to::test<20>() -{ - Messages msgs = read_msgs("bufr/gts-amdar1.bufr", File::BUFR); - ensure_equals(msgs.size(), 1u); - const Msg& msg = Msg::downcast(msgs[0]); - ensure_equals(msg.type, MSG_AMDAR); - IS(ident, "EU0274"); -} - -// WMO AMDAR -template<> template<> -void to::test<21>() -{ - Messages msgs = read_msgs("bufr/gts-amdar2.bufr", File::BUFR); - ensure_equals(msgs.size(), 1u); - const Msg& msg = Msg::downcast(msgs[0]); - ensure_equals(msg.type, MSG_AMDAR); - IS(ident, "EU7866"); -} - -// BUFR that has a variable that goes out of range when converted to local B -// table -template<> template<> -void to::test<22>() -{ - try - { - // Read and interpretate the message - BinaryMessage raw = read_rawmsg("bufr/interpreted-range.bufr", File::BUFR); - std::unique_ptr importer = msg::Importer::create(File::BUFR); - Messages msgs = importer->from_binary(raw); - ensure(false); - } catch (wreport::error_domain& e) { - //cerr << e.code() << "--" << e.what() << endl; - } - - { - wreport::options::LocalOverride o(wreport::options::var_silent_domain_errors, true); - Messages msgs = read_msgs("bufr/interpreted-range.bufr", File::BUFR); - ensure_equals(msgs.size(), 1u); - const Msg& msg = Msg::downcast(msgs[0]); - ensure_equals(msg.type, MSG_SHIP); - IS(ident, "DBBC"); - } -} - -// WMO PILOT, with geopotential levels -template<> template<> -void to::test<23>() -{ - Messages msgs = read_msgs("bufr/pilot-gts1.bufr", File::BUFR); - ensure_equals(msgs.size(), 1u); - const Msg& msg = Msg::downcast(msgs[0]); - ensure_equals(msg.type, MSG_PILOT); -} - -// WMO PILOT, with pressure levels -template<> template<> -void to::test<24>() -{ - Messages msgs = read_msgs("bufr/pilot-gts2.bufr", File::BUFR); - ensure_equals(msgs.size(), 1u); - const Msg& msg = Msg::downcast(msgs[0]); - ensure_equals(msg.type, MSG_PILOT); -} - -// WMO PILOT, with pressure levels -template<> template<> -void to::test<25>() -{ - // FIXME: this still fails - Messages msgs = read_msgs("bufr/temp-tsig-2.bufr", File::BUFR); - ensure_equals(msgs.size(), 1u); - const Msg& msg = Msg::downcast(msgs[0]); - ensure_equals(msg.type, MSG_TEMP); -} - -// WMO pilot pressure -template<> template<> -void to::test<26>() -{ - Messages msgs = read_msgs("bufr/pilot-gts3.bufr", File::BUFR); - ensure_equals(msgs.size(), 1u); - const Msg& msg = Msg::downcast(msgs[0]); - ensure_equals(msg.type, MSG_PILOT); -} - -// WMO pilot geopotential -template<> template<> -void to::test<27>() -{ - Messages msgs = read_msgs("bufr/pilot-gts4.bufr", File::BUFR); - ensure_equals(msgs.size(), 1u); - const Msg& msg = Msg::downcast(msgs[0]); - ensure_equals(msg.type, MSG_PILOT); -} - -template<> template<> -void to::test<28>() -{ - Messages msgs = read_msgs("bufr/vad.bufr", File::BUFR); - ensure_equals(msgs.size(), 1u); - const Msg& msg = Msg::downcast(msgs[0]); - ensure_equals(msg.type, MSG_TEMP); -} - -// Wind profiler -template<> template<> -void to::test<29>() -{ - Messages msgs = read_msgs("bufr/temp-windprof1.bufr", File::BUFR); - ensure_equals(msgs.size(), 1u); - const Msg& msg = Msg::downcast(msgs[0]); - ensure_equals(msg.type, MSG_TEMP); -} - -// Precise import -template<> template<> -void to::test<30>() -{ - msg::Importer::Options opts; - opts.simplified = false; - Messages msgs = read_msgs_opts("bufr/gts-synop-linate.bufr", File::BUFR, opts); - wassert(actual(msgs.size()) == 1u); - const Msg& msg = Msg::downcast(msgs[0]); - const Var* v = msg.get(WR_VAR(0, 12, 101), Level(103, 2000), Trange(3, 0, 43200)); - wassert(actual(v).istrue()); - wassert(actual(v->enqd()) == 284.75); -} - -} diff --git a/dballe/msg/wr_importers/generic.cc b/dballe/msg/wr_importers/generic.cc index e559a4e01..5049f523a 100644 --- a/dballe/msg/wr_importers/generic.cc +++ b/dballe/msg/wr_importers/generic.cc @@ -152,7 +152,7 @@ void GenericImporter::import_var(const Var& var) { // Legacy variable conversions case WR_VAR(0, 8, 1): { - unique_ptr nvar(newvar(WR_VAR(0, 8, 42), convert_BUFR08001_to_BUFR08042(var.enqi()))); + unique_ptr nvar(newvar(WR_VAR(0, 8, 42), (int)convert_BUFR08001_to_BUFR08042(var.enqi()))); nvar->setattrs(var); msg->set(move(nvar), lev, tr); break; diff --git a/dballe/query-test.cc b/dballe/query-test.cc new file mode 100644 index 000000000..d9807d940 --- /dev/null +++ b/dballe/query-test.cc @@ -0,0 +1,50 @@ +#include "core/tests.h" +#include "query.h" + +using namespace std; +using namespace wreport::tests; +using namespace wreport; +using namespace dballe; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("clone", []() { + auto q = Query::create(); + q->set_datetimerange(DatetimeRange( + 2015, 5, MISSING_INT, MISSING_INT, MISSING_INT, MISSING_INT, + 2015, 5, MISSING_INT, MISSING_INT, MISSING_INT, MISSING_INT)); + + auto q1 = q->clone(); + wassert(actual(q1->get_datetimerange().min) == Datetime(2015, 5, 1, 0, 0, 0)); + wassert(actual(q1->get_datetimerange().max) == Datetime(2015, 5, 31, 23, 59, 59)); + }); + add_method("foreach_key", []() { + auto foreach_key = [](const std::string& test_string) -> std::string { + std::string res; + core::Query q; + q.set_from_test_string(test_string); + q.foreach_key([&](const char* key, Var&& var) { + if (!res.empty()) res += ", "; + res += key; + res += "="; + res += var.format(""); + }); + return res; + }; + + wassert(actual(foreach_key("")) == ""); + wassert(actual(foreach_key("latmin=45.0")) == "latmin=45.00000"); + wassert(actual(foreach_key("latmin=45.0, latmax=46.0")) == "latmin=45.00000, latmax=46.00000"); + wassert(actual(foreach_key("latmin=45.0, latmax=46.0, lon=11.0")) == "latmin=45.00000, latmax=46.00000, lon=11.00000"); + wassert(actual(foreach_key("latmin=45.0, latmax=46.0, lonmin=11.0, lonmax=11.5")) == "latmin=45.00000, latmax=46.00000, lonmin=11.00000, lonmax=11.50000"); + }); + } +} test("dballe_query"); + +} diff --git a/dballe/query-tut.cc b/dballe/query-tut.cc deleted file mode 100644 index 4e2f2c7da..000000000 --- a/dballe/query-tut.cc +++ /dev/null @@ -1,51 +0,0 @@ -#include "core/tests.h" -#include "query.h" - -using namespace std; -using namespace wibble::tests; -using namespace wreport; -using namespace dballe; - -namespace { - -typedef dballe::tests::test_group<> test_group; -typedef test_group::Test Test; -typedef test_group::Fixture Fixture; - -std::vector tests { - Test("clone", [](Fixture& f) { - auto q = Query::create(); - q->set_datetimerange(DatetimeRange( - 2015, 5, MISSING_INT, MISSING_INT, MISSING_INT, MISSING_INT, - 2015, 5, MISSING_INT, MISSING_INT, MISSING_INT, MISSING_INT)); - - auto q1 = q->clone(); - wassert(actual(q1->get_datetimerange().min) == Datetime(2015, 5, 1, 0, 0, 0)); - wassert(actual(q1->get_datetimerange().max) == Datetime(2015, 5, 31, 23, 59, 59)); - }), - Test("foreach_key", [](Fixture& f) { - auto foreach_key = [](const std::string& test_string) -> std::string { - std::string res; - core::Query q; - q.set_from_test_string(test_string); - q.foreach_key([&](const char* key, Var&& var) { - if (!res.empty()) res += ", "; - res += key; - res += "="; - res += var.format(""); - }); - return res; - }; - - wassert(actual(foreach_key("")) == ""); - wassert(actual(foreach_key("latmin=45.0")) == "latmin=45.00000"); - wassert(actual(foreach_key("latmin=45.0, latmax=46.0")) == "latmin=45.00000, latmax=46.00000"); - wassert(actual(foreach_key("latmin=45.0, latmax=46.0, lon=11.0")) == "latmin=45.00000, latmax=46.00000, lon=11.00000"); - wassert(actual(foreach_key("latmin=45.0, latmax=46.0, lonmin=11.0, lonmax=11.5")) == "latmin=45.00000, latmax=46.00000, lonmin=11.00000, lonmax=11.50000"); - }), -}; - -test_group newtg("dballe_query", tests); - -} - diff --git a/dballe/record-test.cc b/dballe/record-test.cc new file mode 100644 index 000000000..1686dde22 --- /dev/null +++ b/dballe/record-test.cc @@ -0,0 +1,48 @@ +#include "core/tests.h" +#include "record.h" +#include + +using namespace std; +using namespace wreport::tests; +using namespace dballe; +using namespace wreport; + +namespace { + +ostream& operator<<(ostream& out, Vartype t) +{ + return out << vartype_format(t); +} + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("record", []() { + }); + add_method("foreach_key", []() { + auto rec = Record::create(); + rec->set("lat", 44.5); + rec->set("B12101", 290.4); + vector res; + rec->foreach_key([&](const char* key, std::unique_ptr&& var) { + res.push_back(string(key) + "=" + var->format()); + }); + sort(res.begin(), res.end()); + wassert(actual(res.size()) == 2); + wassert(actual(res[0]) == "B12101=290.40"); + wassert(actual(res[1]) == "lat=44.50000"); + }); + add_method("metadata", []() { + wreport::Varinfo info = Record::key_info("rep_memo"); + wassert(actual(info->type) == Vartype::String); + + info = Record::key_info("ana_id"); + wassert(actual(info->type) == Vartype::Integer); + }); + } +} test("dballe_record"); + +} diff --git a/dballe/record-tut.cc b/dballe/record-tut.cc deleted file mode 100644 index 24ed506a4..000000000 --- a/dballe/record-tut.cc +++ /dev/null @@ -1,43 +0,0 @@ -#include "core/tests.h" -#include "record.h" -#include - -using namespace std; -using namespace wibble::tests; -using namespace dballe; -using namespace wreport; - -namespace { - -typedef dballe::tests::test_group<> test_group; -typedef test_group::Test Test; -typedef test_group::Fixture Fixture; - -std::vector tests { - Test("record", [](Fixture& f) { - }), - Test("foreach_key", [](Fixture& f) { - auto rec = Record::create(); - rec->set("lat", 44.5); - rec->set("B12101", 290.4); - vector res; - rec->foreach_key([&](const char* key, std::unique_ptr&& var) { - res.push_back(string(key) + "=" + var->format()); - }); - sort(res.begin(), res.end()); - wassert(actual(res.size()) == 2); - wassert(actual(res[0]) == "B12101=290.40"); - wassert(actual(res[1]) == "lat=44.50000"); - }), - Test("metadata", [](Fixture& f) { - wreport::Varinfo info = Record::key_info("rep_memo"); - wassert(actual(info->type) == Vartype::String); - - info = Record::key_info("ana_id"); - wassert(actual(info->type) == Vartype::Integer); - }), -}; - -test_group newtg("dballe_record", tests); - -} diff --git a/dballe/simple/dbapi-test.cc b/dballe/simple/dbapi-test.cc new file mode 100644 index 000000000..dfb784963 --- /dev/null +++ b/dballe/simple/dbapi-test.cc @@ -0,0 +1,699 @@ +#include "config.h" +#include "db/tests.h" +#include "dbapi.h" +#include "config.h" + +using namespace std; +using namespace dballe; +using namespace dballe::db; +using namespace dballe::tests; + +namespace { + +void populate_variables(fortran::DbAPI& api) +{ + api.setd("lat", 44.5); + api.setd("lon", 11.5); + api.setc("rep_memo", "synop"); + api.settimerange(254, 0, 0); + api.setdate(2013, 4, 25, 12, 0, 0); + + // Instant temperature, 2 meters above ground + api.setlevel(103, 2000, MISSING_INT, MISSING_INT); + api.setd("B12101", 21.5); + api.prendilo(); + + // Instant wind speed, 10 meters above ground + api.unsetb(); + api.setlevel(103, 10000, MISSING_INT, MISSING_INT); + api.setd("B11002", 2.4); + api.prendilo(); + + api.unsetall(); +} + +class Tests : public FixtureTestCase +{ + using FixtureTestCase::FixtureTestCase; + + void register_tests() override + { + add_method("query_basic", [](Fixture& f) { + // Test vars + fortran::DbAPI api(*f.db, "write", "write", "write"); + populate_variables(api); + + // Query stations + api.unsetall(); + wassert(actual(api.quantesono()) == 1); + api.elencamele(); + wassert(actual(api.enqd("lat")) == 44.5); + wassert(actual(api.enqd("lon")) == 11.5); + + // Query variables + api.unsetall(); + wassert(actual(api.voglioquesto()) == 2); + wassert(actual(string(api.dammelo())) == "B12101"); + wassert(actual(api.enqd("B12101")) == 21.5); + wassert(actual(string(api.dammelo())) == "B11002"); + wassert(actual(api.enqd("lat")) == 44.5); + wassert(actual(api.enqd("lon")) == 11.5); + wassert(actual(api.enqd("B11002")) == 2.4); + + // Delete variables + api.unsetall(); + api.setc("var", "B12101"); + api.dimenticami(); + wassert(actual(api.voglioquesto()) == 0); + }); + add_method("query_context_id", [](Fixture& f) { + // Check that the context ID we get is correct + fortran::DbAPI api(*f.db, "write", "write", "write"); + populate_variables(api); + + // Query a variable + api.setc("var", "B12101"); + wassert(actual(api.voglioquesto()) == 1); + api.dammelo(); + + // Store its context info to access attributes of this variable later + int reference_id = api.enqi("context_id"); + + // Check that the context id that we get points to the right variable + api.unsetall(); + api.seti("context_id", reference_id); + wassert(actual(api.voglioquesto()) == 1); + wassert(actual(api.dammelo()) == "B12101"); + wassert(actual(api.enqi("l1")) == 2000); + }); + add_method("query_attrs", [](Fixture& f) { + // Test attrs + fortran::DbAPI api(*f.db, "write", "write", "write"); + populate_variables(api); + + int reference_id; + + // Query a variable + api.setc("var", "B12101"); + wassert(actual(api.voglioquesto()) == 1); + api.dammelo(); + + wassert(actual(api.test_get_attr_state()) == fortran::DbAPI::ATTR_DAMMELO); + + // Store its context info to access attributes of this variable later + reference_id = api.enqi("context_id"); + + // It has no attributes + wassert(actual(api.voglioancora()) == 0); + wassert(actual(api.test_get_attr_state()) == fortran::DbAPI::ATTR_DAMMELO); + + // Set one attribute after a dammelo + api.seti("*B33007", 50); + api.critica(); + wassert(actual(api.test_get_attr_state()) == fortran::DbAPI::ATTR_DAMMELO); + + // It now has one attribute + wassert(actual(api.voglioancora()) == 1); + + + // Query a different variable, it has no attributes + api.setc("var", "B11002"); + wassert(actual(api.voglioquesto()) == 1); + api.dammelo(); + wassert(actual(api.voglioancora()) == 0); + + + // Query the first variable using its stored reference id + api.seti("*context_id", reference_id); + api.setc("*var_related", "B12101"); + wassert(actual(api.test_get_attr_state()) == fortran::DbAPI::ATTR_REFERENCE); + wassert(actual(api.voglioancora()) == 1); + wassert(actual(api.enqi("*B33007")) == 50); + + // Delete all attributes + api.scusa(); + wassert(actual(api.voglioancora()) == 0); + }); + add_method("insert_attrs_prendilo", [](Fixture& f) { + // Test attrs prendilo + fortran::DbAPI api(*f.db, "write", "write", "write"); + populate_variables(api); + + // Set one attribute after a prendilo + api.setd("lat", 44.5); + api.setd("lon", 11.5); + api.setc("rep_memo", "synop"); + api.setdate(2013, 4, 25, 12, 0, 0); + api.setlevel(1, MISSING_INT, MISSING_INT, MISSING_INT); + api.settimerange(254, 0, 0); + api.setd("B10004", 100000.0); + api.prendilo(); // Pressure at ground level + api.seti("*B33007", 60); + api.critica(); + + // Query it again + api.unsetall(); + api.setc("var", "B10004"); + wassert(actual(api.voglioquesto()) == 1); + api.dammelo(); + wassert(actual(api.voglioancora()) == 1); + wassert(actual(api.enqi("*B33007")) == 60); + }); + add_method("insert_attrs_prendilo_anaid", [](Fixture& f) { + // Test prendilo anaid + fortran::DbAPI api(*f.db, "write", "write", "write"); + populate_variables(api); + + // Run a prendilo + api.setd("lat", 44.5); + api.setd("lon", 11.5); + api.setc("rep_memo", "synop"); + api.setdate(2013, 4, 25, 12, 0, 0); + api.setlevel(1, MISSING_INT, MISSING_INT, MISSING_INT); + api.settimerange(254, 0, 0); + api.setd("B10004", 100000.0); + api.prendilo(); // Pressure at ground level + + int anaid = api.enqi("*ana_id"); + wassert(actual(anaid) != MISSING_INT); + + // Query it back + api.unsetall(); + api.seti("ana_id", anaid); + api.setc("var", "B12101"); + wassert(actual(api.voglioquesto()) == 1); + }); + add_method("insert_auto_repmemo", [](Fixture& f) { + // Check that an unknown rep_memo is correctly handled on insert + fortran::DbAPI api(*f.db, "write", "write", "write"); + + // Insert a record with a rep_memo that is not in the database + api.setc("rep_memo", "insert_auto_repmemo"); + api.setd("lat", 45.6); + api.setd("lon", 11.2); + api.setlevel(1, MISSING_INT, MISSING_INT, MISSING_INT); + api.settimerange(254, MISSING_INT, MISSING_INT); + api.setdate(2015, 4, 25, 12, 30, 45); + api.setd("B12101", 286.4); + api.prendilo(); + + // Query it back + api.unsetall(); + api.setc("rep_memo", "insert_auto_repmemo"); + wassert(actual(api.voglioquesto()) == 1); + wassert(actual(api.dammelo()) == "B12101"); + }); + add_method("undefined_level2", [](Fixture& f) { + // Test handling of values with undefined leveltype2 and l2 + fortran::DbAPI api(*f.db, "write", "write", "write"); + api.setd("lat", 44.5); + api.setd("lon", 11.5); + api.setc("rep_memo", "synop"); + api.setlevel(103, 2000, MISSING_INT, MISSING_INT); + api.settimerange(254, MISSING_INT, MISSING_INT); + api.setdate(2013, 4, 25, 12, 0, 0); + api.setd("B12101", 21.5); + api.prendilo(); + api.unsetall(); + + // Query it back + api.seti("leveltype1", 103); + wassert(actual(api.voglioquesto()) == 1); + + wassert(actual(api.dammelo()) == "B12101"); + wassert(actual(api.enqi("leveltype1")) == 103); + wassert(actual(api.enqi("l1")) == 2000); + wassert(actual(api.enqi("leveltype2")) == fortran::DbAPI::missing_int); + wassert(actual(api.enqi("l2")) == fortran::DbAPI::missing_int); + wassert(actual(api.enqi("pindicator")) == 254); + wassert(actual(api.enqi("p1")) == fortran::DbAPI::missing_int); + wassert(actual(api.enqi("p2")) == fortran::DbAPI::missing_int); + }); + add_method("delete_attrs_dammelo", [](Fixture& f) { + // Test deleting attributes after a dammelo + fortran::DbAPI api(*f.db, "write", "write", "write"); + populate_variables(api); + + // Query all variables and add attributes + api.unsetall(); + wassert(actual(api.voglioquesto()) == 2); + wassert(actual(api.dammelo()) == "B12101"); + api.seti("*B33007", 50); + api.critica(); + wassert(actual(api.dammelo()) == "B11002"); + api.seti("*B33007", 60); + api.critica(); + + // Query all variables again and check that attributes are there + api.unsetall(); + wassert(actual(api.voglioquesto()) == 2); + wassert(actual(api.dammelo()) == "B12101"); + wassert(actual(api.voglioancora()) == 1); + wassert(actual(api.enqi("*B33007")) == 50); + wassert(actual(api.dammelo()) == "B11002"); + wassert(actual(api.voglioancora()) == 1); + wassert(actual(api.enqi("*B33007")) == 60); + + // Query all variables and delete all attributes + api.unsetall(); + wassert(actual(api.voglioquesto()) == 2); + wassert(actual(api.dammelo()) == "B12101"); + api.scusa(); + wassert(actual(api.dammelo()) == "B11002"); + api.scusa(); + + // Query again and check that the attributes are gone + api.unsetall(); + wassert(actual(api.voglioquesto()) == 2); + wassert(actual(api.dammelo()) == "B12101"); + wassert(actual(api.voglioancora()) == 0); + wassert(actual(api.dammelo()) == "B11002"); + wassert(actual(api.voglioancora()) == 0); + + // The QC attrs record should be cleaned + wassert(actual(api.enqi("*B33007")) == MISSING_INT); + }); + add_method("perms_consistency", [](Fixture& f) { + { + fortran::DbAPI api(*f.db, "read", "read", "read"); + try { + api.messages_open_input(dballe::tests::datafile("bufr/synotemp.bufr").c_str(), "r", File::BUFR); + } catch (std::exception& e) { + wassert(actual(e.what()).contains("must be called on a session with writable")); + } + } + { + fortran::DbAPI api(*f.db, "write", "read", "read"); + try { + api.messages_open_input(dballe::tests::datafile("bufr/synotemp.bufr").c_str(), "r", File::BUFR); + } catch (std::exception& e) { + wassert(actual(e.what()).contains("must be called on a session with writable")); + } + } + { + fortran::DbAPI api(*f.db, "read", "add", "read"); + try { + api.messages_open_input(dballe::tests::datafile("bufr/synotemp.bufr").c_str(), "r", File::BUFR); + } catch (std::exception& e) { + wassert(actual(e.what()).contains("must be called on a session with writable")); + } + } + { + fortran::DbAPI api(*f.db, "write", "add", "read"); + api.messages_open_input(dballe::tests::datafile("bufr/synotemp.bufr").c_str(), "r", File::BUFR); + } + { + fortran::DbAPI api(*f.db, "write", "write", "read"); + api.messages_open_input(dballe::tests::datafile("bufr/synotemp.bufr").c_str(), "r", File::BUFR); + } + { + fortran::DbAPI api(*f.db, "write", "add", "write"); + api.messages_open_input(dballe::tests::datafile("bufr/synotemp.bufr").c_str(), "r", File::BUFR); + } + { + fortran::DbAPI api(*f.db, "write", "write", "write"); + api.messages_open_input(dballe::tests::datafile("bufr/synotemp.bufr").c_str(), "r", File::BUFR); + } + }); + add_method("messages_read_messages", [](Fixture& f) { + // 2 messages, 1 subset each + fortran::DbAPI api(*f.db, "write", "write", "write"); + api.messages_open_input(dballe::tests::datafile("bufr/synotemp.bufr").c_str(), "r", File::BUFR); + + // At the beginning, the DB is empty + wassert(actual(api.voglioquesto()) == 0); + + // First message + wassert(actual(api.messages_read_next()).istrue()); + wassert(actual(api.voglioquesto()) == 88); + + // Second message + api.remove_all(); + wassert(actual(api.messages_read_next()).istrue()); + wassert(actual(api.voglioquesto()) == 9); + + // End of messages + api.remove_all(); + wassert(actual(api.messages_read_next()).isfalse()); + wassert(actual(api.voglioquesto()) == 0); + }); + add_method("messages_read_subsets", [](Fixture& f) { + // 1 message, 6 subsets + fortran::DbAPI api(*f.db, "write", "write", "write"); + api.messages_open_input(dballe::tests::datafile("bufr/temp-gts2.bufr").c_str(), "r", File::BUFR); + + // At the beginning, the DB is empty + wassert(actual(api.voglioquesto()) == 0); + + // 6 subsets + api.remove_all(); + wassert(actual(api.messages_read_next()).istrue()); + wassert(actual(api.voglioquesto()) == 193); + api.remove_all(); + wassert(actual(api.messages_read_next()).istrue()); + wassert(actual(api.voglioquesto()) == 182); + api.remove_all(); + wassert(actual(api.messages_read_next()).istrue()); + wassert(actual(api.voglioquesto()) == 170); + api.remove_all(); + wassert(actual(api.messages_read_next()).istrue()); + wassert(actual(api.voglioquesto()) == 184); + api.remove_all(); + wassert(actual(api.messages_read_next()).istrue()); + wassert(actual(api.voglioquesto()) == 256); + api.remove_all(); + wassert(actual(api.messages_read_next()).istrue()); + wassert(actual(api.voglioquesto()) == 213); + + // End of messages + api.remove_all(); + wassert(actual(api.messages_read_next()).isfalse()); + wassert(actual(api.voglioquesto()) == 0); + }); + add_method("messages_read_messages_subsets", [](Fixture& f) { + // 2 messages, 2 subsets each + fortran::DbAPI api(*f.db, "write", "write", "write"); + api.messages_open_input(dballe::tests::datafile("bufr/db-messages1.bufr").c_str(), "r", File::BUFR); + + // At the beginning, the DB is empty + wassert(actual(api.voglioquesto()) == 0); + + // 6 subsets + wassert(actual(api.messages_read_next()).istrue()); + wassert(actual(api.voglioquesto()) == 88); + api.remove_all(); + wassert(actual(api.messages_read_next()).istrue()); + wassert(actual(api.voglioquesto()) == 9); + api.remove_all(); + wassert(actual(api.messages_read_next()).istrue()); + wassert(actual(api.voglioquesto()) == 193); + api.remove_all(); + wassert(actual(api.messages_read_next()).istrue()); + wassert(actual(api.voglioquesto()) == 182); + api.remove_all(); + wassert(actual(api.messages_read_next()).istrue()); + wassert(actual(api.voglioquesto()) == 170); + api.remove_all(); + wassert(actual(api.messages_read_next()).istrue()); + wassert(actual(api.voglioquesto()) == 184); + api.remove_all(); + wassert(actual(api.messages_read_next()).istrue()); + wassert(actual(api.voglioquesto()) == 256); + api.remove_all(); + wassert(actual(api.messages_read_next()).istrue()); + wassert(actual(api.voglioquesto()) == 213); + + // End of messages + api.remove_all(); + wassert(actual(api.messages_read_next()).isfalse()); + wassert(actual(api.voglioquesto()) == 0); + }); + add_method("messages_write", [](Fixture& f) { + // Write one message + { + fortran::DbAPI api(*f.db, "write", "write", "write"); + api.messages_open_output("test.bufr", "wb", File::BUFR); + + api.setd("lat", 44.5); + api.setd("lon", 11.5); + api.setc("rep_memo", "synop"); + api.settimerange(254, 0, 0); + api.setdate(2013, 4, 25, 12, 0, 0); + // Instant temperature, 2 meters above ground + api.setlevel(103, 2000, MISSING_INT, MISSING_INT); + api.setd("B12101", 21.5); + api.prendilo(); + // Instant wind speed, 10 meters above ground + api.unsetb(); + api.setlevel(103, 10000, MISSING_INT, MISSING_INT); + api.setd("B11002", 2.4); + api.prendilo(); + + api.unsetall(); + api.messages_write_next("wmo"); + } + + // Read it back + { + fortran::DbAPI api(*f.db, "write", "write", "write"); + api.messages_open_input("test.bufr", "rb", File::BUFR); + + wassert(actual(api.messages_read_next()).istrue()); + wassert(actual(api.voglioquesto()) == 2); + wassert(actual(api.dammelo()) == "B12101"); + wassert(actual(api.enqd("B12101")) == 21.5); + wassert(actual(api.dammelo()) == "B11002"); + wassert(actual(api.enqd("B11002")) == 2.4); + + wassert(actual(api.messages_read_next()).isfalse()); + } + }); + add_method("messages_bug1", [](Fixture& f) { + // Reproduce an issue reported by Paolo + // 2 messages, 2 subsets each + fortran::DbAPI api(*f.db, "write", "write", "write"); + api.messages_open_input(dballe::tests::datafile("bufr/generic-bug20140312.bufr").c_str(), "r", File::BUFR); + wassert(actual(api.messages_read_next()) == 1); + api.unsetall(); + api.setcontextana(); + wassert(actual(api.voglioquesto()) == 3); + // bug with mem DB: message: "enqi: B00000 (Context ID of the variable) is not defined" + wassert(actual(api.dammelo()) == "B01194"); + wassert(actual(api.dammelo()) == "B05001"); + wassert(actual(api.dammelo()) == "B06001"); + }); + add_method("messages_bug2", [](Fixture& f) { + // Reproduce an issue reported by Paolo + // 2 messages, 2 subsets each + fortran::DbAPI api(*f.db, "write", "write", "write"); + api.messages_open_input(dballe::tests::datafile("bufr/generic-bug20140326.bufr").c_str(), "r", File::BUFR); + wassert(actual(api.messages_read_next()) == 1); + api.unsetall(); + api.setcontextana(); + wassert(actual(api.voglioquesto()) == 5); + wassert(actual(api.dammelo()) == "B01019"); + // Bug: missing variable 000000 in table dballe + wassert(actual(api.voglioancora()) == 0); + }); + add_method("attr_reference_id", [](Fixture& f) { + // Test attr_reference_id behaviour + fortran::DbAPI api(*f.db, "write", "write", "write"); + // Initial data + api.setd("lat", 44.5); + api.setd("lon", 11.5); + api.setc("rep_memo", "synop"); + // One station variable + api.setcontextana(); + api.setd("B07030", 100.0); + api.prendilo(); + // One data variable + api.settimerange(254, 0, 0); + api.setdate(2013, 4, 25, 12, 0, 0); + api.setlevel(103, 2000, MISSING_INT, MISSING_INT); + api.setd("B12101", 21.5); + api.prendilo(); + api.unsetall(); + + // Query the station variable + api.setcontextana(); + api.setc("var", "B07030"); + wassert(actual(api.voglioquesto()) == 1); + api.dammelo(); + // Get its reference id + wassert(actual(api.enqi("context_id")) == MISSING_INT); + // Get its attrs (none) + wassert(actual(api.voglioancora()) == 0); + // Set an attr + api.seti("*B33007", 50); + api.critica(); + // Query the variable again + api.unsetall(); + api.setcontextana(); + api.setc("var", "B07030"); + wassert(actual(api.voglioquesto()) == 1); + api.dammelo(); + // Read its attrs (ok) + wassert(actual(api.voglioancora()) == 1); + // Read attrs setting *context_id and *varid: it fails + api.seti("*context_id", MISSING_INT); + api.setc("*var_related", "B07030"); + try { + api.voglioancora(); + } catch (std::exception& e) { + wassert(actual(e.what()).matches("invalid \\*context_id")); + } + // Query the variable again, to delete all attributes + api.unsetall(); + api.setcontextana(); + api.setc("var", "B07030"); + wassert(actual(api.voglioquesto()) == 1); + api.dammelo(); + api.scusa(); + wassert(actual(api.voglioancora()) == 0); + // Try to delete by *context_id and *varid: it fails + api.seti("*context_id", MISSING_INT); + api.setc("*var_related", "B07030"); + try { + api.scusa(); + } catch (std::exception& e) { + wassert(actual(e.what()).matches("invalid \\*context_id")); + } + + // Query the variable + api.unsetall(); + api.setc("var", "B12101"); + wassert(actual(api.voglioquesto()) == 1); + api.dammelo(); + int ref_id = api.enqi("context_id"); + // Save its ref id (ok) + wassert(actual(ref_id) != MISSING_INT); + // Get its attrs (none) + wassert(actual(api.voglioancora()) == 0); + // Set an attr + api.seti("*B33007", 50); + api.critica(); + // Query the variable again + api.unsetall(); + api.setc("var", "B12101"); + wassert(actual(api.voglioquesto()) == 1); + api.dammelo(); + // Read its attrs (ok) + wassert(actual(api.voglioancora()) == 1); + // Set *context_id and *varid + // Read attrs (ok) + api.seti("*context_id", ref_id); + api.setc("*var_related", "B12101"); + wassert(actual(api.voglioancora()) == 1); + // Query the variable again, to delete all attributes + api.unsetall(); + api.setc("var", "B12101"); + wassert(actual(api.voglioquesto()) == 1); + api.dammelo(); + api.scusa(); + wassert(actual(api.voglioancora()) == 0); + // Try to delete by *context_id and *varid: it works + api.seti("*context_id", ref_id); + api.setc("*var_related", "B12101"); + api.scusa(); + }); + add_method("attrs_bug1", [](Fixture& f) { + // Reproduce a bug when setting attributes + fortran::DbAPI dbapi0(*f.db, "write", "write", "write"); + dbapi0.scopa(); + + // Add a value + dbapi0.unsetall(); + dbapi0.seti("lat", 4500000); + dbapi0.seti("lon", 1000000); + dbapi0.unset("ident"); + dbapi0.seti("mobile", 0); + dbapi0.setc("rep_memo", "generic"); + dbapi0.setdate(2014, 1, 6, 18, 0, 0); + dbapi0.setlevel(105, 2000, 2147483647, 2147483647); + dbapi0.settimerange(4, 3600, 7200); + dbapi0.seti("B13003", 85); + dbapi0.prendilo(); + + // Add attributes + dbapi0.setd("*B33192", 30.000000); + dbapi0.seti("*B33193", 50); + dbapi0.setd("*B33194", 70.000000); + dbapi0.setc("*var_related", "B13003"); + dbapi0.critica(); + + // Read them back + dbapi0.unsetall(); + dbapi0.setc("var", "B13003"); + wassert(actual(dbapi0.voglioquesto()) == 1); + wassert(actual(dbapi0.dammelo()) == "B13003"); + wassert(actual(dbapi0.voglioancora()) == 3); + }); + add_method("stationdata_bug1", [](Fixture& f) { + // Reproduce a bug handling station data + { + fortran::DbAPI dbapi0(*f.db, "write", "write", "write"); + dbapi0.scopa(); + // Copy a message using the API + dbapi0.messages_open_input(dballe::tests::datafile("bufr/generic-bug20140403.bufr").c_str(), "r", File::BUFR); + dbapi0.messages_open_output("test.bufr", "w", File::BUFR); + wassert(actual(dbapi0.messages_read_next()) == 1); + //dbapi0.setcontextana(); + dbapi0.messages_write_next("generic"); + dbapi0.remove_all(); + wassert(actual(dbapi0.messages_read_next()) == 1); + //dbapi0.setcontextana(); + dbapi0.messages_write_next("generic"); + dbapi0.remove_all(); + wassert(actual(dbapi0.messages_read_next()) == 0); + } + + // TODO: decide what is the expected behaviour for exporting only station + // values + + // // Compare the two messages + // std::unique_ptr msgs1 = read_msgs("bufr/generic-bug20140403.bufr", File::BUFR); + // std::unique_ptr msgs2 = read_msgs("./test.bufr", File::BUFR); + // unsigned diffs = msgs1->diff(*msgs2); + // if (diffs) dballe::tests::track_different_msgs(*msgs1, *msgs2, "apicopy"); + // wassert(actual(diffs) == 0); + }); + add_method("segfault1", [](Fixture& f) { + // Reproduce a segfault with mem: + fortran::DbAPI dbapi0(*f.db, "write", "write", "write"); + dbapi0.seti("lat", 4500000); + dbapi0.seti("lon", 1300000); + dbapi0.setc("rep_memo", "generic"); + dbapi0.setcontextana(); + dbapi0.setc("B12102", "26312"); + wassert(dbapi0.prendilo()); + dbapi0.setc("*B33194", "50"); + wassert(dbapi0.critica()); + }); + add_method("attr_insert", [](Fixture& f) { + // Reproduce a problem with attribute insert when inserting a variable + // that already exists in the database + fortran::DbAPI pre(*f.db, "write", "write", "write"); + pre.unsetall(); + pre.seti("lat", 4452128); + pre.seti("lon", 1199127); + pre.unset("ident"); + pre.unset("mobile"); + pre.setc("rep_memo", "locali"); + pre.setdate(2014, 8, 1, 0, 0, 0); + pre.setlevel(103, 2000, 2147483647, 2147483647); + pre.settimerange(254, 0, 0); + pre.setd("B12101", 273.149994); + pre.prendilo(); + + fortran::DbAPI dbapi0(*f.db, "write", "write", "write"); + dbapi0.unsetall(); + dbapi0.seti("lat", 4452128); + dbapi0.seti("lon", 1199127); + dbapi0.unset("ident"); + dbapi0.unset("mobile"); + dbapi0.setc("rep_memo", "locali"); + dbapi0.setdate(2014, 8, 1, 0, 0, 0); + dbapi0.setlevel(103, 2000, 2147483647, 2147483647); + dbapi0.settimerange(254, 0, 0); + dbapi0.setd("B12101", 273.149994); + dbapi0.prendilo(); + dbapi0.seti("*B33192", 0); + dbapi0.setc("*var_related", "B12101"); + dbapi0.critica(); + }); + } +}; + +Tests tg1("dbapi_mem", nullptr, db::MEM); +Tests tg2("dbapi_v6_sqlite", "SQLITE", db::V6); +#ifdef HAVE_ODBC +Tests tg4("dbapi_v6_odbc", "ODBC", db::V6); +#endif +#ifdef HAVE_LIBPQ +Tests tg6("dbapi_v6_postgresql", "POSTGRESQL", db::V6); +#endif +#ifdef HAVE_MYSQL +Tests tg8("dbapi_v6_mysql", "MYSQL", db::V6); +#endif + +} diff --git a/dballe/simple/dbapi-tut.cc b/dballe/simple/dbapi-tut.cc deleted file mode 100644 index 560f825fd..000000000 --- a/dballe/simple/dbapi-tut.cc +++ /dev/null @@ -1,720 +0,0 @@ -/* - * Copyright (C) 2013--2015 ARPA-SIM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Enrico Zini - */ - -#include "config.h" -#include "db/tests.h" -#include "dbapi.h" -#include "config.h" - -using namespace std; -using namespace dballe; -using namespace dballe::db; -using namespace wibble::tests; - -namespace { - -struct Fixture : public dballe::tests::DBFixture -{ - using dballe::tests::DBFixture::DBFixture; - - void populate_variables(fortran::DbAPI& api) - { - api.setd("lat", 44.5); - api.setd("lon", 11.5); - api.setc("rep_memo", "synop"); - api.settimerange(254, 0, 0); - api.setdate(2013, 4, 25, 12, 0, 0); - - // Instant temperature, 2 meters above ground - api.setlevel(103, 2000, MISSING_INT, MISSING_INT); - api.setd("B12101", 21.5); - api.prendilo(); - - // Instant wind speed, 10 meters above ground - api.unsetb(); - api.setlevel(103, 10000, MISSING_INT, MISSING_INT); - api.setd("B11002", 2.4); - api.prendilo(); - - api.unsetall(); - } -}; - -typedef dballe::tests::db_test_group test_group; -typedef test_group::Test Test; - -std::vector tests { - Test("query_basic", [](Fixture& f) { - // Test vars - fortran::DbAPI api(*f.db, "write", "write", "write"); - f.populate_variables(api); - - // Query stations - api.unsetall(); - ensure_equals(api.quantesono(), 1); - api.elencamele(); - ensure_equals(api.enqd("lat"), 44.5); - ensure_equals(api.enqd("lon"), 11.5); - - // Query variables - api.unsetall(); - ensure_equals(api.voglioquesto(), 2); - ensure_equals(string(api.dammelo()), "B12101"); - ensure_equals(api.enqd("B12101"), 21.5); - ensure_equals(string(api.dammelo()), "B11002"); - ensure_equals(api.enqd("lat"), 44.5); - ensure_equals(api.enqd("lon"), 11.5); - ensure_equals(api.enqd("B11002"), 2.4); - - // Delete variables - api.unsetall(); - api.setc("var", "B12101"); - api.dimenticami(); - ensure_equals(api.voglioquesto(), 0); - }), - Test("query_context_id", [](Fixture& f) { - // Check that the context ID we get is correct - fortran::DbAPI api(*f.db, "write", "write", "write"); - f.populate_variables(api); - - // Query a variable - api.setc("var", "B12101"); - ensure_equals(api.voglioquesto(), 1); - api.dammelo(); - - // Store its context info to access attributes of this variable later - int reference_id = api.enqi("context_id"); - - // Check that the context id that we get points to the right variable - api.unsetall(); - api.seti("context_id", reference_id); - wassert(actual(api.voglioquesto()) == 1); - wassert(actual(api.dammelo()) == "B12101"); - ensure_equals(api.enqi("l1"), 2000); - }), - Test("query_attrs", [](Fixture& f) { - // Test attrs - fortran::DbAPI api(*f.db, "write", "write", "write"); - f.populate_variables(api); - - int reference_id; - - // Query a variable - api.setc("var", "B12101"); - ensure_equals(api.voglioquesto(), 1); - api.dammelo(); - - wassert(actual(api.test_get_attr_state()) == fortran::DbAPI::ATTR_DAMMELO); - - // Store its context info to access attributes of this variable later - reference_id = api.enqi("context_id"); - - // It has no attributes - wassert(actual(api.voglioancora()) == 0); - wassert(actual(api.test_get_attr_state()) == fortran::DbAPI::ATTR_DAMMELO); - - // Set one attribute after a dammelo - api.seti("*B33007", 50); - api.critica(); - wassert(actual(api.test_get_attr_state()) == fortran::DbAPI::ATTR_DAMMELO); - - // It now has one attribute - wassert(actual(api.voglioancora()) == 1); - - - // Query a different variable, it has no attributes - api.setc("var", "B11002"); - ensure_equals(api.voglioquesto(), 1); - api.dammelo(); - ensure_equals(api.voglioancora(), 0); - - - // Query the first variable using its stored reference id - api.seti("*context_id", reference_id); - api.setc("*var_related", "B12101"); - wassert(actual(api.test_get_attr_state()) == fortran::DbAPI::ATTR_REFERENCE); - ensure_equals(api.voglioancora(), 1); - ensure_equals(api.enqi("*B33007"), 50); - - // Delete all attributes - api.scusa(); - ensure_equals(api.voglioancora(), 0); - }), - Test("insert_attrs_prendilo", [](Fixture& f) { - // Test attrs prendilo - fortran::DbAPI api(*f.db, "write", "write", "write"); - f.populate_variables(api); - - // Set one attribute after a prendilo - api.setd("lat", 44.5); - api.setd("lon", 11.5); - api.setc("rep_memo", "synop"); - api.setdate(2013, 4, 25, 12, 0, 0); - api.setlevel(1, MISSING_INT, MISSING_INT, MISSING_INT); - api.settimerange(254, 0, 0); - api.setd("B10004", 100000.0); - api.prendilo(); // Pressure at ground level - api.seti("*B33007", 60); - api.critica(); - - // Query it again - api.unsetall(); - api.setc("var", "B10004"); - ensure_equals(api.voglioquesto(), 1); - api.dammelo(); - ensure_equals(api.voglioancora(), 1); - ensure_equals(api.enqi("*B33007"), 60); - }), - Test("insert_attrs_prendilo_anaid", [](Fixture& f) { - // Test prendilo anaid - fortran::DbAPI api(*f.db, "write", "write", "write"); - f.populate_variables(api); - - // Run a prendilo - api.setd("lat", 44.5); - api.setd("lon", 11.5); - api.setc("rep_memo", "synop"); - api.setdate(2013, 4, 25, 12, 0, 0); - api.setlevel(1, MISSING_INT, MISSING_INT, MISSING_INT); - api.settimerange(254, 0, 0); - api.setd("B10004", 100000.0); - api.prendilo(); // Pressure at ground level - - int anaid = api.enqi("*ana_id"); - wassert(actual(anaid) != MISSING_INT); - - // Query it back - api.unsetall(); - api.seti("ana_id", anaid); - api.setc("var", "B12101"); - wassert(actual(api.voglioquesto()) == 1); - }), - Test("insert_auto_repmemo", [](Fixture& f) { - // Check that an unknown rep_memo is correctly handled on insert - fortran::DbAPI api(*f.db, "write", "write", "write"); - - // Insert a record with a rep_memo that is not in the database - api.setc("rep_memo", "insert_auto_repmemo"); - api.setd("lat", 45.6); - api.setd("lon", 11.2); - api.setlevel(1, MISSING_INT, MISSING_INT, MISSING_INT); - api.settimerange(254, MISSING_INT, MISSING_INT); - api.setdate(2015, 4, 25, 12, 30, 45); - api.setd("B12101", 286.4); - api.prendilo(); - - // Query it back - api.unsetall(); - api.setc("rep_memo", "insert_auto_repmemo"); - wassert(actual(api.voglioquesto()) == 1); - wassert(actual(api.dammelo()) == "B12101"); - }), - Test("undefined_level2", [](Fixture& f) { - // Test handling of values with undefined leveltype2 and l2 - fortran::DbAPI api(*f.db, "write", "write", "write"); - api.setd("lat", 44.5); - api.setd("lon", 11.5); - api.setc("rep_memo", "synop"); - api.setlevel(103, 2000, MISSING_INT, MISSING_INT); - api.settimerange(254, MISSING_INT, MISSING_INT); - api.setdate(2013, 4, 25, 12, 0, 0); - api.setd("B12101", 21.5); - api.prendilo(); - api.unsetall(); - - // Query it back - api.seti("leveltype1", 103); - wassert(actual(api.voglioquesto()) == 1); - - wassert(actual(api.dammelo()) == "B12101"); - wassert(actual(api.enqi("leveltype1")) == 103); - wassert(actual(api.enqi("l1")) == 2000); - wassert(actual(api.enqi("leveltype2")) == fortran::DbAPI::missing_int); - wassert(actual(api.enqi("l2")) == fortran::DbAPI::missing_int); - wassert(actual(api.enqi("pindicator")) == 254); - wassert(actual(api.enqi("p1")) == fortran::DbAPI::missing_int); - wassert(actual(api.enqi("p2")) == fortran::DbAPI::missing_int); - }), - Test("delete_attrs_dammelo", [](Fixture& f) { - // Test deleting attributes after a dammelo - fortran::DbAPI api(*f.db, "write", "write", "write"); - f.populate_variables(api); - - // Query all variables and add attributes - api.unsetall(); - wassert(actual(api.voglioquesto()) == 2); - wassert(actual(api.dammelo()) == "B12101"); - api.seti("*B33007", 50); - api.critica(); - wassert(actual(api.dammelo()) == "B11002"); - api.seti("*B33007", 60); - api.critica(); - - // Query all variables again and check that attributes are there - api.unsetall(); - wassert(actual(api.voglioquesto()) == 2); - wassert(actual(api.dammelo()) == "B12101"); - wassert(actual(api.voglioancora()) == 1); - wassert(actual(api.enqi("*B33007")) == 50); - wassert(actual(api.dammelo()) == "B11002"); - wassert(actual(api.voglioancora()) == 1); - wassert(actual(api.enqi("*B33007")) == 60); - - // Query all variables and delete all attributes - api.unsetall(); - wassert(actual(api.voglioquesto()) == 2); - wassert(actual(api.dammelo()) == "B12101"); - api.scusa(); - wassert(actual(api.dammelo()) == "B11002"); - api.scusa(); - - // Query again and check that the attributes are gone - api.unsetall(); - wassert(actual(api.voglioquesto()) == 2); - wassert(actual(api.dammelo()) == "B12101"); - wassert(actual(api.voglioancora()) == 0); - wassert(actual(api.dammelo()) == "B11002"); - wassert(actual(api.voglioancora()) == 0); - - // The QC attrs record should be cleaned - wassert(actual(api.enqi("*B33007")) == MISSING_INT); - }), - Test("perms_consistency", [](Fixture& f) { - { - fortran::DbAPI api(*f.db, "read", "read", "read"); - try { - api.messages_open_input(dballe::tests::datafile("bufr/synotemp.bufr").c_str(), "r", File::BUFR); - } catch (std::exception& e) { - wassert(actual(e.what()).contains("must be called on a session with writable")); - } - } - { - fortran::DbAPI api(*f.db, "write", "read", "read"); - try { - api.messages_open_input(dballe::tests::datafile("bufr/synotemp.bufr").c_str(), "r", File::BUFR); - } catch (std::exception& e) { - wassert(actual(e.what()).contains("must be called on a session with writable")); - } - } - { - fortran::DbAPI api(*f.db, "read", "add", "read"); - try { - api.messages_open_input(dballe::tests::datafile("bufr/synotemp.bufr").c_str(), "r", File::BUFR); - } catch (std::exception& e) { - wassert(actual(e.what()).contains("must be called on a session with writable")); - } - } - { - fortran::DbAPI api(*f.db, "write", "add", "read"); - api.messages_open_input(dballe::tests::datafile("bufr/synotemp.bufr").c_str(), "r", File::BUFR); - } - { - fortran::DbAPI api(*f.db, "write", "write", "read"); - api.messages_open_input(dballe::tests::datafile("bufr/synotemp.bufr").c_str(), "r", File::BUFR); - } - { - fortran::DbAPI api(*f.db, "write", "add", "write"); - api.messages_open_input(dballe::tests::datafile("bufr/synotemp.bufr").c_str(), "r", File::BUFR); - } - { - fortran::DbAPI api(*f.db, "write", "write", "write"); - api.messages_open_input(dballe::tests::datafile("bufr/synotemp.bufr").c_str(), "r", File::BUFR); - } - }), - Test("messages_read_messages", [](Fixture& f) { - // 2 messages, 1 subset each - fortran::DbAPI api(*f.db, "write", "write", "write"); - api.messages_open_input(dballe::tests::datafile("bufr/synotemp.bufr").c_str(), "r", File::BUFR); - - // At the beginning, the DB is empty - wassert(actual(api.voglioquesto()) == 0); - - // First message - wassert(actual(api.messages_read_next()).istrue()); - wassert(actual(api.voglioquesto()) == 88); - - // Second message - api.remove_all(); - wassert(actual(api.messages_read_next()).istrue()); - wassert(actual(api.voglioquesto()) == 9); - - // End of messages - api.remove_all(); - wassert(actual(api.messages_read_next()).isfalse()); - wassert(actual(api.voglioquesto()) == 0); - }), - Test("messages_read_subsets", [](Fixture& f) { - // 1 message, 6 subsets - fortran::DbAPI api(*f.db, "write", "write", "write"); - api.messages_open_input(dballe::tests::datafile("bufr/temp-gts2.bufr").c_str(), "r", File::BUFR); - - // At the beginning, the DB is empty - wassert(actual(api.voglioquesto()) == 0); - - // 6 subsets - api.remove_all(); - wassert(actual(api.messages_read_next()).istrue()); - wassert(actual(api.voglioquesto()) == 193); - api.remove_all(); - wassert(actual(api.messages_read_next()).istrue()); - wassert(actual(api.voglioquesto()) == 182); - api.remove_all(); - wassert(actual(api.messages_read_next()).istrue()); - wassert(actual(api.voglioquesto()) == 170); - api.remove_all(); - wassert(actual(api.messages_read_next()).istrue()); - wassert(actual(api.voglioquesto()) == 184); - api.remove_all(); - wassert(actual(api.messages_read_next()).istrue()); - wassert(actual(api.voglioquesto()) == 256); - api.remove_all(); - wassert(actual(api.messages_read_next()).istrue()); - wassert(actual(api.voglioquesto()) == 213); - - // End of messages - api.remove_all(); - wassert(actual(api.messages_read_next()).isfalse()); - wassert(actual(api.voglioquesto()) == 0); - }), - Test("messages_read_messages_subsets", [](Fixture& f) { - // 2 messages, 2 subsets each - fortran::DbAPI api(*f.db, "write", "write", "write"); - api.messages_open_input(dballe::tests::datafile("bufr/db-messages1.bufr").c_str(), "r", File::BUFR); - - // At the beginning, the DB is empty - wassert(actual(api.voglioquesto()) == 0); - - // 6 subsets - wassert(actual(api.messages_read_next()).istrue()); - wassert(actual(api.voglioquesto()) == 88); - api.remove_all(); - wassert(actual(api.messages_read_next()).istrue()); - wassert(actual(api.voglioquesto()) == 9); - api.remove_all(); - wassert(actual(api.messages_read_next()).istrue()); - wassert(actual(api.voglioquesto()) == 193); - api.remove_all(); - wassert(actual(api.messages_read_next()).istrue()); - wassert(actual(api.voglioquesto()) == 182); - api.remove_all(); - wassert(actual(api.messages_read_next()).istrue()); - wassert(actual(api.voglioquesto()) == 170); - api.remove_all(); - wassert(actual(api.messages_read_next()).istrue()); - wassert(actual(api.voglioquesto()) == 184); - api.remove_all(); - wassert(actual(api.messages_read_next()).istrue()); - wassert(actual(api.voglioquesto()) == 256); - api.remove_all(); - wassert(actual(api.messages_read_next()).istrue()); - wassert(actual(api.voglioquesto()) == 213); - - // End of messages - api.remove_all(); - wassert(actual(api.messages_read_next()).isfalse()); - wassert(actual(api.voglioquesto()) == 0); - }), - Test("messages_write", [](Fixture& f) { - // Write one message - { - fortran::DbAPI api(*f.db, "write", "write", "write"); - api.messages_open_output("test.bufr", "wb", File::BUFR); - - api.setd("lat", 44.5); - api.setd("lon", 11.5); - api.setc("rep_memo", "synop"); - api.settimerange(254, 0, 0); - api.setdate(2013, 4, 25, 12, 0, 0); - // Instant temperature, 2 meters above ground - api.setlevel(103, 2000, MISSING_INT, MISSING_INT); - api.setd("B12101", 21.5); - api.prendilo(); - // Instant wind speed, 10 meters above ground - api.unsetb(); - api.setlevel(103, 10000, MISSING_INT, MISSING_INT); - api.setd("B11002", 2.4); - api.prendilo(); - - api.unsetall(); - api.messages_write_next("wmo"); - } - - // Read it back - { - fortran::DbAPI api(*f.db, "write", "write", "write"); - api.messages_open_input("test.bufr", "rb", File::BUFR); - - wassert(actual(api.messages_read_next()).istrue()); - wassert(actual(api.voglioquesto()) == 2); - wassert(actual(api.dammelo()) == "B12101"); - wassert(actual(api.enqd("B12101")) == 21.5); - wassert(actual(api.dammelo()) == "B11002"); - wassert(actual(api.enqd("B11002")) == 2.4); - - wassert(actual(api.messages_read_next()).isfalse()); - } - }), - Test("messages_bug1", [](Fixture& f) { - // Reproduce an issue reported by Paolo - // 2 messages, 2 subsets each - fortran::DbAPI api(*f.db, "write", "write", "write"); - api.messages_open_input(dballe::tests::datafile("bufr/generic-bug20140312.bufr").c_str(), "r", File::BUFR); - wassert(actual(api.messages_read_next()) == 1); - api.unsetall(); - api.setcontextana(); - wassert(actual(api.voglioquesto()) == 3); - // bug with mem DB: message: "enqi: B00000 (Context ID of the variable) is not defined" - wassert(actual(api.dammelo()) == "B01194"); - wassert(actual(api.dammelo()) == "B05001"); - wassert(actual(api.dammelo()) == "B06001"); - }), - Test("messages_bug2", [](Fixture& f) { - // Reproduce an issue reported by Paolo - // 2 messages, 2 subsets each - fortran::DbAPI api(*f.db, "write", "write", "write"); - api.messages_open_input(dballe::tests::datafile("bufr/generic-bug20140326.bufr").c_str(), "r", File::BUFR); - wassert(actual(api.messages_read_next()) == 1); - api.unsetall(); - api.setcontextana(); - wassert(actual(api.voglioquesto()) == 5); - wassert(actual(api.dammelo()) == "B01019"); - // Bug: missing variable 000000 in table dballe - wassert(actual(api.voglioancora()) == 0); - }), - Test("attr_reference_id", [](Fixture& f) { - // Test attr_reference_id behaviour - fortran::DbAPI api(*f.db, "write", "write", "write"); - // Initial data - api.setd("lat", 44.5); - api.setd("lon", 11.5); - api.setc("rep_memo", "synop"); - // One station variable - api.setcontextana(); - api.setd("B07030", 100.0); - api.prendilo(); - // One data variable - api.settimerange(254, 0, 0); - api.setdate(2013, 4, 25, 12, 0, 0); - api.setlevel(103, 2000, MISSING_INT, MISSING_INT); - api.setd("B12101", 21.5); - api.prendilo(); - api.unsetall(); - - // Query the station variable - api.setcontextana(); - api.setc("var", "B07030"); - wassert(actual(api.voglioquesto()) == 1); - api.dammelo(); - // Get its reference id - wassert(actual(api.enqi("context_id")) == MISSING_INT); - // Get its attrs (none) - wassert(actual(api.voglioancora()) == 0); - // Set an attr - api.seti("*B33007", 50); - api.critica(); - // Query the variable again - api.unsetall(); - api.setcontextana(); - api.setc("var", "B07030"); - wassert(actual(api.voglioquesto()) == 1); - api.dammelo(); - // Read its attrs (ok) - wassert(actual(api.voglioancora()) == 1); - // Read attrs setting *context_id and *varid: it fails - api.seti("*context_id", MISSING_INT); - api.setc("*var_related", "B07030"); - try { - api.voglioancora(); - } catch (std::exception& e) { - wassert(actual(e.what()).matches("invalid \\*context_id")); - } - // Query the variable again, to delete all attributes - api.unsetall(); - api.setcontextana(); - api.setc("var", "B07030"); - wassert(actual(api.voglioquesto()) == 1); - api.dammelo(); - api.scusa(); - wassert(actual(api.voglioancora()) == 0); - // Try to delete by *context_id and *varid: it fails - api.seti("*context_id", MISSING_INT); - api.setc("*var_related", "B07030"); - try { - api.scusa(); - } catch (std::exception& e) { - wassert(actual(e.what()).matches("invalid \\*context_id")); - } - - // Query the variable - api.unsetall(); - api.setc("var", "B12101"); - wassert(actual(api.voglioquesto()) == 1); - api.dammelo(); - int ref_id = api.enqi("context_id"); - // Save its ref id (ok) - wassert(actual(ref_id) != MISSING_INT); - // Get its attrs (none) - wassert(actual(api.voglioancora()) == 0); - // Set an attr - api.seti("*B33007", 50); - api.critica(); - // Query the variable again - api.unsetall(); - api.setc("var", "B12101"); - wassert(actual(api.voglioquesto()) == 1); - api.dammelo(); - // Read its attrs (ok) - wassert(actual(api.voglioancora()) == 1); - // Set *context_id and *varid - // Read attrs (ok) - api.seti("*context_id", ref_id); - api.setc("*var_related", "B12101"); - wassert(actual(api.voglioancora()) == 1); - // Query the variable again, to delete all attributes - api.unsetall(); - api.setc("var", "B12101"); - wassert(actual(api.voglioquesto()) == 1); - api.dammelo(); - api.scusa(); - wassert(actual(api.voglioancora()) == 0); - // Try to delete by *context_id and *varid: it works - api.seti("*context_id", ref_id); - api.setc("*var_related", "B12101"); - api.scusa(); - }), - Test("attrs_bug1", [](Fixture& f) { - // Reproduce a bug when setting attributes - fortran::DbAPI dbapi0(*f.db, "write", "write", "write"); - dbapi0.scopa(); - - // Add a value - dbapi0.unsetall(); - dbapi0.seti("lat", 4500000); - dbapi0.seti("lon", 1000000); - dbapi0.unset("ident"); - dbapi0.seti("mobile", 0); - dbapi0.setc("rep_memo", "generic"); - dbapi0.setdate(2014, 1, 6, 18, 0, 0); - dbapi0.setlevel(105, 2000, 2147483647, 2147483647); - dbapi0.settimerange(4, 3600, 7200); - dbapi0.seti("B13003", 85); - dbapi0.prendilo(); - - // Add attributes - dbapi0.setd("*B33192", 30.000000); - dbapi0.seti("*B33193", 50); - dbapi0.setd("*B33194", 70.000000); - dbapi0.setc("*var_related", "B13003"); - dbapi0.critica(); - - // Read them back - dbapi0.unsetall(); - dbapi0.setc("var", "B13003"); - wassert(actual(dbapi0.voglioquesto()) == 1); - wassert(actual(dbapi0.dammelo()) == "B13003"); - wassert(actual(dbapi0.voglioancora()) == 3); - }), - Test("stationdata_bug1", [](Fixture& f) { - // Reproduce a bug handling station data - { - fortran::DbAPI dbapi0(*f.db, "write", "write", "write"); - dbapi0.scopa(); - // Copy a message using the API - dbapi0.messages_open_input(dballe::tests::datafile("bufr/generic-bug20140403.bufr").c_str(), "r", File::BUFR); - dbapi0.messages_open_output("test.bufr", "w", File::BUFR); - wassert(actual(dbapi0.messages_read_next()) == 1); - //dbapi0.setcontextana(); - dbapi0.messages_write_next("generic"); - dbapi0.remove_all(); - wassert(actual(dbapi0.messages_read_next()) == 1); - //dbapi0.setcontextana(); - dbapi0.messages_write_next("generic"); - dbapi0.remove_all(); - wassert(actual(dbapi0.messages_read_next()) == 0); - } - - // TODO: decide what is the expected behaviour for exporting only station - // values - - // // Compare the two messages - // std::unique_ptr msgs1 = read_msgs("bufr/generic-bug20140403.bufr", File::BUFR); - // std::unique_ptr msgs2 = read_msgs("./test.bufr", File::BUFR); - // unsigned diffs = msgs1->diff(*msgs2); - // if (diffs) dballe::tests::track_different_msgs(*msgs1, *msgs2, "apicopy"); - // wassert(actual(diffs) == 0); - }), - Test("segfault1", [](Fixture& f) { - // Reproduce a segfault with mem: - fortran::DbAPI dbapi0(*f.db, "write", "write", "write"); - dbapi0.seti("lat", 4500000); - dbapi0.seti("lon", 1300000); - dbapi0.setc("rep_memo", "generic"); - dbapi0.setcontextana(); - dbapi0.setc("B12102", "26312"); - wrunchecked(dbapi0.prendilo()); - dbapi0.setc("*B33194", "50"); - wrunchecked(dbapi0.critica()); - }), - Test("attr_insert", [](Fixture& f) { - // Reproduce a problem with attribute insert when inserting a variable - // that already exists in the database - fortran::DbAPI pre(*f.db, "write", "write", "write"); - pre.unsetall(); - pre.seti("lat", 4452128); - pre.seti("lon", 1199127); - pre.unset("ident"); - pre.unset("mobile"); - pre.setc("rep_memo", "locali"); - pre.setdate(2014, 8, 1, 0, 0, 0); - pre.setlevel(103, 2000, 2147483647, 2147483647); - pre.settimerange(254, 0, 0); - pre.setd("B12101", 273.149994); - pre.prendilo(); - - fortran::DbAPI dbapi0(*f.db, "write", "write", "write"); - dbapi0.unsetall(); - dbapi0.seti("lat", 4452128); - dbapi0.seti("lon", 1199127); - dbapi0.unset("ident"); - dbapi0.unset("mobile"); - dbapi0.setc("rep_memo", "locali"); - dbapi0.setdate(2014, 8, 1, 0, 0, 0); - dbapi0.setlevel(103, 2000, 2147483647, 2147483647); - dbapi0.settimerange(254, 0, 0); - dbapi0.setd("B12101", 273.149994); - dbapi0.prendilo(); - dbapi0.seti("*B33192", 0); - dbapi0.setc("*var_related", "B12101"); - dbapi0.critica(); - }), -}; - -test_group tg1("dbapi_mem", nullptr, db::MEM, tests); -test_group tg2("dbapi_v6_sqlite", "SQLITE", db::V6, tests); -#ifdef HAVE_ODBC -test_group tg4("dbapi_v6_odbc", "ODBC", db::V6, tests); -#endif -#ifdef HAVE_LIBPQ -test_group tg6("dbapi_v6_postgresql", "POSTGRESQL", db::V6, tests); -#endif -#ifdef HAVE_MYSQL -test_group tg8("dbapi_v6_mysql", "MYSQL", db::V6, tests); -#endif - -} diff --git a/dballe/simple/msgapi-test.cc b/dballe/simple/msgapi-test.cc new file mode 100644 index 000000000..156859ce6 --- /dev/null +++ b/dballe/simple/msgapi-test.cc @@ -0,0 +1,163 @@ +#include "core/tests.h" +#include "msgapi.h" + +using namespace std; +using namespace dballe; +using namespace dballe::fortran; +using namespace dballe::tests; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("open", []() { + // Open test file + std::string fname = tests::datafile("bufr/simple-generic-group.bufr"); + fortran::MsgAPI api(fname.c_str(), "r", "BUFR"); + + wassert(actual(api.voglioquesto()) == 4); + wassert(actual(api.voglioquesto()) == 4); + wassert(actual(api.voglioquesto()) == 4); + wassert(actual(api.quantesono()) == 1); + }); + add_method("resume", []() { + // Test resuming after a broken BUFR + + // Concatenate a broken BUFR with a good one + BinaryMessage rm1(read_rawmsg("bufr/interpreted-range.bufr", File::BUFR)); + BinaryMessage rm2(read_rawmsg("bufr/temp-gts1.bufr", File::BUFR)); + + // Broken + good + { + string concat = rm1.data + rm2.data; + FILE* out = fopen("test-simple-concat.bufr", "w"); + fwrite(concat.data(), concat.size(), 1, out); + fclose(out); + + fortran::MsgAPI api("test-simple-concat.bufr", "r", "BUFR"); + + // The first one fails + wassert(actual_function([&]() { api.voglioquesto(); }).throws("")); + + // The second one should be read + wassert(actual(api.voglioquesto()) == 555); + } + + // Good + broken + good + { + string concat = rm2.data + rm1.data + rm2.data; + FILE* out = fopen("test-simple-concat.bufr", "w"); + fwrite(concat.data(), concat.size(), 1, out); + fclose(out); + + fortran::MsgAPI api("test-simple-concat.bufr", "r", "BUFR"); + + wassert(actual(api.voglioquesto()) == 555); + wassert(actual_function([&]() { api.voglioquesto(); }).throws("")); + wassert(actual(api.voglioquesto()) == 555); + } + + // Good + broken + broken + good + { + string concat = rm2.data + rm1.data + rm1.data + rm2.data; + FILE* out = fopen("test-simple-concat.bufr", "w"); + fwrite(concat.data(), concat.size(), 1, out); + fclose(out); + + fortran::MsgAPI api("test-simple-concat.bufr", "r", "BUFR"); + + wassert(actual(api.voglioquesto()) == 555); + + wassert(actual_function([&]() { api.voglioquesto(); }).throws("")); + wassert(actual_function([&]() { api.voglioquesto(); }).throws("")); + wassert(actual(api.voglioquesto()) == 555); + } + }); + add_method("read", []() { + // Try reading a file + std::string fname = tests::datafile("bufr/dbapi-emptymsg.bufr"); + fortran::MsgAPI api(fname.c_str(), "r", "BUFR"); + + wassert(actual(api.voglioquesto()) == 99); + wassert(actual(api.voglioquesto()) == 0); + wassert(actual(api.voglioquesto()) == 90); + wassert(actual(api.voglioquesto()) == api.missing_int); + }); + add_method("missing", []() { + // Try reading 'missing' values + std::string fname = tests::datafile("bufr/temp-bad5.bufr"); + fortran::MsgAPI api(fname.c_str(), "r", "BUFR"); + + wassert(actual(api.enqb("latmin")) == API::missing_byte); + //wassert(actual(api.enqc("latmin")) == API::missing_byte); + wassert(actual(api.enqi("latmin")) == API::missing_int); + wassert(actual(api.enqr("latmin")) == API::missing_float); + wassert(actual(api.enqd("latmin")) == API::missing_double); + + wassert(actual(api.enqb("B05002")) == API::missing_byte); + //wassert(actual(api.enqc("B05002")) == API::missing_byte); + wassert(actual(api.enqi("B05002")) == API::missing_int); + wassert(actual(api.enqr("B05002")) == API::missing_float); + wassert(actual(api.enqd("B05002")) == API::missing_double); + + api.unsetall(); + + for (unsigned msgi = 0; ; ++msgi) + { + WREPORT_TEST_INFO(msgloop); + msgloop() << "Message " << msgi; + + int count = wcallchecked(api.voglioquesto()); + if (count == API::missing_int) break; + wassert(actual(count) > 0); + + for (unsigned i = 0; i < (unsigned)count; ++i) + { + msgloop() << "Message " << msgi << " var " << i; + wassert(api.dammelo()); + } + } +#if 0 + n = 1 + do while ( n > 0 ) + call idba_voglioquesto (handle,n) + call ensure_no_error("voglioquesto") + + do i = 1, n + call idba_dammelo (handle,btable) + call ensure_no_error("dammelo") + call idba_enqd (handle,"B11001",dval) + call ensure_no_error("enqd from msg") + call idba_enqr (handle,"B11001",rval) + call ensure_no_error("enqr from msg") + call idba_enqi (handle,"B11001",ival) + call ensure_no_error("enqi from msg") + ! Value does not fit in a byte + !call idba_enqb (handle,"B11001",bval) + !call ensure_no_error("enqb from msg") + end do + end do + + call idba_fatto(handle) + call ensure_no_error("fatto") + + ! If we made it so far, exit with no error + print*,"check_missing: all tests succeed." + + call exit (0) + + end program + + include "check-utils.h" +#endif + }); + } +}; + +Tests newtg("simple_msgapi"); + +} diff --git a/dballe/simple/msgapi-tut.cc b/dballe/simple/msgapi-tut.cc deleted file mode 100644 index 54502c05e..000000000 --- a/dballe/simple/msgapi-tut.cc +++ /dev/null @@ -1,181 +0,0 @@ -#include "core/tests.h" -#include "msgapi.h" - -using namespace std; -using namespace dballe; -using namespace dballe::fortran; -using namespace wibble::tests; - -namespace { - -typedef dballe::tests::test_group<> test_group; -typedef test_group::Test Test; -typedef test_group::Fixture Fixture; - -std::vector tests { - Test("open", [](Fixture& f) { - // Open test file - std::string fname = tests::datafile("bufr/simple-generic-group.bufr"); - fortran::MsgAPI api(fname.c_str(), "r", "BUFR"); - - ensure_equals(api.voglioquesto(), 4); - ensure_equals(api.voglioquesto(), 4); - ensure_equals(api.voglioquesto(), 4); - ensure_equals(api.quantesono(), 1); - }), - Test("resume", [](Fixture& f) { - // Test resuming after a broken BUFR - - // Concatenate a broken BUFR with a good one - BinaryMessage rm1(read_rawmsg("bufr/interpreted-range.bufr", File::BUFR)); - BinaryMessage rm2(read_rawmsg("bufr/temp-gts1.bufr", File::BUFR)); - - // Broken + good - { - string concat = rm1.data + rm2.data; - FILE* out = fopen("test-simple-concat.bufr", "w"); - fwrite(concat.data(), concat.size(), 1, out); - fclose(out); - - fortran::MsgAPI api("test-simple-concat.bufr", "r", "BUFR"); - - // The first one fails - try { - api.voglioquesto(); - ensure(false); - } catch (std::exception) { - } - - // The second one should be read - ensure_equals(api.voglioquesto(), 555); - } - - // Good + broken + good - { - string concat = rm2.data + rm1.data + rm2.data; - FILE* out = fopen("test-simple-concat.bufr", "w"); - fwrite(concat.data(), concat.size(), 1, out); - fclose(out); - - fortran::MsgAPI api("test-simple-concat.bufr", "r", "BUFR"); - - ensure_equals(api.voglioquesto(), 555); - - try { - api.voglioquesto(); - ensure(false); - } catch (std::exception) { - } - - ensure_equals(api.voglioquesto(), 555); - } - - // Good + broken + broken + good - { - string concat = rm2.data + rm1.data + rm1.data + rm2.data; - FILE* out = fopen("test-simple-concat.bufr", "w"); - fwrite(concat.data(), concat.size(), 1, out); - fclose(out); - - fortran::MsgAPI api("test-simple-concat.bufr", "r", "BUFR"); - - ensure_equals(api.voglioquesto(), 555); - - try { - api.voglioquesto(); - ensure(false); - } catch (std::exception) { - } - - try { - api.voglioquesto(); - ensure(false); - } catch (std::exception) { - } - - ensure_equals(api.voglioquesto(), 555); - } - }), - Test("read", [](Fixture& f) { - // Try reading a file - std::string fname = tests::datafile("bufr/dbapi-emptymsg.bufr"); - fortran::MsgAPI api(fname.c_str(), "r", "BUFR"); - - ensure_equals(api.voglioquesto(), 99); - ensure_equals(api.voglioquesto(), 0); - ensure_equals(api.voglioquesto(), 90); - ensure_equals(api.voglioquesto(), api.missing_int); - }), - Test("missing", [](Fixture& f) { - // Try reading 'missing' values - std::string fname = tests::datafile("bufr/temp-bad5.bufr"); - fortran::MsgAPI api(fname.c_str(), "r", "BUFR"); - - wassert(actual(api.enqb("latmin")) == API::missing_byte); - //wassert(actual(api.enqc("latmin")) == API::missing_byte); - wassert(actual(api.enqi("latmin")) == API::missing_int); - wassert(actual(api.enqr("latmin")) == API::missing_float); - wassert(actual(api.enqd("latmin")) == API::missing_double); - - wassert(actual(api.enqb("B05002")) == API::missing_byte); - //wassert(actual(api.enqc("B05002")) == API::missing_byte); - wassert(actual(api.enqi("B05002")) == API::missing_int); - wassert(actual(api.enqr("B05002")) == API::missing_float); - wassert(actual(api.enqd("B05002")) == API::missing_double); - - api.unsetall(); - - for (unsigned msgi = 0; ; ++msgi) - { - WIBBLE_TEST_INFO(msgloop); - msgloop() << "Message " << msgi; - - int count = wcallchecked(api.voglioquesto()); - if (count == API::missing_int) break; - wassert(actual(count) > 0); - - for (unsigned i = 0; i < (unsigned)count; ++i) - { - msgloop() << "Message " << msgi << " var " << i; - wrunchecked(api.dammelo()); - } - } -#if 0 - n = 1 - do while ( n > 0 ) - call idba_voglioquesto (handle,n) - call ensure_no_error("voglioquesto") - - do i = 1, n - call idba_dammelo (handle,btable) - call ensure_no_error("dammelo") - call idba_enqd (handle,"B11001",dval) - call ensure_no_error("enqd from msg") - call idba_enqr (handle,"B11001",rval) - call ensure_no_error("enqr from msg") - call idba_enqi (handle,"B11001",ival) - call ensure_no_error("enqi from msg") - ! Value does not fit in a byte - !call idba_enqb (handle,"B11001",bval) - !call ensure_no_error("enqb from msg") - end do - end do - - call idba_fatto(handle) - call ensure_no_error("fatto") - -! If we made it so far, exit with no error - print*,"check_missing: all tests succeed." - - call exit (0) - - end program - - include "check-utils.h" -#endif - }), -}; - -test_group newtg("simple_msgapi", tests); - -} diff --git a/dballe/tests-main.cc b/dballe/tests-main.cc new file mode 100644 index 000000000..ddac5d688 --- /dev/null +++ b/dballe/tests-main.cc @@ -0,0 +1,139 @@ +#include +#include +#include +#include +#include + +void signal_to_exception(int) +{ + throw std::runtime_error("killing signal catched"); +} + +int main(int argc,const char* argv[]) +{ + using namespace wreport::tests; + + signal(SIGSEGV, signal_to_exception); + signal(SIGILL, signal_to_exception); + +#if 0 + if( (argc == 2 && (! strcmp ("help", argv[1]))) || argc > 3 ) + { + std::cout << "TUT example test application." << std::endl; + std::cout << "Usage: example [regression] | [list] | [ group] [test]" << std::endl; + std::cout << " List all groups: example list" << std::endl; + std::cout << " Run all tests: example regression" << std::endl; + std::cout << " Run one group: example std::auto_ptr" << std::endl; + std::cout << " Run one test: example std::auto_ptr 3" << std::endl;; + } + + // std::cout << "\nFAILURE and EXCEPTION in these tests are FAKE ;)\n\n"; + + tut::runner.get().set_callback(&visi); + + try + { + if( argc == 1 || (argc == 2 && std::string(argv[1]) == "regression") ) + { + tut::runner.get().run_tests(); + } + else if( argc == 2 && std::string(argv[1]) == "list" ) + { + std::cout << "registered test groups:" << std::endl; + tut::groupnames gl = tut::runner.get().list_groups(); + tut::groupnames::const_iterator i = gl.begin(); + tut::groupnames::const_iterator e = gl.end(); + while( i != e ) + { + std::cout << " " << *i << std::endl; + ++i; + } + } + else if( argc == 2 && std::string(argv[1]) != "regression" ) + { + tut::runner.get().run_tests(argv[1]); + } + else if( argc == 3 ) + { + tut::runner.get().run_test(argv[1],::atoi(argv[2])); + } + } + catch( const std::exception& ex ) + { + std::cerr << "tut raised exception: " << ex.what() << std::endl; + } +#endif + + auto& tests = TestRegistry::get(); + + SimpleTestController controller; + + if (const char* whitelist = getenv("TEST_WHITELIST")) + controller.whitelist = whitelist; + + if (const char* blacklist = getenv("TEST_BLACKLIST")) + controller.blacklist = blacklist; + + auto all_results = tests.run_tests(controller); + + unsigned methods_ok = 0; + unsigned methods_failed = 0; + unsigned methods_skipped = 0; + unsigned test_cases_ok = 0; + unsigned test_cases_failed = 0; + + for (const auto& tc_res: all_results) + { + if (!tc_res.fail_setup.empty()) + { + fprintf(stderr, "%s: %s\n", tc_res.test_case.c_str(), tc_res.fail_setup.c_str()); + ++test_cases_failed; + } else { + if (!tc_res.fail_teardown.empty()) + { + fprintf(stderr, "%s: %s\n", tc_res.test_case.c_str(), tc_res.fail_teardown.c_str()); + ++test_cases_failed; + } + else + ++test_cases_ok; + + for (const auto& tm_res: tc_res.methods) + { + if (tm_res.skipped) + ++methods_skipped; + else if (tm_res.is_success()) + ++methods_ok; + else + { + fprintf(stderr, "\n"); + if (tm_res.exception_typeid.empty()) + fprintf(stderr, "%s.%s: %s\n", tm_res.test_case.c_str(), tm_res.test_method.c_str(), tm_res.error_message.c_str()); + else + fprintf(stderr, "%s.%s:[%s] %s\n", tm_res.test_case.c_str(), tm_res.test_method.c_str(), tm_res.exception_typeid.c_str(), tm_res.error_message.c_str()); + for (const auto& frame : tm_res.error_stack) + fprintf(stderr, " %s", frame.format().c_str()); + ++methods_failed; + } + } + } + } + + bool success = true; + + if (test_cases_failed) + { + success = false; + fprintf(stderr, "\n%u/%u test cases had issues initializing or cleaning up\n", + test_cases_failed, test_cases_ok + test_cases_failed); + } + + if (methods_failed) + { + success = false; + fprintf(stderr, "\n%u/%u tests failed\n", methods_failed, methods_ok + methods_failed); + } + else + fprintf(stderr, "%u tests succeeded\n", methods_ok); + + return success ? 0 : 1; +} diff --git a/dballe/types-test.cc b/dballe/types-test.cc new file mode 100644 index 000000000..8b348b90c --- /dev/null +++ b/dballe/types-test.cc @@ -0,0 +1,226 @@ +#include "core/tests.h" +#include "types.h" + +using namespace std; +using namespace wreport::tests; +using namespace dballe; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("date", []() { + wassert(actual(Date(2015, 1, 1)) == Date(2015, 1, 1)); + wassert(actual(Date(2013, 1, 1)) < Date(2014, 1, 1)); + wassert(actual(Date(2013, 1, 1)) < Date(2013, 2, 1)); + wassert(actual(Date(2013, 1, 1)) < Date(2013, 1, 2)); + wassert(actual(Date(1945, 4, 25)) != Date(1945, 4, 26)); + }); + add_method("time", []() { + // Constructor boundary checks + wassert(actual(Time( 0, 0, 0)) == Time(0, 0, 0)); + wassert(actual(Time(23, 59, 60)) == Time(23, 59, 60)); + wassert(actual(Time(13, 1, 1)) < Time(14, 1, 1)); + wassert(actual(Time(13, 1, 1)) < Time(13, 2, 1)); + wassert(actual(Time(13, 1, 1)) < Time(13, 1, 2)); + wassert(actual(Time(19, 4, 25)) != Time(19, 4, 26)); + }); + add_method("datetime", []() { + // Constructor boundary checks + wassert(actual(Datetime(2015, 1, 1, 0, 0, 0)) == Datetime(2015, 1, 1, 0, 0, 0)); + + wassert(actual(Datetime(2013, 1, 1, 0, 0, 0)) < Datetime(2014, 1, 1, 0, 0, 0)); + wassert(actual(Datetime(2013, 1, 1, 0, 0, 0)) < Datetime(2013, 2, 1, 0, 0, 0)); + wassert(actual(Datetime(2013, 1, 1, 0, 0, 0)) < Datetime(2013, 1, 2, 0, 0, 0)); + wassert(actual(Datetime(2013, 1, 1, 0, 0, 0)) < Datetime(2013, 1, 1, 1, 0, 0)); + wassert(actual(Datetime(2013, 1, 1, 0, 0, 0)) < Datetime(2013, 1, 1, 0, 1, 0)); + wassert(actual(Datetime(2013, 1, 1, 0, 0, 0)) < Datetime(2013, 1, 1, 0, 0, 1)); + wassert(actual(Datetime(1945, 4, 25, 8, 0, 0)) != Datetime(1945, 4, 26, 8, 0, 0)); + }); + add_method("datetime_jdays", []() { + // Test Date to/from julian days conversion + Date d(2015, 4, 25); + wassert(actual(d.to_julian()) == 2457138); + + d.from_julian(2457138); + wassert(actual(d.year) == 2015); + wassert(actual(d.month) == 4); + wassert(actual(d.day) == 25); + }); + add_method("datetimerange", []() { + Datetime missing; + Datetime dt_2010(2010, 1, 1, 0, 0, 0); + Datetime dt_2011(2011, 1, 1, 0, 0, 0); + Datetime dt_2012(2012, 1, 1, 0, 0, 0); + Datetime dt_2013(2013, 1, 1, 0, 0, 0); + + // Test equality + wassert(actual(DatetimeRange(missing, missing) == DatetimeRange(missing, missing)).istrue()); + wassert(actual(DatetimeRange(dt_2010, dt_2011) == DatetimeRange(dt_2010, dt_2011)).istrue()); + wassert(actual(DatetimeRange(dt_2010, missing) == DatetimeRange(missing, missing)).isfalse()); + wassert(actual(DatetimeRange(missing, dt_2010) == DatetimeRange(missing, missing)).isfalse()); + wassert(actual(DatetimeRange(missing, missing) == DatetimeRange(dt_2010, missing)).isfalse()); + wassert(actual(DatetimeRange(missing, missing) == DatetimeRange(missing, dt_2010)).isfalse()); + wassert(actual(DatetimeRange(dt_2010, dt_2011) == DatetimeRange(dt_2012, dt_2013)).isfalse()); + + // Test contains + wassert(actual(DatetimeRange(missing, missing).contains(DatetimeRange(missing, missing))).istrue()); + wassert(actual(DatetimeRange(dt_2010, dt_2011).contains(DatetimeRange(dt_2010, dt_2011))).istrue()); + wassert(actual(DatetimeRange(missing, missing).contains(DatetimeRange(dt_2011, dt_2012))).istrue()); + wassert(actual(DatetimeRange(dt_2011, dt_2012).contains(DatetimeRange(missing, missing))).isfalse()); + wassert(actual(DatetimeRange(dt_2010, dt_2013).contains(DatetimeRange(dt_2011, dt_2012))).istrue()); + wassert(actual(DatetimeRange(dt_2010, dt_2012).contains(DatetimeRange(dt_2011, dt_2013))).isfalse()); + wassert(actual(DatetimeRange(missing, dt_2010).contains(DatetimeRange(dt_2011, missing))).isfalse()); + }); + add_method("coords", []() { + wassert(actual(Coords(44.0, 11.0)) == Coords(44.0, 360.0+11.0)); + }); + add_method("latrange", []() { + double dmin, dmax; + LatRange lr; + wassert(actual(lr.is_missing()).istrue()); + wassert(actual(lr.imin) == LatRange::IMIN); + wassert(actual(lr.imax) == LatRange::IMAX); + wassert(actual(lr.dmin()) == LatRange::DMIN); + wassert(actual(lr.dmax()) == LatRange::DMAX); + lr.get(dmin, dmax); + wassert(actual(dmin) == LatRange::DMIN); + wassert(actual(dmax) == LatRange::DMAX); + wassert(actual(lr.contains(0)).istrue()); + + lr = LatRange(40.0, 50.0); + wassert(actual(lr.is_missing()).isfalse()); + wassert(actual(lr.imin) == 4000000); + wassert(actual(lr.imax) == 5000000); + lr.get(dmin, dmax); + wassert(actual(dmin) == 40.0); + wassert(actual(dmax) == 50.0); + wassert(actual(lr.contains(39.9)).isfalse()); + wassert(actual(lr.contains(40.0)).istrue()); + wassert(actual(lr.contains(45.0)).istrue()); + wassert(actual(lr.contains(50.0)).istrue()); + wassert(actual(lr.contains(50.1)).isfalse()); + wassert(actual(lr.contains(4500000)).istrue()); + wassert(actual(lr.contains(5500000)).isfalse()); + + lr.set(-10.0, 10.0); + wassert(actual(lr.imin) == -1000000); + wassert(actual(lr.imax) == 1000000); + wassert(actual(lr) == LatRange(-10.0, 10.0)); + + lr.set(4000000, 5000000); + wassert(actual(lr.imin) == 4000000); + wassert(actual(lr.imax) == 5000000); + wassert(actual(lr) == LatRange(40.0, 50.0)); + }); + add_method("lonrange", []() { + double dmin, dmax; + LonRange lr; + wassert(actual(lr.is_missing()).istrue()); + wassert(actual(lr.imin) == MISSING_INT); + wassert(actual(lr.imax) == MISSING_INT); + wassert(actual(lr.dmin()) == -180.0); + wassert(actual(lr.dmax()) == 180.0); + lr.get(dmin, dmax); + wassert(actual(dmin) == -180.0); + wassert(actual(dmax) == 180.0); + wassert(actual(lr.contains(0)).istrue()); + + lr = LonRange(-18000000, 18000000); + wassert(actual(lr.is_missing()).istrue()); + wassert(actual(lr.imin) == MISSING_INT); + wassert(actual(lr.imax) == MISSING_INT); + + lr = LonRange(-180.0, 180.0); + wassert(actual(lr.is_missing()).istrue()); + wassert(actual(lr.imin) == MISSING_INT); + wassert(actual(lr.imax) == MISSING_INT); + + lr.set(-18000000, 18000000); + wassert(actual(lr.is_missing()).istrue()); + wassert(actual(lr.imin) == MISSING_INT); + wassert(actual(lr.imax) == MISSING_INT); + + lr.set(-180.0, 180.0); + wassert(actual(lr.is_missing()).istrue()); + wassert(actual(lr.imin) == MISSING_INT); + wassert(actual(lr.imax) == MISSING_INT); + + lr = LonRange(40.0, 50.0); + wassert(actual(lr.is_missing()).isfalse()); + wassert(actual(lr.imin) == 4000000); + wassert(actual(lr.imax) == 5000000); + lr.get(dmin, dmax); + wassert(actual(dmin) == 40.0); + wassert(actual(dmax) == 50.0); + wassert(actual(lr.contains(39.9)).isfalse()); + wassert(actual(lr.contains(40.0)).istrue()); + wassert(actual(lr.contains(45.0)).istrue()); + wassert(actual(lr.contains(50.0)).istrue()); + wassert(actual(lr.contains(50.1)).isfalse()); + wassert(actual(lr.contains(4500000)).istrue()); + wassert(actual(lr.contains(5500000)).isfalse()); + + lr = LonRange(50.0, 40.0); + wassert(actual(lr.is_missing()).isfalse()); + wassert(actual(lr.imin) == 5000000); + wassert(actual(lr.imax) == 4000000); + lr.get(dmin, dmax); + wassert(actual(dmin) == 50.0); + wassert(actual(dmax) == 40.0); + wassert(actual(lr.contains(39.9)).istrue()); + wassert(actual(lr.contains(40.0)).istrue()); + wassert(actual(lr.contains(45.0)).isfalse()); + wassert(actual(lr.contains(50.0)).istrue()); + wassert(actual(lr.contains(50.1)).istrue()); + wassert(actual(lr.contains(4500000)).isfalse()); + wassert(actual(lr.contains(5500000)).istrue()); + + lr.set(-10.0, 10.0); + wassert(actual(lr.imin) == -1000000); + wassert(actual(lr.imax) == 1000000); + wassert(actual(lr) == LonRange(-10.0, 10.0)); + + lr.set(350.0, 360.0); + wassert(actual(lr.imin) == -1000000); + wassert(actual(lr.imax) == 0); + wassert(actual(lr) == LonRange(-10.0, 0.0)); + }); + add_method("level_descs", []() { + // Try to get descriptions for all the layers + for (int i = 0; i < 261; ++i) + { + Level(i).describe(); + Level(i, 0).describe(); + Level(i, MISSING_INT, i, MISSING_INT).describe(); + Level(i, 0, i, 0).describe(); + } + }); + add_method("trange_descs", []() { + // Try to get descriptions for all the time ranges + for (int i = 0; i < 256; ++i) + { + Trange(i).describe(); + Trange(i, 0).describe(); + Trange(i, 0, 0).describe(); + } + }); + add_method("known_descs", []() { + // Verify some well-known descriptions + wassert(actual(Level().describe()) == "Information about the station that generated the data"); + wassert(actual(Level(103, 2000).describe()) == "2.000m above ground"); + wassert(actual(Level(103, 2000, 103, 4000).describe()) == + "Layer from [2.000m above ground] to [4.000m above ground]"); + wassert(actual(Trange(254, 86400).describe()) == + "Forecast at t+1d, instantaneous value"); + wassert(actual(Trange(2, 0, 43200).describe()) == "Maximum over 12h at forecast time 0"); + wassert(actual(Trange(3, 194400, 43200).describe()) == "Minimum over 12h at forecast time 2d 6h"); + }); + } +} test("dballe_types"); + +} diff --git a/dballe/types-tut.cc b/dballe/types-tut.cc deleted file mode 100644 index 6e85b6162..000000000 --- a/dballe/types-tut.cc +++ /dev/null @@ -1,226 +0,0 @@ -#include "core/tests.h" -#include "types.h" - -using namespace std; -using namespace wibble::tests; -using namespace dballe; - -namespace { - -typedef dballe::tests::test_group<> test_group; -typedef test_group::Test Test; -typedef test_group::Fixture Fixture; - -std::vector tests { - Test("date", [](Fixture& f) { - wassert(actual(Date(2015, 1, 1)) == Date(2015, 1, 1)); - wassert(actual(Date(2013, 1, 1)) < Date(2014, 1, 1)); - wassert(actual(Date(2013, 1, 1)) < Date(2013, 2, 1)); - wassert(actual(Date(2013, 1, 1)) < Date(2013, 1, 2)); - wassert(actual(Date(1945, 4, 25)) != Date(1945, 4, 26)); - }), - Test("time", [](Fixture& f) { - // Constructor boundary checks - wassert(actual(Time( 0, 0, 0)) == Time(0, 0, 0)); - wassert(actual(Time(23, 59, 60)) == Time(23, 59, 60)); - wassert(actual(Time(13, 1, 1)) < Time(14, 1, 1)); - wassert(actual(Time(13, 1, 1)) < Time(13, 2, 1)); - wassert(actual(Time(13, 1, 1)) < Time(13, 1, 2)); - wassert(actual(Time(19, 4, 25)) != Time(19, 4, 26)); - }), - Test("datetime", [](Fixture& f) { - // Constructor boundary checks - wassert(actual(Datetime(2015, 1, 1, 0, 0, 0)) == Datetime(2015, 1, 1, 0, 0, 0)); - - wassert(actual(Datetime(2013, 1, 1, 0, 0, 0)) < Datetime(2014, 1, 1, 0, 0, 0)); - wassert(actual(Datetime(2013, 1, 1, 0, 0, 0)) < Datetime(2013, 2, 1, 0, 0, 0)); - wassert(actual(Datetime(2013, 1, 1, 0, 0, 0)) < Datetime(2013, 1, 2, 0, 0, 0)); - wassert(actual(Datetime(2013, 1, 1, 0, 0, 0)) < Datetime(2013, 1, 1, 1, 0, 0)); - wassert(actual(Datetime(2013, 1, 1, 0, 0, 0)) < Datetime(2013, 1, 1, 0, 1, 0)); - wassert(actual(Datetime(2013, 1, 1, 0, 0, 0)) < Datetime(2013, 1, 1, 0, 0, 1)); - wassert(actual(Datetime(1945, 4, 25, 8, 0, 0)) != Datetime(1945, 4, 26, 8, 0, 0)); - }), - Test("datetime_jdays", [](Fixture& f) { - // Test Date to/from julian days conversion - Date d(2015, 4, 25); - wassert(actual(d.to_julian()) == 2457138); - - d.from_julian(2457138); - wassert(actual(d.year) == 2015); - wassert(actual(d.month) == 4); - wassert(actual(d.day) == 25); - }), - Test("datetimerange", [](Fixture& f) { - Datetime missing; - Datetime dt_2010(2010, 1, 1, 0, 0, 0); - Datetime dt_2011(2011, 1, 1, 0, 0, 0); - Datetime dt_2012(2012, 1, 1, 0, 0, 0); - Datetime dt_2013(2013, 1, 1, 0, 0, 0); - - // Test equality - wassert(actual(DatetimeRange(missing, missing) == DatetimeRange(missing, missing)).istrue()); - wassert(actual(DatetimeRange(dt_2010, dt_2011) == DatetimeRange(dt_2010, dt_2011)).istrue()); - wassert(actual(DatetimeRange(dt_2010, missing) == DatetimeRange(missing, missing)).isfalse()); - wassert(actual(DatetimeRange(missing, dt_2010) == DatetimeRange(missing, missing)).isfalse()); - wassert(actual(DatetimeRange(missing, missing) == DatetimeRange(dt_2010, missing)).isfalse()); - wassert(actual(DatetimeRange(missing, missing) == DatetimeRange(missing, dt_2010)).isfalse()); - wassert(actual(DatetimeRange(dt_2010, dt_2011) == DatetimeRange(dt_2012, dt_2013)).isfalse()); - - // Test contains - wassert(actual(DatetimeRange(missing, missing).contains(DatetimeRange(missing, missing))).istrue()); - wassert(actual(DatetimeRange(dt_2010, dt_2011).contains(DatetimeRange(dt_2010, dt_2011))).istrue()); - wassert(actual(DatetimeRange(missing, missing).contains(DatetimeRange(dt_2011, dt_2012))).istrue()); - wassert(actual(DatetimeRange(dt_2011, dt_2012).contains(DatetimeRange(missing, missing))).isfalse()); - wassert(actual(DatetimeRange(dt_2010, dt_2013).contains(DatetimeRange(dt_2011, dt_2012))).istrue()); - wassert(actual(DatetimeRange(dt_2010, dt_2012).contains(DatetimeRange(dt_2011, dt_2013))).isfalse()); - wassert(actual(DatetimeRange(missing, dt_2010).contains(DatetimeRange(dt_2011, missing))).isfalse()); - }), - Test("coords", [](Fixture& f) { - wassert(actual(Coords(44.0, 11.0)) == Coords(44.0, 360.0+11.0)); - }), - Test("latrange", [](Fixture& f) { - double dmin, dmax; - LatRange lr; - wassert(actual(lr.is_missing()).istrue()); - wassert(actual(lr.imin) == LatRange::IMIN); - wassert(actual(lr.imax) == LatRange::IMAX); - wassert(actual(lr.dmin()) == LatRange::DMIN); - wassert(actual(lr.dmax()) == LatRange::DMAX); - lr.get(dmin, dmax); - wassert(actual(dmin) == LatRange::DMIN); - wassert(actual(dmax) == LatRange::DMAX); - wassert(actual(lr.contains(0)).istrue()); - - lr = LatRange(40.0, 50.0); - wassert(actual(lr.is_missing()).isfalse()); - wassert(actual(lr.imin) == 4000000); - wassert(actual(lr.imax) == 5000000); - lr.get(dmin, dmax); - wassert(actual(dmin) == 40.0); - wassert(actual(dmax) == 50.0); - wassert(actual(lr.contains(39.9)).isfalse()); - wassert(actual(lr.contains(40.0)).istrue()); - wassert(actual(lr.contains(45.0)).istrue()); - wassert(actual(lr.contains(50.0)).istrue()); - wassert(actual(lr.contains(50.1)).isfalse()); - wassert(actual(lr.contains(4500000)).istrue()); - wassert(actual(lr.contains(5500000)).isfalse()); - - lr.set(-10.0, 10.0); - wassert(actual(lr.imin) == -1000000); - wassert(actual(lr.imax) == 1000000); - wassert(actual(lr) == LatRange(-10.0, 10.0)); - - lr.set(4000000, 5000000); - wassert(actual(lr.imin) == 4000000); - wassert(actual(lr.imax) == 5000000); - wassert(actual(lr) == LatRange(40.0, 50.0)); - }), - Test("lonrange", [](Fixture& f) { - double dmin, dmax; - LonRange lr; - wassert(actual(lr.is_missing()).istrue()); - wassert(actual(lr.imin) == MISSING_INT); - wassert(actual(lr.imax) == MISSING_INT); - wassert(actual(lr.dmin()) == -180.0); - wassert(actual(lr.dmax()) == 180.0); - lr.get(dmin, dmax); - wassert(actual(dmin) == -180.0); - wassert(actual(dmax) == 180.0); - wassert(actual(lr.contains(0)).istrue()); - - lr = LonRange(-18000000, 18000000); - wassert(actual(lr.is_missing()).istrue()); - wassert(actual(lr.imin) == MISSING_INT); - wassert(actual(lr.imax) == MISSING_INT); - - lr = LonRange(-180.0, 180.0); - wassert(actual(lr.is_missing()).istrue()); - wassert(actual(lr.imin) == MISSING_INT); - wassert(actual(lr.imax) == MISSING_INT); - - lr.set(-18000000, 18000000); - wassert(actual(lr.is_missing()).istrue()); - wassert(actual(lr.imin) == MISSING_INT); - wassert(actual(lr.imax) == MISSING_INT); - - lr.set(-180.0, 180.0); - wassert(actual(lr.is_missing()).istrue()); - wassert(actual(lr.imin) == MISSING_INT); - wassert(actual(lr.imax) == MISSING_INT); - - lr = LonRange(40.0, 50.0); - wassert(actual(lr.is_missing()).isfalse()); - wassert(actual(lr.imin) == 4000000); - wassert(actual(lr.imax) == 5000000); - lr.get(dmin, dmax); - wassert(actual(dmin) == 40.0); - wassert(actual(dmax) == 50.0); - wassert(actual(lr.contains(39.9)).isfalse()); - wassert(actual(lr.contains(40.0)).istrue()); - wassert(actual(lr.contains(45.0)).istrue()); - wassert(actual(lr.contains(50.0)).istrue()); - wassert(actual(lr.contains(50.1)).isfalse()); - wassert(actual(lr.contains(4500000)).istrue()); - wassert(actual(lr.contains(5500000)).isfalse()); - - lr = LonRange(50.0, 40.0); - wassert(actual(lr.is_missing()).isfalse()); - wassert(actual(lr.imin) == 5000000); - wassert(actual(lr.imax) == 4000000); - lr.get(dmin, dmax); - wassert(actual(dmin) == 50.0); - wassert(actual(dmax) == 40.0); - wassert(actual(lr.contains(39.9)).istrue()); - wassert(actual(lr.contains(40.0)).istrue()); - wassert(actual(lr.contains(45.0)).isfalse()); - wassert(actual(lr.contains(50.0)).istrue()); - wassert(actual(lr.contains(50.1)).istrue()); - wassert(actual(lr.contains(4500000)).isfalse()); - wassert(actual(lr.contains(5500000)).istrue()); - - lr.set(-10.0, 10.0); - wassert(actual(lr.imin) == -1000000); - wassert(actual(lr.imax) == 1000000); - wassert(actual(lr) == LonRange(-10.0, 10.0)); - - lr.set(350.0, 360.0); - wassert(actual(lr.imin) == -1000000); - wassert(actual(lr.imax) == 0); - wassert(actual(lr) == LonRange(-10.0, 0.0)); - }), - Test("level_descs", [](Fixture& f) { - // Try to get descriptions for all the layers - for (int i = 0; i < 261; ++i) - { - Level(i).describe(); - Level(i, 0).describe(); - Level(i, MISSING_INT, i, MISSING_INT).describe(); - Level(i, 0, i, 0).describe(); - } - }), - Test("trange_descs", [](Fixture& f) { - // Try to get descriptions for all the time ranges - for (int i = 0; i < 256; ++i) - { - Trange(i).describe(); - Trange(i, 0).describe(); - Trange(i, 0, 0).describe(); - } - }), - Test("known_descs", [](Fixture& f) { - // Verify some well-known descriptions - wassert(actual(Level().describe()) == "Information about the station that generated the data"); - wassert(actual(Level(103, 2000).describe()) == "2.000m above ground"); - wassert(actual(Level(103, 2000, 103, 4000).describe()) == - "Layer from [2.000m above ground] to [4.000m above ground]"); - wassert(actual(Trange(254, 86400).describe()) == - "Forecast at t+1d, instantaneous value"); - wassert(actual(Trange(2, 0, 43200).describe()) == "Maximum over 12h at forecast time 0"); - wassert(actual(Trange(3, 194400, 43200).describe()) == "Minimum over 12h at forecast time 2d 6h"); - }), -}; - -test_group newtg("dballe_types", tests); - -} diff --git a/dballe/types.cc b/dballe/types.cc index ee11a46a8..2fc3f4fcd 100644 --- a/dballe/types.cc +++ b/dballe/types.cc @@ -558,6 +558,11 @@ void Coords::print(FILE* out, const char* end) const * LatRange */ +constexpr int LatRange::IMIN; +constexpr int LatRange::IMAX; +constexpr double LatRange::DMIN; +constexpr double LatRange::DMAX; + LatRange::LatRange(int min, int max) : imin(min), imax(max) {} LatRange::LatRange(double min, double max) diff --git a/dballe/types.h b/dballe/types.h index 4c7005bfa..cd2f27489 100644 --- a/dballe/types.h +++ b/dballe/types.h @@ -13,7 +13,7 @@ namespace dballe { /** * Value to use for missing parts of level and time range values */ -static const int MISSING_INT = INT_MAX; +static constexpr int MISSING_INT = INT_MAX; /** * Calendar date. @@ -390,9 +390,9 @@ struct Coords struct LatRange { /// Minimum possible integer value - static const int IMIN = -9000000; + static constexpr int IMIN = -9000000; /// Maximum possible integer value - static const int IMAX = 9000000; + static constexpr int IMAX = 9000000; /// Minimum possible double value static constexpr double DMIN = -90.0; /// Maximum possible double value diff --git a/dballe/var-test.cc b/dballe/var-test.cc new file mode 100644 index 000000000..d422855ab --- /dev/null +++ b/dballe/var-test.cc @@ -0,0 +1,36 @@ +#include "core/tests.h" +#include "var.h" + +using namespace std; +using namespace wreport::tests; +using namespace dballe; +using namespace wreport; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("resolve", []() { + wassert(actual(resolve_varcode("B12101")) == WR_VAR(0, 12, 101)); + wassert(actual(resolve_varcode("t")) == WR_VAR(0, 12, 101)); + try { + resolve_varcode("B121"); + throw TestFailed("an exception should be thrown"); + } catch (TestFailed) { + throw; + } catch (std::exception& e) { + wassert(actual(e.what()).contains("cannot parse")); + } + }); + add_method("varinfo", []() { + Varinfo i = varinfo(WR_VAR(0, 12, 101)); + wassert(actual(i->unit) == "K"); + }); + } +} test("dballe_var"); + +} diff --git a/dballe/var-tut.cc b/dballe/var-tut.cc deleted file mode 100644 index 95efcfb26..000000000 --- a/dballe/var-tut.cc +++ /dev/null @@ -1,35 +0,0 @@ -#include "core/tests.h" -#include "var.h" - -using namespace std; -using namespace wibble::tests; -using namespace dballe; -using namespace wreport; - -namespace { - -typedef dballe::tests::test_group<> test_group; -typedef test_group::Test Test; -typedef test_group::Fixture Fixture; - -std::vector tests { - Test("resolve", [](Fixture& f) { - wassert(actual(resolve_varcode("B12101")) == WR_VAR(0, 12, 101)); - wassert(actual(resolve_varcode("t")) == WR_VAR(0, 12, 101)); - try { - resolve_varcode("B121"); - ensure(false); - } catch (std::exception& e) { - wassert(actual(e.what()).contains("cannot parse")); - } - }), - Test("varinfo", [](Fixture& f) { - Varinfo i = varinfo(WR_VAR(0, 12, 101)); - wassert(actual(i->unit) == "K"); - }), -}; - -test_group newtg("dballe_var", tests); - -} - diff --git a/debian/control b/debian/control index 31ff70692..628c0c4d5 100644 --- a/debian/control +++ b/debian/control @@ -7,7 +7,6 @@ Build-Depends: debhelper (>= 9), dh-python, dh-autoreconf, libsqlite3-dev, libwreport-dev (>= 3.0), libpopt-dev, unixodbc-dev, gperf, liblua5.1-0-dev, libcnf-dev, gfortran | g77, - libwibble-dev (>= 1.0), libwibble-dev (<< 2.0), doxygen-latex, ghostscript, python-all-dev, python3-all-dev, python-docutils, python-numpy, python3-numpy, python-six, python3-six diff --git a/run-check b/run-check index cc771d563..427e07c0b 100755 --- a/run-check +++ b/run-check @@ -1,5 +1,9 @@ #!/bin/sh +TOP_SRCDIR=$(cd $(dirname $0) && pwd) +export LD_LIBRARY_PATH="$TOP_SRCDIR/dballe:$LD_LIBRARY_PATH" +export WREPORT_EXTRA_TABLES=$TOP_SRCDIR/tables + for conffile in ./run-check.conf .git/run-check.conf do if [ -f "$conffile" ] diff --git a/src/dbadb.cc b/src/dbadb.cc index 77b82f18f..11d7e695c 100644 --- a/src/dbadb.cc +++ b/src/dbadb.cc @@ -302,7 +302,7 @@ struct ImportCmd : public DatabaseCmd unique_ptr db = connect(); - const char* forced_repmemo = dbadb::parse_op_report(*db, op_report); + const char* forced_repmemo = op_report; Dbadb dbadb(*db); return dbadb.do_import(get_filenames(optCon), reader, import_flags, forced_repmemo);