summaryrefslogtreecommitdiffstats
path: root/searchsummary/src/tests
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@yahoo-inc.com>2016-06-15 23:09:44 +0200
committerJon Bratseth <bratseth@yahoo-inc.com>2016-06-15 23:09:44 +0200
commit72231250ed81e10d66bfe70701e64fa5fe50f712 (patch)
tree2728bba1131a6f6e5bdf95afec7d7ff9358dac50 /searchsummary/src/tests
Publish
Diffstat (limited to 'searchsummary/src/tests')
-rw-r--r--searchsummary/src/tests/docsumformat/.gitignore21
-rw-r--r--searchsummary/src/tests/docsumformat/CMakeLists.txt8
-rwxr-xr-xsearchsummary/src/tests/docsumformat/docsum-index.sh16
-rw-r--r--searchsummary/src/tests/docsumformat/docsum-pack.cpp631
-rw-r--r--searchsummary/src/tests/docsumformat/docsum-parse.cpp201
-rwxr-xr-xsearchsummary/src/tests/docsumformat/dotest.sh13
-rw-r--r--searchsummary/src/tests/docsumformat/parsetest/.gitignore2
-rw-r--r--searchsummary/src/tests/docsumformat/parsetest/OK.correct.117
-rw-r--r--searchsummary/src/tests/docsumformat/parsetest/OK.correct.214
-rw-r--r--searchsummary/src/tests/docsumformat/parsetest/OK.correct.35
-rw-r--r--searchsummary/src/tests/docsumformat/parsetest/OK.correct.417
-rw-r--r--searchsummary/src/tests/docsumformat/parsetest/OK.correct.53
-rw-r--r--searchsummary/src/tests/docsumformat/parsetest/OK.correct.65
-rw-r--r--searchsummary/src/tests/docsumformat/parsetest/OK.correct.711
-rw-r--r--searchsummary/src/tests/docsumformat/parsetest/OK.correct.87
-rw-r--r--searchsummary/src/tests/docsumformat/parsetest/OK.correct.913
-rw-r--r--searchsummary/src/tests/docsumformat/parsetest/README24
-rw-r--r--searchsummary/src/tests/docsumformat/parsetest/correct.114
-rw-r--r--searchsummary/src/tests/docsumformat/parsetest/correct.214
-rw-r--r--searchsummary/src/tests/docsumformat/parsetest/correct.33
-rw-r--r--searchsummary/src/tests/docsumformat/parsetest/correct.417
-rw-r--r--searchsummary/src/tests/docsumformat/parsetest/correct.54
-rw-r--r--searchsummary/src/tests/docsumformat/parsetest/correct.62
-rw-r--r--searchsummary/src/tests/docsumformat/parsetest/correct.711
-rw-r--r--searchsummary/src/tests/docsumformat/parsetest/correct.87
-rw-r--r--searchsummary/src/tests/docsumformat/parsetest/correct.913
-rw-r--r--searchsummary/src/tests/docsumformat/parsetest/incorrect.10
-rw-r--r--searchsummary/src/tests/docsumformat/parsetest/incorrect.214
-rw-r--r--searchsummary/src/tests/docsumformat/parsetest/incorrect.314
-rw-r--r--searchsummary/src/tests/docsumformat/parsetest/incorrect.414
-rw-r--r--searchsummary/src/tests/docsumformat/parsetest/incorrect.514
-rw-r--r--searchsummary/src/tests/docsumformat/parsetest/incorrect.613
-rw-r--r--searchsummary/src/tests/docsumformat/parsetest/incorrect.714
-rw-r--r--searchsummary/src/tests/docsumformat/parsetest/incorrect.82
-rw-r--r--searchsummary/src/tests/docsummary/.gitignore4
-rw-r--r--searchsummary/src/tests/docsummary/CMakeLists.txt8
-rw-r--r--searchsummary/src/tests/docsummary/positionsdfw_test.cpp142
-rw-r--r--searchsummary/src/tests/docsummary/slime_summary/.gitignore1
-rw-r--r--searchsummary/src/tests/docsummary/slime_summary/CMakeLists.txt8
-rw-r--r--searchsummary/src/tests/docsummary/slime_summary/FILES1
-rw-r--r--searchsummary/src/tests/docsummary/slime_summary/slime_summary_test.cpp125
-rw-r--r--searchsummary/src/tests/extractkeywords/.gitignore7
-rw-r--r--searchsummary/src/tests/extractkeywords/CMakeLists.txt8
-rw-r--r--searchsummary/src/tests/extractkeywords/extractkeywordstest.cpp295
-rw-r--r--searchsummary/src/tests/extractkeywords/extractkeywordstest.h34
-rwxr-xr-xsearchsummary/src/tests/extractkeywords/runtests.sh29
-rw-r--r--searchsummary/src/tests/extractkeywords/testowner.ATS1
47 files changed, 1841 insertions, 0 deletions
diff --git a/searchsummary/src/tests/docsumformat/.gitignore b/searchsummary/src/tests/docsumformat/.gitignore
new file mode 100644
index 00000000000..2c841cbd43d
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/.gitignore
@@ -0,0 +1,21 @@
+*.cfg
+*.core
+*.ilk
+*.out
+*.pdb
+*.pid
+.depend
+Makefile
+core
+core.*
+datapart.*
+docsum-index
+docsum-pack
+docsum-parse
+index.cf
+merged
+meta-info.txt
+schema.txt
+summary.cf
+version.txt
+searchsummary_docsum-pack_app
diff --git a/searchsummary/src/tests/docsumformat/CMakeLists.txt b/searchsummary/src/tests/docsumformat/CMakeLists.txt
new file mode 100644
index 00000000000..ac8d2151792
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/CMakeLists.txt
@@ -0,0 +1,8 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(searchsummary_docsum-pack_app
+ SOURCES
+ docsum-pack.cpp
+ DEPENDS
+ searchsummary
+)
+vespa_add_test(NAME searchsummary_docsum-pack_app COMMAND searchsummary_docsum-pack_app)
diff --git a/searchsummary/src/tests/docsumformat/docsum-index.sh b/searchsummary/src/tests/docsumformat/docsum-index.sh
new file mode 100755
index 00000000000..0d313191685
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/docsum-index.sh
@@ -0,0 +1,16 @@
+#!/bin/sh -e
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+findex=../../../bin/findex
+
+echo "CLEAN"
+rm -f index.cf
+rm -f summary.cf
+rm -rf merged
+rm -rf datapart.*
+
+echo "DOCSUM-INDEX"
+./docsum-index
+
+echo "AUTOINDEX"
+$findex autoindex
diff --git a/searchsummary/src/tests/docsumformat/docsum-pack.cpp b/searchsummary/src/tests/docsumformat/docsum-pack.cpp
new file mode 100644
index 00000000000..3f1b088bd12
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/docsum-pack.cpp
@@ -0,0 +1,631 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright (C) 2001-2003 Fast Search & Transfer ASA
+// Copyright (C) 2003 Overture Services Norway AS
+
+
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("docsum-pack");
+#include <vespa/searchlib/util/rawbuf.h>
+#include <vespa/searchsummary/docsummary/urlresult.h>
+#include <vespa/searchsummary/docsummary/resultconfig.h>
+#include <vespa/searchsummary/docsummary/resultpacker.h>
+
+using namespace search::docsummary;
+
+
+// needed to resolve external symbol from httpd.h on AIX
+void FastS_block_usr2() {}
+
+
+class MyApp : public FastOS_Application
+{
+private:
+ bool _rc;
+ uint32_t _cnt;
+ search::docsummary::ResultConfig _config;
+ search::docsummary::ResultPacker _packer;
+
+public:
+ MyApp()
+ : _rc(false),
+ _cnt(0u),
+ _config(),
+ _packer(&_config)
+ {
+ }
+
+ // log test results
+ void ReportTestResult(uint32_t line, bool rc);
+ bool RTR(uint32_t line, bool rc)
+ { ReportTestResult(line, rc); return rc; }
+
+ // compare runtime info (,but ignore result class)
+ bool Equal(search::docsummary::ResEntry *a, search::docsummary::ResEntry *b);
+ bool Equal(search::docsummary::GeneralResult *a, search::docsummary::GeneralResult *b);
+
+ void TestFieldIndex(uint32_t line, search::docsummary::GeneralResult *gres,
+ const char *field, int idx);
+
+ void TestIntValue(uint32_t line, search::docsummary::GeneralResult *gres,
+ const char *field, uint32_t value);
+
+ void TestDoubleValue(uint32_t line, search::docsummary::GeneralResult *gres,
+ const char *field, double value);
+
+ void TestInt64Value(uint32_t line, search::docsummary::GeneralResult *gres,
+ const char *field, uint64_t value);
+
+ void TestStringValue(uint32_t line, search::docsummary::GeneralResult *gres,
+ const char *field, const char *value);
+
+ void TestDataValue(uint32_t line, search::docsummary::GeneralResult *gres,
+ const char *field, const char *value);
+
+ void TestBasic();
+ void TestFailLong();
+ void TestFailShort();
+ void TestFailOrder();
+ void TestCompress();
+ void TestCompat();
+ void TestBasicInplace();
+ void TestCompressInplace();
+
+ int Main();
+};
+
+
+void
+MyApp::ReportTestResult(uint32_t line, bool rc)
+{
+ _cnt++;
+
+ if (rc) {
+ LOG(info, "Test case %d: SUCCESS", _cnt);
+ } else {
+ LOG(error, "Test case %d: FAIL (see %s:%d)", _cnt, __FILE__, line);
+ _rc = false;
+ }
+}
+
+
+bool
+MyApp::Equal(search::docsummary::ResEntry *a, search::docsummary::ResEntry *b)
+{
+ if (a->_type != b->_type)
+ return false;
+
+ if (a->_intval != b->_intval)
+ return false;
+
+ if (a->_type != RES_INT &&
+ memcmp(a->_pt, b->_pt, a->_intval) != 0)
+ return false;
+
+ return true;
+}
+
+
+bool
+MyApp::Equal(search::docsummary::GeneralResult *a, search::docsummary::GeneralResult *b)
+{
+ uint32_t numEntries = a->GetClass()->GetNumEntries();
+
+ if (b->GetClass()->GetNumEntries() != numEntries)
+ return false;
+
+ for (uint32_t i = 0; i < numEntries; i++) {
+
+ if (!Equal(a->GetEntry(i), b->GetEntry(i)))
+ return false;
+
+ if (a->GetClass()->GetEntry(i)->_bindname != b->GetClass()->GetEntry(i)->_bindname)
+ return false;
+ }
+
+ return true;
+}
+
+
+void
+MyApp::TestFieldIndex(uint32_t line, search::docsummary::GeneralResult *gres,
+ const char *field, int idx)
+{
+ bool rc = (gres != NULL &&
+ gres->GetClass()->GetIndexFromName(field) == idx);
+
+ RTR(line, rc);
+}
+
+
+void
+MyApp::TestIntValue(uint32_t line, search::docsummary::GeneralResult *gres,
+ const char *field, uint32_t value)
+{
+ search::docsummary::ResEntry *entry
+ = (gres != NULL) ? gres->GetEntry(field) : NULL;
+
+ bool rc = (entry != NULL &&
+ entry->_type == RES_INT &&
+ entry->_intval == value);
+
+ RTR(line, rc);
+}
+
+
+void
+MyApp::TestDoubleValue(uint32_t line, search::docsummary::GeneralResult *gres,
+ const char *field, double value)
+{
+ search::docsummary::ResEntry *entry
+ = (gres != NULL) ? gres->GetEntry(field) : NULL;
+
+ bool rc = (entry != NULL &&
+ entry->_type == RES_DOUBLE &&
+ entry->_doubleval == value);
+
+ RTR(line, rc);
+}
+
+
+void
+MyApp::TestInt64Value(uint32_t line, search::docsummary::GeneralResult *gres,
+ const char *field, uint64_t value)
+{
+ search::docsummary::ResEntry *entry
+ = (gres != NULL) ? gres->GetEntry(field) : NULL;
+
+ bool rc = (entry != NULL &&
+ entry->_type == RES_INT64 &&
+ entry->_int64val == value);
+
+ RTR(line, rc);
+}
+
+
+void
+MyApp::TestStringValue(uint32_t line, search::docsummary::GeneralResult *gres,
+ const char *field, const char *value)
+{
+ search::docsummary::ResEntry *entry
+ = (gres != NULL) ? gres->GetEntry(field) : NULL;
+
+ bool rc = (entry != NULL &&
+ entry->_type == RES_STRING &&
+ entry->_stringlen == strlen(value) &&
+ strncmp(entry->_stringval, value, entry->_stringlen) == 0);
+
+ if (!rc && entry != NULL) {
+ LOG(warning,
+ "string value '%.*s' != '%s'",
+ (int) entry->_stringlen,
+ entry->_stringval, value);
+ }
+
+ RTR(line, rc);
+}
+
+
+void
+MyApp::TestDataValue(uint32_t line, search::docsummary::GeneralResult *gres,
+ const char *field, const char *value)
+{
+ search::docsummary::ResEntry *entry
+ = (gres != NULL) ? gres->GetEntry(field) : NULL;
+
+ bool rc = (entry != NULL &&
+ entry->_type == RES_DATA &&
+ entry->_datalen == strlen(value) &&
+ strncmp(entry->_dataval, value, entry->_datalen) == 0);
+
+ RTR(line, rc);
+}
+
+
+void
+MyApp::TestBasic()
+{
+ const char *buf;
+ uint32_t buflen;
+
+ search::docsummary::urlresult *res;
+ search::docsummary::GeneralResult *gres;
+
+ uint32_t intval = 4;
+ uint16_t shortval = 2;
+ uint8_t byteval = 1;
+ float floatval = 4.5;
+ double doubleval = 8.75;
+ uint64_t int64val = 8;
+ const char *strval = "This is a string";
+ const char *datval = "This is data";
+ const char *lstrval = "This is a long string";
+ const char *ldatval = "This is long data";
+
+ RTR(__LINE__, _packer.Init(0));
+ RTR(__LINE__, _packer.AddInteger(intval));
+ RTR(__LINE__, _packer.AddShort(shortval));
+ RTR(__LINE__, _packer.AddByte(byteval));
+ RTR(__LINE__, _packer.AddFloat(floatval));
+ RTR(__LINE__, _packer.AddDouble(doubleval));
+ RTR(__LINE__, _packer.AddInt64(int64val));
+ RTR(__LINE__, _packer.AddString(strval, strlen(strval)));
+ RTR(__LINE__, _packer.AddData(datval, strlen(datval)));
+ RTR(__LINE__, _packer.AddLongString(lstrval, strlen(lstrval)));
+ RTR(__LINE__, _packer.AddLongData(ldatval, strlen(ldatval)));
+ RTR(__LINE__, _packer.GetDocsumBlob(&buf, &buflen));
+
+ res = _config.Unpack(0, 0, 0, buf, buflen);
+ gres = res->IsGeneral() ? (search::docsummary::GeneralResult *) res : NULL;
+
+ RTR(__LINE__, gres != NULL);
+ TestIntValue (__LINE__, gres, "integer", 4);
+ TestIntValue (__LINE__, gres, "short", 2);
+ TestIntValue (__LINE__, gres, "byte", 1);
+ TestDoubleValue(__LINE__, gres, "float", floatval);
+ TestDoubleValue(__LINE__, gres, "double", doubleval);
+ TestInt64Value (__LINE__, gres, "int64", int64val);
+ TestStringValue(__LINE__, gres, "string", strval);
+ TestDataValue (__LINE__, gres, "data", datval);
+ TestStringValue(__LINE__, gres, "longstring", lstrval);
+ TestDataValue (__LINE__, gres, "longdata", ldatval);
+ RTR(__LINE__, (gres != NULL &&
+ gres->GetClass()->GetNumEntries() == 10));
+ RTR(__LINE__, (gres != NULL &&
+ gres->GetClass()->GetClassID() == 0));
+ delete res;
+}
+
+
+void
+MyApp::TestFailLong()
+{
+ const char *buf;
+ uint32_t buflen;
+
+ uint32_t intval = 4;
+ uint16_t shortval = 2;
+ uint8_t byteval = 1;
+ float floatval = 4.5;
+ double doubleval = 8.75;
+ uint64_t int64val = 8;
+ const char *strval = "This is a string";
+ const char *datval = "This is data";
+ const char *lstrval = "This is a long string";
+ const char *ldatval = "This is long data";
+
+ RTR(__LINE__, _packer.Init(0));
+ RTR(__LINE__, _packer.AddInteger(intval));
+ RTR(__LINE__, _packer.AddShort(shortval));
+ RTR(__LINE__, _packer.AddByte(byteval));
+ RTR(__LINE__, _packer.AddFloat(floatval));
+ RTR(__LINE__, _packer.AddDouble(doubleval));
+ RTR(__LINE__, _packer.AddInt64(int64val));
+ RTR(__LINE__, _packer.AddString(strval, strlen(strval)));
+ RTR(__LINE__, _packer.AddData(datval, strlen(datval)));
+ RTR(__LINE__, _packer.AddLongString(lstrval, strlen(lstrval)));
+ RTR(__LINE__, _packer.AddLongData(ldatval, strlen(ldatval)));
+ RTR(__LINE__, !_packer.AddByte(byteval));
+ RTR(__LINE__, !_packer.GetDocsumBlob(&buf, &buflen));
+}
+
+
+void
+MyApp::TestFailShort()
+{
+ const char *buf;
+ uint32_t buflen;
+
+ uint32_t intval = 4;
+ uint16_t shortval = 2;
+ uint8_t byteval = 1;
+ float floatval = 4.5;
+ double doubleval = 8.75;
+ uint64_t int64val = 8;
+ const char *strval = "This is a string";
+ const char *datval = "This is data";
+ const char *lstrval = "This is a long string";
+
+ RTR(__LINE__, _packer.Init(0));
+ RTR(__LINE__, _packer.AddInteger(intval));
+ RTR(__LINE__, _packer.AddShort(shortval));
+ RTR(__LINE__, _packer.AddByte(byteval));
+ RTR(__LINE__, _packer.AddFloat(floatval));
+ RTR(__LINE__, _packer.AddDouble(doubleval));
+ RTR(__LINE__, _packer.AddInt64(int64val));
+ RTR(__LINE__, _packer.AddString(strval, strlen(strval)));
+ RTR(__LINE__, _packer.AddData(datval, strlen(datval)));
+ RTR(__LINE__, _packer.AddLongString(lstrval, strlen(lstrval)));
+ RTR(__LINE__, !_packer.GetDocsumBlob(&buf, &buflen));
+}
+
+
+void
+MyApp::TestFailOrder()
+{
+ const char *buf;
+ uint32_t buflen;
+
+ uint32_t intval = 4;
+ uint16_t shortval = 2;
+ uint8_t byteval = 1;
+ float floatval = 4.5;
+ double doubleval = 8.75;
+ uint64_t int64val = 8;
+ const char *strval = "This is a string";
+ const char *datval = "This is data";
+ const char *lstrval = "This is a long string";
+ const char *ldatval = "This is long data";
+
+ RTR(__LINE__, _packer.Init(0));
+ RTR(__LINE__, _packer.AddInteger(intval));
+ RTR(__LINE__, _packer.AddShort(shortval));
+ RTR(__LINE__, !_packer.AddString(strval, strlen(strval)));
+ RTR(__LINE__, !_packer.AddByte(byteval));
+ RTR(__LINE__, !_packer.AddFloat(floatval));
+ RTR(__LINE__, !_packer.AddDouble(doubleval));
+ RTR(__LINE__, !_packer.AddInt64(int64val));
+ RTR(__LINE__, !_packer.AddData(datval, strlen(datval)));
+ RTR(__LINE__, !_packer.AddLongString(lstrval, strlen(lstrval)));
+ RTR(__LINE__, !_packer.AddLongData(ldatval, strlen(ldatval)));
+ RTR(__LINE__, !_packer.GetDocsumBlob(&buf, &buflen));
+}
+
+
+void
+MyApp::TestCompress()
+{
+ const char *buf;
+ uint32_t buflen;
+
+ search::docsummary::urlresult *res;
+ search::docsummary::GeneralResult *gres;
+
+ const char *lstrval = "string string string";
+ const char *ldatval = "data data data";
+
+ RTR(__LINE__, _packer.Init(2));
+ RTR(__LINE__, _packer.AddLongString(lstrval, strlen(lstrval)));
+ RTR(__LINE__, _packer.AddLongData(ldatval, strlen(ldatval)));
+ RTR(__LINE__, _packer.GetDocsumBlob(&buf, &buflen));
+
+ res = _config.Unpack(0, 0, 0, buf, buflen);
+ gres = res->IsGeneral() ? (search::docsummary::GeneralResult *) res : NULL;
+
+ RTR(__LINE__, gres != NULL);
+ TestStringValue(__LINE__, gres, "text", lstrval);
+ TestDataValue (__LINE__, gres, "data", ldatval);
+ RTR(__LINE__, (gres != NULL &&
+ gres->GetClass()->GetNumEntries() == 2));
+ RTR(__LINE__, (gres != NULL &&
+ gres->GetClass()->GetClassID() == 2));
+ delete res;
+}
+
+
+void
+MyApp::TestCompat()
+{
+ const char *buf;
+ uint32_t buflen;
+
+ search::docsummary::urlresult *res1;
+ search::docsummary::GeneralResult *gres1;
+
+ search::docsummary::urlresult *res2;
+ search::docsummary::GeneralResult *gres2;
+
+ const char *strval = "string string string string";
+ const char *datval = "data data data data";
+
+ RTR(__LINE__, _packer.Init(1));
+ RTR(__LINE__, _packer.AddData(strval, strlen(strval)));
+ RTR(__LINE__, _packer.AddString(datval, strlen(datval)));
+ RTR(__LINE__, _packer.GetDocsumBlob(&buf, &buflen));
+ res1 = _config.Unpack(0, 0, 0, buf, buflen);
+ gres1 = res1->IsGeneral() ? (search::docsummary::GeneralResult *) res1 : NULL;
+
+ RTR(__LINE__, _packer.Init(2));
+ RTR(__LINE__, _packer.AddLongData(strval, strlen(strval)));
+ RTR(__LINE__, _packer.AddLongString(datval, strlen(datval)));
+ RTR(__LINE__, _packer.GetDocsumBlob(&buf, &buflen));
+ res2 = _config.Unpack(0, 0, 0, buf, buflen);
+ gres2 = res2->IsGeneral() ? (search::docsummary::GeneralResult *) res2 : NULL;
+
+ RTR(__LINE__, gres1 != NULL);
+ RTR(__LINE__, gres2 != NULL);
+
+ TestStringValue(__LINE__, gres1, "text", strval);
+ TestDataValue (__LINE__, gres1, "data", datval);
+ TestFieldIndex (__LINE__, gres1, "text", 0);
+ TestFieldIndex (__LINE__, gres1, "data", 1);
+ RTR(__LINE__, (gres1 != NULL &&
+ gres1->GetClass()->GetNumEntries() == 2));
+
+ TestStringValue(__LINE__, gres2, "text", strval);
+ TestDataValue (__LINE__, gres2, "data", datval);
+ TestFieldIndex (__LINE__, gres2, "text", 0);
+ TestFieldIndex (__LINE__, gres2, "data", 1);
+ RTR(__LINE__, (gres2 != NULL &&
+ gres2->GetClass()->GetNumEntries() == 2));
+
+ RTR(__LINE__, (gres1 != NULL &&
+ gres1->GetClass()->GetClassID() == 1));
+ RTR(__LINE__, (gres2 != NULL &&
+ gres2->GetClass()->GetClassID() == 2));
+
+ RTR(__LINE__, (gres1 != NULL && gres2 != NULL &&
+ Equal(gres1, gres2)));
+
+ delete res1;
+ delete res2;
+}
+
+
+void
+MyApp::TestBasicInplace()
+{
+ const char *buf;
+ uint32_t buflen;
+
+ const search::docsummary::ResultClass *resClass;
+ search::docsummary::GeneralResult *gres;
+
+ uint32_t intval = 4;
+ uint16_t shortval = 2;
+ uint8_t byteval = 1;
+ float floatval = 4.5;
+ double doubleval = 8.75;
+ uint64_t int64val = 8;
+ const char *strval = "This is a string";
+ const char *datval = "This is data";
+ const char *lstrval = "This is a long string";
+ const char *ldatval = "This is long data";
+
+ RTR(__LINE__, _packer.Init(0));
+ RTR(__LINE__, _packer.AddInteger(intval));
+ RTR(__LINE__, _packer.AddShort(shortval));
+ RTR(__LINE__, _packer.AddByte(byteval));
+ RTR(__LINE__, _packer.AddFloat(floatval));
+ RTR(__LINE__, _packer.AddDouble(doubleval));
+ RTR(__LINE__, _packer.AddInt64(int64val));
+ RTR(__LINE__, _packer.AddString(strval, strlen(strval)));
+ RTR(__LINE__, _packer.AddData(datval, strlen(datval)));
+ RTR(__LINE__, _packer.AddLongString(lstrval, strlen(lstrval)));
+ RTR(__LINE__, _packer.AddLongData(ldatval, strlen(ldatval)));
+ RTR(__LINE__, _packer.GetDocsumBlob(&buf, &buflen));
+
+ resClass = _config.LookupResultClass(_config.GetClassID(buf, buflen));
+ if (resClass == NULL) {
+ gres = NULL;
+ } else {
+ DocsumStoreValue value(buf, buflen);
+ gres = new search::docsummary::GeneralResult(resClass, 0, 0, 0);
+ if (!gres->inplaceUnpack(value)) {
+ delete gres;
+ gres = NULL;
+ }
+ }
+
+ RTR(__LINE__, gres != NULL);
+ TestIntValue (__LINE__, gres, "integer", 4);
+ TestIntValue (__LINE__, gres, "short", 2);
+ TestIntValue (__LINE__, gres, "byte", 1);
+ TestDoubleValue(__LINE__, gres, "float", floatval);
+ TestDoubleValue(__LINE__, gres, "double", doubleval);
+ TestInt64Value (__LINE__, gres, "int64", int64val);
+ TestStringValue(__LINE__, gres, "string", strval);
+ TestDataValue (__LINE__, gres, "data", datval);
+ TestStringValue(__LINE__, gres, "longstring", lstrval);
+ TestDataValue (__LINE__, gres, "longdata", ldatval);
+ RTR(__LINE__, (gres != NULL &&
+ gres->GetClass()->GetNumEntries() == 10));
+ RTR(__LINE__, (gres != NULL &&
+ gres->GetClass()->GetClassID() == 0));
+ delete gres;
+}
+
+
+void
+MyApp::TestCompressInplace()
+{
+ const char *buf;
+ uint32_t buflen;
+
+ search::RawBuf field1(32768);
+ search::RawBuf field2(32768);
+ const search::docsummary::ResultClass *resClass;
+ search::docsummary::GeneralResult *gres;
+
+ const char *lstrval = "string string string";
+ const char *ldatval = "data data data";
+
+ RTR(__LINE__, _packer.Init(2));
+ RTR(__LINE__, _packer.AddLongString(lstrval, strlen(lstrval)));
+ RTR(__LINE__, _packer.AddLongData(ldatval, strlen(ldatval)));
+ RTR(__LINE__, _packer.GetDocsumBlob(&buf, &buflen));
+
+ resClass = _config.LookupResultClass(_config.GetClassID(buf, buflen));
+ if (resClass == NULL) {
+ gres = NULL;
+ } else {
+ DocsumStoreValue value(buf, buflen);
+ gres = new search::docsummary::GeneralResult(resClass, 0, 0, 0);
+ if (!gres->inplaceUnpack(value)) {
+ delete gres;
+ gres = NULL;
+ }
+ }
+
+ search::docsummary::ResEntry *e1 = (gres == NULL) ? NULL : gres->GetEntry("text");
+ search::docsummary::ResEntry *e2 = (gres == NULL) ? NULL : gres->GetEntry("data");
+
+ if (e1 != NULL)
+ e1->_extract_field(&field1);
+ if (e2 != NULL)
+ e2->_extract_field(&field2);
+
+ RTR(__LINE__, gres != NULL);
+ RTR(__LINE__, e1 != NULL);
+ RTR(__LINE__, e2 != NULL);
+ RTR(__LINE__, strcmp(field1.GetDrainPos(), lstrval) == 0);
+ RTR(__LINE__, strcmp(field2.GetDrainPos(), ldatval) == 0);
+ RTR(__LINE__, strlen(lstrval) == field1.GetUsedLen());
+ RTR(__LINE__, strlen(ldatval) == field2.GetUsedLen());
+ RTR(__LINE__, (gres != NULL &&
+ gres->GetClass()->GetNumEntries() == 2));
+ RTR(__LINE__, (gres != NULL &&
+ gres->GetClass()->GetClassID() == 2));
+ delete gres;
+}
+
+
+
+int
+MyApp::Main()
+{
+ _rc = true;
+ _cnt = 0;
+
+ search::docsummary::ResultClass *resClass;
+
+ resClass = _config.AddResultClass("c0", 0);
+ resClass->AddConfigEntry("integer", RES_INT);
+ resClass->AddConfigEntry("short", RES_SHORT);
+ resClass->AddConfigEntry("byte", RES_BYTE);
+ resClass->AddConfigEntry("float", RES_FLOAT);
+ resClass->AddConfigEntry("double", RES_DOUBLE);
+ resClass->AddConfigEntry("int64", RES_INT64);
+ resClass->AddConfigEntry("string", RES_STRING);
+ resClass->AddConfigEntry("data", RES_DATA);
+ resClass->AddConfigEntry("longstring", RES_LONG_STRING);
+ resClass->AddConfigEntry("longdata", RES_LONG_DATA);
+
+ resClass = _config.AddResultClass("c1", 1);
+ resClass->AddConfigEntry("text", RES_STRING);
+ resClass->AddConfigEntry("data", RES_DATA);
+
+ resClass = _config.AddResultClass("c2", 2);
+ resClass->AddConfigEntry("text", RES_LONG_STRING);
+ resClass->AddConfigEntry("data", RES_LONG_DATA);
+
+ TestBasic();
+ TestFailLong();
+ TestFailShort();
+ TestFailOrder();
+ TestCompress();
+ TestCompat();
+ TestBasicInplace();
+ TestCompressInplace();
+
+ LOG(info, "CONCLUSION: %s", (_rc) ? "SUCCESS" : "FAIL");
+ return (_rc ? 0 : 1);
+}
+
+
+int
+main(int argc, char **argv)
+{
+ MyApp myapp;
+ return myapp.Entry(argc, argv);
+}
diff --git a/searchsummary/src/tests/docsumformat/docsum-parse.cpp b/searchsummary/src/tests/docsumformat/docsum-parse.cpp
new file mode 100644
index 00000000000..5fa7009464c
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/docsum-parse.cpp
@@ -0,0 +1,201 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright (C) 2001-2003 Fast Search & Transfer ASA
+// Copyright (C) 2003 Overture Services Norway AS
+
+
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("docsum-parse");
+#include <vespa/fnet/frt/frt.h>
+#include <vespa/fastlib/io/bufferedfile.h>
+#include <vespa/searchsummary/docsummary/urlresult.h>
+#include <vespa/searchsummary/docsummary/resultconfig.h>
+
+
+// needed to resolve external symbol from httpd.h on AIX
+void FastS_block_usr2() {}
+
+
+class MyApp : public FastOS_Application
+{
+public:
+ bool Equal(search::docsummary::ResConfigEntry *a, search::docsummary::ResConfigEntry *b);
+ bool Equal(search::docsummary::ResultClass *a, search::docsummary::ResultClass *b);
+ bool Equal(search::docsummary::ResultConfig *a, search::docsummary::ResultConfig *b);
+ bool TestCorrect(const char *dirname, const char *filename);
+ bool TestIncorrect(const char *dirname, const char *filename);
+ int Main();
+};
+
+
+bool
+MyApp::Equal(search::docsummary::ResConfigEntry *a, search::docsummary::ResConfigEntry *b)
+{
+ return ((a->_type == b->_type)
+ && (strcmp(a->_bindname, b->_bindname) == 0));
+}
+
+
+bool
+MyApp::Equal(search::docsummary::ResultClass *a, search::docsummary::ResultClass *b)
+{
+ bool rc = true;
+
+ rc = rc && (a->GetNumEntries() == b->GetNumEntries());
+ rc = rc && (a->GetClassID() == b->GetClassID());
+ rc = rc && (strcmp(a->GetClassName(), b->GetClassName()) == 0);
+
+ for (uint32_t i = 0; rc && i < a->GetNumEntries(); i++) {
+ rc = rc && Equal(a->GetEntry(i), b->GetEntry(i));
+ }
+
+ return rc;
+}
+
+
+bool
+MyApp::Equal(search::docsummary::ResultConfig *a, search::docsummary::ResultConfig *b)
+{
+ bool rc = true;
+
+ search::docsummary::ResultClass *resClassA;
+ search::docsummary::ResultClass *resClassB;
+
+ rc = rc && (a->GetNumResultClasses() == b->GetNumResultClasses());
+
+ resClassA = a->GetResultClasses();
+ resClassB = b->GetResultClasses();
+
+ while(rc && resClassA != NULL && resClassB != NULL) {
+ rc = rc && Equal(resClassA, resClassB);
+ resClassA = resClassA->GetNextClass();
+ resClassB = resClassB->GetNextClass();
+ }
+ rc = rc && (resClassA == NULL);
+ rc = rc && (resClassB == NULL);
+
+ return rc;
+}
+
+
+bool
+MyApp::TestCorrect(const char *dirname, const char *filename)
+{
+ char str1[512]; // test input file
+ char str2[512]; // test output file
+ char str3[512]; // summary.cf verification file
+
+ search::docsummary::ResultConfig a;
+ search::docsummary::ResultConfig b;
+ search::docsummary::ResultConfig c;
+ search::docsummary::ResultConfig d;
+
+ sprintf(str1, "%s%s%s", dirname,
+ FastOS_FileInterface::GetPathSeparator(), filename);
+ sprintf(str2, "%s%sout.%s", dirname,
+ FastOS_FileInterface::GetPathSeparator(), filename);
+ sprintf(str3, "%s%sOK.%s", dirname,
+ FastOS_FileInterface::GetPathSeparator(), filename);
+
+ if (!a.ReadConfig(str1)) {
+ LOG(error, "could not read config from : %s", str1);
+ return false;
+ }
+
+ if (!a.WriteConfig(str2)) {
+ LOG(error, "could not write config to : %s", str2);
+ return false;
+ }
+
+ if (!b.ReadConfig(str2)) {
+ LOG(error, "could not read config from : %s", str2);
+ return false;
+ }
+
+ if (!c.ReadConfig(str3)) {
+ LOG(error, "could not read config from : %s", str3);
+ return false;
+ }
+
+ if (!Equal(&a, &b)) {
+ LOG(error, "%s and %s does not contain the same config", str1, str2);
+ return false;
+ }
+
+ if (!Equal(&a, &c)) {
+ LOG(error, "%s and %s does not contain the same config", str1, str3);
+ return false;
+ }
+
+ if (!Equal(&b, &c)) {
+ LOG(error, "%s and %s does not contain the same config", str2, str3);
+ return false;
+ }
+
+ FRT_RPCRequest *req = new FRT_RPCRequest();
+ assert(req != NULL);
+ c.GetConfig(req);
+ d.SetConfig(req);
+ if (!Equal(&c, &d)) {
+ LOG(error, "RPC get/set failed (%s)", str3);
+ req->SubRef();
+ return false;
+ }
+ req->SubRef();
+
+ return true;
+}
+
+
+bool
+MyApp::TestIncorrect(const char *dirname, const char *filename)
+{
+ char str[512];
+
+ sprintf(str, "%s%s%s", dirname,
+ FastOS_FileInterface::GetPathSeparator(), filename);
+
+ search::docsummary::ResultConfig resConfig;
+
+ if (resConfig.ReadConfig(str)) {
+ LOG(error, "'%s' did not give parse error", str);
+ return false;
+ }
+ return true;
+}
+
+
+int
+MyApp::Main()
+{
+ bool rc = true;
+
+ FastOS_DirectoryScan dirScan("parsetest");
+ LOG(info, "looking for input files in 'parsetest'...");
+ while (dirScan.ReadNext()) {
+ if (strncmp(dirScan.GetName(), "correct.", 8) == 0) {
+ if (TestCorrect("parsetest", dirScan.GetName())) {
+ LOG(info, "'%s' : positive test PASSED", dirScan.GetName());
+ } else {
+ LOG(error, "'%s' : positive test FAILED", dirScan.GetName());
+ rc = false;
+ }
+ } else if (strncmp(dirScan.GetName(), "incorrect.", 10) == 0) {
+ if (TestIncorrect("parsetest", dirScan.GetName())) {
+ LOG(info, "'%s' : negative test PASSED", dirScan.GetName());
+ } else {
+ LOG(error, "'%s' : negative test FAILED", dirScan.GetName());
+ rc = false;
+ }
+ }
+ }
+ return (rc ? 0 : 1);
+}
+
+
+int
+main(int argc, char **argv)
+{
+ MyApp myapp;
+ return myapp.Entry(argc, argv);
+}
diff --git a/searchsummary/src/tests/docsumformat/dotest.sh b/searchsummary/src/tests/docsumformat/dotest.sh
new file mode 100755
index 00000000000..64097b0061d
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/dotest.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+echo "running pack test..."
+./docsum-pack > packtest.out 2>&1
+res=$?
+if [ $res -eq 0 ]; then
+ echo "pack test PASSED"
+else
+ echo "pack test FAILED!"
+ echo "please check packtest.out"
+ exit 1
+fi
diff --git a/searchsummary/src/tests/docsumformat/parsetest/.gitignore b/searchsummary/src/tests/docsumformat/parsetest/.gitignore
new file mode 100644
index 00000000000..19815d313ff
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/parsetest/.gitignore
@@ -0,0 +1,2 @@
+*.out
+out.*
diff --git a/searchsummary/src/tests/docsumformat/parsetest/OK.correct.1 b/searchsummary/src/tests/docsumformat/parsetest/OK.correct.1
new file mode 100644
index 00000000000..8238b53f81c
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/parsetest/OK.correct.1
@@ -0,0 +1,17 @@
+idtype none
+
+class default id 0
+field URL type string
+field TITLE type string
+field TEASER type string
+field DSHOST type integer
+field DSKEY type integer
+field BYTES type integer
+field WORDS type integer
+field MODDATE type integer
+field CRAWLDATE type integer
+field LANG1 type byte
+field LANG2 type byte
+field LANG3 type byte
+field LANG4 type byte
+field CHARSET type integer
diff --git a/searchsummary/src/tests/docsumformat/parsetest/OK.correct.2 b/searchsummary/src/tests/docsumformat/parsetest/OK.correct.2
new file mode 100644
index 00000000000..8996c2dac4c
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/parsetest/OK.correct.2
@@ -0,0 +1,14 @@
+idtype byte
+
+class document id 1
+field title type string
+field teaser type string
+field url type string
+field date type integer
+
+class image id 2
+field title type string
+field date type integer
+field width type short
+field height type short
+field bitmaps type byte
diff --git a/searchsummary/src/tests/docsumformat/parsetest/OK.correct.3 b/searchsummary/src/tests/docsumformat/parsetest/OK.correct.3
new file mode 100644
index 00000000000..ae29fa40335
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/parsetest/OK.correct.3
@@ -0,0 +1,5 @@
+idtype none
+
+class default id 0
+field TITLE type string
+field DATE type integer
diff --git a/searchsummary/src/tests/docsumformat/parsetest/OK.correct.4 b/searchsummary/src/tests/docsumformat/parsetest/OK.correct.4
new file mode 100644
index 00000000000..8238b53f81c
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/parsetest/OK.correct.4
@@ -0,0 +1,17 @@
+idtype none
+
+class default id 0
+field URL type string
+field TITLE type string
+field TEASER type string
+field DSHOST type integer
+field DSKEY type integer
+field BYTES type integer
+field WORDS type integer
+field MODDATE type integer
+field CRAWLDATE type integer
+field LANG1 type byte
+field LANG2 type byte
+field LANG3 type byte
+field LANG4 type byte
+field CHARSET type integer
diff --git a/searchsummary/src/tests/docsumformat/parsetest/OK.correct.5 b/searchsummary/src/tests/docsumformat/parsetest/OK.correct.5
new file mode 100644
index 00000000000..6b6dc874a68
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/parsetest/OK.correct.5
@@ -0,0 +1,3 @@
+idtype byte
+
+class myclass id 42
diff --git a/searchsummary/src/tests/docsumformat/parsetest/OK.correct.6 b/searchsummary/src/tests/docsumformat/parsetest/OK.correct.6
new file mode 100644
index 00000000000..38416fdf45e
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/parsetest/OK.correct.6
@@ -0,0 +1,5 @@
+idtype none
+
+class default id 0
+field TEASER type longstring
+field DOCTEXT type longdata
diff --git a/searchsummary/src/tests/docsumformat/parsetest/OK.correct.7 b/searchsummary/src/tests/docsumformat/parsetest/OK.correct.7
new file mode 100644
index 00000000000..d1f17d25141
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/parsetest/OK.correct.7
@@ -0,0 +1,11 @@
+idtype short
+
+class class_1 id 1
+field title type string
+field rawteaser type data
+field doctext type longdata
+field dynteaser type longstring
+
+class class_2 id 2
+field title type string
+field rawteaser type longdata
diff --git a/searchsummary/src/tests/docsumformat/parsetest/OK.correct.8 b/searchsummary/src/tests/docsumformat/parsetest/OK.correct.8
new file mode 100644
index 00000000000..e929b872a05
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/parsetest/OK.correct.8
@@ -0,0 +1,7 @@
+idtype integer
+
+class class_50 id 50
+field title type data
+
+class class_100 id 100
+field title type string
diff --git a/searchsummary/src/tests/docsumformat/parsetest/OK.correct.9 b/searchsummary/src/tests/docsumformat/parsetest/OK.correct.9
new file mode 100644
index 00000000000..668505be77d
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/parsetest/OK.correct.9
@@ -0,0 +1,13 @@
+idtype none
+
+class default id 0
+field f0 type integer
+field f1 type short
+field f2 type byte
+field f3 type float
+field f4 type double
+field f5 type int64
+field f6 type string
+field f7 type data
+field f8 type longstring
+field f9 type longdata
diff --git a/searchsummary/src/tests/docsumformat/parsetest/README b/searchsummary/src/tests/docsumformat/parsetest/README
new file mode 100644
index 00000000000..2de83e1b0cb
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/parsetest/README
@@ -0,0 +1,24 @@
+The files in this directory are used to test the parsing of document
+summary format config files. The files are named by the following
+rules:
+
+incorrect.* : these files are incorrect; loading them should fail.
+
+correct.* : these files are correct; loading them should succeed.
+
+OK.correct.* : these files contain normalized config on 'summary.cf'
+ format that matches the config contained in
+ the corresponding 'correct.*' files.
+
+The 'docsum-parse' program loops through all files in this
+directory. For each file that has a name beginning with 'incorrect.',
+it checks that loading document summary format config from it
+fails. For each file that has a name beginning with 'correct.', it
+checks that document summary format config may be read from the
+file. It then writes the config back to a file named 'out.correct.<>',
+reads the newly generated file back in, reads the corresponding
+'OK.correct.<>' file and checks that all 3 configs are exactly the
+same.
+
+New tests may be added simply be adding files conforming to the above
+rules to this directory.
diff --git a/searchsummary/src/tests/docsumformat/parsetest/correct.1 b/searchsummary/src/tests/docsumformat/parsetest/correct.1
new file mode 100644
index 00000000000..0b3d57b7f9c
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/parsetest/correct.1
@@ -0,0 +1,14 @@
+STRING URL
+STRING TITLE
+STRING TEASER
+INT DSHOST
+INT DSKEY
+INT BYTES
+INT WORDS
+INT MODDATE
+INT CRAWLDATE
+BYTE LANG1
+BYTE LANG2
+BYTE LANG3
+BYTE LANG4
+INT CHARSET
diff --git a/searchsummary/src/tests/docsumformat/parsetest/correct.2 b/searchsummary/src/tests/docsumformat/parsetest/correct.2
new file mode 100644
index 00000000000..8996c2dac4c
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/parsetest/correct.2
@@ -0,0 +1,14 @@
+idtype byte
+
+class document id 1
+field title type string
+field teaser type string
+field url type string
+field date type integer
+
+class image id 2
+field title type string
+field date type integer
+field width type short
+field height type short
+field bitmaps type byte
diff --git a/searchsummary/src/tests/docsumformat/parsetest/correct.3 b/searchsummary/src/tests/docsumformat/parsetest/correct.3
new file mode 100644
index 00000000000..8a16e3f3fd1
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/parsetest/correct.3
@@ -0,0 +1,3 @@
+idtype byte
+STRING TITLE
+INT DATE
diff --git a/searchsummary/src/tests/docsumformat/parsetest/correct.4 b/searchsummary/src/tests/docsumformat/parsetest/correct.4
new file mode 100644
index 00000000000..8238b53f81c
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/parsetest/correct.4
@@ -0,0 +1,17 @@
+idtype none
+
+class default id 0
+field URL type string
+field TITLE type string
+field TEASER type string
+field DSHOST type integer
+field DSKEY type integer
+field BYTES type integer
+field WORDS type integer
+field MODDATE type integer
+field CRAWLDATE type integer
+field LANG1 type byte
+field LANG2 type byte
+field LANG3 type byte
+field LANG4 type byte
+field CHARSET type integer
diff --git a/searchsummary/src/tests/docsumformat/parsetest/correct.5 b/searchsummary/src/tests/docsumformat/parsetest/correct.5
new file mode 100644
index 00000000000..d179537e208
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/parsetest/correct.5
@@ -0,0 +1,4 @@
+idtype byte
+class myclass id 42
+STRING TITLE
+INT DATE
diff --git a/searchsummary/src/tests/docsumformat/parsetest/correct.6 b/searchsummary/src/tests/docsumformat/parsetest/correct.6
new file mode 100644
index 00000000000..a4e41ec72d8
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/parsetest/correct.6
@@ -0,0 +1,2 @@
+LONGSTRING TEASER
+LONGDATA DOCTEXT
diff --git a/searchsummary/src/tests/docsumformat/parsetest/correct.7 b/searchsummary/src/tests/docsumformat/parsetest/correct.7
new file mode 100644
index 00000000000..d1f17d25141
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/parsetest/correct.7
@@ -0,0 +1,11 @@
+idtype short
+
+class class_1 id 1
+field title type string
+field rawteaser type data
+field doctext type longdata
+field dynteaser type longstring
+
+class class_2 id 2
+field title type string
+field rawteaser type longdata
diff --git a/searchsummary/src/tests/docsumformat/parsetest/correct.8 b/searchsummary/src/tests/docsumformat/parsetest/correct.8
new file mode 100644
index 00000000000..e929b872a05
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/parsetest/correct.8
@@ -0,0 +1,7 @@
+idtype integer
+
+class class_50 id 50
+field title type data
+
+class class_100 id 100
+field title type string
diff --git a/searchsummary/src/tests/docsumformat/parsetest/correct.9 b/searchsummary/src/tests/docsumformat/parsetest/correct.9
new file mode 100644
index 00000000000..668505be77d
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/parsetest/correct.9
@@ -0,0 +1,13 @@
+idtype none
+
+class default id 0
+field f0 type integer
+field f1 type short
+field f2 type byte
+field f3 type float
+field f4 type double
+field f5 type int64
+field f6 type string
+field f7 type data
+field f8 type longstring
+field f9 type longdata
diff --git a/searchsummary/src/tests/docsumformat/parsetest/incorrect.1 b/searchsummary/src/tests/docsumformat/parsetest/incorrect.1
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/parsetest/incorrect.1
diff --git a/searchsummary/src/tests/docsumformat/parsetest/incorrect.2 b/searchsummary/src/tests/docsumformat/parsetest/incorrect.2
new file mode 100644
index 00000000000..600380f898d
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/parsetest/incorrect.2
@@ -0,0 +1,14 @@
+idtype int
+
+class document id 1
+field title type string
+field teaser type string
+field url type string
+field date type integer
+
+class image id 2
+field title type string
+field date type integer
+field width type short
+field height type short
+field bitmaps type byte
diff --git a/searchsummary/src/tests/docsumformat/parsetest/incorrect.3 b/searchsummary/src/tests/docsumformat/parsetest/incorrect.3
new file mode 100644
index 00000000000..35d46b73f96
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/parsetest/incorrect.3
@@ -0,0 +1,14 @@
+idtype byte
+
+class document id 1
+field title type string
+field teaser type string
+field url type string
+field date type integer
+
+class image id 1
+field title type string
+field date type integer
+field width type short
+field height type short
+field bitmaps type byte
diff --git a/searchsummary/src/tests/docsumformat/parsetest/incorrect.4 b/searchsummary/src/tests/docsumformat/parsetest/incorrect.4
new file mode 100644
index 00000000000..f50c143b4be
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/parsetest/incorrect.4
@@ -0,0 +1,14 @@
+idtype byte
+
+class document id 1
+field title type string
+field teaser type string
+field url type string
+field date type int
+
+class image id 2
+field title type string
+field date type integer
+field width type short
+field height type short
+field bitmaps type byte
diff --git a/searchsummary/src/tests/docsumformat/parsetest/incorrect.5 b/searchsummary/src/tests/docsumformat/parsetest/incorrect.5
new file mode 100644
index 00000000000..6579c30a29d
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/parsetest/incorrect.5
@@ -0,0 +1,14 @@
+idtype byte
+
+class document id 1
+field title type string
+field teaser type string
+field url type string
+field url type integer
+
+class image id 2
+field title type string
+field date type integer
+field width type short
+field height type short
+field bitmaps type byte
diff --git a/searchsummary/src/tests/docsumformat/parsetest/incorrect.6 b/searchsummary/src/tests/docsumformat/parsetest/incorrect.6
new file mode 100644
index 00000000000..2ce1ab9507e
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/parsetest/incorrect.6
@@ -0,0 +1,13 @@
+idtype byte
+
+field title type string
+field teaser type string
+field url type string
+field date type integer
+
+class image id 2
+field title type string
+field date type integer
+field width type short
+field height type short
+field bitmaps type byte
diff --git a/searchsummary/src/tests/docsumformat/parsetest/incorrect.7 b/searchsummary/src/tests/docsumformat/parsetest/incorrect.7
new file mode 100644
index 00000000000..e51bb1d2d48
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/parsetest/incorrect.7
@@ -0,0 +1,14 @@
+STRING URL
+STRING TITLE
+STRING TITLE
+INT DSHOST
+INT DSKEY
+INT BYTES
+INT WORDS
+INT MODDATE
+INT CRAWLDATE
+BYTE LANG1
+BYTE LANG2
+BYTE LANG3
+BYTE LANG4
+INT CHARSET
diff --git a/searchsummary/src/tests/docsumformat/parsetest/incorrect.8 b/searchsummary/src/tests/docsumformat/parsetest/incorrect.8
new file mode 100644
index 00000000000..7639557b734
--- /dev/null
+++ b/searchsummary/src/tests/docsumformat/parsetest/incorrect.8
@@ -0,0 +1,2 @@
+idtype byte
+STRING TITLE
diff --git a/searchsummary/src/tests/docsummary/.gitignore b/searchsummary/src/tests/docsummary/.gitignore
new file mode 100644
index 00000000000..3f0be20ca74
--- /dev/null
+++ b/searchsummary/src/tests/docsummary/.gitignore
@@ -0,0 +1,4 @@
+*_test
+.depend
+Makefile
+searchsummary_positionsdfw_test_app
diff --git a/searchsummary/src/tests/docsummary/CMakeLists.txt b/searchsummary/src/tests/docsummary/CMakeLists.txt
new file mode 100644
index 00000000000..7eaa78d923e
--- /dev/null
+++ b/searchsummary/src/tests/docsummary/CMakeLists.txt
@@ -0,0 +1,8 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(searchsummary_positionsdfw_test_app
+ SOURCES
+ positionsdfw_test.cpp
+ DEPENDS
+ searchsummary
+)
+vespa_add_test(NAME searchsummary_positionsdfw_test_app COMMAND searchsummary_positionsdfw_test_app)
diff --git a/searchsummary/src/tests/docsummary/positionsdfw_test.cpp b/searchsummary/src/tests/docsummary/positionsdfw_test.cpp
new file mode 100644
index 00000000000..59f91e12ef7
--- /dev/null
+++ b/searchsummary/src/tests/docsummary/positionsdfw_test.cpp
@@ -0,0 +1,142 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Unit tests for positionsdfw.
+
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("positionsdfw_test");
+
+#include <vespa/searchlib/attribute/extendableattributes.h>
+#include <vespa/searchlib/attribute/iattributemanager.h>
+#include <vespa/searchsummary/docsummary/docsumfieldwriter.h>
+#include <vespa/searchsummary/docsummary/positionsdfw.h>
+#include <vespa/searchlib/util/rawbuf.h>
+#include <vespa/vespalib/testkit/testapp.h>
+
+using search::RawBuf;
+using search::IAttributeManager;
+using search::SingleInt64ExtAttribute;
+using search::attribute::IAttributeContext;
+using search::attribute::IAttributeVector;
+using vespalib::string;
+using std::vector;
+
+namespace search {
+namespace docsummary {
+
+namespace {
+
+class Test : public vespalib::TestApp {
+ void requireThat2DPositionFieldIsWritten();
+
+public:
+ int Main();
+};
+
+int
+Test::Main()
+{
+ TEST_INIT("positionsdfw_test");
+
+ TEST_DO(requireThat2DPositionFieldIsWritten());
+
+ TEST_DONE();
+}
+
+struct MyEnvironment : IDocsumEnvironment {
+ IAttributeManager *attribute_man;
+
+ MyEnvironment() : attribute_man(0) {}
+
+ virtual IAttributeManager *getAttributeManager() { return attribute_man; }
+ virtual string lookupIndex(const string &s) const { return s; }
+ virtual juniper::Juniper *getJuniper() { return 0; }
+};
+
+class MyAttributeContext : public IAttributeContext {
+ const IAttributeVector &_attr;
+public:
+ MyAttributeContext(const IAttributeVector &attr) : _attr(attr) {}
+ virtual const IAttributeVector *getAttribute(const string &) const {
+ return &_attr;
+ }
+ virtual const IAttributeVector *getAttributeStableEnum(
+ const string &) const { abort(); }
+ virtual void getAttributeList(vector<const IAttributeVector *> &) const
+ { abort(); }
+};
+
+class MyAttributeManager : public IAttributeManager {
+ const IAttributeVector &_attr;
+public:
+
+ MyAttributeManager(const IAttributeVector &attr) : _attr(attr) {}
+ virtual AttributeGuard::UP getAttribute(const string &) const {
+ abort();
+ }
+ virtual AttributeGuard::UP getAttributeStableEnum(const string &) const {
+ abort();
+ }
+ virtual void getAttributeList(vector<AttributeGuard> &) const {
+ abort();
+ }
+ virtual IAttributeContext::UP createContext() const {
+ return IAttributeContext::UP(new MyAttributeContext(_attr));
+ }
+};
+
+struct MyGetDocsumsStateCallback : GetDocsumsStateCallback {
+ virtual void FillSummaryFeatures(GetDocsumsState *, IDocsumEnvironment *) {}
+ virtual void FillRankFeatures(GetDocsumsState *, IDocsumEnvironment *) {}
+ virtual void ParseLocation(GetDocsumsState *) {}
+};
+
+template <typename AttrType>
+void checkWritePositionField(Test &test, AttrType &attr,
+ uint32_t doc_id, const string &expected) {
+ for (AttributeVector::DocId i = 0; i < doc_id + 1; ) {
+ attr.addDoc(i);
+ if (i == 007) {
+ attr.add((int64_t) -1);
+ } else if (i == 0x42) {
+ attr.add(0xAAAAaaaaAAAAaaaa);
+ } else if (i == 0x17) {
+ attr.add(0x5555aaaa5555aaab);
+ } else if (i == 42) {
+ attr.add(0x8000000000000000);
+ } else {
+ attr.add(i); // value = docid
+ }
+ }
+
+ MyAttributeManager attribute_man(attr);
+ PositionsDFW::UP writer =
+ createPositionsDFW(attr.getName().c_str(), &attribute_man);
+ ASSERT_TRUE(writer.get());
+ ResType res_type = RES_LONG_STRING;
+ RawBuf target(1024);
+ MyGetDocsumsStateCallback callback;
+ GetDocsumsState state(callback);
+ state._attributes.push_back(&attr);
+
+ writer->WriteField(doc_id, 0, &state, res_type, &target);
+
+ test.EXPECT_EQUAL(expected.size(), *(const uint32_t *)(target.GetDrainPos()));
+ const char *p = target.GetDrainPos() + 4;
+ test.EXPECT_EQUAL(expected, string(p, p + expected.size()));
+}
+
+void Test::requireThat2DPositionFieldIsWritten() {
+ SingleInt64ExtAttribute attr("foo");
+ checkWritePositionField(*this, attr, 0x3e, "<position x=\"6\" y=\"7\" latlong=\"N0.000007;E0.000006\" />");
+ checkWritePositionField(*this, attr, 007, "<position x=\"-1\" y=\"-1\" latlong=\"S0.000001;W0.000001\" />");
+ checkWritePositionField(*this, attr, 0x42, "<position x=\"0\" y=\"-1\" latlong=\"S0.000001;E0.000000\" />");
+ checkWritePositionField(*this, attr, 0x17, "<position x=\"-16711935\" y=\"16711935\" latlong=\"N16.711935;W16.711935\" />");
+ checkWritePositionField(*this, attr, 42, "");
+
+}
+
+} // namespace
+} // namespace docsummary
+} // namespace search
+
+TEST_APPHOOK(search::docsummary::Test);
diff --git a/searchsummary/src/tests/docsummary/slime_summary/.gitignore b/searchsummary/src/tests/docsummary/slime_summary/.gitignore
new file mode 100644
index 00000000000..1df864db333
--- /dev/null
+++ b/searchsummary/src/tests/docsummary/slime_summary/.gitignore
@@ -0,0 +1 @@
+searchsummary_slime_summary_test_app
diff --git a/searchsummary/src/tests/docsummary/slime_summary/CMakeLists.txt b/searchsummary/src/tests/docsummary/slime_summary/CMakeLists.txt
new file mode 100644
index 00000000000..a2bd3bbc610
--- /dev/null
+++ b/searchsummary/src/tests/docsummary/slime_summary/CMakeLists.txt
@@ -0,0 +1,8 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(searchsummary_slime_summary_test_app
+ SOURCES
+ slime_summary_test.cpp
+ DEPENDS
+ searchsummary
+)
+vespa_add_test(NAME searchsummary_slime_summary_test_app COMMAND searchsummary_slime_summary_test_app)
diff --git a/searchsummary/src/tests/docsummary/slime_summary/FILES b/searchsummary/src/tests/docsummary/slime_summary/FILES
new file mode 100644
index 00000000000..6d3a81d3ffc
--- /dev/null
+++ b/searchsummary/src/tests/docsummary/slime_summary/FILES
@@ -0,0 +1 @@
+slime_summary_test.cpp
diff --git a/searchsummary/src/tests/docsummary/slime_summary/slime_summary_test.cpp b/searchsummary/src/tests/docsummary/slime_summary/slime_summary_test.cpp
new file mode 100644
index 00000000000..6509491d0ac
--- /dev/null
+++ b/searchsummary/src/tests/docsummary/slime_summary/slime_summary_test.cpp
@@ -0,0 +1,125 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/test_kit.h>
+#include <vespa/searchsummary/docsummary/docsumwriter.h>
+#include <vespa/searchsummary/docsummary/resultpacker.h>
+#include <vespa/searchlib/util/rawbuf.h>
+#include <vespa/vespalib/data/slime/slime.h>
+#include <vespa/vespalib/data/slime/simple_buffer.h>
+#include <vespa/vespalib/data/slime/json_format.h>
+#include <vespa/vespalib/data/slime/binary_format.h>
+#include <vespa/searchlib/util/slime_output_raw_buf_adapter.h>
+
+using namespace vespalib::slime::convenience;
+using namespace search::docsummary;
+
+namespace {
+
+struct FieldBlock {
+ Slime slime;
+ search::RawBuf binary;
+
+ explicit FieldBlock(const vespalib::string &jsonInput)
+ : slime(), binary(1024)
+ {
+ size_t used = vespalib::slime::JsonFormat::decode(jsonInput, slime);
+ EXPECT_EQUAL(jsonInput.size(), used);
+ search::SlimeOutputRawBufAdapter adapter(binary);
+ vespalib::slime::BinaryFormat::encode(slime, adapter);
+ }
+ const char *data() const { return binary.GetDrainPos(); }
+ size_t dataLen() const { return binary.GetUsedLen(); }
+};
+
+struct DocsumFixture : IDocsumStore, GetDocsumsStateCallback {
+ std::unique_ptr<DynamicDocsumWriter> writer;
+ std::unique_ptr<ResultPacker> packer;
+ GetDocsumsState state;
+ DocsumFixture() : writer(), packer(), state(*this) {
+ ResultConfig *config = new ResultConfig();
+ ResultClass *cfg = config->AddResultClass("default", 0);
+ EXPECT_TRUE(cfg != 0);
+ EXPECT_TRUE(cfg->AddConfigEntry("int_field", RES_INT));
+ EXPECT_TRUE(cfg->AddConfigEntry("short_field", RES_SHORT));
+ EXPECT_TRUE(cfg->AddConfigEntry("byte_field", RES_BYTE));
+ EXPECT_TRUE(cfg->AddConfigEntry("float_field", RES_FLOAT));
+ EXPECT_TRUE(cfg->AddConfigEntry("double_field", RES_DOUBLE));
+ EXPECT_TRUE(cfg->AddConfigEntry("int64_field", RES_INT64));
+ EXPECT_TRUE(cfg->AddConfigEntry("string_field", RES_STRING));
+ EXPECT_TRUE(cfg->AddConfigEntry("data_field", RES_DATA));
+ EXPECT_TRUE(cfg->AddConfigEntry("longstring_field", RES_LONG_STRING));
+ EXPECT_TRUE(cfg->AddConfigEntry("longdata_field", RES_LONG_DATA));
+ EXPECT_TRUE(cfg->AddConfigEntry("xmlstring_field", RES_XMLSTRING));
+ EXPECT_TRUE(cfg->AddConfigEntry("jsonstring_field", RES_JSONSTRING));
+ EXPECT_TRUE(cfg->AddConfigEntry("bad_jsonstring_field", RES_JSONSTRING));
+ config->CreateEnumMaps();
+ writer.reset(new DynamicDocsumWriter(config, 0));
+ packer.reset(new ResultPacker(writer->GetResultConfig()));
+ state._args.setFlags(search::fs4transport::GDFLAG_ALLOW_SLIME);
+ }
+ void getDocsum(Slime &slime) {
+ uint32_t classId;
+ search::RawBuf buf(4096);
+ writer->WriteDocsum(1u, &state, this, &buf);
+ ASSERT_GREATER(buf.GetUsedLen(), sizeof(classId));
+ memcpy(&classId, buf.GetDrainPos(), sizeof(classId));
+ buf.Drain(sizeof(classId));
+ EXPECT_EQUAL(classId, ::search::fs4transport::SLIME_MAGIC_ID);
+ EXPECT_GREATER(vespalib::slime::BinaryFormat
+ ::decode(Memory(buf.GetDrainPos(), buf.GetUsedLen()), slime), 0u);
+ }
+ virtual uint32_t getNumDocs() { return 2; }
+ virtual DocsumStoreValue getMappedDocsum(uint32_t docid, bool useSlimeInsideFields) {
+ EXPECT_EQUAL(true, useSlimeInsideFields);
+ EXPECT_EQUAL(1u, docid);
+ EXPECT_TRUE(packer->Init(0));
+ EXPECT_TRUE(packer->AddInteger(4));
+ EXPECT_TRUE(packer->AddShort(2));
+ EXPECT_TRUE(packer->AddByte(1));
+ EXPECT_TRUE(packer->AddFloat(4.5));
+ EXPECT_TRUE(packer->AddDouble(8.75));
+ EXPECT_TRUE(packer->AddInt64(8));
+ EXPECT_TRUE(packer->AddString( "string",
+ strlen("string")));
+ EXPECT_TRUE(packer->AddData( "data",
+ strlen("data")));
+ EXPECT_TRUE(packer->AddLongString( "long_string",
+ strlen("long_string")));
+ EXPECT_TRUE(packer->AddLongData( "long_data",
+ strlen("long_data")));
+ EXPECT_TRUE(packer->AddLongString( "xml_string",
+ strlen("xml_string")));
+ FieldBlock jsf1("{foo:1, bar:2}");
+ EXPECT_TRUE(packer->AddLongData(jsf1.data(), jsf1.dataLen()));
+ EXPECT_TRUE(packer->AddLongString("abc", 3));
+ const char *buf;
+ uint32_t len;
+ EXPECT_TRUE(packer->GetDocsumBlob(&buf, &len));
+ return DocsumStoreValue(buf, len);
+ }
+ uint32_t getSummaryClassId() const override { return 0; }
+ virtual void FillSummaryFeatures(GetDocsumsState *, IDocsumEnvironment *) {}
+ virtual void FillRankFeatures(GetDocsumsState *, IDocsumEnvironment *) {}
+ virtual void ParseLocation(GetDocsumsState *) {}
+};
+
+} // namespace <unnamed>
+
+TEST_FF("require that docsum can be written as slime", DocsumFixture(), Slime()) {
+ f1.getDocsum(f2);
+ EXPECT_EQUAL(f2.get()["int_field"].asLong(), 4u);
+ EXPECT_EQUAL(f2.get()["short_field"].asLong(), 2u);
+ EXPECT_EQUAL(f2.get()["byte_field"].asLong(), 1u);
+ EXPECT_EQUAL(f2.get()["float_field"].asDouble(), 4.5);
+ EXPECT_EQUAL(f2.get()["double_field"].asDouble(), 8.75);
+ EXPECT_EQUAL(f2.get()["int64_field"].asLong(), 8u);
+ EXPECT_EQUAL(f2.get()["string_field"].asString().make_string(), std::string("string"));
+ EXPECT_EQUAL(f2.get()["data_field"].asData().make_string(), std::string("data"));
+ EXPECT_EQUAL(f2.get()["longstring_field"].asString().make_string(), std::string("long_string"));
+ EXPECT_EQUAL(f2.get()["longdata_field"].asData().make_string(), std::string("long_data"));
+ EXPECT_EQUAL(f2.get()["xmlstring_field"].asString().make_string(), std::string("xml_string"));
+ EXPECT_EQUAL(f2.get()["jsonstring_field"]["foo"].asLong(), 1u);
+ EXPECT_EQUAL(f2.get()["jsonstring_field"]["bar"].asLong(), 2u);
+ EXPECT_EQUAL(f2.get()["bad_jsonstring_field"].type().getId(), 0u);
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/searchsummary/src/tests/extractkeywords/.gitignore b/searchsummary/src/tests/extractkeywords/.gitignore
new file mode 100644
index 00000000000..1b50b24b284
--- /dev/null
+++ b/searchsummary/src/tests/extractkeywords/.gitignore
@@ -0,0 +1,7 @@
+*.core
+.depend
+Makefile
+core
+core.*
+extractkeywordstest
+searchsummary_extractkeywordstest_app
diff --git a/searchsummary/src/tests/extractkeywords/CMakeLists.txt b/searchsummary/src/tests/extractkeywords/CMakeLists.txt
new file mode 100644
index 00000000000..d726ffe794c
--- /dev/null
+++ b/searchsummary/src/tests/extractkeywords/CMakeLists.txt
@@ -0,0 +1,8 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(searchsummary_extractkeywordstest_app
+ SOURCES
+ extractkeywordstest.cpp
+ DEPENDS
+ searchsummary
+)
+vespa_add_test(NAME searchsummary_extractkeywordstest_app COMMAND sh runtests.sh)
diff --git a/searchsummary/src/tests/extractkeywords/extractkeywordstest.cpp b/searchsummary/src/tests/extractkeywords/extractkeywordstest.cpp
new file mode 100644
index 00000000000..59d949f40ca
--- /dev/null
+++ b/searchsummary/src/tests/extractkeywords/extractkeywordstest.cpp
@@ -0,0 +1,295 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright (C) 2001-2003 Fast Search & Transfer ASA
+// Copyright (C) 2003 Overture Services Norway AS
+
+#include <vespa/fastos/fastos.h>
+#include <vespa/searchsummary/docsummary/keywordextractor.h>
+#include <vespa/searchlib/parsequery/simplequerystack.h>
+#include <vespa/searchlib/util/rawbuf.h>
+#include "extractkeywordstest.h"
+
+#define NUMTESTS 5
+
+int
+ExtractKeywordsTest::Main()
+{
+ int doTest[NUMTESTS];
+ int low, high, accnum, num;
+ int indicator;
+ bool verify = false;
+ int multiplier = 1;
+ bool failed = false;
+
+ if (_argc == 1)
+ Usage(_argv[0]);
+
+ // default initialize to not run any tests.
+ for (int n = 0; n < NUMTESTS; n++)
+ doTest[n] = 0;
+
+ // parse the command line arguments
+ for (int i = 1; i < _argc; i++) {
+ low = 0;
+ high = NUMTESTS - 1;
+ char *p = _argv[i];
+
+ // Check if a multiplier is specified
+ if (*p == '*') {
+ p++;
+ accnum = 0;
+ while (*p != '\0') {
+ num = *p - '0';
+ accnum = accnum * 10 + num;
+ p++;
+ }
+ multiplier = accnum;
+ continue;
+ }
+
+ // Default is to run the tests specified, unless the first char is '/'
+ indicator = 1;
+ if (*p == '/') {
+ p++;
+ indicator = 0;
+ }
+
+ // Find the first number
+ accnum = 0;
+ while (*p != '-' && *p != '\0') {
+ num = *p - '0';
+ accnum = accnum * 10 + num;
+ p++;
+ }
+ if (accnum >= NUMTESTS)
+ continue;
+ low = accnum;
+ // Check for range operator
+ if (*p == '-') {
+ p++;
+ // Find the second number
+ accnum = 0;
+ while (*p != '\0') {
+ num = *p - '0';
+ accnum = accnum * 10 + num;
+ p++;
+ }
+ if (accnum > 0)
+ high = accnum < NUMTESTS ? accnum : NUMTESTS-1;
+ } else
+ high = low;
+
+ // Indicate the runrequest for the desired range.
+ for (int j = low; j <= high; j++)
+ doTest[j] = indicator;
+ }
+
+ // Remove unused tests.
+ // doTest[1] = 0;
+
+ // Remember time
+ if (multiplier > 1) {
+ printf("Running all tests %d times.\n", multiplier);
+ verify = false;
+ } else {
+ verify = true;
+ }
+
+ int testCnt = 0;
+
+ // init keyword extractor
+ _extractor = new search::docsummary::KeywordExtractor(NULL);
+ _extractor->AddLegalIndexSpec("*");
+
+ FastOS_Time timer;
+ timer.SetNow();
+
+ // Actually run the tests that we wanted.
+ for (int j = 0; j < multiplier; j++)
+ for (int k = 0; k < NUMTESTS; k++)
+ if (doTest[k] == 1) {
+ if (!RunTest(k, verify))
+ failed = true;
+ testCnt++;
+ }
+
+ // Print time taken
+ double timeTaken = timer.MilliSecsToNow();
+
+ printf("Time taken : %f ms\n", timeTaken);
+ printf("Number of tests run: %d\n", testCnt);
+ double avgTestPrMSec = static_cast<double>(testCnt) / timeTaken;
+ printf("Tests pr Sec: %f\n", avgTestPrMSec * 1000.0);
+
+ delete _extractor;
+ _extractor = NULL;
+
+ return failed ? 1 : 0;
+}
+
+bool
+ExtractKeywordsTest::ShowResult(int testNo,
+ const char *actual, const char *correct)
+{
+ const char *act_word = actual;
+ const char *cor_word = correct;
+ printf("%03d: ", testNo);
+
+ while (*act_word != '\0') {
+ if (strcmp(act_word, cor_word) != 0) {
+ printf("fail. Keywords differ for act: %s, corr: %s\n",
+ act_word, cor_word);
+ return false;
+ } else {
+ act_word += strlen(act_word) + 1;
+ cor_word += strlen(cor_word) + 1;
+ }
+ }
+ if (*cor_word != '\0') {
+ printf("fail. actual list shorter than correct at %s\n", cor_word);
+ return false;
+ }
+ printf("ok\n");
+ return true;
+}
+
+/**
+ *
+ * @param testno The test to run.
+ * @param verify Verify the result of the test.
+ */
+bool
+ExtractKeywordsTest::RunTest(int testno, bool verify)
+{
+ search::SimpleQueryStack stack;
+ search::RawBuf buf(32768);
+ const char *correct = NULL;
+ const char *keywords = NULL;
+
+ switch (testno) {
+ case 0:
+ {
+ // Simple term query
+ stack.Push(new search::ParseItem(search::ParseItem::ITEM_TERM, "foobar"));
+
+ stack.AppendBuffer(&buf);
+ keywords = _extractor->ExtractKeywords(vespalib::stringref(buf.GetDrainPos(), buf.GetUsedLen()));
+ correct = "foobar\0\0";
+
+ if (verify) ShowResult(testno, keywords, correct);
+ free(const_cast<char *>(keywords));
+ break;
+ }
+
+ case 1:
+ {
+ // multi term query
+ stack.Push(new search::ParseItem(search::ParseItem::ITEM_TERM, "foobar"));
+ stack.Push(new search::ParseItem(search::ParseItem::ITEM_TERM, "foo"));
+ stack.Push(new search::ParseItem(search::ParseItem::ITEM_TERM, "bar"));
+ stack.Push(new search::ParseItem(search::ParseItem::ITEM_OR, 3));
+
+ stack.AppendBuffer(&buf);
+ keywords = _extractor->ExtractKeywords(vespalib::stringref(buf.GetDrainPos(), buf.GetUsedLen()));
+ correct = "bar\0foo\0foobar\0\0";
+
+ if (verify) ShowResult(testno, keywords, correct);
+ free(const_cast<char *>(keywords));
+ break;
+ }
+
+ case 2:
+ {
+ // phrase term query
+ stack.Push(new search::ParseItem(search::ParseItem::ITEM_TERM, "foobar"));
+ stack.Push(new search::ParseItem(search::ParseItem::ITEM_TERM, "foo"));
+ stack.Push(new search::ParseItem(search::ParseItem::ITEM_TERM, "bar"));
+ stack.Push(new search::ParseItem(search::ParseItem::ITEM_PHRASE, 3));
+
+ stack.AppendBuffer(&buf);
+ keywords = _extractor->ExtractKeywords(vespalib::stringref(buf.GetDrainPos(), buf.GetUsedLen()));
+ correct = "bar foo foobar\0\0";
+
+ if (verify) ShowResult(testno, keywords, correct);
+ free(const_cast<char *>(keywords));
+ break;
+ }
+
+ case 3:
+ {
+ // multiple phrase and term query
+ stack.Push(new search::ParseItem(search::ParseItem::ITEM_TERM, "xyzzy"));
+ stack.Push(new search::ParseItem(search::ParseItem::ITEM_TERM, "xyz"));
+ stack.Push(new search::ParseItem(search::ParseItem::ITEM_PHRASE, 2));
+ stack.Push(new search::ParseItem(search::ParseItem::ITEM_TERM, "foobar"));
+ stack.Push(new search::ParseItem(search::ParseItem::ITEM_TERM, "foo"));
+ stack.Push(new search::ParseItem(search::ParseItem::ITEM_TERM, "bar"));
+ stack.Push(new search::ParseItem(search::ParseItem::ITEM_PHRASE, 3));
+ stack.Push(new search::ParseItem(search::ParseItem::ITEM_TERM, "baz"));
+ stack.Push(new search::ParseItem(search::ParseItem::ITEM_TERM, "zog"));
+ stack.Push(new search::ParseItem(search::ParseItem::ITEM_AND, 3));
+
+ stack.AppendBuffer(&buf);
+ keywords = _extractor->ExtractKeywords(vespalib::stringref(buf.GetDrainPos(), buf.GetUsedLen()));
+ correct = "zog\0baz\0bar foo foobar\0xyz xyzzy\0\0";
+
+ if (verify) ShowResult(testno, keywords, correct);
+ free(const_cast<char *>(keywords));
+ break;
+ }
+
+ case 4:
+ {
+ // phrase term query with wrong argument items
+ stack.Push(new search::ParseItem(search::ParseItem::ITEM_TERM, "foobar"));
+ stack.Push(new search::ParseItem(search::ParseItem::ITEM_TERM, "foo"));
+ stack.Push(new search::ParseItem(search::ParseItem::ITEM_AND, 2));
+ stack.Push(new search::ParseItem(search::ParseItem::ITEM_TERM, "bar"));
+ stack.Push(new search::ParseItem(search::ParseItem::ITEM_PHRASE, 2));
+
+ stack.AppendBuffer(&buf);
+ keywords = _extractor->ExtractKeywords(vespalib::stringref(buf.GetDrainPos(), buf.GetUsedLen()));
+ correct = "\0";
+
+ if (verify) ShowResult(testno, keywords, correct);
+ free(const_cast<char *>(keywords));
+ break;
+ }
+
+ default:
+ {
+ printf("%03d: no such test\n", testno);
+ return false;
+ }
+ }
+
+ bool result = true;
+ /*
+ if (verify) {
+ result = ShowResult(testno, pq->GetStack(), correct);
+ delete correct;
+ } else {
+ result = true;
+ }
+ delete pq;
+ */
+ return result;
+}
+
+void
+ExtractKeywordsTest::Usage(char *progname)
+{
+ printf("%s {testnospec}+\n\
+ Where testnospec is:\n\
+ num: single test\n\
+ num-num: inclusive range (open range permitted)\n",progname);
+ printf("There are tests from %d to %d\n\n", 0, NUMTESTS-1);
+ exit(-1);
+}
+
+int
+main(int argc, char** argv)
+{
+ ExtractKeywordsTest tester;
+ return tester.Entry(argc, argv);
+}
+
diff --git a/searchsummary/src/tests/extractkeywords/extractkeywordstest.h b/searchsummary/src/tests/extractkeywords/extractkeywordstest.h
new file mode 100644
index 00000000000..1a037fcb9cd
--- /dev/null
+++ b/searchsummary/src/tests/extractkeywords/extractkeywordstest.h
@@ -0,0 +1,34 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright (C) 2001-2003 Fast Search & Transfer ASA
+// Copyright (C) 2003 Overture Services Norway AS
+
+#pragma once
+
+#include <vespa/fastos/fastos.h>
+
+namespace search {
+namespace docummary {
+class KeywordExtractor;
+}
+}
+
+class ExtractKeywordsTest : public FastOS_Application
+{
+private:
+ ExtractKeywordsTest(const ExtractKeywordsTest &);
+ ExtractKeywordsTest& operator=(const ExtractKeywordsTest &);
+
+ search::docsummary::KeywordExtractor *_extractor;
+
+ int Main();
+ void Usage(char *progname);
+ bool ShowResult(int testNo, const char *actual, const char *correct);
+ bool RunTest(int i, bool verify);
+
+public:
+ ExtractKeywordsTest(void)
+ : _extractor(NULL)
+ {
+ }
+};
+
diff --git a/searchsummary/src/tests/extractkeywords/runtests.sh b/searchsummary/src/tests/extractkeywords/runtests.sh
new file mode 100755
index 00000000000..2c09bb25460
--- /dev/null
+++ b/searchsummary/src/tests/extractkeywords/runtests.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#
+# $Id$
+#
+# Copyright (C) 2000-2003 Fast Search & Transfer ASA
+# Copyright (C) 2003 Overture Services Norway AS
+#
+# All Rights Reserved
+#
+
+if $VALGRIND ./searchsummary_extractkeywordstest_app -
+then
+ :
+else
+ echo FAILED: searchsummary_extractkeywordstest_app test failed
+ exit 1
+fi
+
+if $VALGRIND ./searchsummary_extractkeywordstest_app - '*1000'
+then
+ :
+else
+ echo FAILED: searchsummary_extractkeywordstest_app test failed
+ exit 1
+fi
+
+echo SUCCESS: searchsummary_extractkeywordstest_app test completed
+exit 0
diff --git a/searchsummary/src/tests/extractkeywords/testowner.ATS b/searchsummary/src/tests/extractkeywords/testowner.ATS
new file mode 100644
index 00000000000..6d03b0836a4
--- /dev/null
+++ b/searchsummary/src/tests/extractkeywords/testowner.ATS
@@ -0,0 +1 @@
+vlarsen