diff options
Diffstat (limited to 'qpid/cpp')
147 files changed, 13813 insertions, 1494 deletions
diff --git a/qpid/cpp/bindings/qmf2/examples/python/agent.py b/qpid/cpp/bindings/qmf2/examples/python/agent.py index b24890f531..a9f1a14349 100755 --- a/qpid/cpp/bindings/qmf2/examples/python/agent.py +++ b/qpid/cpp/bindings/qmf2/examples/python/agent.py @@ -19,7 +19,7 @@ # under the License. # -import cqpid +import qpid_messaging from qmf2 import * @@ -34,7 +34,7 @@ class ExampleAgent(AgentHandler): ## ## Create and open a messaging connection to a broker. ## - self.connection = cqpid.Connection(url, "{reconnect:True}") + self.connection = qpid_messaging.Connection(url, "{reconnect:True}") self.session = None self.connection.open() diff --git a/qpid/cpp/bindings/qmf2/examples/python/find_agents.py b/qpid/cpp/bindings/qmf2/examples/python/find_agents.py index 5fd71b3f1c..852f23f747 100644 --- a/qpid/cpp/bindings/qmf2/examples/python/find_agents.py +++ b/qpid/cpp/bindings/qmf2/examples/python/find_agents.py @@ -17,7 +17,7 @@ # under the License. # -import cqpid +import qpid_messaging import qmf2 class FindAgents(qmf2.ConsoleHandler): @@ -45,7 +45,7 @@ class FindAgents(qmf2.ConsoleHandler): url = "localhost" options = "" -connection = cqpid.Connection(url, options) +connection = qpid_messaging.Connection(url, options) connection.open() session = qmf2.ConsoleSession(connection) diff --git a/qpid/cpp/bindings/qmf2/python/qmf2.py b/qpid/cpp/bindings/qmf2/python/qmf2.py index 9f2d8556f4..1d35b93601 100644 --- a/qpid/cpp/bindings/qmf2/python/qmf2.py +++ b/qpid/cpp/bindings/qmf2/python/qmf2.py @@ -18,7 +18,7 @@ # import cqmf2 -import cqpid +import qpid_messaging from threading import Thread import time @@ -124,7 +124,7 @@ class AgentHandler(Thread): def run(self): event = cqmf2.AgentEvent() while self.__running: - valid = self.__agent._impl.nextEvent(event, cqpid.Duration.SECOND) + valid = self.__agent._impl.nextEvent(event, qpid_messaging.Duration.SECOND) if valid and self.__running: if event.getType() == cqmf2.AGENT_METHOD: self.method(event, event.getMethodName(), event.getArguments(), event.getArgumentSubtypes(), @@ -156,7 +156,7 @@ class ConsoleHandler(Thread): def run(self): event = cqmf2.ConsoleEvent() while self.__running: - valid = self.__session._impl.nextEvent(event, cqpid.Duration.SECOND) + valid = self.__session._impl.nextEvent(event, qpid_messaging.Duration.SECOND) if valid and self.__running: if event.getType() == cqmf2.CONSOLE_AGENT_ADD: self.agentAdded(Agent(event.getAgent())) @@ -434,7 +434,7 @@ class Agent(object): q_arg = q._impl else: q_arg = q - dur = cqpid.Duration(cqpid.Duration.SECOND.getMilliseconds() * timeout) + dur = qpid_messaging.Duration(qpid_messaging.Duration.SECOND.getMilliseconds() * timeout) result = self._impl.query(q_arg, dur) if result.getType() == cqmf2.CONSOLE_EXCEPTION: raise Exception(Data(result.getData(0))) @@ -449,7 +449,7 @@ class Agent(object): def loadSchemaInfo(self, timeout=30): """ """ - dur = cqpid.Duration(cqpid.Duration.SECOND.getMilliseconds() * timeout) + dur = qpid_messaging.Duration(qpid_messaging.Duration.SECOND.getMilliseconds() * timeout) self._impl.querySchema(dur) def getPackages(self): @@ -473,7 +473,7 @@ class Agent(object): def getSchema(self, schemaId, timeout=30): """ """ - dur = cqpid.Duration(cqpid.Duration.SECOND.getMilliseconds() * timeout) + dur = qpid_messaging.Duration(qpid_messaging.Duration.SECOND.getMilliseconds() * timeout) return Schema(self._impl.getSchema(schemaId._impl, dur)) ## TODO: Async query @@ -555,7 +555,7 @@ class Data(object): return Agent(self._impl.getAgent()) def update(self, timeout=5): - dur = cqpid.Duration(cqpid.Duration.SECOND.getMilliseconds() * timeout) + dur = qpid_messaging.Duration(qpid_messaging.Duration.SECOND.getMilliseconds() * timeout) agent = self._impl.getAgent() query = cqmf2.Query(self._impl.getAddr()) result = agent.query(query, dur) @@ -590,7 +590,7 @@ class Data(object): timeout = 30 if '_timeout' in kwargs: timeout = kwargs['_timeout'] - dur = cqpid.Duration(cqpid.Duration.SECOND.getMilliseconds() * timeout) + dur = qpid_messaging.Duration(qpid_messaging.Duration.SECOND.getMilliseconds() * timeout) ## ## Get the list of arguments from the schema, isolate those that are IN or IN_OUT, diff --git a/qpid/cpp/bindings/qpid/dotnet/configure-windows.ps1 b/qpid/cpp/bindings/qpid/dotnet/configure-windows.ps1 index 60b2b539bd..5ee3a8ee46 100644 --- a/qpid/cpp/bindings/qpid/dotnet/configure-windows.ps1 +++ b/qpid/cpp/bindings/qpid/dotnet/configure-windows.ps1 @@ -153,6 +153,8 @@ $global:vsVersion = '' $global:cmakeGenerator = ''
$global:vsSubdir = ''
$global:cmakeCompiler = ''
+$global:cmakeCommandLine32 = ''
+$global:cmakeCommandLine64 = ''
#############################
# Select-Folder
@@ -325,7 +327,8 @@ function WriteDotnetBindingEnvSetupBat [string] $nBits,
[string] $outfileName,
[string] $studioVersion,
- [string] $studioSubdir
+ [string] $studioSubdir,
+ [string] $cmakeLine
)
$out = @("@ECHO OFF
@@ -337,6 +340,8 @@ REM REM > call $outfileName
REM >
REM
+REM The solution was generated with cmake command line:
+REM $cmakeLine
ECHO %PATH% | FINDSTR /I boost > NUL
IF %ERRORLEVEL% EQU 0 ECHO WARNING: Boost is defined in your path multiple times!
SET PATH=$boostRoot\lib;%PATH%
@@ -387,7 +392,7 @@ function SelectVisualStudioVersion { $Form.width = 350
$Form.height = 150
- $Form.Text = ”Select Visual Studio Version”
+ $Form.Text = "Select Visual Studio Version"
$DropDown = new-object System.Windows.Forms.ComboBox
$DropDown.Location = new-object System.Drawing.Size(120,10)
@@ -502,10 +507,11 @@ if ($defined64) { # 32-bit X86
#
if ($make32) {
- $env:BOOST_ROOT = "$boost32"
cd "$build32"
Write-Host "Running 32-bit CMake in $build32 ..."
- CMake -G "$global:cmakeGenerator" "-DGEN_DOXYGEN=No" "-DCMAKE_INSTALL_PREFIX=install_x86" "-DBoost_COMPILER=$global:cmakeCompiler" $cppDir
+ $global:cmakeCommandLine32 = "CMake -G ""$global:cmakeGenerator"" ""-DGEN_DOXYGEN=No"" ""-DCMAKE_INSTALL_PREFIX=install_x86"" ""-DBoost_COMPILER=$global:cmakeCompiler"" ""-DBOOST_ROOT=$boost32"" $cppDir"
+ Write-Host "$global:cmakeCommadLine32"
+ CMake -G "$global:cmakeGenerator" "-DGEN_DOXYGEN=No" "-DCMAKE_INSTALL_PREFIX=install_x86" "-DBoost_COMPILER=$global:cmakeCompiler" "-DBOOST_ROOT=$boost32" $cppDir
} else {
Write-Host "Skipped 32-bit CMake."
}
@@ -514,10 +520,11 @@ if ($make32) { # 64-bit X64
#
if ($make64) {
- $env:BOOST_ROOT = "$boost64"
cd "$build64"
Write-Host "Running 64-bit CMake in $build64"
- CMake -G "$global:cmakeGenerator Win64" "-DGEN_DOXYGEN=No" "-DCMAKE_INSTALL_PREFIX=install_x64" "-DBoost_COMPILER=$global:cmakeCompiler" $cppDir
+ $global:cmakeCommandLine64 = "CMake -G ""$global:cmakeGenerator Win64"" ""-DGEN_DOXYGEN=No"" ""-DCMAKE_INSTALL_PREFIX=install_x64"" ""-DBoost_COMPILER=$global:cmakeCompiler"" ""-DBOOST_ROOT=$boost64"" $cppDir"
+ Write-Host "$global:cmakeCommadLine64"
+ CMake -G "$global:cmakeGenerator Win64" "-DGEN_DOXYGEN=No" "-DCMAKE_INSTALL_PREFIX=install_x64" "-DBoost_COMPILER=$global:cmakeCompiler" "-DBOOST_ROOT=$boost64" $cppDir
} else {
Write-Host "Skipped 64-bit CMake."
}
@@ -569,7 +576,8 @@ if ($defined32) { -nBits "32" `
-outfileName "setenv-messaging-$global:vsSubdir-x86-32bit.bat" `
-studioVersion "$global:vsVersion" `
- -studioSubdir "$global:vsSubdir"
+ -studioSubdir "$global:vsSubdir" `
+ -cmakeLine "$global:cmakeCommandLine32"
} else {
Write-Host "Skipped writing 32-bit scripts."
@@ -620,7 +628,8 @@ if ($defined64) { -nBits "64" `
-outfileName "setenv-messaging-$global:vsSubdir-x64-64bit.bat" `
-studioVersion "$global:vsVersion" `
- -studioSubdir "$global:vsSubdir"
+ -studioSubdir "$global:vsSubdir" `
+ -cmakeLine "$global:cmakeCommandLine64"
} else {
Write-Host "Skipped writing 64-bit scripts."
diff --git a/qpid/cpp/bld-winsdk.ps1 b/qpid/cpp/bld-winsdk.ps1 index 42e6e636a4..671123c17f 100644 --- a/qpid/cpp/bld-winsdk.ps1 +++ b/qpid/cpp/bld-winsdk.ps1 @@ -356,7 +356,7 @@ if ($args.length -lt 3) { exit } -$qpid_src = "qpid" +$qpid_src = Split-Path -leaf $global:sourceDirectory $boostRoot = $args[0] $ver = $args[1] $generator = "" diff --git a/qpid/cpp/etc/qpidd.in b/qpid/cpp/etc/qpidd.in index b53ea40a1f..91448add02 100755 --- a/qpid/cpp/etc/qpidd.in +++ b/qpid/cpp/etc/qpidd.in @@ -63,8 +63,9 @@ if [ $RETVAL = 4 ]; then fi start() { + [[ $QPID_DATA_DIR ]] || QPID_DATA_DIR=/var/lib/qpidd echo -n $"Starting Qpid AMQP daemon: " - daemon --pidfile $pidfile --check $prog --user qpidd @sbindir@/$prog ${QPID_DATA_DIR:+--data-dir $QPID_DATA_DIR} --daemon $QPIDD_OPTIONS + daemon --pidfile $pidfile --check $prog --user qpidd @sbindir@/$prog --data-dir $QPID_DATA_DIR --daemon $QPIDD_OPTIONS RETVAL=$? echo [ $RETVAL = 0 ] && touch $lockfile diff --git a/qpid/cpp/examples/README.txt b/qpid/cpp/examples/README.txt index 494dab7cd9..74de2788c3 100644 --- a/qpid/cpp/examples/README.txt +++ b/qpid/cpp/examples/README.txt @@ -221,9 +221,4 @@ On Windows: C:\Program Files\qpidc-0.7\examples\request-response> server C:\Program Files\qpidc-0.7\examples\request-response> client -== qmf-console == - -This directory contains examples which demonstrate integration with -the Qpid Management Framework (QMF). Refer to the README.txt file -within the directory for more information. diff --git a/qpid/cpp/examples/examples.sln b/qpid/cpp/examples/examples.sln index 6f96105d97..8511fe3cce 100644 --- a/qpid/cpp/examples/examples.sln +++ b/qpid/cpp/examples/examples.sln @@ -32,14 +32,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "messaging_drain", "messagin EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "messaging_spout", "messaging\messaging_spout.vcproj", "{D3115AC9-91C4-4D79-BCAC-DE837C70F1EA}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qmf_console_console", "qmf-console\qmf-console_console.vcproj", "{490473E1-FECA-1BAD-2E13-3FFA2B8669C3}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qmf_console_ping", "qmf-console\qmf-console_ping.vcproj", "{C1FFDE95-3442-49AE-9985-7EEE3D45B4A3}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qmf_console_printevents", "qmf-console\qmf-console_printevents.vcproj", "{72C74624-FECA-1BAD-2E13-3FFA2B8669C3}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qmf_console_queuestats", "qmf-console\qmf-console_queuestats.vcproj", "{B21825EA-FECA-1BAD-2E13-3FFA2B8669C3}"
-EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
@@ -96,22 +88,6 @@ Global {D3115AC9-91C4-4D79-BCAC-DE837C70F1EA}.Release|Win32.Build.0 = Release|Win32
{D3115AC9-91C4-4D79-BCAC-DE837C70F1EA}.Release|x64.ActiveCfg = Release|x64
{D3115AC9-91C4-4D79-BCAC-DE837C70F1EA}.Release|x64.Build.0 = Release|x64
- {490473E1-FECA-1BAD-2E13-3FFA2B8669C3}.Debug|Win32.ActiveCfg = Debug|Win32
- {490473E1-FECA-1BAD-2E13-3FFA2B8669C3}.Debug|Win32.Build.0 = Debug|Win32
- {490473E1-FECA-1BAD-2E13-3FFA2B8669C3}.Release|Win32.ActiveCfg = Release|Win32
- {490473E1-FECA-1BAD-2E13-3FFA2B8669C3}.Release|Win32.Build.0 = Release|Win32
- {C1FFDE95-3442-49AE-9985-7EEE3D45B4A3}.Debug|Win32.ActiveCfg = Debug|Win32
- {C1FFDE95-3442-49AE-9985-7EEE3D45B4A3}.Debug|Win32.Build.0 = Debug|Win32
- {C1FFDE95-3442-49AE-9985-7EEE3D45B4A3}.Release|Win32.ActiveCfg = Release|Win32
- {C1FFDE95-3442-49AE-9985-7EEE3D45B4A3}.Release|Win32.Build.0 = Release|Win32
- {72C74624-FECA-1BAD-2E13-3FFA2B8669C3}.Debug|Win32.ActiveCfg = Debug|Win32
- {72C74624-FECA-1BAD-2E13-3FFA2B8669C3}.Debug|Win32.Build.0 = Debug|Win32
- {72C74624-FECA-1BAD-2E13-3FFA2B8669C3}.Release|Win32.ActiveCfg = Release|Win32
- {72C74624-FECA-1BAD-2E13-3FFA2B8669C3}.Release|Win32.Build.0 = Release|Win32
- {B21825EA-FECA-1BAD-2E13-3FFA2B8669C3}.Debug|Win32.ActiveCfg = Debug|Win32
- {B21825EA-FECA-1BAD-2E13-3FFA2B8669C3}.Debug|Win32.Build.0 = Debug|Win32
- {B21825EA-FECA-1BAD-2E13-3FFA2B8669C3}.Release|Win32.ActiveCfg = Release|Win32
- {B21825EA-FECA-1BAD-2E13-3FFA2B8669C3}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/qpid/cpp/examples/messaging/hello_world.cpp b/qpid/cpp/examples/messaging/hello_world.cpp index 86342b3c47..a868a64b5e 100644 --- a/qpid/cpp/examples/messaging/hello_world.cpp +++ b/qpid/cpp/examples/messaging/hello_world.cpp @@ -33,9 +33,9 @@ int main(int argc, char** argv) { std::string broker = argc > 1 ? argv[1] : "localhost:5672"; std::string address = argc > 2 ? argv[2] : "amq.topic"; std::string connectionOptions = argc > 3 ? argv[3] : ""; - - Connection connection(broker, connectionOptions); + try { + Connection connection(broker, connectionOptions); connection.open(); Session session = connection.createSession(); @@ -47,12 +47,11 @@ int main(int argc, char** argv) { Message message = receiver.fetch(Duration::SECOND * 1); std::cout << message.getContent() << std::endl; session.acknowledge(); - + connection.close(); return 0; } catch(const std::exception& error) { std::cerr << error.what() << std::endl; - connection.close(); - return 1; + return 1; } } diff --git a/qpid/cpp/examples/qmf-agent/example.cpp b/qpid/cpp/examples/qmf-agent/example.cpp deleted file mode 100644 index f9be4f0164..0000000000 --- a/qpid/cpp/examples/qmf-agent/example.cpp +++ /dev/null @@ -1,242 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -#include <qpid/management/Manageable.h> -#include <qpid/management/ManagementObject.h> -#include <qpid/agent/ManagementAgent.h> -#include <qpid/sys/Mutex.h> -#include <qpid/sys/Time.h> -#include <qpid/log/Statement.h> -#include "qpid/types/Variant.h" -#include "qmf/org/apache/qpid/agent/example/Parent.h" -#include "qmf/org/apache/qpid/agent/example/Child.h" -#include "qmf/org/apache/qpid/agent/example/ArgsParentCreate_child.h" -#include "qmf/org/apache/qpid/agent/example/ArgsParentTest_method.h" -#include "qmf/org/apache/qpid/agent/example/EventChildCreated.h" -#include "qmf/org/apache/qpid/agent/example/Package.h" - -#include <signal.h> -#include <cstdlib> -#include <iostream> - -#include <sstream> - -static bool running = true; - -using namespace std; -using qpid::management::ManagementAgent; -using qpid::management::ManagementObject; -using qpid::management::Manageable; -using qpid::management::Args; -using qpid::sys::Mutex; -using qpid::types::Variant; -namespace _qmf = qmf::org::apache::qpid::agent::example; - -class ChildClass; - -//============================================================== -// CoreClass is the operational class that corresponds to the -// "Parent" class in the management schema. -//============================================================== -class CoreClass : public Manageable -{ - string name; - ManagementAgent* agent; - _qmf::Parent* mgmtObject; - std::vector<ChildClass*> children; - Mutex vectorLock; - -public: - - CoreClass(ManagementAgent* agent, string _name); - ~CoreClass() { mgmtObject->resourceDestroy(); } - - ManagementObject* GetManagementObject(void) const - { return mgmtObject; } - - void doLoop(); - bool AuthorizeMethod(uint32_t methodId, Args& args, const string& userId); - status_t ManagementMethod(uint32_t methodId, Args& args, string& text); -}; - -class ChildClass : public Manageable -{ - string name; - _qmf::Child* mgmtObject; - -public: - - ChildClass(ManagementAgent* agent, CoreClass* parent, string name); - ~ChildClass() { mgmtObject->resourceDestroy(); } - - ManagementObject* GetManagementObject(void) const - { return mgmtObject; } - - void doWork() - { - mgmtObject->inc_count(2); - } -}; - -CoreClass::CoreClass(ManagementAgent* _agent, string _name) : name(_name), agent(_agent) -{ - static uint64_t persistId = 0x111222333444555LL; - mgmtObject = new _qmf::Parent(agent, this, name); - - agent->addObject(mgmtObject); - mgmtObject->set_state("IDLE"); - - Variant::Map args; - Variant::Map subMap; - args["first"] = "String data"; - args["second"] = 34; - subMap["string-data"] = "Text"; - subMap["numeric-data"] = 10000; - args["map-data"] = subMap; - mgmtObject->set_args(args); - - Variant::List list; - list.push_back(20000); - list.push_back("string-item"); - mgmtObject->set_list(list); -} - -void CoreClass::doLoop() -{ - // Periodically bump a counter to provide a changing statistical value - while (running) { - qpid::sys::sleep(1); - mgmtObject->inc_count(); - mgmtObject->set_state("IN_LOOP"); - - { - Mutex::ScopedLock _lock(vectorLock); - - for (std::vector<ChildClass*>::iterator iter = children.begin(); - iter != children.end(); - iter++) { - (*iter)->doWork(); - } - } - } -} - - -bool CoreClass::AuthorizeMethod(uint32_t methodId, Args& args, const string& userId) -{ - QPID_LOG(trace, "AuthorizeMethod for methodId=" << methodId << " userId=" << userId); - return methodId != _qmf::Parent::METHOD_AUTH_FAIL; -} - - -Manageable::status_t CoreClass::ManagementMethod(uint32_t methodId, Args& args, string& /*text*/) -{ - Mutex::ScopedLock _lock(vectorLock); - - switch (methodId) { - case _qmf::Parent::METHOD_CREATE_CHILD: { - _qmf::ArgsParentCreate_child& ioArgs = (_qmf::ArgsParentCreate_child&) args; - - ChildClass *child = new ChildClass(agent, this, ioArgs.i_name); - ioArgs.o_childRef = child->GetManagementObject()->getObjectId(); - - children.push_back(child); - - agent->raiseEvent(_qmf::EventChildCreated(ioArgs.i_name)); - - return STATUS_OK; - } - - case _qmf::Parent::METHOD_TEST_METHOD: { - _qmf::ArgsParentTest_method& ioArgs = (_qmf::ArgsParentTest_method&) args; - - ioArgs.io_aMap["add"] = "me"; - ioArgs.io_aList.push_back(Variant("Stuff")); - // TBD - return STATUS_OK; - } - } - - return STATUS_NOT_IMPLEMENTED; -} - -ChildClass::ChildClass(ManagementAgent* agent, CoreClass* parent, string name) -{ - mgmtObject = new _qmf::Child(agent, this, parent, name); - - agent->addObject(mgmtObject); -} - - -//============================================================== -// Main program -//============================================================== - -ManagementAgent::Singleton* singleton; - -void shutdown(int) -{ - running = false; -} - -int main_int(int argc, char** argv) -{ - singleton = new ManagementAgent::Singleton(); - const char* host = argc>1 ? argv[1] : "127.0.0.1"; - int port = argc>2 ? atoi(argv[2]) : 5672; - - signal(SIGINT, shutdown); - - // Create the qmf management agent - ManagementAgent* agent = singleton->getInstance(); - - // Register the Qmf_example schema with the agent - _qmf::Package packageInit(agent); - - // Name the agent. - agent->setName("apache.org", "qmf-example"); - - // Start the agent. It will attempt to make a connection to the - // management broker - agent->init(host, port, 5, false, ".magentdata"); - - // Allocate some core objects - CoreClass core1(agent, "Example Core Object #1"); - CoreClass core2(agent, "Example Core Object #2"); - CoreClass core3(agent, "Example Core Object #3"); - - core1.doLoop(); - - // done, cleanup and exit - delete singleton; - - return 0; -} - -int main(int argc, char** argv) -{ - try { - return main_int(argc, argv); - } catch(std::exception& e) { - cout << "Top Level Exception: " << e.what() << endl; - } -} - diff --git a/qpid/cpp/examples/qmf-agent/qmf_agent.vcproj b/qpid/cpp/examples/qmf-agent/qmf_agent.vcproj deleted file mode 100644 index 2a1c04b367..0000000000 --- a/qpid/cpp/examples/qmf-agent/qmf_agent.vcproj +++ /dev/null @@ -1,443 +0,0 @@ -<?xml version="1.0" encoding="Windows-1252"?>
-<!--
- -
- - Licensed to the Apache Software Foundation (ASF) under one
- - or more contributor license agreements. See the NOTICE file
- - distributed with this work for additional information
- - regarding copyright ownership. The ASF licenses this file
- - to you under the Apache License, Version 2.0 (the
- - "License"); you may not use this file except in compliance
- - with the License. You may obtain a copy of the License at
- -
- - http://www.apache.org/licenses/LICENSE-2.0
- -
- - Unless required by applicable law or agreed to in writing,
- - software distributed under the License is distributed on an
- - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- - KIND, either express or implied. See the License for the
- - specific language governing permissions and limitations
- - under the License.
- -
- -->
-<VisualStudioProject
- ProjectType="Visual C++"
- Version="9.00"
- Name="qmf_agent"
- ProjectGUID="{771767FB-FECA-1BAD-2E13-3FFA2B8669C3}"
- RootNamespace="qmf_console_ping"
- Keyword="Win32Proj"
- TargetFrameworkVersion="0"
- >
- <Platforms>
- <Platform
- Name="Win32"
- />
- <Platform
- Name="x64"
- />
- </Platforms>
- <ToolFiles>
- </ToolFiles>
- <Configurations>
- <Configuration
- Name="Debug|Win32"
- OutputDirectory="."
- IntermediateDirectory="Debug\qmf_agent\I386"
- ConfigurationType="1"
- CharacterSet="0"
- >
- <Tool
- Name="VCPreBuildEventTool"
- />
- <Tool
- Name="VCCustomBuildTool"
- />
- <Tool
- Name="VCXMLDataGeneratorTool"
- />
- <Tool
- Name="VCWebServiceProxyGeneratorTool"
- />
- <Tool
- Name="VCMIDLTool"
- AdditionalOptions=""
- AdditionalIncludeDirectories=""
- TypeLibraryName="$(InputName).tlb"
- HeaderFileName="$(InputName).h"
- InterfaceIdentifierFileName="$(InputName)_i.c"
- ProxyFileName="$(InputName)_p.c"
- />
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,$(QPID_ROOT)\include,..\..\include"
- PreprocessorDefinitions="_DEBUG;WIN32;_CONSOLE;_CRT_NONSTDC_NO_WARNINGS;NOMINMAX;WIN32_LEAN_AND_MEAN;_SCL_SECURE_NO_WARNINGS"
- MinimalRebuild="false"
- BasicRuntimeChecks="3"
- RuntimeLibrary="3"
- RuntimeTypeInfo="true"
- WarningLevel="3"
- Detect64BitPortabilityProblems="false"
- DebugInformationFormat="3"
- DisableSpecificWarnings="4244;4800"
- />
- <Tool
- Name="VCManagedResourceCompilerTool"
- />
- <Tool
- Name="VCResourceCompilerTool"
- PreprocessorDefinitions="_DEBUG;NOMINMAX;WIN32_LEAN_AND_MEAN;_SCL_SECURE_NO_WARNINGS"
- Culture="1033"
- AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,$(QPID_ROOT)\include,..\..\include"
- />
- <Tool
- Name="VCPreLinkEventTool"
- />
- <Tool
- Name="VCLinkerTool"
- AdditionalDependencies="qpidcommond.lib qpidclientd.lib qmfagentd.lib"
- OutputFile="$(OutDir)\qmf_agent.exe"
- LinkIncremental="2"
- SuppressStartupBanner="true"
- AdditionalLibraryDirectories=".;$(BOOST_ROOT)\lib;$(QPID_ROOT)\bin,..\..\bin"
- GenerateDebugInformation="true"
- SubSystem="1"
- TargetMachine="1"
- />
- <Tool
- Name="VCALinkTool"
- />
- <Tool
- Name="VCManifestTool"
- />
- <Tool
- Name="VCXDCMakeTool"
- />
- <Tool
- Name="VCBscMakeTool"
- />
- <Tool
- Name="VCFxCopTool"
- />
- <Tool
- Name="VCAppVerifierTool"
- />
- <Tool
- Name="VCPostBuildEventTool"
- />
- </Configuration>
- <Configuration
- Name="Release|Win32"
- OutputDirectory="Release"
- IntermediateDirectory="Release\qmf_console_ping\I386"
- ConfigurationType="1"
- CharacterSet="0"
- >
- <Tool
- Name="VCPreBuildEventTool"
- />
- <Tool
- Name="VCCustomBuildTool"
- />
- <Tool
- Name="VCXMLDataGeneratorTool"
- />
- <Tool
- Name="VCWebServiceProxyGeneratorTool"
- />
- <Tool
- Name="VCMIDLTool"
- AdditionalOptions=""
- AdditionalIncludeDirectories=""
- TypeLibraryName="$(InputName).tlb"
- HeaderFileName="$(InputName).h"
- InterfaceIdentifierFileName="$(InputName)_i.c"
- ProxyFileName="$(InputName)_p.c"
- />
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,$(QPID_ROOT)\include,..\..\include"
- PreprocessorDefinitions="NDEBUG;WIN32;_CONSOLE;_CRT_NONSTDC_NO_WARNINGS;NOMINMAX;WIN32_LEAN_AND_MEAN;_SCL_SECURE_NO_WARNINGS"
- RuntimeLibrary="2"
- RuntimeTypeInfo="true"
- WarningLevel="3"
- Detect64BitPortabilityProblems="false"
- DisableSpecificWarnings="4244;4800"
- />
- <Tool
- Name="VCManagedResourceCompilerTool"
- />
- <Tool
- Name="VCResourceCompilerTool"
- PreprocessorDefinitions="NDEBUG;NOMINMAX;WIN32_LEAN_AND_MEAN;_SCL_SECURE_NO_WARNINGS"
- Culture="1033"
- AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,$(QPID_ROOT)\include,..\..\include"
- />
- <Tool
- Name="VCPreLinkEventTool"
- />
- <Tool
- Name="VCLinkerTool"
- AdditionalDependencies="qpidcommon.lib qpidclient.lib qmfagent.lib"
- OutputFile="$(OutDir)\ping.exe"
- LinkIncremental="1"
- SuppressStartupBanner="true"
- AdditionalLibraryDirectories=".;$(BOOST_ROOT)\lib;$(QPID_ROOT)\bin,..\..\bin"
- GenerateDebugInformation="true"
- SubSystem="1"
- OptimizeReferences="2"
- EnableCOMDATFolding="2"
- TargetMachine="1"
- />
- <Tool
- Name="VCALinkTool"
- />
- <Tool
- Name="VCManifestTool"
- />
- <Tool
- Name="VCXDCMakeTool"
- />
- <Tool
- Name="VCBscMakeTool"
- />
- <Tool
- Name="VCFxCopTool"
- />
- <Tool
- Name="VCAppVerifierTool"
- />
- <Tool
- Name="VCPostBuildEventTool"
- />
- </Configuration>
- <Configuration
- Name="Debug|x64"
- OutputDirectory="."
- IntermediateDirectory="Debug\qmf_console_ping\AMD64"
- ConfigurationType="1"
- CharacterSet="0"
- >
- <Tool
- Name="VCPreBuildEventTool"
- />
- <Tool
- Name="VCCustomBuildTool"
- />
- <Tool
- Name="VCXMLDataGeneratorTool"
- />
- <Tool
- Name="VCWebServiceProxyGeneratorTool"
- />
- <Tool
- Name="VCMIDLTool"
- AdditionalOptions=""
- AdditionalIncludeDirectories=""
- TypeLibraryName="$(InputName).tlb"
- HeaderFileName="$(InputName).h"
- InterfaceIdentifierFileName="$(InputName)_i.c"
- ProxyFileName="$(InputName)_p.c"
- />
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,$(QPID_ROOT)\include,..\..\include"
- PreprocessorDefinitions="_DEBUG;WIN32;_CONSOLE;_CRT_NONSTDC_NO_WARNINGS;_AMD64_;_WIN64;NOMINMAX;WIN32_LEAN_AND_MEAN;_SCL_SECURE_NO_WARNINGS"
- MinimalRebuild="false"
- BasicRuntimeChecks="3"
- RuntimeLibrary="3"
- RuntimeTypeInfo="true"
- WarningLevel="3"
- Detect64BitPortabilityProblems="false"
- DebugInformationFormat="3"
- DisableSpecificWarnings="4244;4800"
- />
- <Tool
- Name="VCManagedResourceCompilerTool"
- />
- <Tool
- Name="VCResourceCompilerTool"
- PreprocessorDefinitions="_DEBUG;NOMINMAX;WIN32_LEAN_AND_MEAN;_SCL_SECURE_NO_WARNINGS;_WIN64"
- Culture="1033"
- AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,$(QPID_ROOT)\include,..\..\include"
- />
- <Tool
- Name="VCPreLinkEventTool"
- />
- <Tool
- Name="VCLinkerTool"
- AdditionalOptions="/machine:AMD64"
- AdditionalDependencies="qpidcommond.lib qpidclientd.lib qmfagentd.lib"
- OutputFile="$(OutDir)\ping.exe"
- LinkIncremental="2"
- SuppressStartupBanner="true"
- AdditionalLibraryDirectories=".;$(BOOST_ROOT)\lib;$(QPID_ROOT)\bin,..\..\bin"
- GenerateDebugInformation="true"
- SubSystem="1"
- TargetMachine="17"
- />
- <Tool
- Name="VCALinkTool"
- />
- <Tool
- Name="VCXDCMakeTool"
- />
- <Tool
- Name="VCBscMakeTool"
- />
- <Tool
- Name="VCFxCopTool"
- />
- <Tool
- Name="VCPostBuildEventTool"
- />
- </Configuration>
- <Configuration
- Name="Release|x64"
- OutputDirectory="Release"
- IntermediateDirectory="Release\qmf_console_ping\AMD64"
- ConfigurationType="1"
- CharacterSet="0"
- >
- <Tool
- Name="VCPreBuildEventTool"
- />
- <Tool
- Name="VCCustomBuildTool"
- />
- <Tool
- Name="VCXMLDataGeneratorTool"
- />
- <Tool
- Name="VCWebServiceProxyGeneratorTool"
- />
- <Tool
- Name="VCMIDLTool"
- AdditionalOptions=""
- AdditionalIncludeDirectories=""
- TypeLibraryName="$(InputName).tlb"
- HeaderFileName="$(InputName).h"
- InterfaceIdentifierFileName="$(InputName)_i.c"
- ProxyFileName="$(InputName)_p.c"
- />
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,$(QPID_ROOT)\include,..\..\include"
- PreprocessorDefinitions="NDEBUG;WIN32;_CONSOLE;_CRT_NONSTDC_NO_WARNINGS;_AMD64_;_WIN64;NOMINMAX;WIN32_LEAN_AND_MEAN;_SCL_SECURE_NO_WARNINGS"
- RuntimeLibrary="2"
- RuntimeTypeInfo="true"
- WarningLevel="3"
- Detect64BitPortabilityProblems="false"
- DisableSpecificWarnings="4244;4800"
- />
- <Tool
- Name="VCManagedResourceCompilerTool"
- />
- <Tool
- Name="VCResourceCompilerTool"
- PreprocessorDefinitions="NDEBUG;NOMINMAX;WIN32_LEAN_AND_MEAN;_SCL_SECURE_NO_WARNINGS;_WIN64"
- Culture="1033"
- AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,$(QPID_ROOT)\include,..\..\include"
- />
- <Tool
- Name="VCPreLinkEventTool"
- />
- <Tool
- Name="VCLinkerTool"
- AdditionalOptions="/machine:AMD64"
- AdditionalDependencies="qpidcommon.lib qpidclient.lib qmfagent.lib"
- OutputFile="$(OutDir)\ping.exe"
- LinkIncremental="1"
- SuppressStartupBanner="true"
- AdditionalLibraryDirectories=".;$(BOOST_ROOT)\lib;$(QPID_ROOT)\bin,..\..\bin"
- GenerateDebugInformation="true"
- SubSystem="1"
- OptimizeReferences="2"
- EnableCOMDATFolding="2"
- TargetMachine="17"
- />
- <Tool
- Name="VCALinkTool"
- />
- <Tool
- Name="VCXDCMakeTool"
- />
- <Tool
- Name="VCBscMakeTool"
- />
- <Tool
- Name="VCFxCopTool"
- />
- <Tool
- Name="VCPostBuildEventTool"
- />
- </Configuration>
- </Configurations>
- <References>
- </References>
- <Files>
- <Filter
- Name="Source Files"
- Filter="cpp;cxx;cc;C;c"
- >
- <File
- RelativePath=".\gen\qmf\org\apache\qpid\agent\example\Child.cpp"
- >
- </File>
- <File
- RelativePath=".\gen\qmf\org\apache\qpid\agent\example\EventChildCreated.cpp"
- >
- </File>
- <File
- RelativePath=".\gen\qmf\org\apache\qpid\agent\example\EventChildDestroyed.cpp"
- >
- </File>
- <File
- RelativePath=".\example.cpp"
- >
- </File>
- <File
- RelativePath=".\gen\qmf\org\apache\qpid\agent\example\Package.cpp"
- >
- </File>
- <File
- RelativePath=".\gen\qmf\org\apache\qpid\agent\example\Parent.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Header Files"
- Filter="h;hpp;hxx;hh"
- >
- <File
- RelativePath=".\gen\qmf\org\apache\qpid\agent\example\ArgsParentCreate_child.h"
- >
- </File>
- <File
- RelativePath=".\gen\qmf\org\apache\qpid\agent\example\Child.h"
- >
- </File>
- <File
- RelativePath=".\gen\qmf\org\apache\qpid\agent\example\EventChildCreated.h"
- >
- </File>
- <File
- RelativePath=".\gen\qmf\org\apache\qpid\agent\example\EventChildDestroyed.h"
- >
- </File>
- <File
- RelativePath=".\gen\qmf\org\apache\qpid\agent\example\Package.h"
- >
- </File>
- <File
- RelativePath=".\gen\qmf\org\apache\qpid\agent\example\Parent.h"
- >
- </File>
- </Filter>
- </Files>
- <Globals>
- </Globals>
-</VisualStudioProject>
diff --git a/qpid/cpp/examples/qmf-agent/schema.xml b/qpid/cpp/examples/qmf-agent/schema.xml deleted file mode 100644 index 2a3bb461cc..0000000000 --- a/qpid/cpp/examples/qmf-agent/schema.xml +++ /dev/null @@ -1,75 +0,0 @@ -<schema package="org.apache.qpid.agent.example"> - -<!-- - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. ---> - - <!-- - =============================================================== - Parent - =============================================================== - --> - <class name="Parent"> - - This class represents a parent object - - <property name="name" type="lstr" access="RC" index="y"/> - <property name="args" type="map" access="RO"/> - <property name="list" type="list" access="RO"/> - - <statistic name="state" type="sstr" desc="Operational state of the link"/> - <statistic name="count" type="count64" unit="tick" desc="Counter that increases monotonically"/> - - <method name="create_child" desc="Create child object"> - <arg name="name" dir="I" type="lstr"/> - <arg name="childRef" dir="O" type="objId"/> - </method> - - <method name="test_method" desc="Test Method with Map and List Arguments"> - <arg name="aMap" dir="IO" type="map"/> - <arg name="aList" dir="IO" type="list"/> - </method> - - <method name="auth_fail" desc="Method that fails authorization"> - </method> - - </class> - - - <!-- - =============================================================== - Child - =============================================================== - --> - <class name="Child"> - <property name="ParentRef" type="objId" references="Parent" access="RC" index="y" parentRef="y"/> - <property name="name" type="lstr" access="RC" index="y"/> - - <statistic name="count" type="count64" unit="tick" desc="Counter that increases monotonically"/> - - <method name="delete"/> - </class> - - <eventArguments> - <arg name="childName" type="lstr"/> - </eventArguments> - - <event name="ChildCreated" args="childName"/> - <event name="ChildDestroyed" args="childName"/> -</schema> - diff --git a/qpid/cpp/include/qpid/messaging/Handle.h b/qpid/cpp/include/qpid/messaging/Handle.h index 97a8f00b54..2edab26744 100644 --- a/qpid/cpp/include/qpid/messaging/Handle.h +++ b/qpid/cpp/include/qpid/messaging/Handle.h @@ -53,14 +53,15 @@ template <class T> class Handle { void swap(Handle<T>& h) { T* t = h.impl; h.impl = impl; impl = t; } + private: + // Not implemented, subclasses must implement. + Handle(const Handle&); + Handle& operator=(const Handle&); + protected: typedef T Impl; QPID_MESSAGING_INLINE_EXTERN Handle() :impl() {} - // Not implemented,subclasses must implement. - QPID_MESSAGING_EXTERN Handle(const Handle&); - QPID_MESSAGING_EXTERN Handle& operator=(const Handle&); - Impl* impl; friend class PrivateImplRef<T>; diff --git a/qpid/cpp/include/qpid/swig_perl_typemaps.i b/qpid/cpp/include/qpid/swig_perl_typemaps.i index da24bfe402..f1425ebd67 100644 --- a/qpid/cpp/include/qpid/swig_perl_typemaps.i +++ b/qpid/cpp/include/qpid/swig_perl_typemaps.i @@ -192,8 +192,10 @@ } %typemap (out) uint8_t, uint16_t, uint32_t, uint64_t { - sv_setuv($result, (UV)$1); - argvi++; + SV* tmp = sv_newmortal(); + sv_setuv(tmp, (UV)$1); + $result = tmp; + argvi++; } %typemap (in) int8_t, int16_t, int32_t, int64_t { @@ -206,8 +208,10 @@ } %typemap (out) int8_t, int16_t, int32_t, int64_t { - sv_setiv($result, (IV)$1); - argvi++; + SV* tmp = sv_newmortal(); + sv_setiv(tmp, (IV)$1); + $result = tmp; + argvi++; } %typemap(in) bool { diff --git a/qpid/cpp/src/CMakeLists.txt b/qpid/cpp/src/CMakeLists.txt index e7cf72e745..ea377142c3 100644 --- a/qpid/cpp/src/CMakeLists.txt +++ b/qpid/cpp/src/CMakeLists.txt @@ -299,14 +299,14 @@ if (CMAKE_COMPILER_IS_GNUCXX) if (CMAKE_SYSTEM_NAME STREQUAL SunOS) set (CATCH_UNDEFINED "") endif (CMAKE_SYSTEM_NAME STREQUAL SunOS) - set (COMPILER_FLAGS "-fvisibility-inlines-hidden -Wl,--as-needed") + set (COMPILER_FLAGS "-fvisibility-inlines-hidden") # gcc 4.1.2 on RHEL 5 needs -Wno-attributes to avoid an error that's fixed # in later gcc versions. execute_process(COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) if (GCC_VERSION VERSION_EQUAL 4.1.2) - set (COMPILER_FLAGS "-Wl,--as-needed") - message (STATUS "Cannot use -fvisibility=hidden on gcc 4.1.2") + message (STATUS "Cannot restrict library symbol export on gcc 4.1.2") + set (HIDE_SYMBOL_FLAGS "-fno-visibility-inlines-hidden") else (GCC_VERSION VERSION_EQUAL 4.1.2) set (HIDE_SYMBOL_FLAGS "-fno-visibility-inlines-hidden -fvisibility=hidden") endif (GCC_VERSION VERSION_EQUAL 4.1.2) @@ -890,10 +890,6 @@ if (CMAKE_SYSTEM_NAME STREQUAL Windows) windows/SCM.cpp ) - set (qpidmessaging_platform_SOURCES - qpid/messaging/HandleInstantiator.cpp - ) - else (CMAKE_SYSTEM_NAME STREQUAL Windows) # POSIX (Non-Windows) platforms have a lot of overlap in sources; the only @@ -991,9 +987,6 @@ else (CMAKE_SYSTEM_NAME STREQUAL Windows) set (qpidd_platform_SOURCES posix/QpiddBroker.cpp ) - - set (qpidmessaging_platform_SOURCES - ) endif (CMAKE_SYSTEM_NAME STREQUAL Windows) set (qpidcommon_SOURCES @@ -1203,8 +1196,8 @@ install (DIRECTORY ../include/qpid PATTERN ".svn" EXCLUDE) install_pdb (qpidclient ${QPID_COMPONENT_CLIENT}) -set (qpidmessaging_SOURCES_hidden - qpid/messaging/AddressParser.h +set (qpidmessaging_SOURCES + ${amqpc_SOURCES} qpid/messaging/AddressImpl.h qpid/messaging/ConnectionImpl.h qpid/messaging/ReceiverImpl.h @@ -1228,16 +1221,8 @@ set (qpidmessaging_SOURCES_hidden qpid/client/amqp0_10/SessionImpl.cpp qpid/client/amqp0_10/SenderImpl.h qpid/client/amqp0_10/SenderImpl.cpp -) -set_source_files_properties( - ${qpidmessaging_SOURCES_hidden} - PROPERTIES - COMPILE_FLAGS "${HIDE_SYMBOL_FLAGS}") - -set (qpidmessaging_SOURCES - ${qpidmessaging_platform_SOURCES} - ${qpidmessaging_SOURCES_hidden} qpid/messaging/Address.cpp + qpid/messaging/AddressParser.h qpid/messaging/AddressParser.cpp # The functions in here are not in the public interface, but qmf uses them qpid/messaging/Connection.cpp qpid/messaging/Duration.cpp @@ -1256,17 +1241,14 @@ set (qpidmessaging_SOURCES qpid/messaging/amqp/EncodedMessage.h qpid/messaging/amqp/EncodedMessage.cpp ) -set_source_files_properties( - ${qpidmessaging_SOURCES} - PROPERTIES - COMPILE_FLAGS "${HIDE_SYMBOL_FLAGS}") add_msvc_version (qpidmessaging library dll) add_library (qpidmessaging SHARED ${qpidmessaging_SOURCES}) -target_link_libraries (qpidmessaging qpidtypes qpidclient qpidcommon) +target_link_libraries (qpidmessaging qpidtypes qpidclient qpidcommon ${PROTON_LIBRARIES}) set_target_properties (qpidmessaging PROPERTIES LINK_FLAGS "${HIDE_SYMBOL_FLAGS} ${LINK_VERSION_SCRIPT_FLAG}" + COMPILE_FLAGS "${HIDE_SYMBOL_FLAGS}" VERSION ${qpidmessaging_version} SOVERSION ${qpidmessaging_version_major}) install (TARGETS qpidmessaging @@ -1533,6 +1515,7 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h) add_subdirectory(qpid/store) add_subdirectory(tests) +add_subdirectory(tests/legacystore) # Support for pkg-config diff --git a/qpid/cpp/src/amqp.cmake b/qpid/cpp/src/amqp.cmake index 38b7d01315..52316d22b3 100644 --- a/qpid/cpp/src/amqp.cmake +++ b/qpid/cpp/src/amqp.cmake @@ -163,14 +163,22 @@ if (BUILD_AMQP) qpid/messaging/amqp/TcpTransport.h qpid/messaging/amqp/TcpTransport.cpp ) - add_library (amqpc MODULE ${amqpc_SOURCES}) - target_link_libraries (amqpc qpidmessaging qpidtypes qpidclient qpidcommon ${PROTON_LIBRARIES}) - set_target_properties (amqpc PROPERTIES - PREFIX "" - LINK_FLAGS "${CATCH_UNDEFINED}") - install (TARGETS amqpc - DESTINATION ${QPIDC_MODULE_DIR} - COMPONENT ${QPID_COMPONENT_CLIENT}) + if (WIN32) + set(proton_dll "${PROTON_LIBRARY_DIRS}/${PROTON_LIBRARIES}.dll") + set(proton_dlld "${PROTON_LIBRARY_DIRS}/${PROTON_LIBRARIES}d.dll") + install (PROGRAMS ${proton_dll} + DESTINATION ${QPID_INSTALL_LIBDIR} + COMPONENT ${QPID_COMPONENT_COMMON} + CONFIGURATIONS Release|MinSizeRel|RelWithDebInfo) + install (PROGRAMS ${proton_dlld} + DESTINATION ${QPID_INSTALL_LIBDIR} + COMPONENT ${QPID_COMPONENT_COMMON} + CONFIGURATIONS Debug) + endif (WIN32) +else (BUILD_AMQP) + # ensure that qpid build ignores proton + UNSET( amqpc_SOURCES ) + UNSET( PROTON_LIBRARIES ) endif (BUILD_AMQP) diff --git a/qpid/cpp/src/legacystore.cmake b/qpid/cpp/src/legacystore.cmake index 1fdb51aa32..878dc5f247 100644 --- a/qpid/cpp/src/legacystore.cmake +++ b/qpid/cpp/src/legacystore.cmake @@ -146,12 +146,34 @@ if (BUILD_LEGACYSTORE) ) target_link_libraries (legacystore + ${clock_gettime_LIB} aio uuid qpidcommon qpidtypes qpidbroker ${DB_LIBRARY} ) + # For use in the store tests only + add_library (legacystore_shared SHARED + ${legacy_jrnl_SOURCES} + ${legacy_store_SOURCES} + ${legacy_qmf_SOURCES} + ) + + set_target_properties (legacystore_shared PROPERTIES + COMPILE_DEFINITIONS _IN_QPID_BROKER + INCLUDE_DIRECTORIES "${legacy_include_DIRECTORIES}" + ) + + target_link_libraries (legacystore_shared + ${clock_gettime_LIB} + aio + uuid + qpidcommon qpidtypes qpidbroker + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${DB_LIBRARY} + ) + install(TARGETS legacystore DESTINATION ${QPIDD_MODULE_DIR} COMPONENT ${QPID_COMPONENT_BROKER}) diff --git a/qpid/cpp/src/qpid/amqp/descriptors.h b/qpid/cpp/src/qpid/amqp/descriptors.h index e395fc25d7..857231ddda 100644 --- a/qpid/cpp/src/qpid/amqp/descriptors.h +++ b/qpid/cpp/src/qpid/amqp/descriptors.h @@ -33,7 +33,7 @@ const std::string DELIVERY_ANNOTATIONS_SYMBOL("amqp:delivery-annotations:map"); const std::string MESSAGE_ANNOTATIONS_SYMBOL("amqp:message-annotations:map"); const std::string APPLICATION_PROPERTIES_SYMBOL("amqp:application-properties:map"); const std::string AMQP_SEQUENCE_SYMBOL("amqp:amqp-sequence:list"); -const std::string AMQP_VALUE_SYMBOL("amqp:amqp-sequence:*"); +const std::string AMQP_VALUE_SYMBOL("amqp:amqp-value:*"); const std::string DATA_SYMBOL("amqp:data:binary"); const std::string FOOTER_SYMBOL("amqp:footer:map"); diff --git a/qpid/cpp/src/qpid/amqp_0_10/Codecs.cpp b/qpid/cpp/src/qpid/amqp_0_10/Codecs.cpp index 930f402f85..49d152cc05 100644 --- a/qpid/cpp/src/qpid/amqp_0_10/Codecs.cpp +++ b/qpid/cpp/src/qpid/amqp_0_10/Codecs.cpp @@ -575,6 +575,11 @@ void translate(const boost::shared_ptr<FieldValue> from, Variant& to) to = toVariant(from); } +boost::shared_ptr<framing::FieldValue> translate(const types::Variant& from) +{ + return toFieldValue(from); +} + const std::string ListCodec::contentType("amqp/list"); const std::string MapCodec::contentType("amqp/map"); diff --git a/qpid/cpp/src/qpid/amqp_0_10/Codecs.h b/qpid/cpp/src/qpid/amqp_0_10/Codecs.h index 408307eb7a..79d76bcc4b 100644 --- a/qpid/cpp/src/qpid/amqp_0_10/Codecs.h +++ b/qpid/cpp/src/qpid/amqp_0_10/Codecs.h @@ -80,6 +80,7 @@ QPID_COMMON_EXTERN void translate(const boost::shared_ptr<qpid::framing::FieldVa qpid::types::Variant& to); QPID_COMMON_EXTERN void translate(const types::Variant& from, boost::shared_ptr<qpid::framing::FieldValue> to); +QPID_COMMON_EXTERN boost::shared_ptr<qpid::framing::FieldValue> translate(const types::Variant& from); }} // namespace qpid::amqp_0_10 diff --git a/qpid/cpp/src/qpid/broker/Broker.cpp b/qpid/cpp/src/qpid/broker/Broker.cpp index 2770c9d66b..dd2769ec92 100644 --- a/qpid/cpp/src/qpid/broker/Broker.cpp +++ b/qpid/cpp/src/qpid/broker/Broker.cpp @@ -355,6 +355,11 @@ Broker::Broker(const Broker::Options& conf) : //recover any objects via object factories objects.restore(*this); + // Assign to queues their users who created them (can be done after ACL is loaded in Plugin::initializeAll above + if ((getAcl()) && (store.get())) { + queues.eachQueue(boost::bind(&qpid::broker::Queue::updateAclUserQueueCount, _1)); + } + if(conf.enableMgmt) { if (getAcl()) { mgmtObject->set_maxConns(getAcl()->getMaxConnectTotal()); @@ -1289,8 +1294,9 @@ std::pair<boost::shared_ptr<Queue>, bool> Broker::createQueue( if (!acl->authorise(userId,acl::ACT_CREATE,acl::OBJ_QUEUE,name,¶ms) ) throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied queue create request from " << userId)); - if (!acl->approveCreateQueue(userId,name) ) - throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied queue create request from " << userId)); + if (!queues.find(name)) + if (!acl->approveCreateQueue(userId,name) ) + throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied queue create request from " << userId)); } Exchange::shared_ptr alternate; diff --git a/qpid/cpp/src/qpid/broker/Exchange.cpp b/qpid/cpp/src/qpid/broker/Exchange.cpp index 0a25d57cd8..efd83a3225 100644 --- a/qpid/cpp/src/qpid/broker/Exchange.cpp +++ b/qpid/cpp/src/qpid/broker/Exchange.cpp @@ -135,7 +135,7 @@ void Exchange::doRoute(Deliverable& msg, ConstBindingList b) if (mgmtExchange != 0) { qmf::org::apache::qpid::broker::Exchange::PerThreadStats *eStats = mgmtExchange->getStatistics(); - uint64_t contentSize = msg.getMessage().getContentSize(); + uint64_t contentSize = msg.getMessage().getMessageSize(); eStats->msgReceives += 1; eStats->byteReceives += contentSize; diff --git a/qpid/cpp/src/qpid/broker/Message.cpp b/qpid/cpp/src/qpid/broker/Message.cpp index e71125b4ab..deca238f22 100644 --- a/qpid/cpp/src/qpid/broker/Message.cpp +++ b/qpid/cpp/src/qpid/broker/Message.cpp @@ -71,9 +71,9 @@ bool Message::isPersistent() const return getEncoding().isPersistent(); } -uint64_t Message::getContentSize() const +uint64_t Message::getMessageSize() const { - return getEncoding().getContentSize(); + return getEncoding().getMessageSize(); } boost::intrusive_ptr<AsyncCompletion> Message::getIngressCompletion() const diff --git a/qpid/cpp/src/qpid/broker/Message.h b/qpid/cpp/src/qpid/broker/Message.h index 84f62a771d..fdc919242e 100644 --- a/qpid/cpp/src/qpid/broker/Message.h +++ b/qpid/cpp/src/qpid/broker/Message.h @@ -68,7 +68,7 @@ public: virtual std::string getRoutingKey() const = 0; virtual bool isPersistent() const = 0; virtual uint8_t getPriority() const = 0; - virtual uint64_t getContentSize() const = 0; + virtual uint64_t getMessageSize() const = 0; virtual qpid::amqp::MessageId getMessageId() const = 0; virtual qpid::amqp::MessageId getCorrelationId() const = 0; virtual std::string getPropertyAsString(const std::string& key) const = 0; @@ -83,7 +83,7 @@ public: QPID_BROKER_EXTERN Message(); QPID_BROKER_EXTERN ~Message(); - bool isRedelivered() const { return deliveryCount; } + bool isRedelivered() const { return deliveryCount > 0; } void deliver() { ++deliveryCount; } void undeliver() { --deliveryCount; } int getDeliveryCount() const { return deliveryCount; } @@ -119,7 +119,7 @@ public: QPID_BROKER_EXTERN qpid::types::Variant getProperty(const std::string& key) const; void processProperties(qpid::amqp::MapHandler&) const; - QPID_BROKER_EXTERN uint64_t getContentSize() const; + QPID_BROKER_EXTERN uint64_t getMessageSize() const; QPID_BROKER_EXTERN Encoding& getEncoding(); QPID_BROKER_EXTERN const Encoding& getEncoding() const; diff --git a/qpid/cpp/src/qpid/broker/Queue.cpp b/qpid/cpp/src/qpid/broker/Queue.cpp index d1f1afd61a..449488435d 100644 --- a/qpid/cpp/src/qpid/broker/Queue.cpp +++ b/qpid/cpp/src/qpid/broker/Queue.cpp @@ -88,7 +88,7 @@ inline void mgntEnqStats(const Message& msg, _qmf::Queue::PerThreadStats *qStats = mgmtObject->getStatistics(); _qmf::Broker::PerThreadStats *bStats = brokerMgmtObject->getStatistics(); - uint64_t contentSize = msg.getContentSize(); + uint64_t contentSize = msg.getMessageSize(); qStats->msgTotalEnqueues +=1; bStats->msgTotalEnqueues += 1; qStats->byteTotalEnqueues += contentSize; @@ -111,7 +111,7 @@ inline void mgntDeqStats(const Message& msg, if (mgmtObject != 0){ _qmf::Queue::PerThreadStats *qStats = mgmtObject->getStatistics(); _qmf::Broker::PerThreadStats *bStats = brokerMgmtObject->getStatistics(); - uint64_t contentSize = msg.getContentSize(); + uint64_t contentSize = msg.getMessageSize(); qStats->msgTotalDequeues += 1; bStats->msgTotalDequeues += 1; @@ -131,7 +131,15 @@ inline void mgntDeqStats(const Message& msg, QueueSettings merge(const QueueSettings& inputs, const Broker::Options& globalOptions) { QueueSettings settings(inputs); - if (!settings.maxDepth.hasSize() && globalOptions.queueLimit) { + settings.maxDepth = QueueDepth(); + if (inputs.maxDepth.hasCount() && inputs.maxDepth.getCount()) { + settings.maxDepth.setCount(inputs.maxDepth.getCount()); + } + if (inputs.maxDepth.hasSize()) { + if (inputs.maxDepth.getSize()) { + settings.maxDepth.setSize(inputs.maxDepth.getSize()); + } + } else if (globalOptions.queueLimit) { settings.maxDepth.setSize(globalOptions.queueLimit); } return settings; @@ -194,7 +202,7 @@ Queue::Queue(const string& _name, const QueueSettings& _settings, redirectSource(false) { current.setCount(0);//always track depth in messages - if (settings.maxDepth.hasSize()) current.setSize(0);//track depth in bytes only if policy requires it + if (settings.maxDepth.getSize()) current.setSize(0);//track depth in bytes only if policy requires it if (settings.traceExcludes.size()) { split(traceExclude, settings.traceExcludes, ", "); } @@ -297,7 +305,7 @@ void Queue::deliverTo(Message msg, TxBuffer* txn) void Queue::recoverPrepared(const Message& msg) { Mutex::ScopedLock locker(messageLock); - current += QueueDepth(1, msg.getContentSize()); + current += QueueDepth(1, msg.getMessageSize()); } void Queue::recover(Message& msg) @@ -311,7 +319,7 @@ void Queue::process(Message& msg) push(msg); if (mgmtObject != 0){ _qmf::Queue::PerThreadStats *qStats = mgmtObject->getStatistics(); - const uint64_t contentSize = msg.getContentSize(); + const uint64_t contentSize = msg.getMessageSize(); qStats->msgTxnEnqueues += 1; qStats->byteTxnEnqueues += contentSize; mgmtObject->statisticsUpdated(); @@ -853,7 +861,7 @@ bool Queue::enqueue(TransactionContext* ctxt, Message& msg) { Mutex::ScopedLock locker(messageLock); - if (!checkDepth(QueueDepth(1, msg.getContentSize()), msg)) { + if (!checkDepth(QueueDepth(1, msg.getMessageSize()), msg)) { return false; } } @@ -883,7 +891,7 @@ void Queue::enqueueAborted(const Message& msg) //Called when any transactional enqueue is aborted (including but //not limited to a recovered dtx transaction) Mutex::ScopedLock locker(messageLock); - current -= QueueDepth(1, msg.getContentSize()); + current -= QueueDepth(1, msg.getMessageSize()); } void Queue::enqueueCommited(Message& msg) @@ -911,7 +919,7 @@ void Queue::dequeueCommited(const Message& msg) observeDequeue(msg, locker, settings.autodelete ? &autodelete : 0); if (mgmtObject != 0) { mgmtObject->inc_msgTxnDequeues(); - mgmtObject->inc_byteTxnDequeues(msg.getContentSize()); + mgmtObject->inc_byteTxnDequeues(msg.getMessageSize()); } } @@ -954,7 +962,7 @@ void Queue::dequeueCommitted(const QueueCursor& cursor) Mutex::ScopedLock locker(messageLock); Message* msg = messages->find(cursor); if (msg) { - const uint64_t contentSize = msg->getContentSize(); + const uint64_t contentSize = msg->getMessageSize(); observeDequeue(*msg, locker, settings.autodelete ? &autodelete : 0); if (mgmtObject != 0) { mgmtObject->inc_msgTxnDequeues(); @@ -978,7 +986,7 @@ void Queue::dequeueCommitted(const QueueCursor& cursor) */ void Queue::observeDequeue(const Message& msg, const Mutex::ScopedLock& lock, ScopedAutoDelete* autodelete) { - current -= QueueDepth(1, msg.getContentSize()); + current -= QueueDepth(1, msg.getMessageSize()); mgntDeqStats(msg, mgmtObject, brokerMgmtObject); for (Observers::const_iterator i = observers.begin(); i != observers.end(); ++i) { try{ @@ -1182,18 +1190,27 @@ void Queue::encode(Buffer& buffer) const buffer.putShortString(name); buffer.put(encodableSettings); buffer.putShortString(alternateExchange.get() ? alternateExchange->getName() : std::string("")); + buffer.putShortString(userId); } uint32_t Queue::encodedSize() const { return name.size() + 1/*short string size octet*/ + (alternateExchange.get() ? alternateExchange->getName().size() : 0) + 1 /* short string */ + + userId.size() + 1 /* short string */ + encodableSettings.encodedSize(); } +void Queue::updateAclUserQueueCount() +{ + if (broker->getAcl()) + broker->getAcl()->approveCreateQueue(userId, name); +} + Queue::shared_ptr Queue::restore( QueueRegistry& queues, Buffer& buffer ) { string name; + string _userId; buffer.getShortString(name); FieldTable ft; buffer.get(ft); @@ -1207,6 +1224,12 @@ Queue::shared_ptr Queue::restore( QueueRegistry& queues, Buffer& buffer ) result.first->alternateExchangeName.assign(altExch); } + //get userId of queue's creator; ACL counters for userId are done after ACL plugin is initialized + if (buffer.available()) { + buffer.getShortString(_userId); + result.first->setOwningUser(_userId); + } + return result.first; } diff --git a/qpid/cpp/src/qpid/broker/Queue.h b/qpid/cpp/src/qpid/broker/Queue.h index a832b95feb..a7eb71c6bb 100644 --- a/qpid/cpp/src/qpid/broker/Queue.h +++ b/qpid/cpp/src/qpid/broker/Queue.h @@ -204,6 +204,7 @@ class Queue : public boost::enable_shared_from_this<Queue>, QueueDepth current; QueueBindings bindings; std::string alternateExchangeName; + std::string userId; // queue owner for ACL quota purposes boost::shared_ptr<Exchange> alternateExchange; framing::SequenceNumber sequence; qmf::org::apache::qpid::broker::Queue::shared_ptr mgmtObject; @@ -384,6 +385,10 @@ class Queue : public boost::enable_shared_from_this<Queue>, /** Get the message at position pos, returns true if found and sets msg */ QPID_BROKER_EXTERN bool find(framing::SequenceNumber pos, Message& msg ) const; + // Remember the queue's owner so acl quotas can be restored after restart + void setOwningUser(std::string& _userId) { userId = _userId; } + void updateAclUserQueueCount(); + QPID_BROKER_EXTERN void setAlternateExchange(boost::shared_ptr<Exchange> exchange); QPID_BROKER_EXTERN boost::shared_ptr<Exchange> getAlternateExchange(); QPID_BROKER_EXTERN bool isLocal(const Message& msg); diff --git a/qpid/cpp/src/qpid/broker/QueueFlowLimit.cpp b/qpid/cpp/src/qpid/broker/QueueFlowLimit.cpp index 9b2e31c925..607dc09fe8 100644 --- a/qpid/cpp/src/qpid/broker/QueueFlowLimit.cpp +++ b/qpid/cpp/src/qpid/broker/QueueFlowLimit.cpp @@ -112,7 +112,7 @@ void QueueFlowLimit::enqueued(const Message& msg) sys::Mutex::ScopedLock l(indexLock); ++count; - size += msg.getContentSize(); + size += msg.getMessageSize(); if (!flowStopped) { if (flowStopCount && count > flowStopCount) { @@ -150,7 +150,7 @@ void QueueFlowLimit::dequeued(const Message& msg) throw Exception(QPID_MSG("Flow limit count underflow on dequeue. Queue=" << queueName)); } - uint64_t _size = msg.getContentSize(); + uint64_t _size = msg.getMessageSize(); if (_size <= size) { size -= _size; } else { diff --git a/qpid/cpp/src/qpid/broker/QueueRegistry.cpp b/qpid/cpp/src/qpid/broker/QueueRegistry.cpp index 606a8cceae..1283a42e6d 100644 --- a/qpid/cpp/src/qpid/broker/QueueRegistry.cpp +++ b/qpid/cpp/src/qpid/broker/QueueRegistry.cpp @@ -64,6 +64,8 @@ QueueRegistry::declare(const string& name, const QueueSettings& settings, //Move this to factory also? if (alternate) queue->setAlternateExchange(alternate);//need to do this *before* create + queue->setOwningUser(userId); + if (!recovering) { //create persistent record if required queue->create(); diff --git a/qpid/cpp/src/qpid/broker/QueueSettings.cpp b/qpid/cpp/src/qpid/broker/QueueSettings.cpp index c505217dbb..0b4a268489 100644 --- a/qpid/cpp/src/qpid/broker/QueueSettings.cpp +++ b/qpid/cpp/src/qpid/broker/QueueSettings.cpp @@ -112,10 +112,10 @@ QueueSettings::QueueSettings(bool d, bool a) : bool QueueSettings::handle(const std::string& key, const qpid::types::Variant& value) { - if (key == MAX_COUNT && value.asUint32() > 0) { + if (key == MAX_COUNT) { maxDepth.setCount(value); return true; - } else if (key == MAX_SIZE && value.asUint64() > 0) { + } else if (key == MAX_SIZE) { maxDepth.setSize(value); return true; } else if (key == POLICY_TYPE) { diff --git a/qpid/cpp/src/qpid/broker/QueueSettings.h b/qpid/cpp/src/qpid/broker/QueueSettings.h index 8d72115e18..e150e365c2 100644 --- a/qpid/cpp/src/qpid/broker/QueueSettings.h +++ b/qpid/cpp/src/qpid/broker/QueueSettings.h @@ -110,7 +110,7 @@ struct QueueSettings void validate() const; QPID_BROKER_EXTERN void populate(const std::map<std::string, qpid::types::Variant>& inputs, std::map<std::string, qpid::types::Variant>& unused); QPID_BROKER_EXTERN void populate(const qpid::framing::FieldTable& inputs, qpid::framing::FieldTable& unused); - std::map<std::string, qpid::types::Variant> asMap() const; + QPID_BROKER_EXTERN std::map<std::string, qpid::types::Variant> asMap() const; struct Aliases : std::map<std::string, std::string> { diff --git a/qpid/cpp/src/qpid/broker/ThresholdAlerts.cpp b/qpid/cpp/src/qpid/broker/ThresholdAlerts.cpp index 345b9d89d5..afb9d9ff4e 100644 --- a/qpid/cpp/src/qpid/broker/ThresholdAlerts.cpp +++ b/qpid/cpp/src/qpid/broker/ThresholdAlerts.cpp @@ -44,7 +44,7 @@ ThresholdAlerts::ThresholdAlerts(const std::string& n, void ThresholdAlerts::enqueued(const Message& m) { - size += m.getContentSize(); + size += m.getMessageSize(); ++count; if (sizeGoingUp && sizeThreshold && size >= sizeThreshold) { @@ -64,7 +64,7 @@ void ThresholdAlerts::enqueued(const Message& m) void ThresholdAlerts::dequeued(const Message& m) { - size -= m.getContentSize(); + size -= m.getMessageSize(); --count; if (!sizeGoingUp && sizeThreshold && size <= sizeThresholdDown) { diff --git a/qpid/cpp/src/qpid/broker/amqp/Message.cpp b/qpid/cpp/src/qpid/broker/amqp/Message.cpp index 572eea3881..7015db324b 100644 --- a/qpid/cpp/src/qpid/broker/amqp/Message.cpp +++ b/qpid/cpp/src/qpid/broker/amqp/Message.cpp @@ -31,6 +31,7 @@ #include "qpid/log/Statement.h" #include "qpid/framing/Buffer.h" #include <string.h> +#include <boost/lexical_cast.hpp> namespace qpid { namespace broker { @@ -79,8 +80,51 @@ uint8_t Message::getPriority() const else return priority.get(); } -std::string Message::getPropertyAsString(const std::string& /*key*/) const { return empty; } -std::string Message::getAnnotationAsString(const std::string& /*key*/) const { return empty; } +namespace { +class StringRetriever : public MapHandler +{ + public: + StringRetriever(const std::string& k) : key(k) {} + void handleBool(const qpid::amqp::CharSequence& actualKey, bool actualValue) { process(actualKey, actualValue); } + void handleUint8(const qpid::amqp::CharSequence& actualKey, uint8_t actualValue) { process(actualKey, actualValue); } + void handleUint16(const qpid::amqp::CharSequence& actualKey, uint16_t actualValue) { process(actualKey, actualValue); } + void handleUint32(const qpid::amqp::CharSequence& actualKey, uint32_t actualValue) { process(actualKey, actualValue); } + void handleUint64(const qpid::amqp::CharSequence& actualKey, uint64_t actualValue) { process(actualKey, actualValue); } + void handleInt8(const qpid::amqp::CharSequence& actualKey, int8_t actualValue) { process(actualKey, actualValue); } + void handleInt16(const qpid::amqp::CharSequence& actualKey, int16_t actualValue) { process(actualKey, actualValue); } + void handleInt32(const qpid::amqp::CharSequence& actualKey, int32_t actualValue) { process(actualKey, actualValue); } + void handleInt64(const qpid::amqp::CharSequence& actualKey, int64_t actualValue) { process(actualKey, actualValue); } + void handleFloat(const qpid::amqp::CharSequence& actualKey, float actualValue) { process(actualKey, actualValue); } + void handleDouble(const qpid::amqp::CharSequence& actualKey, double actualValue) { process(actualKey, actualValue); } + void handleVoid(const qpid::amqp::CharSequence&) { /*nothing to do*/ } + void handleString(const qpid::amqp::CharSequence& actualKey, const qpid::amqp::CharSequence& actualValue, const qpid::amqp::CharSequence& /*encoding*/) + { + if (isRequestedKey(actualKey)) value = std::string(actualValue.data, actualValue.size); + } + std::string getValue() const { return value; } + private: + const std::string key; + std::string value; + + template <typename T> void process(const qpid::amqp::CharSequence& actualKey, T actualValue) + { + if (isRequestedKey(actualKey)) value = boost::lexical_cast<std::string>(actualValue); + } + + bool isRequestedKey(const qpid::amqp::CharSequence& actualKey) + { + //TODO: avoid allocating new string by just iterating over chars + return key == std::string(actualKey.data, actualKey.size); + } +}; +} + +std::string Message::getPropertyAsString(const std::string& key) const +{ + StringRetriever sr(key); + processProperties(sr); + return sr.getValue(); +} namespace { class PropertyAdapter : public Reader { @@ -135,19 +179,30 @@ namespace { state(KEY) {} }; + +void processMapData(const CharSequence& source, MapHandler& handler) +{ + qpid::amqp::Decoder d(source.data, source.size); + PropertyAdapter adapter(handler); + d.read(adapter); + +} } void Message::processProperties(MapHandler& mh) const { - qpid::amqp::Decoder d(applicationProperties.data, applicationProperties.size); - PropertyAdapter mha(mh); - d.read(mha); + processMapData(applicationProperties, mh); +} + +std::string Message::getAnnotationAsString(const std::string& key) const +{ + StringRetriever sr(key); + processMapData(messageAnnotations, sr); + if (sr.getValue().empty()) processMapData(deliveryAnnotations, sr); + return sr.getValue(); + } -//getContentSize() is primarily used in stats about the number of -//bytes enqueued/dequeued etc, not sure whether this is the right name -//and whether it should indeed only be the content that is thus -//measured -uint64_t Message::getContentSize() const { return data.size(); } +uint64_t Message::getMessageSize() const { return data.size(); } //getContent() is used primarily for decoding qmf messages in //management and ha, but also by the xml exchange std::string Message::getContent() const diff --git a/qpid/cpp/src/qpid/broker/amqp/Message.h b/qpid/cpp/src/qpid/broker/amqp/Message.h index 3a7c4529de..5bf49ef556 100644 --- a/qpid/cpp/src/qpid/broker/amqp/Message.h +++ b/qpid/cpp/src/qpid/broker/amqp/Message.h @@ -44,7 +44,7 @@ class Message : public qpid::broker::Message::Encoding, private qpid::amqp::Mess std::string getRoutingKey() const; bool isPersistent() const; uint8_t getPriority() const; - uint64_t getContentSize() const; + uint64_t getMessageSize() const; std::string getPropertyAsString(const std::string& key) const; std::string getAnnotationAsString(const std::string& key) const; bool getTtl(uint64_t&) const; diff --git a/qpid/cpp/src/qpid/broker/amqp/NodeProperties.cpp b/qpid/cpp/src/qpid/broker/amqp/NodeProperties.cpp index eb30c78128..45536c2262 100644 --- a/qpid/cpp/src/qpid/broker/amqp/NodeProperties.cpp +++ b/qpid/cpp/src/qpid/broker/amqp/NodeProperties.cpp @@ -20,10 +20,13 @@ */ #include "qpid/broker/amqp/NodeProperties.h" #include "qpid/broker/amqp/DataReader.h" +#include "qpid/broker/Exchange.h" +#include "qpid/broker/Queue.h" #include "qpid/broker/QueueSettings.h" #include "qpid/amqp/CharSequence.h" #include "qpid/amqp/Descriptor.h" #include "qpid/amqp/descriptors.h" +#include "qpid/amqp_0_10/Codecs.h" #include "qpid/types/Variant.h" #include "qpid/broker/QueueSettings.h" #include "qpid/log/Statement.h" @@ -100,7 +103,7 @@ bool getLifetimeDescriptorSymbol(QueueSettings::LifetimePolicy policy, pn_bytes_ } -NodeProperties::NodeProperties() : queue(true), durable(false), autoDelete(false), exclusive(false), exchangeType("topic"), lifetime(QueueSettings::DELETE_IF_UNUSED) {} +NodeProperties::NodeProperties() : received(false), queue(true), durable(false), autoDelete(false), exclusive(false), exchangeType("topic"), lifetime(QueueSettings::DELETE_IF_UNUSED) {} void NodeProperties::read(pn_data_t* data) { @@ -108,26 +111,92 @@ void NodeProperties::read(pn_data_t* data) reader.read(data); } -void NodeProperties::write(pn_data_t* data) +void NodeProperties::write(pn_data_t* data, boost::shared_ptr<Queue> node) { - pn_data_put_map(data); - pn_data_enter(data); - pn_data_put_symbol(data, convert(SUPPORTED_DIST_MODES)); - pn_data_put_string(data, convert(queue ? MOVE : COPY)); - pn_bytes_t symbol; - if (autoDelete && getLifetimeDescriptorSymbol(lifetime, symbol)) { - pn_data_put_symbol(data, convert(LIFETIME_POLICY)); - pn_data_put_described(data); + if (received) { + pn_data_put_map(data); pn_data_enter(data); - pn_data_put_symbol(data, symbol); - pn_data_put_list(data); + pn_data_put_symbol(data, convert(SUPPORTED_DIST_MODES)); + pn_data_put_string(data, convert(MOVE));//TODO: should really add COPY as well, since queues can be browsed + pn_bytes_t symbol; + if (autoDelete && node->isAutoDelete() && getLifetimeDescriptorSymbol(node->getSettings().lifetime, symbol)) { + pn_data_put_symbol(data, convert(LIFETIME_POLICY)); + pn_data_put_described(data); + pn_data_enter(data); + pn_data_put_symbol(data, symbol); + pn_data_put_list(data); + pn_data_exit(data); + } + if (durable && node->isDurable()) { + pn_data_put_symbol(data, convert(DURABLE)); + pn_data_put_bool(data, true); + } + if (exclusive && node->hasExclusiveOwner()) { + pn_data_put_symbol(data, convert(EXCLUSIVE)); + pn_data_put_bool(data, true); + } + if (!alternateExchange.empty() && node->getAlternateExchange()) { + pn_data_put_symbol(data, convert(ALTERNATE_EXCHANGE)); + pn_data_put_string(data, convert(node->getAlternateExchange()->getName())); + } + + qpid::types::Variant::Map actual = node->getSettings().asMap(); + qpid::types::Variant::Map unrecognised; + QueueSettings dummy; + dummy.populate(actual, unrecognised); + for (qpid::types::Variant::Map::const_iterator i = unrecognised.begin(); i != unrecognised.end(); ++i) { + actual.erase(i->first); + } + for (qpid::types::Variant::Map::const_iterator i = properties.begin(); i != properties.end(); ++i) { + qpid::types::Variant::Map::const_iterator j = actual.find(i->first); + if (j != actual.end()) { + pn_data_put_symbol(data, convert(j->first)); + pn_data_put_string(data, convert(j->second.asString())); + } + } + pn_data_exit(data); } - pn_data_exit(data); } +namespace { +const std::string QPID_MSG_SEQUENCE("qpid.msg_sequence"); +const std::string QPID_IVE("qpid.ive"); +} +void NodeProperties::write(pn_data_t* data, boost::shared_ptr<Exchange> node) +{ + if (received) { + pn_data_put_map(data); + pn_data_enter(data); + pn_data_put_symbol(data, convert(SUPPORTED_DIST_MODES)); + pn_data_put_string(data, convert(COPY)); + if (durable && node->isDurable()) { + pn_data_put_symbol(data, convert(DURABLE)); + pn_data_put_bool(data, true); + } + if (!exchangeType.empty()) { + pn_data_put_symbol(data, convert(EXCHANGE_TYPE)); + pn_data_put_string(data, convert(node->getType())); + } + if (!alternateExchange.empty() && node->getAlternate()) { + pn_data_put_symbol(data, convert(ALTERNATE_EXCHANGE)); + pn_data_put_string(data, convert(node->getAlternate()->getName())); + } + + for (qpid::types::Variant::Map::const_iterator i = properties.begin(); i != properties.end(); ++i) { + if ((i->first == QPID_MSG_SEQUENCE || i->first == QPID_IVE) && node->getArgs().isSet(i->first)) { + pn_data_put_symbol(data, convert(i->first)); + pn_data_put_bool(data, true); + } + } + + pn_data_exit(data); + } +} + void NodeProperties::process(const std::string& key, const qpid::types::Variant& value, const Descriptor* d) { + received = true; QPID_LOG(debug, "Processing node property " << key << " = " << value); if (key == SUPPORTED_DIST_MODES) { if (value == MOVE) queue = true; @@ -248,6 +317,7 @@ QueueSettings NodeProperties::getQueueSettings() qpid::types::Variant::Map unused; settings.populate(properties, unused); settings.lifetime = lifetime; + qpid::amqp_0_10::translate(unused, settings.storeSettings); return settings; } @@ -277,4 +347,9 @@ bool NodeProperties::trackControllingLink() const return lifetime == QueueSettings::DELETE_ON_CLOSE || lifetime == QueueSettings::DELETE_IF_EMPTY; } +const qpid::types::Variant::Map& NodeProperties::getProperties() const +{ + return properties; +} + }}} // namespace qpid::broker::amqp diff --git a/qpid/cpp/src/qpid/broker/amqp/NodeProperties.h b/qpid/cpp/src/qpid/broker/amqp/NodeProperties.h index 03780c10a9..df96d5a023 100644 --- a/qpid/cpp/src/qpid/broker/amqp/NodeProperties.h +++ b/qpid/cpp/src/qpid/broker/amqp/NodeProperties.h @@ -24,10 +24,13 @@ #include "qpid/amqp/MapReader.h" #include "qpid/types/Variant.h" #include "qpid/broker/QueueSettings.h" +#include <boost/shared_ptr.hpp> struct pn_data_t; namespace qpid { namespace broker { +class Exchange; +class Queue; struct QueueSettings; namespace amqp { @@ -36,7 +39,8 @@ class NodeProperties : public qpid::amqp::MapReader public: NodeProperties(); void read(pn_data_t*); - void write(pn_data_t*); + void write(pn_data_t*,boost::shared_ptr<Queue>); + void write(pn_data_t*,boost::shared_ptr<Exchange>); void onNullValue(const qpid::amqp::CharSequence&, const qpid::amqp::Descriptor*); void onBooleanValue(const qpid::amqp::CharSequence&, bool, const qpid::amqp::Descriptor*); void onUByteValue(const qpid::amqp::CharSequence&, uint8_t, const qpid::amqp::Descriptor*); @@ -61,7 +65,9 @@ class NodeProperties : public qpid::amqp::MapReader std::string getExchangeType() const; std::string getAlternateExchange() const; bool trackControllingLink() const; + const qpid::types::Variant::Map& getProperties() const; private: + bool received; bool queue; bool durable; bool autoDelete; diff --git a/qpid/cpp/src/qpid/broker/amqp/Outgoing.cpp b/qpid/cpp/src/qpid/broker/amqp/Outgoing.cpp index e8c05ed7b0..1b7a47b360 100644 --- a/qpid/cpp/src/qpid/broker/amqp/Outgoing.cpp +++ b/qpid/cpp/src/qpid/broker/amqp/Outgoing.cpp @@ -45,6 +45,17 @@ void Outgoing::wakeup() session.wakeup(); } +namespace { +bool requested_reliable(pn_link_t* link) +{ + return pn_link_remote_snd_settle_mode(link) == PN_SND_UNSETTLED; +} +bool requested_unreliable(pn_link_t* link) +{ + return pn_link_remote_snd_settle_mode(link) == PN_SND_SETTLED; +} +} + OutgoingFromQueue::OutgoingFromQueue(Broker& broker, const std::string& source, const std::string& target, boost::shared_ptr<Queue> q, pn_link_t* l, Session& session, qpid::sys::OutputControl& o, SubscriptionType type, bool e, bool p) : Outgoing(broker, session, source, target, pn_link_name(l)), @@ -54,7 +65,8 @@ OutgoingFromQueue::OutgoingFromQueue(Broker& broker, const std::string& source, queue(q), deliveries(5000), link(l), out(o), current(0), outstanding(0), buffer(1024)/*used only for header at present*/, - unreliable(pn_link_remote_snd_settle_mode(link) == PN_SND_SETTLED) + //for exclusive queues, assume unreliable unless reliable is explicitly requested; otherwise assume reliable unless unreliable requested + unreliable(exclusive ? !requested_reliable(link) : requested_unreliable(link)) { for (size_t i = 0 ; i < deliveries.capacity(); ++i) { deliveries[i].init(i); @@ -106,8 +118,8 @@ void OutgoingFromQueue::handle(pn_delivery_t* delivery) write(&buffer[0], encoder.getPosition()); Translation t(r.msg); t.write(*this); - if (unreliable) pn_delivery_settle(delivery); if (pn_link_advance(link)) { + if (unreliable) pn_delivery_settle(delivery); --outstanding; outgoingMessageSent(); QPID_LOG(debug, "Sent message " << r.msg.getSequence() << " from " << queue->getName() << ", index=" << r.index); @@ -121,6 +133,10 @@ void OutgoingFromQueue::handle(pn_delivery_t* delivery) } else if (pn_delivery_updated(delivery)) { assert(r.delivery == delivery); r.disposition = pn_delivery_remote_state(delivery); + if (!r.disposition && pn_delivery_settled(delivery)) { + //if peer has settled without setting state, assume accepted + r.disposition = PN_ACCEPTED; + } if (r.disposition) { switch (r.disposition) { case PN_ACCEPTED: @@ -132,17 +148,18 @@ void OutgoingFromQueue::handle(pn_delivery_t* delivery) outgoingMessageRejected(); break; case PN_RELEASED: - if (preAcquires()) queue->release(r.cursor, false);//TODO: for PN_RELEASED, delivery count should not be incremented + if (preAcquires()) queue->release(r.cursor, false);//for PN_RELEASED, delivery count should not be incremented outgoingMessageRejected();//TODO: not quite true... break; case PN_MODIFIED: - if (preAcquires()) queue->release(r.cursor, true);//TODO: proper handling of modified + if (preAcquires()) queue->release(r.cursor, pn_disposition_is_failed(pn_delivery_remote(delivery))); + //TODO: handle undeliverable-here and message-annotations outgoingMessageRejected();//TODO: not quite true... break; default: QPID_LOG(warning, "Unhandled disposition: " << r.disposition); } - //TODO: ony settle once any dequeue on store has completed + //TODO: only settle once any dequeue on store has completed pn_delivery_settle(delivery); r.reset(); } diff --git a/qpid/cpp/src/qpid/broker/amqp/Session.cpp b/qpid/cpp/src/qpid/broker/amqp/Session.cpp index 4627d724a5..c4265ef420 100644 --- a/qpid/cpp/src/qpid/broker/amqp/Session.cpp +++ b/qpid/cpp/src/qpid/broker/amqp/Session.cpp @@ -44,6 +44,7 @@ #include "qpid/framing/FieldTable.h" #include "qpid/framing/MessageTransferBody.h" #include "qpid/log/Statement.h" +#include "qpid/amqp_0_10/Codecs.h" #include <boost/intrusive_ptr.hpp> #include <boost/format.hpp> #include <map> @@ -100,11 +101,13 @@ void readCapabilities(pn_data_t* data, F f) if (type == PN_ARRAY) { pn_data_enter(data); while (pn_data_next(data)) { - f(convert(pn_data_get_symbol(data))); + std::string s = convert(pn_data_get_symbol(data)); + f(s); } pn_data_exit(data); } else if (type == PN_SYMBOL) { - f(convert(pn_data_get_symbol(data))); + std::string s = convert(pn_data_get_symbol(data)); + f(s); } else { QPID_LOG(error, "Skipping capabilities field of type " << pn_type_name(type)); } @@ -200,25 +203,32 @@ Session::ResolvedNode Session::resolve(const std::string name, pn_terminus_t* te node.exchange = connection.getBroker().getExchanges().find(name); node.queue = connection.getBroker().getQueues().find(name); node.topic = connection.getTopics().get(name); + bool createOnDemand = is_capability_requested(CREATE_ON_DEMAND, pn_terminus_capabilities(terminus)); + //Strictly speaking, properties should only be specified when the + //terminus is dynamic. However we will not enforce that here. If + //properties are set on the attach request, we will set them on + //our reply. This allows the 'create' and 'assert' options in the + //qpid messaging API to be implemented over 1.0. + node.properties.read(pn_terminus_properties(terminus)); + if (node.topic) node.exchange = node.topic->getExchange(); - if (node.exchange && !node.queue && is_capability_requested(CREATE_ON_DEMAND, pn_terminus_capabilities(terminus))) { - node.properties.read(pn_terminus_properties(terminus)); + if (node.exchange && !node.queue && createOnDemand) { if (!node.properties.getExchangeType().empty() && node.properties.getExchangeType() != node.exchange->getType()) { + //emulate 0-10 exchange-declare behaviour throw Exception(qpid::amqp::error_conditions::PRECONDITION_FAILED, "Exchange of different type already exists"); } } if (!node.queue && !node.exchange) { - if (pn_terminus_is_dynamic(terminus) || is_capability_requested(CREATE_ON_DEMAND, pn_terminus_capabilities(terminus))) { + if (pn_terminus_is_dynamic(terminus) || createOnDemand) { //is it a queue or an exchange? - node.properties.read(pn_terminus_properties(terminus)); if (node.properties.isQueue()) { node.queue = connection.getBroker().createQueue(name, node.properties.getQueueSettings(), this, node.properties.getAlternateExchange(), connection.getUserId(), connection.getId()).first; } else { qpid::framing::FieldTable args; + qpid::amqp_0_10::translate(node.properties.getProperties(), args); node.exchange = connection.getBroker().createExchange(name, node.properties.getExchangeType(), node.properties.isDurable(), node.properties.getAlternateExchange(), args, connection.getUserId(), connection.getId()).first; } - node.created = true; } else { size_t i = name.find('@'); if (i != std::string::npos && (i+1) < name.length()) { @@ -320,12 +330,11 @@ void Session::setupIncoming(pn_link_t* link, pn_terminus_t* target, const std::s if (node.queue) { setCapabilities(pn_terminus_capabilities(target), pn_terminus_capabilities(pn_link_target(link)), node.queue); authorise.incoming(node.queue); + node.properties.write(pn_terminus_properties(pn_link_target(link)), node.queue); } else if (node.exchange) { setCapabilities(pn_terminus_capabilities(target), pn_terminus_capabilities(pn_link_target(link)), node.exchange); authorise.incoming(node.exchange); - } - if (node.created) { - node.properties.write(pn_terminus_properties(pn_link_target(link))); + node.properties.write(pn_terminus_properties(pn_link_target(link)), node.exchange); } const char* sourceAddress = pn_terminus_get_address(pn_link_remote_source(link)); @@ -357,10 +366,12 @@ void Session::setupIncoming(pn_link_t* link, pn_terminus_t* target, const std::s void Session::setupOutgoing(pn_link_t* link, pn_terminus_t* source, const std::string& name) { ResolvedNode node = resolve(name, source, false); - if (node.queue) setCapabilities(pn_terminus_capabilities(source), pn_terminus_capabilities(pn_link_source(link)), node.queue); - else if (node.exchange) setCapabilities(pn_terminus_capabilities(source), pn_terminus_capabilities(pn_link_source(link)), node.exchange); - if (node.created) { - node.properties.write(pn_terminus_properties(pn_link_source(link))); + if (node.queue) { + setCapabilities(pn_terminus_capabilities(source), pn_terminus_capabilities(pn_link_source(link)), node.queue); + node.properties.write(pn_terminus_properties(pn_link_source(link)), node.queue); + } else if (node.exchange) { + setCapabilities(pn_terminus_capabilities(source), pn_terminus_capabilities(pn_link_source(link)), node.exchange); + node.properties.write(pn_terminus_properties(pn_link_source(link)), node.exchange); } Filter filter; @@ -385,11 +396,14 @@ void Session::setupOutgoing(pn_link_t* link, pn_terminus_t* source, const std::s authorise.access(node.exchange);//do separate access check before trying to create the queue bool shared = is_capability_requested(SHARED, pn_terminus_capabilities(source)); bool durable = pn_terminus_get_durability(source); - QueueSettings settings(durable, !durable); + bool autodelete = !durable && pn_link_remote_snd_settle_mode(link) == PN_SND_SETTLED; + QueueSettings settings(durable, autodelete); + std::string altExchange; if (node.topic) { settings = node.topic->getPolicy(); settings.durable = durable; - settings.autodelete = !durable; + settings.autodelete = autodelete; + altExchange = node.topic->getAlternateExchange(); } settings.autoDeleteDelay = pn_terminus_get_timeout(source); if (settings.autoDeleteDelay) { @@ -408,7 +422,7 @@ void Session::setupOutgoing(pn_link_t* link, pn_terminus_t* source, const std::s queueName << connection.getContainerId() << "_" << pn_link_name(link); } boost::shared_ptr<qpid::broker::Queue> queue - = connection.getBroker().createQueue(queueName.str(), settings, this, "", connection.getUserId(), connection.getId()).first; + = connection.getBroker().createQueue(queueName.str(), settings, this, altExchange, connection.getUserId(), connection.getId()).first; if (!shared) queue->setExclusiveOwner(this); authorise.outgoing(node.exchange, queue, filter); filter.bind(node.exchange, queue); @@ -607,6 +621,11 @@ void IncomingToExchange::handle(qpid::broker::Message& message) authorise.route(exchange, message); DeliverableMessage deliverable(message, 0); exchange->route(deliverable); + if (!deliverable.delivered) { + if (exchange->getAlternate()) { + exchange->getAlternate()->route(deliverable); + } + } } }}} // namespace qpid::broker::amqp diff --git a/qpid/cpp/src/qpid/broker/amqp/Session.h b/qpid/cpp/src/qpid/broker/amqp/Session.h index b94d3c226d..a991ac9e3e 100644 --- a/qpid/cpp/src/qpid/broker/amqp/Session.h +++ b/qpid/cpp/src/qpid/broker/amqp/Session.h @@ -100,8 +100,6 @@ class Session : public ManagedSession, public boost::enable_shared_from_this<Ses boost::shared_ptr<qpid::broker::amqp::Topic> topic; boost::shared_ptr<Relay> relay; NodeProperties properties; - bool created; - ResolvedNode() : created(false) {} }; ResolvedNode resolve(const std::string name, pn_terminus_t* terminus, bool incoming); diff --git a/qpid/cpp/src/qpid/broker/amqp/Topic.cpp b/qpid/cpp/src/qpid/broker/amqp/Topic.cpp index 4bb581628b..9640988834 100644 --- a/qpid/cpp/src/qpid/broker/amqp/Topic.cpp +++ b/qpid/cpp/src/qpid/broker/amqp/Topic.cpp @@ -31,6 +31,7 @@ namespace { const std::string TOPIC("topic"); const std::string EXCHANGE("exchange"); const std::string DURABLE("durable"); +const std::string ALTERNATE_EXCHANGE("alternate-exchange"); const std::string EMPTY; std::string getProperty(const std::string& k, const qpid::types::Variant::Map& m) @@ -52,22 +53,25 @@ qpid::types::Variant::Map filter(const qpid::types::Variant::Map& properties) qpid::types::Variant::Map filtered = properties; filtered.erase(DURABLE); filtered.erase(EXCHANGE); + filtered.erase(ALTERNATE_EXCHANGE); return filtered; } } Topic::Topic(Broker& broker, const std::string& n, const qpid::types::Variant::Map& properties) - : PersistableObject(n, TOPIC, properties), name(n), durable(testProperty(DURABLE, properties)), exchange(broker.getExchanges().get(getProperty(EXCHANGE, properties))) + : PersistableObject(n, TOPIC, properties), name(n), durable(testProperty(DURABLE, properties)), exchange(broker.getExchanges().get(getProperty(EXCHANGE, properties))), + alternateExchange(getProperty(ALTERNATE_EXCHANGE, properties)) { if (exchange->getName().empty()) throw qpid::Exception("Exchange must be specified."); qpid::types::Variant::Map unused; - policy.populate(properties, unused); + qpid::types::Variant::Map filtered = filter(properties); + policy.populate(filtered, unused); qpid::management::ManagementAgent* agent = broker.getManagementAgent(); if (agent != 0) { topic = _qmf::Topic::shared_ptr(new _qmf::Topic(agent, this, name, exchange->GetManagementObject()->getObjectId(), durable)); - topic->set_properties(filter(properties)); + topic->set_properties(filtered); agent->addObject(topic); } } @@ -99,7 +103,10 @@ const std::string& Topic::getName() const { return name; } - +const std::string& Topic::getAlternateExchange() const +{ + return alternateExchange; +} boost::shared_ptr<Topic> TopicRegistry::createTopic(Broker& broker, const std::string& name, const qpid::types::Variant::Map& properties) { boost::shared_ptr<Topic> topic(new Topic(broker, name, properties)); diff --git a/qpid/cpp/src/qpid/broker/amqp/Topic.h b/qpid/cpp/src/qpid/broker/amqp/Topic.h index decebdb0d4..e08830ba0f 100644 --- a/qpid/cpp/src/qpid/broker/amqp/Topic.h +++ b/qpid/cpp/src/qpid/broker/amqp/Topic.h @@ -51,6 +51,7 @@ class Topic : public PersistableObject, public management::Manageable ~Topic(); const std::string& getName() const; const QueueSettings& getPolicy() const; + const std::string& getAlternateExchange() const; boost::shared_ptr<Exchange> getExchange(); bool isDurable() const; boost::shared_ptr<qpid::management::ManagementObject> GetManagementObject() const; @@ -59,6 +60,7 @@ class Topic : public PersistableObject, public management::Manageable bool durable; boost::shared_ptr<Exchange> exchange; QueueSettings policy; + std::string alternateExchange; qmf::org::apache::qpid::broker::Topic::shared_ptr topic; }; diff --git a/qpid/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.cpp b/qpid/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.cpp index e3a967796b..8055b3fe1a 100644 --- a/qpid/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.cpp +++ b/qpid/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.cpp @@ -52,6 +52,11 @@ uint64_t MessageTransfer::getContentSize() const return frames.getContentSize(); } +uint64_t MessageTransfer::getMessageSize() const +{ + return getRequiredCredit(); +} + std::string MessageTransfer::getAnnotationAsString(const std::string& key) const { const qpid::framing::MessageProperties* mp = getProperties<qpid::framing::MessageProperties>(); @@ -180,7 +185,7 @@ class SendHeader copy.castBody<AMQHeaderBody>()->get<MessageProperties>(true); for (qpid::types::Variant::Map::const_iterator i = annotations.begin(); i != annotations.end(); ++i) { - props->getApplicationHeaders().setString(i->first, i->second.asString()); + props->getApplicationHeaders().set(i->first, qpid::amqp_0_10::translate(i->second)); } } if (redelivered || ttl || timestamp) { @@ -397,7 +402,7 @@ boost::intrusive_ptr<PersistableMessage> MessageTransfer::merge(const std::map<s boost::intrusive_ptr<MessageTransfer> clone(new MessageTransfer(this->frames)); qpid::framing::MessageProperties* mp = clone->frames.getHeaders()->get<qpid::framing::MessageProperties>(true); for (qpid::types::Variant::Map::const_iterator i = annotations.begin(); i != annotations.end(); ++i) { - mp->getApplicationHeaders().setString(i->first, i->second); + mp->getApplicationHeaders().set(i->first, qpid::amqp_0_10::translate(i->second)); } return clone; } diff --git a/qpid/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.h b/qpid/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.h index d32c0d07fd..422446db51 100644 --- a/qpid/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.h +++ b/qpid/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.h @@ -45,6 +45,7 @@ class MessageTransfer : public qpid::broker::Message::Encoding, public qpid::bro bool isPersistent() const; uint8_t getPriority() const; uint64_t getContentSize() const; + uint64_t getMessageSize() const; qpid::amqp::MessageId getMessageId() const; qpid::amqp::MessageId getCorrelationId() const; std::string getPropertyAsString(const std::string& key) const; diff --git a/qpid/cpp/src/qpid/client/QueueOptions.cpp b/qpid/cpp/src/qpid/client/QueueOptions.cpp index 460f3f5490..e589bd76cd 100644 --- a/qpid/cpp/src/qpid/client/QueueOptions.cpp +++ b/qpid/cpp/src/qpid/client/QueueOptions.cpp @@ -38,7 +38,6 @@ const std::string QueueOptions::strFLOW_TO_DISK("flow_to_disk"); const std::string QueueOptions::strRING("ring"); const std::string QueueOptions::strRING_STRICT("ring_strict"); const std::string QueueOptions::strLastValueQueue("qpid.last_value_queue"); -const std::string QueueOptions::strPersistLastNode("qpid.persist_last_node"); const std::string QueueOptions::strLVQMatchProperty("qpid.LVQ_key"); const std::string QueueOptions::strLastValueQueueNoBrowse("qpid.last_value_queue_no_browse"); const std::string QueueOptions::strQueueEventMode("qpid.queue_event_generation"); @@ -74,11 +73,6 @@ void QueueOptions::setSizePolicy(QueueSizePolicy sp, uint64_t maxSize, uint32_t } -void QueueOptions::setPersistLastNode() -{ - setInt(strPersistLastNode, 1); -} - void QueueOptions::setOrdering(QueueOrderingPolicy op) { if (op == LVQ){ @@ -102,11 +96,6 @@ void QueueOptions::clearSizePolicy() erase(strTypeKey); } -void QueueOptions::clearPersistLastNode() -{ - erase(strPersistLastNode); -} - void QueueOptions::clearOrdering() { erase(strLastValueQueue); diff --git a/qpid/cpp/src/qpid/client/QueueOptions.h b/qpid/cpp/src/qpid/client/QueueOptions.h index 3984b63fdd..a2f30a50b5 100644 --- a/qpid/cpp/src/qpid/client/QueueOptions.h +++ b/qpid/cpp/src/qpid/client/QueueOptions.h @@ -56,12 +56,6 @@ class QPID_CLIENT_CLASS_EXTERN QueueOptions: public framing::FieldTable QPID_CLIENT_EXTERN void setSizePolicy(QueueSizePolicy sp, uint64_t maxSize, uint32_t maxCount ); /** - * Enables the persisting of a queue to the store module when a cluster fails down to it's last - * node. Does so optimistically. Will start persisting when cluster count >1 again. - */ - QPID_CLIENT_EXTERN void setPersistLastNode(); - - /** * Sets the odering policy on the Queue, default ordering is FIFO. */ QPID_CLIENT_EXTERN void setOrdering(QueueOrderingPolicy op); @@ -72,11 +66,6 @@ class QPID_CLIENT_CLASS_EXTERN QueueOptions: public framing::FieldTable QPID_CLIENT_EXTERN void clearSizePolicy(); /** - * Clear Persist Last Node Policy - */ - QPID_CLIENT_EXTERN void clearPersistLastNode(); - - /** * get the key used match LVQ in args for message transfer */ QPID_CLIENT_EXTERN void getLVQKey(std::string& key); @@ -116,7 +105,6 @@ class QPID_CLIENT_CLASS_EXTERN QueueOptions: public framing::FieldTable static QPID_CLIENT_EXTERN const std::string strRING; static QPID_CLIENT_EXTERN const std::string strRING_STRICT; static QPID_CLIENT_EXTERN const std::string strLastValueQueue; - static QPID_CLIENT_EXTERN const std::string strPersistLastNode; static QPID_CLIENT_EXTERN const std::string strLVQMatchProperty; static QPID_CLIENT_EXTERN const std::string strLastValueQueueNoBrowse; static QPID_CLIENT_EXTERN const std::string strQueueEventMode; diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp b/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp index 982bfa3503..71527bc323 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp +++ b/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp @@ -490,7 +490,7 @@ void SessionImpl::releaseImpl(qpid::messaging::Message& m) { SequenceSet set; set.add(MessageImplAccess::get(m).getInternalId()); - session.messageRelease(set); + session.messageRelease(set, true); } void SessionImpl::receiverCancelled(const std::string& name) diff --git a/qpid/cpp/src/qpid/legacystore/jrnl/deq_rec.cpp b/qpid/cpp/src/qpid/legacystore/jrnl/deq_rec.cpp index f820c3c075..270ebdd69e 100644 --- a/qpid/cpp/src/qpid/legacystore/jrnl/deq_rec.cpp +++ b/qpid/cpp/src/qpid/legacystore/jrnl/deq_rec.cpp @@ -270,12 +270,14 @@ deq_rec::decode(rec_hdr& h, void* rptr, u_int32_t rec_offs_dblks, u_int32_t max_ // Get and check header _deq_hdr.hdr_copy(h); rd_cnt = sizeof(rec_hdr); - _deq_hdr._deq_rid = *(u_int64_t*)((char*)rptr + rd_cnt); + //_deq_hdr._deq_rid = *(u_int64_t*)((char*)rptr + rd_cnt); + std::memcpy((void*)&_deq_hdr._deq_rid, (char*)rptr + rd_cnt, sizeof(u_int64_t)); rd_cnt += sizeof(u_int64_t); #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) rd_cnt += sizeof(u_int32_t); // Filler 0 #endif - _deq_hdr._xidsize = *(std::size_t*)((char*)rptr + rd_cnt); + //_deq_hdr._xidsize = *(std::size_t*)((char*)rptr + rd_cnt); + std::memcpy((void*)&_deq_hdr._xidsize, (char*)rptr + rd_cnt, sizeof(std::size_t)); rd_cnt = _deq_hdr.size(); chk_hdr(); if (_deq_hdr._xidsize) diff --git a/qpid/cpp/src/qpid/legacystore/jrnl/enq_rec.cpp b/qpid/cpp/src/qpid/legacystore/jrnl/enq_rec.cpp index 468599836b..94f42066a0 100644 --- a/qpid/cpp/src/qpid/legacystore/jrnl/enq_rec.cpp +++ b/qpid/cpp/src/qpid/legacystore/jrnl/enq_rec.cpp @@ -360,7 +360,8 @@ enq_rec::decode(rec_hdr& h, void* rptr, u_int32_t rec_offs_dblks, u_int32_t max_ #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) rd_cnt += sizeof(u_int32_t); // Filler 0 #endif - _enq_hdr._xidsize = *(std::size_t*)((char*)rptr + rd_cnt); + //_enq_hdr._xidsize = *(std::size_t*)((char*)rptr + rd_cnt); + std::memcpy((void*)&_enq_hdr._xidsize, (char*)rptr + rd_cnt, sizeof(std::size_t)); rd_cnt += sizeof(std::size_t); #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) rd_cnt += sizeof(u_int32_t); // Filler 0 @@ -368,7 +369,8 @@ enq_rec::decode(rec_hdr& h, void* rptr, u_int32_t rec_offs_dblks, u_int32_t max_ #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) rd_cnt += sizeof(u_int32_t); // Filler 1 #endif - _enq_hdr._dsize = *(std::size_t*)((char*)rptr + rd_cnt); + //_enq_hdr._dsize = *(std::size_t*)((char*)rptr + rd_cnt); + std::memcpy((void*)&_enq_hdr._dsize, (char*)rptr + rd_cnt, sizeof(std::size_t)); rd_cnt = _enq_hdr.size(); chk_hdr(); if (_enq_hdr._xidsize + (_enq_hdr.is_external() ? 0 : _enq_hdr._dsize)) diff --git a/qpid/cpp/src/qpid/legacystore/jrnl/txn_rec.cpp b/qpid/cpp/src/qpid/legacystore/jrnl/txn_rec.cpp index 918a6ce902..1008e7700e 100644 --- a/qpid/cpp/src/qpid/legacystore/jrnl/txn_rec.cpp +++ b/qpid/cpp/src/qpid/legacystore/jrnl/txn_rec.cpp @@ -270,7 +270,8 @@ txn_rec::decode(rec_hdr& h, void* rptr, u_int32_t rec_offs_dblks, u_int32_t max_ #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) rd_cnt += sizeof(u_int32_t); // Filler 0 #endif - _txn_hdr._xidsize = *(std::size_t*)((char*)rptr + rd_cnt); + //_txn_hdr._xidsize = *(std::size_t*)((char*)rptr + rd_cnt); + std::memcpy((void*)&_txn_hdr._xidsize, (char*)rptr + rd_cnt, sizeof(std::size_t)); rd_cnt = _txn_hdr.size(); chk_hdr(); _buff = std::malloc(_txn_hdr._xidsize); diff --git a/qpid/cpp/src/qpid/management/ManagementAgent.cpp b/qpid/cpp/src/qpid/management/ManagementAgent.cpp index d80dd6e6a3..d4a07f407c 100644 --- a/qpid/cpp/src/qpid/management/ManagementAgent.cpp +++ b/qpid/cpp/src/qpid/management/ManagementAgent.cpp @@ -2122,19 +2122,21 @@ bool ManagementAgent::authorizeAgentMessage(Message& msg) string methodName; string cid; + boost::intrusive_ptr<const qpid::broker::amqp_0_10::MessageTransfer> transfer = protocols->translate(msg); // - // If the message is larger than our working buffer size, we can't determine if it's - // authorized or not. In this case, return true (authorized) if there is no ACL in place, - // otherwise return false; + // If the message is larger than our working buffer size (or if it + // could not be converted to an 0-10 messgae-transfer), we can't + // determine if it's authorized or not. In this case, return true + // (authorized) if there is no ACL in place, otherwise return + // false; // - if (msg.getContentSize() > qmfV1BufferSize) + if (!transfer || transfer->getContentSize() > qmfV1BufferSize) return broker->getAcl() == 0; - inBuffer.putRawData(msg.getContent()); + inBuffer.putRawData(transfer->getContent()); uint32_t bufferLen = inBuffer.getPosition(); inBuffer.reset(); - boost::intrusive_ptr<const qpid::broker::amqp_0_10::MessageTransfer> transfer = protocols->translate(msg); const framing::MessageProperties* p = transfer ? transfer->getFrames().getHeaders()->get<framing::MessageProperties>() : 0; @@ -2283,13 +2285,13 @@ void ManagementAgent::dispatchAgentCommand(Message& msg, bool viaLocal) ResizableBuffer inBuffer(qmfV1BufferSize); uint8_t opcode; - if (msg.getContentSize() > qmfV1BufferSize) { + if (transfer->getContentSize() > qmfV1BufferSize) { QPID_LOG(debug, "ManagementAgent::dispatchAgentCommandLH: Message too large: " << - msg.getContentSize()); + transfer->getContentSize()); return; } - inBuffer.putRawData(msg.getContent()); + inBuffer.putRawData(transfer->getContent()); uint32_t bufferLen = inBuffer.getPosition(); inBuffer.reset(); diff --git a/qpid/cpp/src/qpid/messaging/Connection.cpp b/qpid/cpp/src/qpid/messaging/Connection.cpp index 71618d1057..c8a60fc56b 100644 --- a/qpid/cpp/src/qpid/messaging/Connection.cpp +++ b/qpid/cpp/src/qpid/messaging/Connection.cpp @@ -31,6 +31,9 @@ namespace qpid { namespace messaging { +// Explicitly instantiate Handle superclass +template class Handle<ConnectionImpl>; + using namespace qpid::types; typedef PrivateImplRef<qpid::messaging::Connection> PI; diff --git a/qpid/cpp/src/qpid/messaging/HandleInstantiator.cpp b/qpid/cpp/src/qpid/messaging/HandleInstantiator.cpp deleted file mode 100644 index c9a7680bb4..0000000000 --- a/qpid/cpp/src/qpid/messaging/HandleInstantiator.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-#include "qpid/messaging/Connection.h"
-#include "qpid/messaging/Receiver.h"
-#include "qpid/messaging/Sender.h"
-#include "qpid/messaging/Session.h"
-
-namespace qpid {
-namespace messaging {
-
-using namespace qpid::types;
-
-void HandleInstantiatorDoNotCall(void)
-{
- // This function exists to instantiate various template Handle
- // bool functions. The instances are then available to
- // the qpidmessaging DLL and subsequently exported.
- // This function must not be exported nor called called.
- // For further information refer to
- // https://issues.apache.org/jira/browse/QPID-2926
-
- Connection connection;
- if (connection.isValid()) connection.close();
- if (connection.isNull() ) connection.close();
- if (connection ) connection.close();
- if (!connection ) connection.close();
-
- Receiver receiver;
- if (receiver.isValid()) receiver.close();
- if (receiver.isNull() ) receiver.close();
- if (receiver ) receiver.close();
- if (!receiver ) receiver.close();
-
- Sender sender;
- if (sender.isValid()) sender.close();
- if (sender.isNull() ) sender.close();
- if (sender ) sender.close();
- if (!sender ) sender.close();
-
- Session session;
- if (session.isValid()) session.close();
- if (session.isNull() ) session.close();
- if (session ) session.close();
- if (!session ) session.close();
-}
-}} // namespace qpid::messaging
diff --git a/qpid/cpp/src/qpid/messaging/ProtocolRegistry.cpp b/qpid/cpp/src/qpid/messaging/ProtocolRegistry.cpp index 0232da8ae1..9e1e5f23fe 100644 --- a/qpid/cpp/src/qpid/messaging/ProtocolRegistry.cpp +++ b/qpid/cpp/src/qpid/messaging/ProtocolRegistry.cpp @@ -19,7 +19,7 @@ * */ #include "ProtocolRegistry.h" -#include "qpid/Exception.h" +#include "qpid/messaging/exceptions.h" #include "qpid/client/amqp0_10/ConnectionImpl.h" #include "qpid/client/LoadPlugins.h" #include <map> @@ -61,7 +61,7 @@ ConnectionImpl* ProtocolRegistry::create(const std::string& url, const Variant:: Registry::const_iterator i = theRegistry().find(name.asString()); if (i != theRegistry().end()) return (i->second)(url, stripped); else if (name.asString() == "amqp0-10") return new qpid::client::amqp0_10::ConnectionImpl(url, stripped); - else throw qpid::Exception("Unsupported protocol: " + name.asString()); + else throw MessagingException("Unsupported protocol: " + name.asString()); } return 0; } diff --git a/qpid/cpp/src/qpid/messaging/Receiver.cpp b/qpid/cpp/src/qpid/messaging/Receiver.cpp index f60e5f55b3..18670ec068 100644 --- a/qpid/cpp/src/qpid/messaging/Receiver.cpp +++ b/qpid/cpp/src/qpid/messaging/Receiver.cpp @@ -29,6 +29,9 @@ namespace qpid { namespace messaging { +// Explicitly instantiate Handle superclass +template class Handle<ReceiverImpl>; + typedef PrivateImplRef<qpid::messaging::Receiver> PI; Receiver::Receiver(ReceiverImpl* impl) { PI::ctor(*this, impl); } diff --git a/qpid/cpp/src/qpid/messaging/Sender.cpp b/qpid/cpp/src/qpid/messaging/Sender.cpp index a60de3d606..a26f2544c8 100644 --- a/qpid/cpp/src/qpid/messaging/Sender.cpp +++ b/qpid/cpp/src/qpid/messaging/Sender.cpp @@ -27,6 +27,10 @@ namespace qpid { namespace messaging { + +// Explicitly instantiate Handle superclass +template class Handle<SenderImpl>; + typedef PrivateImplRef<qpid::messaging::Sender> PI; Sender::Sender(SenderImpl* impl) { PI::ctor(*this, impl); } diff --git a/qpid/cpp/src/qpid/messaging/Session.cpp b/qpid/cpp/src/qpid/messaging/Session.cpp index cccfd9a873..fd0519705d 100644 --- a/qpid/cpp/src/qpid/messaging/Session.cpp +++ b/qpid/cpp/src/qpid/messaging/Session.cpp @@ -30,6 +30,9 @@ namespace qpid { namespace messaging { +// Explicitly instantiate Handle superclass +template class Handle<SessionImpl>; + typedef PrivateImplRef<qpid::messaging::Session> PI; Session::Session(SessionImpl* impl) { PI::ctor(*this, impl); } diff --git a/qpid/cpp/src/qpid/messaging/amqp/AddressHelper.cpp b/qpid/cpp/src/qpid/messaging/amqp/AddressHelper.cpp index beac50cdac..9d0f60a8b7 100644 --- a/qpid/cpp/src/qpid/messaging/amqp/AddressHelper.cpp +++ b/qpid/cpp/src/qpid/messaging/amqp/AddressHelper.cpp @@ -22,6 +22,7 @@ #include "qpid/messaging/Address.h" #include "qpid/messaging/AddressImpl.h" #include "qpid/amqp/descriptors.h" +#include "qpid/types/encodings.h" #include "qpid/log/Statement.h" #include <vector> #include <set> @@ -290,6 +291,127 @@ void write(pn_data_t* data, const Variant& value) break; } } +bool read(pn_data_t* data, pn_type_t type, qpid::types::Variant& value); +bool read(pn_data_t* data, qpid::types::Variant& value) +{ + return read(data, pn_data_type(data), value); +} +void readList(pn_data_t* data, qpid::types::Variant::List& value) +{ + size_t count = pn_data_get_list(data); + pn_data_enter(data); + for (size_t i = 0; i < count && pn_data_next(data); ++i) { + qpid::types::Variant e; + if (read(data, e)) value.push_back(e); + } + pn_data_exit(data); +} +void readMap(pn_data_t* data, qpid::types::Variant::Map& value) +{ + size_t count = pn_data_get_list(data); + pn_data_enter(data); + for (size_t i = 0; i < (count/2) && pn_data_next(data); ++i) { + std::string key = convert(pn_data_get_symbol(data)); + pn_data_next(data); + qpid::types::Variant e; + if (read(data, e)) value[key]= e; + } + pn_data_exit(data); +} +void readArray(pn_data_t* data, qpid::types::Variant::List& value) +{ + size_t count = pn_data_get_array(data); + pn_type_t type = pn_data_get_array_type(data); + pn_data_enter(data); + for (size_t i = 0; i < count && pn_data_next(data); ++i) { + qpid::types::Variant e; + if (read(data, type, e)) value.push_back(e); + } + pn_data_exit(data); +} +bool read(pn_data_t* data, pn_type_t type, qpid::types::Variant& value) +{ + switch (type) { + case PN_NULL: + if (value.getType() != qpid::types::VAR_VOID) value = qpid::types::Variant(); + return true; + case PN_BOOL: + value = pn_data_get_bool(data); + return true; + case PN_UBYTE: + value = pn_data_get_ubyte(data); + return true; + case PN_BYTE: + value = pn_data_get_byte(data); + return true; + case PN_USHORT: + value = pn_data_get_ushort(data); + return true; + case PN_SHORT: + value = pn_data_get_short(data); + return true; + case PN_UINT: + value = pn_data_get_uint(data); + return true; + case PN_INT: + value = pn_data_get_int(data); + return true; + case PN_CHAR: + value = pn_data_get_char(data); + return true; + case PN_ULONG: + value = pn_data_get_ulong(data); + return true; + case PN_LONG: + value = pn_data_get_long(data); + return true; + case PN_TIMESTAMP: + value = pn_data_get_timestamp(data); + return true; + case PN_FLOAT: + value = pn_data_get_float(data); + return true; + case PN_DOUBLE: + value = pn_data_get_double(data); + return true; + case PN_UUID: + value = qpid::types::Uuid(pn_data_get_uuid(data).bytes); + return true; + case PN_BINARY: + value = convert(pn_data_get_binary(data)); + value.setEncoding(qpid::types::encodings::BINARY); + return true; + case PN_STRING: + value = convert(pn_data_get_string(data)); + value.setEncoding(qpid::types::encodings::UTF8); + return true; + case PN_SYMBOL: + value = convert(pn_data_get_string(data)); + value.setEncoding(qpid::types::encodings::ASCII); + return true; + case PN_LIST: + value = qpid::types::Variant::List(); + readList(data, value.asList()); + return true; + break; + case PN_MAP: + value = qpid::types::Variant::Map(); + readMap(data, value.asMap()); + return true; + case PN_ARRAY: + value = qpid::types::Variant::List(); + readArray(data, value.asList()); + return true; + case PN_DESCRIBED: + case PN_DECIMAL32: + case PN_DECIMAL64: + case PN_DECIMAL128: + default: + return false; + } + +} + const uint32_t DEFAULT_DURABLE_TIMEOUT(15*60);//15 minutes const uint32_t DEFAULT_TIMEOUT(0); } @@ -354,7 +476,7 @@ AddressHelper::AddressHelper(const Address& address) : properties[LIFETIME_POLICY] = DELETE_ON_CLOSE; } - if (properties.size() && !(isTemporary || createPolicy.size())) { + if (properties.size() && !(isTemporary || !createPolicy.empty() || !assertPolicy.empty())) { QPID_LOG(warning, "Properties will be ignored! " << address); } @@ -420,10 +542,48 @@ void AddressHelper::addFilter(const std::string& name, const std::string& descri filters.push_back(Filter(name, descriptor, value)); } +namespace { +bool checkLifetimePolicy(const std::string& requested, const std::string& actual) +{ + if (actual == qpid::amqp::lifetime_policy::DELETE_ON_CLOSE_SYMBOL && requested == DELETE_ON_CLOSE) return true; + else if (actual == qpid::amqp::lifetime_policy::DELETE_ON_NO_LINKS_SYMBOL && requested == DELETE_IF_UNUSED) return true; + else if (actual == qpid::amqp::lifetime_policy::DELETE_ON_NO_MESSAGES_SYMBOL && requested == DELETE_IF_EMPTY) return true; + else if (actual == qpid::amqp::lifetime_policy::DELETE_ON_NO_LINKS_OR_MESSAGES_SYMBOL && requested == DELETE_IF_UNUSED_AND_EMPTY) return true; + else return actual == requested; +} +bool checkLifetimePolicy(const std::string& requested, uint64_t actual) +{ + if (actual == qpid::amqp::lifetime_policy::DELETE_ON_CLOSE_CODE) + return checkLifetimePolicy(requested, qpid::amqp::lifetime_policy::DELETE_ON_CLOSE_SYMBOL); + else if (actual == qpid::amqp::lifetime_policy::DELETE_ON_NO_LINKS_CODE) + return checkLifetimePolicy(requested, qpid::amqp::lifetime_policy::DELETE_ON_NO_LINKS_SYMBOL); + else if (actual == qpid::amqp::lifetime_policy::DELETE_ON_NO_MESSAGES_CODE) + return checkLifetimePolicy(requested, qpid::amqp::lifetime_policy::DELETE_ON_NO_MESSAGES_SYMBOL); + else if (actual == qpid::amqp::lifetime_policy::DELETE_ON_NO_LINKS_OR_MESSAGES_CODE) + return checkLifetimePolicy(requested, qpid::amqp::lifetime_policy::DELETE_ON_NO_LINKS_OR_MESSAGES_SYMBOL); + else + return false; +} +bool checkLifetimePolicy(const std::string& requested, pn_data_t* actual) +{ + bool result(false); + if (pn_data_is_described(actual)) { + pn_data_enter(actual); + pn_data_next(actual); + if (pn_data_type(actual) == PN_ULONG) { + result = checkLifetimePolicy(requested, pn_data_get_ulong(actual)); + } else if (pn_data_type(actual) == PN_SYMBOL) { + result = checkLifetimePolicy(requested, convert(pn_data_get_symbol(actual))); + } + pn_data_exit(actual); + } + return result; +} +} void AddressHelper::checkAssertion(pn_terminus_t* terminus, CheckMode mode) { if (assertEnabled(mode)) { - QPID_LOG(debug, "checking assertions: " << capabilities); + QPID_LOG(debug, "checking capabilities: " << capabilities); //ensure all desired capabilities have been offered std::set<std::string> desired; for (Variant::List::const_iterator i = capabilities.begin(); i != capabilities.end(); ++i) { @@ -493,6 +653,40 @@ void AddressHelper::checkAssertion(pn_terminus_t* terminus, CheckMode mode) } } if (!first) throw qpid::messaging::AssertionFailed(missing.str()); + + //assert on properties (Note: this violates the AMQP 1.0 + //specification - as does the create option - by sending + //node-properties even if the dynamic option is not + //set. However this can be avoided by not specifying any node + //properties when asserting) + if (!type.empty() || durableNode || !properties.empty()) { + qpid::types::Variant::Map requested = properties; + if (!type.empty()) requested[SUPPORTED_DIST_MODES] = type == TOPIC ? COPY : MOVE; + if (durableNode) requested[DURABLE] = true; + + data = pn_terminus_properties(terminus); + if (pn_data_next(data)) { + size_t count = pn_data_get_map(data); + pn_data_enter(data); + for (size_t i = 0; i < count && pn_data_next(data); ++i) { + std::string key = convert(pn_data_get_symbol(data)); + pn_data_next(data); + qpid::types::Variant::Map::const_iterator j = requested.find(key); + qpid::types::Variant v; + if (j != requested.end() && + ((key == LIFETIME_POLICY && checkLifetimePolicy(j->second.asString(), data)) || + (read(data, v) && v.asString() == j->second.asString()))) { + requested.erase(j->first); + } + } + pn_data_exit(data); + if (!requested.empty()) { + std::stringstream missing; + missing << "Requested node properties not met: " << requested; + throw qpid::messaging::AssertionFailed(missing.str()); + } + } + } } } @@ -582,6 +776,8 @@ void AddressHelper::configure(pn_link_t* link, pn_terminus_t* terminus, CheckMod //application expects name of node to be as specified setNodeProperties(terminus); createOnDemand = true; + } else if (assertEnabled(mode)) { + setNodeProperties(terminus); } } @@ -616,6 +812,13 @@ void AddressHelper::configure(pn_link_t* link, pn_terminus_t* terminus, CheckMod } if (isUnreliable()) { pn_link_set_snd_settle_mode(link, PN_SND_SETTLED); + } else if (!reliability.empty()) { + if (reliability == EXACTLY_ONCE ) { + QPID_LOG(warning, "Unsupported reliability mode: " << reliability); + } else if (reliability != AT_LEAST_ONCE ) { + QPID_LOG(warning, "Unrecognised reliability mode: " << reliability); + } + pn_link_set_snd_settle_mode(link, PN_SND_UNSETTLED); } } @@ -688,7 +891,7 @@ void AddressHelper::setNodeProperties(pn_terminus_t* terminus) putLifetimePolicy(data, toLifetimePolicy(i->second.asString())); } else { pn_data_put_symbol(data, convert(i->first)); - pn_data_put_string(data, convert(i->second.asString())); + write(data, i->second); } } pn_data_exit(data); diff --git a/qpid/cpp/src/qpid/messaging/amqp/ConnectionContext.cpp b/qpid/cpp/src/qpid/messaging/amqp/ConnectionContext.cpp index dba5cb1e1c..b230aeaf0d 100644 --- a/qpid/cpp/src/qpid/messaging/amqp/ConnectionContext.cpp +++ b/qpid/cpp/src/qpid/messaging/amqp/ConnectionContext.cpp @@ -234,6 +234,14 @@ void ConnectionContext::acknowledge(boost::shared_ptr<SessionContext> ssn, qpid: wakeupDriver(); } +void ConnectionContext::nack(boost::shared_ptr<SessionContext> ssn, qpid::messaging::Message& message, bool reject) +{ + qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock); + checkClosed(ssn); + ssn->nack(MessageImplAccess::get(message).getInternalId(), reject); + wakeupDriver(); +} + void ConnectionContext::detach(boost::shared_ptr<SessionContext> ssn, boost::shared_ptr<SenderContext> lnk) { qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock); @@ -468,6 +476,15 @@ void ConnectionContext::checkClosed(boost::shared_ptr<SessionContext> ssn) } } +bool ConnectionContext::isClosed(boost::shared_ptr<SessionContext> ssn, boost::shared_ptr<ReceiverContext> lnk) +{ + try { + checkClosed(ssn, lnk->receiver); + return false; + } catch (const LinkError&) { + return true; + } +} void ConnectionContext::checkClosed(boost::shared_ptr<SessionContext> ssn, boost::shared_ptr<ReceiverContext> lnk) { checkClosed(ssn, lnk->receiver); @@ -584,7 +601,7 @@ std::size_t ConnectionContext::decodePlain(const char* buffer, std::size_t size) lock.notifyAll(); return n; } else if (n == PN_ERR) { - throw qpid::Exception(QPID_MSG("Error on input: " << getError())); + throw MessagingException(QPID_MSG("Error on input: " << getError())); } else { return 0; } @@ -608,7 +625,7 @@ std::size_t ConnectionContext::encodePlain(char* buffer, std::size_t size) haveOutput = true; return n; } else if (n == PN_ERR) { - throw qpid::Exception(QPID_MSG("Error on output: " << getError())); + throw MessagingException(QPID_MSG("Error on output: " << getError())); } else if (n == PN_EOS) { haveOutput = false; return 0;//Is this right? diff --git a/qpid/cpp/src/qpid/messaging/amqp/ConnectionContext.h b/qpid/cpp/src/qpid/messaging/amqp/ConnectionContext.h index 2fdba7a3b2..b31ffe62e9 100644 --- a/qpid/cpp/src/qpid/messaging/amqp/ConnectionContext.h +++ b/qpid/cpp/src/qpid/messaging/amqp/ConnectionContext.h @@ -77,10 +77,12 @@ class ConnectionContext : public qpid::sys::ConnectionCodec, public qpid::messag void attach(boost::shared_ptr<SessionContext>, boost::shared_ptr<ReceiverContext>); void detach(boost::shared_ptr<SessionContext>, boost::shared_ptr<SenderContext>); void detach(boost::shared_ptr<SessionContext>, boost::shared_ptr<ReceiverContext>); + bool isClosed(boost::shared_ptr<SessionContext>, boost::shared_ptr<ReceiverContext>); void send(boost::shared_ptr<SessionContext>, boost::shared_ptr<SenderContext> ctxt, const qpid::messaging::Message& message, bool sync); bool fetch(boost::shared_ptr<SessionContext> ssn, boost::shared_ptr<ReceiverContext> lnk, qpid::messaging::Message& message, qpid::messaging::Duration timeout); bool get(boost::shared_ptr<SessionContext> ssn, boost::shared_ptr<ReceiverContext> lnk, qpid::messaging::Message& message, qpid::messaging::Duration timeout); void acknowledge(boost::shared_ptr<SessionContext> ssn, qpid::messaging::Message* message, bool cumulative); + void nack(boost::shared_ptr<SessionContext> ssn, qpid::messaging::Message& message, bool reject); void setOption(const std::string& name, const qpid::types::Variant& value); std::string getAuthenticatedUsername(); diff --git a/qpid/cpp/src/qpid/messaging/amqp/EncodedMessage.cpp b/qpid/cpp/src/qpid/messaging/amqp/EncodedMessage.cpp index 266060c117..504c5030a8 100644 --- a/qpid/cpp/src/qpid/messaging/amqp/EncodedMessage.cpp +++ b/qpid/cpp/src/qpid/messaging/amqp/EncodedMessage.cpp @@ -20,7 +20,9 @@ */ #include "qpid/messaging/amqp/EncodedMessage.h" #include "qpid/messaging/Address.h" +#include "qpid/messaging/exceptions.h" #include "qpid/messaging/MessageImpl.h" +#include "qpid/Exception.h" #include "qpid/amqp/Decoder.h" #include "qpid/amqp/DataBuilder.h" #include "qpid/amqp/ListBuilder.h" @@ -100,66 +102,73 @@ const char* EncodedMessage::getData() const void EncodedMessage::init(qpid::messaging::MessageImpl& impl) { - //initial scan of raw data - qpid::amqp::Decoder decoder(data, size); - InitialScan reader(*this, impl); - decoder.read(reader); - bareMessage = reader.getBareMessage(); - if (bareMessage.data && !bareMessage.size) { - bareMessage.size = (data + size) - bareMessage.data; + try { + //initial scan of raw data + qpid::amqp::Decoder decoder(data, size); + InitialScan reader(*this, impl); + decoder.read(reader); + bareMessage = reader.getBareMessage(); + if (bareMessage.data && !bareMessage.size) { + bareMessage.size = (data + size) - bareMessage.data; + } + } catch (const qpid::Exception& e) { + throw FetchError(e.what()); } - } void EncodedMessage::setNestAnnotationsOption(bool b) { nestAnnotations = b; } void EncodedMessage::populate(qpid::types::Variant::Map& map) const { - //decode application properties - if (applicationProperties) { - qpid::amqp::Decoder decoder(applicationProperties.data, applicationProperties.size); - decoder.readMap(map); - } - //add in 'x-amqp-' prefixed values - if (!!firstAcquirer) { - map["x-amqp-first-acquirer"] = firstAcquirer.get(); - } - if (!!deliveryCount) { - map["x-amqp-delivery-count"] = deliveryCount.get(); - } - if (to) { - map["x-amqp-to"] = to.str(); - } - if (!!absoluteExpiryTime) { - map["x-amqp-absolute-expiry-time"] = absoluteExpiryTime.get(); - } - if (!!creationTime) { - map["x-amqp-creation-time"] = creationTime.get(); - } - if (groupId) { - map["x-amqp-group-id"] = groupId.str(); - } - if (!!groupSequence) { - map["x-amqp-qroup-sequence"] = groupSequence.get(); - } - if (replyToGroupId) { - map["x-amqp-reply-to-group-id"] = replyToGroupId.str(); - } - //add in any annotations - if (deliveryAnnotations) { - qpid::amqp::Decoder decoder(deliveryAnnotations.data, deliveryAnnotations.size); - if (nestAnnotations) { - map["x-amqp-delivery-annotations"] = decoder.readMap(); - } else { + try { + //decode application properties + if (applicationProperties) { + qpid::amqp::Decoder decoder(applicationProperties.data, applicationProperties.size); decoder.readMap(map); } - } - if (messageAnnotations) { - qpid::amqp::Decoder decoder(messageAnnotations.data, messageAnnotations.size); - if (nestAnnotations) { - map["x-amqp-message-annotations"] = decoder.readMap(); - } else { - decoder.readMap(map); + //add in 'x-amqp-' prefixed values + if (!!firstAcquirer) { + map["x-amqp-first-acquirer"] = firstAcquirer.get(); + } + if (!!deliveryCount) { + map["x-amqp-delivery-count"] = deliveryCount.get(); + } + if (to) { + map["x-amqp-to"] = to.str(); + } + if (!!absoluteExpiryTime) { + map["x-amqp-absolute-expiry-time"] = absoluteExpiryTime.get(); + } + if (!!creationTime) { + map["x-amqp-creation-time"] = creationTime.get(); + } + if (groupId) { + map["x-amqp-group-id"] = groupId.str(); + } + if (!!groupSequence) { + map["x-amqp-qroup-sequence"] = groupSequence.get(); } + if (replyToGroupId) { + map["x-amqp-reply-to-group-id"] = replyToGroupId.str(); + } + //add in any annotations + if (deliveryAnnotations) { + qpid::amqp::Decoder decoder(deliveryAnnotations.data, deliveryAnnotations.size); + if (nestAnnotations) { + map["x-amqp-delivery-annotations"] = decoder.readMap(); + } else { + decoder.readMap(map); + } + } + if (messageAnnotations) { + qpid::amqp::Decoder decoder(messageAnnotations.data, messageAnnotations.size); + if (nestAnnotations) { + map["x-amqp-message-annotations"] = decoder.readMap(); + } else { + decoder.readMap(map); + } + } + } catch (const qpid::Exception& e) { + throw FetchError(e.what()); } } qpid::amqp::CharSequence EncodedMessage::getBareMessage() const @@ -169,7 +178,15 @@ qpid::amqp::CharSequence EncodedMessage::getBareMessage() const void EncodedMessage::getReplyTo(qpid::messaging::Address& a) const { - a = qpid::messaging::Address(replyTo.str()); + std::string rt = replyTo.str(); + std::string::size_type i = rt.find('/'); + if (i != std::string::npos && i > 0 && rt.find('/', i+1) == std::string::npos) { + //handle <name>/<subject> special case + a.setName(rt.substr(0, i)); + a.setSubject(rt.substr(i+1)); + } else { + a.setName(rt); + } } void EncodedMessage::getSubject(std::string& s) const { @@ -193,35 +210,39 @@ void EncodedMessage::getCorrelationId(std::string& s) const } void EncodedMessage::getBody(std::string& raw, qpid::types::Variant& c) const { - if (!content.isVoid()) { - c = content;//integer types, floats, bool etc - //TODO: populate raw data? - } else { - if (bodyType.empty() - || bodyType == qpid::amqp::typecodes::BINARY_NAME - || bodyType == qpid::types::encodings::UTF8 - || bodyType == qpid::types::encodings::ASCII) - { - c = std::string(body.data, body.size); - c.setEncoding(bodyType); - } else if (bodyType == qpid::amqp::typecodes::LIST_NAME) { - qpid::amqp::ListBuilder builder; - qpid::amqp::Decoder decoder(body.data, body.size); - decoder.read(builder); - c = builder.getList(); - raw.assign(body.data, body.size); - } else if (bodyType == qpid::amqp::typecodes::MAP_NAME) { - qpid::amqp::DataBuilder builder = qpid::amqp::DataBuilder(qpid::types::Variant::Map()); - qpid::amqp::Decoder decoder(body.data, body.size); - decoder.read(builder); - c = builder.getValue().asMap(); - raw.assign(body.data, body.size); - } else if (bodyType == qpid::amqp::typecodes::UUID_NAME) { - if (body.size == qpid::types::Uuid::SIZE) c = qpid::types::Uuid(body.data); - raw.assign(body.data, body.size); - } else if (bodyType == qpid::amqp::typecodes::ARRAY_NAME) { - raw.assign(body.data, body.size); + try { + if (!content.isVoid()) { + c = content;//integer types, floats, bool etc + //TODO: populate raw data? + } else { + if (bodyType.empty() + || bodyType == qpid::amqp::typecodes::BINARY_NAME + || bodyType == qpid::types::encodings::UTF8 + || bodyType == qpid::types::encodings::ASCII) + { + c = std::string(body.data, body.size); + c.setEncoding(bodyType); + } else if (bodyType == qpid::amqp::typecodes::LIST_NAME) { + qpid::amqp::ListBuilder builder; + qpid::amqp::Decoder decoder(body.data, body.size); + decoder.read(builder); + c = builder.getList(); + raw.assign(body.data, body.size); + } else if (bodyType == qpid::amqp::typecodes::MAP_NAME) { + qpid::amqp::DataBuilder builder = qpid::amqp::DataBuilder(qpid::types::Variant::Map()); + qpid::amqp::Decoder decoder(body.data, body.size); + decoder.read(builder); + c = builder.getValue().asMap(); + raw.assign(body.data, body.size); + } else if (bodyType == qpid::amqp::typecodes::UUID_NAME) { + if (body.size == qpid::types::Uuid::SIZE) c = qpid::types::Uuid(body.data); + raw.assign(body.data, body.size); + } else if (bodyType == qpid::amqp::typecodes::ARRAY_NAME) { + raw.assign(body.data, body.size); + } } + } catch (const qpid::Exception& e) { + throw FetchError(e.what()); } } diff --git a/qpid/cpp/src/qpid/messaging/amqp/ReceiverContext.cpp b/qpid/cpp/src/qpid/messaging/amqp/ReceiverContext.cpp index 473c120e27..2a45ccee44 100644 --- a/qpid/cpp/src/qpid/messaging/amqp/ReceiverContext.cpp +++ b/qpid/cpp/src/qpid/messaging/amqp/ReceiverContext.cpp @@ -123,11 +123,6 @@ Address ReceiverContext::getAddress() const return address; } -bool ReceiverContext::isClosed() const -{ - return false;//TODO -} - void ReceiverContext::reset(pn_session_t* session) { receiver = pn_receiver(session, name.c_str()); diff --git a/qpid/cpp/src/qpid/messaging/amqp/ReceiverContext.h b/qpid/cpp/src/qpid/messaging/amqp/ReceiverContext.h index fb8ea0d336..c68ea10ba3 100644 --- a/qpid/cpp/src/qpid/messaging/amqp/ReceiverContext.h +++ b/qpid/cpp/src/qpid/messaging/amqp/ReceiverContext.h @@ -55,7 +55,6 @@ class ReceiverContext void close(); const std::string& getName() const; const std::string& getSource() const; - bool isClosed() const; void configure(); void verify(); Address getAddress() const; diff --git a/qpid/cpp/src/qpid/messaging/amqp/ReceiverHandle.cpp b/qpid/cpp/src/qpid/messaging/amqp/ReceiverHandle.cpp index 177f47896b..bd7082079c 100644 --- a/qpid/cpp/src/qpid/messaging/amqp/ReceiverHandle.cpp +++ b/qpid/cpp/src/qpid/messaging/amqp/ReceiverHandle.cpp @@ -100,7 +100,7 @@ qpid::messaging::Session ReceiverHandle::getSession() const bool ReceiverHandle::isClosed() const { - return receiver->isClosed(); + return connection->isClosed(session, receiver); } Address ReceiverHandle::getAddress() const diff --git a/qpid/cpp/src/qpid/messaging/amqp/SenderContext.cpp b/qpid/cpp/src/qpid/messaging/amqp/SenderContext.cpp index 94851da273..6eed7b43d6 100644 --- a/qpid/cpp/src/qpid/messaging/amqp/SenderContext.cpp +++ b/qpid/cpp/src/qpid/messaging/amqp/SenderContext.cpp @@ -21,6 +21,8 @@ #include "qpid/messaging/amqp/SenderContext.h" #include "qpid/messaging/amqp/EncodedMessage.h" #include "qpid/messaging/AddressImpl.h" +#include "qpid/messaging/exceptions.h" +#include "qpid/Exception.h" #include "qpid/amqp/descriptors.h" #include "qpid/amqp/MapHandler.h" #include "qpid/amqp/MessageEncoder.h" @@ -42,7 +44,7 @@ SenderContext::SenderContext(pn_session_t* session, const std::string& n, const : name(n), address(a), helper(address), - sender(pn_sender(session, n.c_str())), capacity(1000), unreliable(helper.isUnreliable()) {} + sender(pn_sender(session, n.c_str())), capacity(50), unreliable(helper.isUnreliable()) {} SenderContext::~SenderContext() { @@ -435,59 +437,63 @@ void SenderContext::Delivery::reset() void SenderContext::Delivery::encode(const qpid::messaging::MessageImpl& msg, const qpid::messaging::Address& address) { - boost::shared_ptr<const EncodedMessage> original = msg.getEncoded(); - - if (original && !changedSubject(msg, address)) { //still have the content as received, send at least the bare message unaltered - //do we need to alter the header? are durable, priority, ttl, first-acquirer, delivery-count different from what was received? - if (original->hasHeaderChanged(msg)) { - //since as yet have no annotations, just write the revised header then the rest of the message as received - encoded.resize(16/*max header size*/ + original->getBareMessage().size); - qpid::amqp::MessageEncoder encoder(encoded.getData(), encoded.getSize()); + try { + boost::shared_ptr<const EncodedMessage> original = msg.getEncoded(); + + if (original && !changedSubject(msg, address)) { //still have the content as received, send at least the bare message unaltered + //do we need to alter the header? are durable, priority, ttl, first-acquirer, delivery-count different from what was received? + if (original->hasHeaderChanged(msg)) { + //since as yet have no annotations, just write the revised header then the rest of the message as received + encoded.resize(16/*max header size*/ + original->getBareMessage().size); + qpid::amqp::MessageEncoder encoder(encoded.getData(), encoded.getSize()); + HeaderAdapter header(msg); + encoder.writeHeader(header); + ::memcpy(encoded.getData() + encoder.getPosition(), original->getBareMessage().data, original->getBareMessage().size); + } else { + //since as yet have no annotations, if the header hasn't + //changed and we still have the original bare message, can + //send the entire content as is + encoded.resize(original->getSize()); + ::memcpy(encoded.getData(), original->getData(), original->getSize()); + } + } else { HeaderAdapter header(msg); + PropertiesAdapter properties(msg, address.getSubject()); + ApplicationPropertiesAdapter applicationProperties(msg.getHeaders()); + //compute size: + size_t contentSize = qpid::amqp::MessageEncoder::getEncodedSize(header) + + qpid::amqp::MessageEncoder::getEncodedSize(properties) + + qpid::amqp::MessageEncoder::getEncodedSize(applicationProperties); + if (msg.getContent().isVoid()) { + contentSize += qpid::amqp::MessageEncoder::getEncodedSizeForContent(msg.getBytes()); + } else { + contentSize += qpid::amqp::MessageEncoder::getEncodedSizeForValue(msg.getContent()) + 3/*descriptor*/; + } + encoded.resize(contentSize); + QPID_LOG(debug, "Sending message, buffer is " << encoded.getSize() << " bytes") + qpid::amqp::MessageEncoder encoder(encoded.getData(), encoded.getSize()); + //write header: encoder.writeHeader(header); - ::memcpy(encoded.getData() + encoder.getPosition(), original->getBareMessage().data, original->getBareMessage().size); - } else { - //since as yet have no annotations, if the header hasn't - //changed and we still have the original bare message, can - //send the entire content as is - encoded.resize(original->getSize()); - ::memcpy(encoded.getData(), original->getData(), original->getSize()); - } - } else { - HeaderAdapter header(msg); - PropertiesAdapter properties(msg, address.getSubject()); - ApplicationPropertiesAdapter applicationProperties(msg.getHeaders()); - //compute size: - size_t contentSize = qpid::amqp::MessageEncoder::getEncodedSize(header) - + qpid::amqp::MessageEncoder::getEncodedSize(properties) - + qpid::amqp::MessageEncoder::getEncodedSize(applicationProperties); - if (msg.getContent().isVoid()) { - contentSize += qpid::amqp::MessageEncoder::getEncodedSizeForContent(msg.getBytes()); - } else { - contentSize += qpid::amqp::MessageEncoder::getEncodedSizeForValue(msg.getContent()) + 3/*descriptor*/; - } - encoded.resize(contentSize); - QPID_LOG(debug, "Sending message, buffer is " << encoded.getSize() << " bytes") - qpid::amqp::MessageEncoder encoder(encoded.getData(), encoded.getSize()); - //write header: - encoder.writeHeader(header); - //write delivery-annotations, write message-annotations (none yet supported) - //write properties - encoder.writeProperties(properties); - //write application-properties - encoder.writeApplicationProperties(applicationProperties); - //write body - if (!msg.getContent().isVoid()) { - //write as AmqpValue - encoder.writeValue(msg.getContent(), &qpid::amqp::message::AMQP_VALUE); - } else if (msg.getBytes().size()) { - encoder.writeBinary(msg.getBytes(), &qpid::amqp::message::DATA);//structured content not yet directly supported - } - if (encoder.getPosition() < encoded.getSize()) { - QPID_LOG(debug, "Trimming buffer from " << encoded.getSize() << " to " << encoder.getPosition()); - encoded.trim(encoder.getPosition()); + //write delivery-annotations, write message-annotations (none yet supported) + //write properties + encoder.writeProperties(properties); + //write application-properties + encoder.writeApplicationProperties(applicationProperties); + //write body + if (!msg.getContent().isVoid()) { + //write as AmqpValue + encoder.writeValue(msg.getContent(), &qpid::amqp::message::AMQP_VALUE); + } else if (msg.getBytes().size()) { + encoder.writeBinary(msg.getBytes(), &qpid::amqp::message::DATA);//structured content not yet directly supported + } + if (encoder.getPosition() < encoded.getSize()) { + QPID_LOG(debug, "Trimming buffer from " << encoded.getSize() << " to " << encoder.getPosition()); + encoded.trim(encoder.getPosition()); + } + //write footer (no annotations yet supported) } - //write footer (no annotations yet supported) + } catch (const qpid::Exception& e) { + throw SendError(e.what()); } } void SenderContext::Delivery::send(pn_link_t* sender, bool unreliable) diff --git a/qpid/cpp/src/qpid/messaging/amqp/SessionContext.cpp b/qpid/cpp/src/qpid/messaging/amqp/SessionContext.cpp index 8f2a7d15d8..e13ccdbc36 100644 --- a/qpid/cpp/src/qpid/messaging/amqp/SessionContext.cpp +++ b/qpid/cpp/src/qpid/messaging/amqp/SessionContext.cpp @@ -140,6 +140,23 @@ void SessionContext::acknowledge(const qpid::framing::SequenceNumber& id, bool c } } +void SessionContext::nack(const qpid::framing::SequenceNumber& id, bool reject) +{ + DeliveryMap::iterator i = unacked.find(id); + if (i != unacked.end()) { + if (reject) { + QPID_LOG(debug, "rejecting message with id=" << id); + pn_delivery_update(i->second, PN_REJECTED); + } else { + QPID_LOG(debug, "releasing message with id=" << id); + pn_delivery_update(i->second, PN_MODIFIED); + pn_disposition_set_failed(pn_delivery_local(i->second), true); + } + pn_delivery_settle(i->second); + unacked.erase(i); + } +} + bool SessionContext::settled() { bool result = true; diff --git a/qpid/cpp/src/qpid/messaging/amqp/SessionContext.h b/qpid/cpp/src/qpid/messaging/amqp/SessionContext.h index 5d68f6d8de..75a67a7d15 100644 --- a/qpid/cpp/src/qpid/messaging/amqp/SessionContext.h +++ b/qpid/cpp/src/qpid/messaging/amqp/SessionContext.h @@ -79,6 +79,7 @@ class SessionContext void acknowledge(); void acknowledge(const qpid::framing::SequenceNumber& id, bool cummulative); void acknowledge(DeliveryMap::iterator begin, DeliveryMap::iterator end); + void nack(const qpid::framing::SequenceNumber& id, bool reject); }; }}} // namespace qpid::messaging::amqp diff --git a/qpid/cpp/src/qpid/messaging/amqp/SessionHandle.cpp b/qpid/cpp/src/qpid/messaging/amqp/SessionHandle.cpp index 45635e4ced..50fe4ef30e 100644 --- a/qpid/cpp/src/qpid/messaging/amqp/SessionHandle.cpp +++ b/qpid/cpp/src/qpid/messaging/amqp/SessionHandle.cpp @@ -57,18 +57,17 @@ void SessionHandle::acknowledge(bool /*sync*/) void SessionHandle::acknowledge(qpid::messaging::Message& msg, bool cumulative) { - //TODO: handle cumulative connection->acknowledge(session, &msg, cumulative); } -void SessionHandle::reject(qpid::messaging::Message&) +void SessionHandle::reject(qpid::messaging::Message& msg) { - + connection->nack(session, msg, true); } -void SessionHandle::release(qpid::messaging::Message&) +void SessionHandle::release(qpid::messaging::Message& msg) { - + connection->nack(session, msg, false); } void SessionHandle::close() diff --git a/qpid/cpp/src/tests/MessageTest.cpp b/qpid/cpp/src/tests/MessageTest.cpp index 666a2c6297..28f8b0c2b8 100644 --- a/qpid/cpp/src/tests/MessageTest.cpp +++ b/qpid/cpp/src/tests/MessageTest.cpp @@ -65,7 +65,7 @@ QPID_AUTO_TEST_CASE(testEncodeDecode) msg = registry.decode(buffer); BOOST_CHECK_EQUAL(routingKey, msg.getRoutingKey()); - BOOST_CHECK_EQUAL((uint64_t) data.size(), msg.getContentSize()); + BOOST_CHECK_EQUAL((uint64_t) data.size(), msg.getContent().size()); BOOST_CHECK_EQUAL(data, msg.getContent()); //BOOST_CHECK_EQUAL(messageId, msg->getProperties<MessageProperties>()->getMessageId()); BOOST_CHECK_EQUAL(string("xyz"), msg.getPropertyAsString("abc")); diff --git a/qpid/cpp/src/tests/MessagingSessionTests.cpp b/qpid/cpp/src/tests/MessagingSessionTests.cpp index 5cc595c56f..323d7e8231 100644 --- a/qpid/cpp/src/tests/MessagingSessionTests.cpp +++ b/qpid/cpp/src/tests/MessagingSessionTests.cpp @@ -397,6 +397,14 @@ QPID_AUTO_TEST_CASE(testBrowse) receive(browser1, 10); Receiver browser2 = fix.session.createReceiver(fix.queue + "; {mode:browse}"); receive(browser2, 10); + Receiver releaser1 = fix.session.createReceiver(fix.queue); + Message m1 = releaser1.fetch(messaging::Duration::SECOND*5); + BOOST_CHECK(!m1.getRedelivered()); + fix.session.release(m1); + Receiver releaser2 = fix.session.createReceiver(fix.queue); + Message m2 = releaser2.fetch(messaging::Duration::SECOND*5); + BOOST_CHECK(m2.getRedelivered()); + fix.session.release(m2); Receiver consumer = fix.session.createReceiver(fix.queue); receive(consumer, 10); fix.session.acknowledge(); @@ -738,6 +746,7 @@ QPID_AUTO_TEST_CASE(testRelease) Message m2 = receiver.fetch(Duration::SECOND * 1); BOOST_CHECK_EQUAL(m1.getContent(), out.getContent()); BOOST_CHECK_EQUAL(m1.getContent(), m2.getContent()); + BOOST_CHECK(m2.getRedelivered()); fix.session.acknowledge(true); } diff --git a/qpid/cpp/src/tests/QueueFlowLimitTest.cpp b/qpid/cpp/src/tests/QueueFlowLimitTest.cpp index d305ca452b..7b0a776062 100644 --- a/qpid/cpp/src/tests/QueueFlowLimitTest.cpp +++ b/qpid/cpp/src/tests/QueueFlowLimitTest.cpp @@ -77,8 +77,14 @@ public: Message createMessage(uint32_t size) { static uint32_t seqNum; - Message msg = MessageUtils::createMessage(qpid::types::Variant::Map(), std::string (size, 'x')); - msg.setSequence(++seqNum); + //Need to compute what data size is required to make a given + //overall size (use one byte of content in test message to ensure + //content frame is added) + Message test = MessageUtils::createMessage(qpid::types::Variant::Map(), std::string("x")); + size_t min = test.getMessageSize() - 1; + if (min > size) throw qpid::Exception("Can't create message that small!"); + Message msg = MessageUtils::createMessage(qpid::types::Variant::Map(), std::string (size - min, 'x')); + msg.setSequence(++seqNum);//this doesn't affect message size return msg; } } @@ -100,18 +106,18 @@ QPID_AUTO_TEST_CASE(testFlowCount) std::deque<Message> msgs; for (size_t i = 0; i < 6; i++) { - msgs.push_back(createMessage(10)); + msgs.push_back(createMessage(100)); flow->enqueued(msgs.back()); BOOST_CHECK(!flow->isFlowControlActive()); } BOOST_CHECK(!flow->isFlowControlActive()); // 6 on queue - msgs.push_back(createMessage(10)); + msgs.push_back(createMessage(100)); flow->enqueued(msgs.back()); BOOST_CHECK(!flow->isFlowControlActive()); // 7 on queue - msgs.push_back(createMessage(10)); + msgs.push_back(createMessage(100)); flow->enqueued(msgs.back()); BOOST_CHECK(flow->isFlowControlActive()); // 8 on queue, ON - msgs.push_back(createMessage(10)); + msgs.push_back(createMessage(100)); flow->enqueued(msgs.back()); BOOST_CHECK(flow->isFlowControlActive()); // 9 on queue, no change to flow control @@ -136,69 +142,69 @@ QPID_AUTO_TEST_CASE(testFlowCount) QPID_AUTO_TEST_CASE(testFlowSize) { FieldTable args; - args.setUInt64(QueueFlowLimit::flowStopSizeKey, 70); - args.setUInt64(QueueFlowLimit::flowResumeSizeKey, 50); + args.setUInt64(QueueFlowLimit::flowStopSizeKey, 700); + args.setUInt64(QueueFlowLimit::flowResumeSizeKey, 460); std::auto_ptr<TestFlow> flow(TestFlow::createTestFlow(args)); BOOST_CHECK_EQUAL((uint32_t) 0, flow->getFlowStopCount()); BOOST_CHECK_EQUAL((uint32_t) 0, flow->getFlowResumeCount()); - BOOST_CHECK_EQUAL((uint32_t) 70, flow->getFlowStopSize()); - BOOST_CHECK_EQUAL((uint32_t) 50, flow->getFlowResumeSize()); + BOOST_CHECK_EQUAL((uint32_t) 700, flow->getFlowStopSize()); + BOOST_CHECK_EQUAL((uint32_t) 460, flow->getFlowResumeSize()); BOOST_CHECK(!flow->isFlowControlActive()); BOOST_CHECK(flow->monitorFlowControl()); std::deque<Message> msgs; for (size_t i = 0; i < 6; i++) { - msgs.push_back(createMessage(10)); + msgs.push_back(createMessage(100)); flow->enqueued(msgs.back()); BOOST_CHECK(!flow->isFlowControlActive()); } - BOOST_CHECK(!flow->isFlowControlActive()); // 60 on queue + BOOST_CHECK(!flow->isFlowControlActive()); // 600 on queue BOOST_CHECK_EQUAL(6u, flow->getFlowCount()); - BOOST_CHECK_EQUAL(60u, flow->getFlowSize()); + BOOST_CHECK_EQUAL(600u, flow->getFlowSize()); - Message msg_9 = createMessage(9); - flow->enqueued(msg_9); - BOOST_CHECK(!flow->isFlowControlActive()); // 69 on queue - Message tinyMsg_1 = createMessage(1); + Message msg_50 = createMessage(50); + flow->enqueued(msg_50); + BOOST_CHECK(!flow->isFlowControlActive()); // 650 on queue + Message tinyMsg_1 = createMessage(40); flow->enqueued(tinyMsg_1); - BOOST_CHECK(!flow->isFlowControlActive()); // 70 on queue + BOOST_CHECK(!flow->isFlowControlActive()); // 690 on queue - Message tinyMsg_2 = createMessage(1); + Message tinyMsg_2 = createMessage(40); flow->enqueued(tinyMsg_2); - BOOST_CHECK(flow->isFlowControlActive()); // 71 on queue, ON - msgs.push_back(createMessage(10)); + BOOST_CHECK(flow->isFlowControlActive()); // 730 on queue, ON + msgs.push_back(createMessage(100)); flow->enqueued(msgs.back()); - BOOST_CHECK(flow->isFlowControlActive()); // 81 on queue + BOOST_CHECK(flow->isFlowControlActive()); // 830 on queue BOOST_CHECK_EQUAL(10u, flow->getFlowCount()); - BOOST_CHECK_EQUAL(81u, flow->getFlowSize()); + BOOST_CHECK_EQUAL(830u, flow->getFlowSize()); flow->dequeued(msgs.front()); msgs.pop_front(); - BOOST_CHECK(flow->isFlowControlActive()); // 71 on queue + BOOST_CHECK(flow->isFlowControlActive()); // 730 on queue flow->dequeued(msgs.front()); msgs.pop_front(); - BOOST_CHECK(flow->isFlowControlActive()); // 61 on queue + BOOST_CHECK(flow->isFlowControlActive()); // 630 on queue flow->dequeued(msgs.front()); msgs.pop_front(); - BOOST_CHECK(flow->isFlowControlActive()); // 51 on queue + BOOST_CHECK(flow->isFlowControlActive()); // 530 on queue flow->dequeued(tinyMsg_1); - BOOST_CHECK(flow->isFlowControlActive()); // 50 on queue + BOOST_CHECK(flow->isFlowControlActive()); // 490 on queue flow->dequeued(tinyMsg_2); - BOOST_CHECK(!flow->isFlowControlActive()); // 49 on queue, OFF + BOOST_CHECK(!flow->isFlowControlActive()); // 450 on queue, OFF - flow->dequeued(msg_9); - BOOST_CHECK(!flow->isFlowControlActive()); // 40 on queue + flow->dequeued(msg_50); + BOOST_CHECK(!flow->isFlowControlActive()); // 400 on queue flow->dequeued(msgs.front()); msgs.pop_front(); - BOOST_CHECK(!flow->isFlowControlActive()); // 30 on queue + BOOST_CHECK(!flow->isFlowControlActive()); // 300 on queue flow->dequeued(msgs.front()); msgs.pop_front(); - BOOST_CHECK(!flow->isFlowControlActive()); // 20 on queue + BOOST_CHECK(!flow->isFlowControlActive()); // 200 on queue BOOST_CHECK_EQUAL(2u, flow->getFlowCount()); - BOOST_CHECK_EQUAL(20u, flow->getFlowSize()); + BOOST_CHECK_EQUAL(200u, flow->getFlowSize()); } QPID_AUTO_TEST_CASE(testFlowArgs) @@ -227,13 +233,13 @@ QPID_AUTO_TEST_CASE(testFlowCombo) FieldTable args; args.setInt(QueueFlowLimit::flowStopCountKey, 10); args.setInt(QueueFlowLimit::flowResumeCountKey, 5); - args.setUInt64(QueueFlowLimit::flowStopSizeKey, 200); - args.setUInt64(QueueFlowLimit::flowResumeSizeKey, 100); + args.setUInt64(QueueFlowLimit::flowStopSizeKey, 2000); + args.setUInt64(QueueFlowLimit::flowResumeSizeKey, 1000); - std::deque<Message> msgs_1; - std::deque<Message> msgs_10; std::deque<Message> msgs_50; std::deque<Message> msgs_100; + std::deque<Message> msgs_500; + std::deque<Message> msgs_1000; Message msg; @@ -243,104 +249,104 @@ QPID_AUTO_TEST_CASE(testFlowCombo) // verify flow control comes ON when only count passes its stop point. for (size_t i = 0; i < 10; i++) { - msgs_10.push_back(createMessage(10)); - flow->enqueued(msgs_10.back()); + msgs_100.push_back(createMessage(100)); + flow->enqueued(msgs_100.back()); BOOST_CHECK(!flow->isFlowControlActive()); } - // count:10 size:100 + // count:10 size:1000 - msgs_1.push_back(createMessage(1)); - flow->enqueued(msgs_1.back()); // count:11 size: 101 ->ON + msgs_50.push_back(createMessage(50)); + flow->enqueued(msgs_50.back()); // count:11 size: 1050 ->ON BOOST_CHECK(flow->isFlowControlActive()); for (size_t i = 0; i < 6; i++) { - flow->dequeued(msgs_10.front()); - msgs_10.pop_front(); + flow->dequeued(msgs_100.front()); + msgs_100.pop_front(); BOOST_CHECK(flow->isFlowControlActive()); } - // count:5 size: 41 + // count:5 size: 450 - flow->dequeued(msgs_1.front()); // count: 4 size: 40 ->OFF - msgs_1.pop_front(); + flow->dequeued(msgs_50.front()); // count: 4 size: 400 ->OFF + msgs_50.pop_front(); BOOST_CHECK(!flow->isFlowControlActive()); for (size_t i = 0; i < 4; i++) { - flow->dequeued(msgs_10.front()); - msgs_10.pop_front(); + flow->dequeued(msgs_100.front()); + msgs_100.pop_front(); BOOST_CHECK(!flow->isFlowControlActive()); } // count:0 size:0 // verify flow control comes ON when only size passes its stop point. - msgs_100.push_back(createMessage(100)); - flow->enqueued(msgs_100.back()); // count:1 size: 100 + msgs_1000.push_back(createMessage(1000)); + flow->enqueued(msgs_1000.back()); // count:1 size: 1000 BOOST_CHECK(!flow->isFlowControlActive()); - msgs_50.push_back(createMessage(50)); - flow->enqueued(msgs_50.back()); // count:2 size: 150 + msgs_500.push_back(createMessage(500)); + flow->enqueued(msgs_500.back()); // count:2 size: 1500 BOOST_CHECK(!flow->isFlowControlActive()); - msgs_50.push_back(createMessage(50)); - flow->enqueued(msgs_50.back()); // count:3 size: 200 + msgs_500.push_back(createMessage(500)); + flow->enqueued(msgs_500.back()); // count:3 size: 2000 BOOST_CHECK(!flow->isFlowControlActive()); - msgs_1.push_back(createMessage(1)); - flow->enqueued(msgs_1.back()); // count:4 size: 201 ->ON + msgs_50.push_back(createMessage(50)); + flow->enqueued(msgs_50.back()); // count:4 size: 2050 ->ON BOOST_CHECK(flow->isFlowControlActive()); - flow->dequeued(msgs_100.front()); // count:3 size:101 - msgs_100.pop_front(); + flow->dequeued(msgs_1000.front()); // count:3 size:1050 + msgs_1000.pop_front(); BOOST_CHECK(flow->isFlowControlActive()); - flow->dequeued(msgs_1.front()); // count:2 size:100 - msgs_1.pop_front(); + flow->dequeued(msgs_50.front()); // count:2 size:1000 + msgs_50.pop_front(); BOOST_CHECK(flow->isFlowControlActive()); - flow->dequeued(msgs_50.front()); // count:1 size:50 ->OFF - msgs_50.pop_front(); + flow->dequeued(msgs_500.front()); // count:1 size:500 ->OFF + msgs_500.pop_front(); BOOST_CHECK(!flow->isFlowControlActive()); // verify flow control remains ON until both thresholds drop below their // resume point. for (size_t i = 0; i < 8; i++) { - msgs_10.push_back(createMessage(10)); - flow->enqueued(msgs_10.back()); + msgs_100.push_back(createMessage(100)); + flow->enqueued(msgs_100.back()); BOOST_CHECK(!flow->isFlowControlActive()); } - // count:9 size:130 + // count:9 size:1300 - msgs_10.push_back(createMessage(10)); - flow->enqueued(msgs_10.back()); // count:10 size: 140 + msgs_100.push_back(createMessage(100)); + flow->enqueued(msgs_100.back()); // count:10 size: 1400 BOOST_CHECK(!flow->isFlowControlActive()); - msgs_1.push_back(createMessage(1)); - flow->enqueued(msgs_1.back()); // count:11 size: 141 ->ON + msgs_50.push_back(createMessage(50)); + flow->enqueued(msgs_50.back()); // count:11 size: 1450 ->ON BOOST_CHECK(flow->isFlowControlActive()); - msgs_100.push_back(createMessage(100)); - flow->enqueued(msgs_100.back()); // count:12 size: 241 (both thresholds crossed) + msgs_1000.push_back(createMessage(1000)); + flow->enqueued(msgs_1000.back()); // count:12 size: 2450 (both thresholds crossed) BOOST_CHECK(flow->isFlowControlActive()); - // at this point: 9@10 + 1@50 + 1@100 + 1@1 == 12@241 + // at this point: 9@100 + 1@500 + 1@1000 + 1@50 == 12@2450 - flow->dequeued(msgs_50.front()); // count:11 size:191 - msgs_50.pop_front(); + flow->dequeued(msgs_500.front()); // count:11 size:1950 + msgs_500.pop_front(); BOOST_CHECK(flow->isFlowControlActive()); for (size_t i = 0; i < 9; i++) { - flow->dequeued(msgs_10.front()); - msgs_10.pop_front(); + flow->dequeued(msgs_100.front()); + msgs_100.pop_front(); BOOST_CHECK(flow->isFlowControlActive()); } - // count:2 size:101 - flow->dequeued(msgs_1.front()); // count:1 size:100 - msgs_1.pop_front(); + // count:2 size:1050 + flow->dequeued(msgs_50.front()); // count:1 size:1000 + msgs_50.pop_front(); BOOST_CHECK(flow->isFlowControlActive()); // still active due to size - flow->dequeued(msgs_100.front()); // count:0 size:0 ->OFF - msgs_100.pop_front(); + flow->dequeued(msgs_1000.front()); // count:0 size:0 ->OFF + msgs_1000.pop_front(); BOOST_CHECK(!flow->isFlowControlActive()); } diff --git a/qpid/cpp/src/tests/QueueOptionsTest.cpp b/qpid/cpp/src/tests/QueueOptionsTest.cpp index f2fbaba2c1..bdb83d7d22 100644 --- a/qpid/cpp/src/tests/QueueOptionsTest.cpp +++ b/qpid/cpp/src/tests/QueueOptionsTest.cpp @@ -63,16 +63,9 @@ QPID_AUTO_TEST_CASE(testFlags) { QueueOptions ft; - ft.setPersistLastNode(); ft.setOrdering(LVQ); - - BOOST_CHECK(1 == ft.getAsInt(QueueOptions::strPersistLastNode)); BOOST_CHECK(1 == ft.getAsInt(QueueOptions::strLastValueQueue)); - - ft.clearPersistLastNode(); ft.setOrdering(FIFO); - - BOOST_CHECK(!ft.isSet(QueueOptions::strPersistLastNode)); BOOST_CHECK(!ft.isSet(QueueOptions::strLastValueQueue)); } @@ -87,16 +80,6 @@ QPID_AUTO_TEST_CASE(testSetOrdering) } -QPID_AUTO_TEST_CASE(testClearPersistLastNode) -{ - //ensure clear works even if not preceded by the setting on the - //option - QueueOptions ft; - ft.clearPersistLastNode(); - BOOST_CHECK(!ft.isSet(QueueOptions::strPersistLastNode)); -} - - QPID_AUTO_TEST_SUITE_END() }} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/QueuePolicyTest.cpp b/qpid/cpp/src/tests/QueuePolicyTest.cpp index ce1b0addea..f61c283fd4 100644 --- a/qpid/cpp/src/tests/QueuePolicyTest.cpp +++ b/qpid/cpp/src/tests/QueuePolicyTest.cpp @@ -69,9 +69,15 @@ QPID_AUTO_TEST_CASE(testRingPolicyCount) QPID_AUTO_TEST_CASE(testRingPolicySize) { - std::string hundredBytes = std::string(100, 'h'); - std::string fourHundredBytes = std::string (400, 'f'); - std::string thousandBytes = std::string(1000, 't'); + //The message size now includes all headers as well as the content + //aka body, so compute the amount of data needed to hit a given + //overall size + std::string q("my-ring-queue"); + size_t minMessageSize = 25/*minimum size of headers*/ + q.size()/*routing key length*/ + 4/*default exchange, added by broker*/; + + std::string hundredBytes = std::string(100 - minMessageSize, 'h'); + std::string fourHundredBytes = std::string (400 - minMessageSize, 'f'); + std::string thousandBytes = std::string(1000 - minMessageSize, 't'); // Ring queue, 500 bytes maxSize @@ -79,7 +85,6 @@ QPID_AUTO_TEST_CASE(testRingPolicySize) args.setSizePolicy(RING, 500, 0); SessionFixture f; - std::string q("my-ring-queue"); f.session.queueDeclare(arg::queue=q, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args); // A. Send messages 0 .. 5, each 100 bytes diff --git a/qpid/cpp/src/tests/assertions.py b/qpid/cpp/src/tests/assertions.py new file mode 100644 index 0000000000..b1a06aea83 --- /dev/null +++ b/qpid/cpp/src/tests/assertions.py @@ -0,0 +1,141 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +from qpid.tests.messaging.implementation import * +from qpid.tests.messaging import VersionTest + +class AssertionTests (VersionTest): + """ + Tests for assertions with qpidd + """ + def test_queues_alternate_exchange1(self): + name = str(uuid4()) + self.ssn.sender("%s; {create:always, node:{properties:{alternate-exchange:amq.fanout}}}" % name) + self.ssn.sender("%s; {assert:always, node:{properties:{alternate-exchange:amq.fanout}}}" % name) + try: + self.ssn.sender("%s; {assert:always, node:{properties:{alternate-exchange:amq.topic}}}" % name) + assert False, "Expected assertion to fail on alternate-exchange" + except AssertionFailed: None + except MessagingError: None + + def test_queues_alternate_exchange2(self): + name = str(uuid4()) + self.ssn.sender("%s; {create:always, node:{x-declare:{alternate-exchange:amq.fanout}}}" % name) + self.ssn.sender("%s; {assert:always, node:{x-declare:{alternate-exchange:amq.fanout}}}" % name) + try: + self.ssn.sender("%s; {assert:always, node:{x-declare:{alternate-exchange:amq.topic}}}" % name) + assert False, "Expected assertion to fail on alternate-exchange" + except AssertionFailed: None + except MessagingError: None + + def test_queue_type(self): + name = str(uuid4()) + self.ssn.sender("%s; {create:always}" % name) + self.ssn.sender("%s; {assert:always, node:{type:queue}}" % name) + try: + self.ssn.sender("%s; {assert:always, node:{type:topic}}" % name) + assert False, "Expected assertion to fail on type" + except AssertionFailed: None + except MessagingError: None + + def test_queue_durability(self): + name = str(uuid4()) + self.ssn.sender("%s; {create:always}" % name) + self.ssn.sender("%s; {assert:always, node:{durable:False}}" % name) + try: + self.ssn.sender("%s; {assert:always, node:{durable:True}}" % name) + assert False, "Expected assertion to fail on durability" + except AssertionFailed: None + except MessagingError: None + + def test_queue_options(self): + name = str(uuid4()) + self.ssn.sender("%s; {create:always, node:{x-declare:{arguments:{foo:bar,'qpid.last_value_queue_key':abc}}}}" % name) + self.ssn.sender("%s; {assert:always, node:{x-declare:{arguments:{'qpid.last_value_queue_key':abc}}}}" % name) + try: + self.ssn.sender("%s; {assert:always, node:{x-declare:{arguments:{foo:bar}}}}" % name) + assert False, "Expected assertion to fail on unrecognised option" + except AssertionFailed: None + except MessagingError: None + try: + self.ssn.sender("%s; {assert:always, node:{x-declare:{arguments:{'qpid.max_count':10}}}}" % name) + assert False, "Expected assertion to fail on unspecified option" + except AssertionFailed: None + except MessagingError: None + try: + self.ssn.sender("%s; {assert:always, node:{x-declare:{arguments:{'qpid.last_value_key':xyz}}}}" % name) + assert False, "Expected assertion to fail on option with different value" + except AssertionFailed: None + except MessagingError: None + + def test_exchanges_alternate_exchange1(self): + name = str(uuid4()) + self.ssn.sender("%s; {create:always, node:{type:topic, properties:{alternate-exchange:amq.fanout}}}" % name) + self.ssn.sender("%s; {assert:always, node:{type:topic, properties:{alternate-exchange:amq.fanout}}}" % name) + try: + self.ssn.sender("%s; {assert:always, node:{properties:{alternate-exchange:amq.topic}}}" % name) + assert False, "Expected assertion to fail on alternate-exchange" + except AssertionFailed: None + except MessagingError: None + + def test_exchanges_alternate_exchange2(self): + name = str(uuid4()) + self.ssn.sender("%s; {create:always, node:{type:topic, x-declare:{alternate-exchange:amq.fanout}}}" % name) + self.ssn.sender("%s; {assert:always, node:{type:topic, x-declare:{alternate-exchange:amq.fanout}}}" % name) + try: + self.ssn.sender("%s; {assert:always, node:{x-declare:{alternate-exchange:amq.topic}}}" % name) + assert False, "Expected assertion to fail on alternate-exchange" + except AssertionFailed: None + except MessagingError: None + + def test_exchange_type(self): + name = str(uuid4()) + self.ssn.sender("%s; {create:always, node:{type:topic}}" % name) + self.ssn.sender("%s; {assert:always, node:{type:topic}}" % name) + try: + self.ssn.sender("%s; {assert:always, node:{type:queue}}" % name) + assert False, "Expected assertion to fail on type" + except AssertionFailed: None + except MessagingError: None + + def test_exchange_durability(self): + name = str(uuid4()) + self.ssn.sender("%s; {create:always, node:{type:topic}}" % name) + self.ssn.sender("%s; {assert:always, node:{durable:False}}" % name) + try: + self.ssn.sender("%s; {assert:always, node:{durable:True}}" % name) + assert False, "Expected assertion to fail on durability" + except AssertionFailed: None + except MessagingError: None + + def test_exchange_options(self): + name = str(uuid4()) + self.ssn.sender("%s; {create:always, node:{type:topic, x-declare:{arguments:{foo:bar,'qpid.msg_sequence':True}}}}" % name) + self.ssn.sender("%s; {assert:always, node:{x-declare:{arguments:{'qpid.msg_sequence':True}}}}" % name) + try: + self.ssn.sender("%s; {assert:always, node:{x-declare:{arguments:{foo:bar}}}}" % name) + assert False, "Expected assertion to fail on unrecognised option" + except AssertionFailed: None + except MessagingError: None + try: + self.ssn.sender("%s; {assert:always, node:{x-declare:{arguments:{'qpid.ive':True}}}}" % name) + assert False, "Expected assertion to fail on unspecified option" + except AssertionFailed: None + except MessagingError: None + diff --git a/qpid/cpp/src/tests/brokertest.py b/qpid/cpp/src/tests/brokertest.py index 2786454399..d1f020a945 100644 --- a/qpid/cpp/src/tests/brokertest.py +++ b/qpid/cpp/src/tests/brokertest.py @@ -424,7 +424,6 @@ class BrokerTest(TestCase): ha_lib = os.getenv("HA_LIB") xml_lib = os.getenv("XML_LIB") amqp_lib = os.getenv("AMQP_LIB") - amqpc_lib = os.getenv("AMQPC_LIB") qpid_config_exec = os.getenv("QPID_CONFIG_EXEC") qpid_route_exec = os.getenv("QPID_ROUTE_EXEC") receiver_exec = os.getenv("RECEIVER_EXEC") diff --git a/qpid/cpp/src/tests/interlink_tests.py b/qpid/cpp/src/tests/interlink_tests.py index 608d4ac890..c7269ac5e9 100755 --- a/qpid/cpp/src/tests/interlink_tests.py +++ b/qpid/cpp/src/tests/interlink_tests.py @@ -46,7 +46,6 @@ class AmqpBrokerTest(BrokerTest): """ def setUp(self): BrokerTest.setUp(self) - os.putenv("QPID_LOAD_MODULE", BrokerTest.amqpc_lib) self.port_holder = HaPort(self) self.broker = self.amqp_broker(port_holder=self.port_holder) self.default_config = Config(self.broker) diff --git a/qpid/cpp/src/tests/legacystore/CMakeLists.txt b/qpid/cpp/src/tests/legacystore/CMakeLists.txt index 6cfaa7ec17..0dec2caab8 100644 --- a/qpid/cpp/src/tests/legacystore/CMakeLists.txt +++ b/qpid/cpp/src/tests/legacystore/CMakeLists.txt @@ -70,48 +70,119 @@ set (qpid_test_boost_libs # womp on each other's store directory. # -# define_selftest +# define_legacystore_test # macro to accept the name of a single source file and to create a # unit test executable that runs the source. # -MACRO (define_selftest theSourceFile) +MACRO (define_legacystore_test theSourceFile) add_executable (legacystore_${theSourceFile} - unit_test ${theSourceFile} + unit_test ${platform_test_additions}) target_link_libraries (legacystore_${theSourceFile} ${qpid_test_boost_libs} - qpidmessaging qpidbroker qmfconsole legacystore) -get_property(ls_include TARGET legacystore_${theSourceFile} PROPERTY INCLUDE_DIRECTORIES) -list(APPEND ls_include ${abs_top_srcdir}/src/qpid/legacystore) -list(APPEND ls_include ${abs_top_srcdir}/src/tests) -set_target_properties (legacystore_${theSourceFile} PROPERTIES - INCLUDE_DIRECTORIES "${ls_include}" - COMPILE_DEFINITIONS _IN_QPID_BROKER) + qpidmessaging qpidtypes qpidbroker qpidcommon legacystore_shared) +set_target_properties (legacystore_${theSourceFile} PROPERTIES COMPILE_DEFINITIONS _IN_QPID_BROKER) remember_location(legacystore_${theSourceFile}) -set(test_wrap ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_test${test_script_suffix}) add_test (legacystore_${theSourceFile} ${test_wrap} ${legacystore_${theSourceFile}_LOCATION}) -ENDMACRO (define_selftest) - -# add_definitions(-H) +ENDMACRO (define_legacystore_test) + +define_legacystore_test (SimpleTest) +define_legacystore_test (OrderingTest) +define_legacystore_test (TransactionalTest) +define_legacystore_test (TwoPhaseCommitTest) + +# Journal tests +MACRO (define_journal_test mainSourceFile) +if ("${ARGV1}" STREQUAL "LONG") + set (testname "journal_long_${mainSourceFile}") +else () + set (testname "journal_${mainSourceFile}") +endif () +add_executable (${testname} + jrnl/${mainSourceFile} + unit_test + ${platform_test_additions}) +target_link_libraries (${testname} + ${qpid_test_boost_libs} + ${clock_gettime_LIB} legacystore_shared) +if ("${ARGV1}" STREQUAL "LONG") + set_target_properties(${testname} PROPERTIES COMPILE_DEFINITIONS LONG_TEST) +endif () +remember_location(${testname}) +add_test (${testname} ${test_wrap} ${${testname}_LOCATION}) +unset (testname) +ENDMACRO (define_journal_test) + +define_journal_test (_ut_time_ns) +define_journal_test (_ut_jexception) +define_journal_test (_ut_jerrno) +define_journal_test (_ut_rec_hdr) +define_journal_test (_ut_jinf) +define_journal_test (_ut_jdir) +define_journal_test (_ut_enq_map) +define_journal_test (_ut_txn_map) +define_journal_test (_ut_lpmgr) +define_journal_test (_st_basic) +define_journal_test (_st_basic_txn) +define_journal_test (_st_read) +define_journal_test (_st_read_txn) +define_journal_test (_st_auto_expand) +define_journal_test (_ut_lpmgr LONG) +define_journal_test (_st_basic LONG) +define_journal_test (_st_read LONG) + +add_executable(jtt + jrnl/jtt/args.cpp + jrnl/jtt/data_src.cpp + jrnl/jtt/jrnl_init_params.cpp + jrnl/jtt/jrnl_instance.cpp + jrnl/jtt/main.cpp + jrnl/jtt/read_arg.cpp + jrnl/jtt/test_case.cpp + jrnl/jtt/test_case_result.cpp + jrnl/jtt/test_case_result_agregation.cpp + jrnl/jtt/test_case_set.cpp + jrnl/jtt/test_mgr.cpp) + +target_link_libraries (jtt + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${clock_gettime_LIB} legacystore_shared) + +add_test(journal_jtt ${CMAKE_CURRENT_BINARY_DIR}/jtt -c ${CMAKE_CURRENT_SOURCE_DIR}/jrnl/jtt/jtt.csv) + +add_executable (jtt__ut + jrnl/jtt/_ut_data_src.cpp + jrnl/jtt/_ut_jrnl_init_params.cpp + jrnl/jtt/_ut_read_arg.cpp + jrnl/jtt/_ut_jrnl_instance.cpp + jrnl/jtt/_ut_test_case.cpp + jrnl/jtt/_ut_test_case_result.cpp + jrnl/jtt/_ut_test_case_result_agregation.cpp + jrnl/jtt/_ut_test_case_set.cpp + jrnl/jtt/args.cpp + jrnl/jtt/data_src.cpp + jrnl/jtt/jrnl_init_params.cpp + jrnl/jtt/jrnl_instance.cpp + jrnl/jtt/read_arg.cpp + jrnl/jtt/test_case.cpp + jrnl/jtt/test_case_set.cpp + jrnl/jtt/test_case_result.cpp + jrnl/jtt/test_case_result_agregation.cpp + unit_test.cpp) + +target_link_libraries (jtt__ut + ${qpid_test_boost_libs} + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${clock_gettime_LIB} legacystore_shared) -define_selftest (SimpleTest) -define_selftest (OrderingTest) -define_selftest (TransactionalTest) -define_selftest (TwoPhaseCommitTest) +add_test(NAME journal_jtt_ut WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/jrnl/jtt COMMAND ${CMAKE_CURRENT_BINARY_DIR}/jtt__ut) # # Other test programs # -# This should ideally be done as part of the test run, but I don't know a way -# to get these arguments and the working directory set like Makefile.am does, -# and have that run during the test pass. -if (PYTHON_EXECUTABLE) - set (python_bld ${CMAKE_CURRENT_BINARY_DIR}/python) - execute_process(COMMAND ${PYTHON_EXECUTABLE} setup.py install --prefix=${pythoon_bld} --install-lib=${python_bld} --install-scripts=${python_bld}/commands - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/../python) -endif (PYTHON_EXECUTABLE) +add_test (legacystore_python_tests ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_python_tests${test_script_suffix}) endif (BUILD_LEGACYSTORE) diff --git a/qpid/cpp/src/tests/legacystore/OrderingTest.cpp b/qpid/cpp/src/tests/legacystore/OrderingTest.cpp index 92a09f0c60..74a9db1c73 100644 --- a/qpid/cpp/src/tests/legacystore/OrderingTest.cpp +++ b/qpid/cpp/src/tests/legacystore/OrderingTest.cpp @@ -20,16 +20,18 @@ */ #include "unit_test.h" +#include "MessageUtils.h" +#include "qpid/broker/Queue.h" +#include "qpid/broker/RecoveryManagerImpl.h" +#include "qpid/broker/PersistableObject.h" +#include "qpid/framing/AMQHeaderBody.h" #include "qpid/legacystore/MessageStoreImpl.h" -#include <iostream> -#include "MessageUtils.h" -#include <qpid/broker/Queue.h> -#include <qpid/broker/RecoveryManagerImpl.h> -#include <qpid/framing/AMQHeaderBody.h> #include "qpid/log/Logger.h" #include "qpid/sys/Timer.h" +#include <iostream> + using namespace qpid; using namespace qpid::broker; using namespace qpid::framing; @@ -48,7 +50,7 @@ QPID_AUTO_TEST_SUITE(OrderingTest) const std::string test_filename("OrderingTest"); const char* tdp = getenv("TMP_DATA_DIR"); -const std::string test_dir(tdp && strlen(tdp) > 0 ? tdp : "/tmp/OrderingTest"); +const std::string test_dir(tdp && strlen(tdp) > 0 ? tdp : "/var/tmp/OrderingTest"); // === Helper fns === @@ -118,7 +120,8 @@ void restart() sys::Timer t; DtxManager mgr(t); mgr.setStore (store.get()); - RecoveryManagerImpl recoveryMgr(queues, exchanges, links, mgr, br.getProtocolRegistry()); + RecoveredObjects ro; + RecoveryManagerImpl recoveryMgr(queues, exchanges, links, mgr, br.getProtocolRegistry(), ro); store->recover(recoveryMgr); queue = queues.find(name); diff --git a/qpid/cpp/src/tests/legacystore/SimpleTest.cpp b/qpid/cpp/src/tests/legacystore/SimpleTest.cpp index a49333d876..c769bdeb75 100644 --- a/qpid/cpp/src/tests/legacystore/SimpleTest.cpp +++ b/qpid/cpp/src/tests/legacystore/SimpleTest.cpp @@ -20,21 +20,23 @@ */ #include "unit_test.h" +#include "MessageUtils.h" +#include "qpid/broker/DirectExchange.h" +#include "qpid/broker/Queue.h" +#include "qpid/broker/QueueSettings.h" +#include "qpid/broker/RecoveryManagerImpl.h" +#include "qpid/broker/PersistableObject.h" +#include "qpid/framing/AMQHeaderBody.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/framing/FieldValue.h" #include "qpid/legacystore/MessageStoreImpl.h" -#include <iostream> -#include "tests/legacystore/MessageUtils.h" #include "qpid/legacystore/StoreException.h" -#include "qpid/broker/DirectExchange.h" -#include <qpid/broker/Queue.h> -#include <qpid/broker/QueueSettings.h> -#include <qpid/broker/RecoveryManagerImpl.h> -#include <qpid/framing/AMQHeaderBody.h> -#include <qpid/framing/FieldTable.h> -#include <qpid/framing/FieldValue.h> #include "qpid/log/Logger.h" #include "qpid/sys/Timer.h" +#include <iostream> + qpid::broker::Broker::Options opts; qpid::broker::Broker br(opts); @@ -57,15 +59,15 @@ QPID_AUTO_TEST_SUITE(SimpleTest) const string test_filename("SimpleTest"); const char* tdp = getenv("TMP_DATA_DIR"); -const string test_dir(tdp && strlen(tdp) > 0 ? tdp : "/tmp/SimpleTest"); +const string test_dir(tdp && strlen(tdp) > 0 ? tdp : "/var/tmp/SimpleTest"); // === Helper fns === -struct DummyHandler : OutputHandler +struct DummyHandler : FrameHandler { std::vector<AMQFrame> frames; - virtual void send(AMQFrame& frame){ + virtual void handle(AMQFrame& frame){ frames.push_back(frame); } }; @@ -75,7 +77,8 @@ void recover(MessageStoreImpl& store, QueueRegistry& queues, ExchangeRegistry& e sys::Timer t; DtxManager mgr(t); mgr.setStore (&store); - RecoveryManagerImpl recovery(queues, exchanges, links, mgr, br.getProtocolRegistry()); + RecoveredObjects ro; + RecoveryManagerImpl recovery(queues, exchanges, links, mgr, br.getProtocolRegistry(), ro); store.recover(recovery); } @@ -232,8 +235,8 @@ QPID_AUTO_TEST_CASE(QueueCreateWithSettings) recover(store, registry); Queue::shared_ptr queue = registry.find(name); BOOST_REQUIRE(queue); - BOOST_CHECK_EQUAL(settings.maxDepth.getCount(), 202); - BOOST_CHECK_EQUAL(settings.maxDepth.getSize(), 1003); + BOOST_CHECK_EQUAL(settings.maxDepth.getCount(), 202u); + BOOST_CHECK_EQUAL(settings.maxDepth.getSize(), 1003u); BOOST_CHECK_EQUAL(settings.maxDepth.getCount(), queue->getSettings().maxDepth.getCount()); BOOST_CHECK_EQUAL(settings.maxDepth.getSize(), queue->getSettings().maxDepth.getSize()); } @@ -307,7 +310,7 @@ QPID_AUTO_TEST_CASE(Enqueue) BOOST_CHECK_EQUAL(routingKey, msg.getRoutingKey()); BOOST_CHECK_EQUAL(messageId, MessageUtils::getMessageId(msg)); BOOST_CHECK_EQUAL(std::string("xyz"), msg.getAnnotation("abc")); - BOOST_CHECK_EQUAL((u_int64_t) 14, msg.getContentSize()); + BOOST_CHECK_EQUAL((u_int64_t) 14, msg.getContent().size()); DummyHandler handler; MessageUtils::deliver(msg, handler, 100); diff --git a/qpid/cpp/src/tests/legacystore/TransactionalTest.cpp b/qpid/cpp/src/tests/legacystore/TransactionalTest.cpp index 2d3f6f922c..d1bc34d5a7 100644 --- a/qpid/cpp/src/tests/legacystore/TransactionalTest.cpp +++ b/qpid/cpp/src/tests/legacystore/TransactionalTest.cpp @@ -20,18 +20,20 @@ */ #include "unit_test.h" - -#include "qpid/legacystore/MessageStoreImpl.h" -#include <iostream> #include "MessageUtils.h" -#include "qpid/legacystore/StoreException.h" + #include "qpid/broker/Queue.h" #include "qpid/broker/RecoveryManagerImpl.h" +#include "qpid/broker/PersistableObject.h" #include "qpid/framing/AMQHeaderBody.h" +#include "qpid/legacystore/MessageStoreImpl.h" +#include "qpid/legacystore/StoreException.h" #include "qpid/log/Statement.h" #include "qpid/log/Logger.h" #include "qpid/sys/Timer.h" +#include <iostream> + using namespace mrg::msgstore; using namespace qpid; using namespace qpid::broker; @@ -53,7 +55,7 @@ QPID_AUTO_TEST_SUITE(TransactionalTest) const string test_filename("TransactionalTest"); const char* tdp = getenv("TMP_DATA_DIR"); -const string test_dir(tdp && strlen(tdp) > 0 ? tdp : "/tmp/TransactionalTest"); +const string test_dir(tdp && strlen(tdp) > 0 ? tdp : "/var/tmp/TransactionalTest"); // Test txn context which has special setCompleteFailure() method which prevents entire "txn complete" process from hapenning class TestTxnCtxt : public TxnCtxt @@ -141,7 +143,8 @@ void restart() sys::Timer t; DtxManager mgr(t); mgr.setStore (store.get()); - RecoveryManagerImpl recovery(*queues, exchanges, links, mgr, br.getProtocolRegistry()); + RecoveredObjects ro; + RecoveryManagerImpl recovery(*queues, exchanges, links, mgr, br.getProtocolRegistry(), ro); store->recover(recovery); queueA = queues->find(nameA); diff --git a/qpid/cpp/src/tests/legacystore/TwoPhaseCommitTest.cpp b/qpid/cpp/src/tests/legacystore/TwoPhaseCommitTest.cpp index 92e49df9e3..25bb9dc607 100644 --- a/qpid/cpp/src/tests/legacystore/TwoPhaseCommitTest.cpp +++ b/qpid/cpp/src/tests/legacystore/TwoPhaseCommitTest.cpp @@ -20,18 +20,20 @@ */ #include "unit_test.h" - -#include "qpid/legacystore/MessageStoreImpl.h" -#include <iostream> #include "MessageUtils.h" + #include "qpid/broker/Queue.h" #include "qpid/broker/RecoveryManagerImpl.h" +#include "qpid/broker/PersistableObject.h" #include "qpid/framing/AMQHeaderBody.h" -#include "qpid/log/Statement.h" +#include "qpid/legacystore/MessageStoreImpl.h" #include "qpid/legacystore/TxnCtxt.h" #include "qpid/log/Logger.h" +#include "qpid/log/Statement.h" #include "qpid/sys/Timer.h" +#include <iostream> + using namespace mrg::msgstore; using namespace qpid; using namespace qpid::broker; @@ -54,7 +56,7 @@ QPID_AUTO_TEST_SUITE(TwoPhaseCommitTest) const string test_filename("TwoPhaseCommitTest"); const char* tdp = getenv("TMP_DATA_DIR"); -string test_dir(tdp && strlen(tdp) > 0 ? tdp : "/tmp/TwoPhaseCommitTest"); +string test_dir(tdp && strlen(tdp) > 0 ? tdp : "/var/tmp/TwoPhaseCommitTest"); // === Helper fns === @@ -386,7 +388,8 @@ class TwoPhaseCommitTest links = std::auto_ptr<LinkRegistry>(new LinkRegistry); dtxmgr = std::auto_ptr<DtxManager>(new DtxManager(t)); dtxmgr->setStore (store.get()); - RecoveryManagerImpl recovery(*queues, exchanges, *links, *dtxmgr, br.getProtocolRegistry()); + RecoveredObjects ro; + RecoveryManagerImpl recovery(*queues, exchanges, *links, *dtxmgr, br.getProtocolRegistry(), ro); store->recover(recovery); queueA = queues->find(nameA); diff --git a/qpid/cpp/src/tests/legacystore/federation/Makefile.am b/qpid/cpp/src/tests/legacystore/federation/Makefile.am new file mode 100644 index 0000000000..c48e861a65 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/federation/Makefile.am @@ -0,0 +1,46 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +abs_srcdir=@abs_srcdir@ + +TMP_DATA_DIR=$(abs_srcdir)/../tmp_data_dir + +TESTS = \ + run_federation_sys_tests + +LONG_TESTS = \ + run_long_federation_sys_tests + +EXTRA_DIST = \ + federation_tests_env.sh \ + run_federation_sys_tests \ + run_long_federation_sys_tests + +TESTS_ENVIRONMENT = \ + QPID_DIR=$(QPID_DIR) \ + QPID_BLD=$(QPID_BLD) \ + TMP_DATA_DIR=$(TMP_DATA_DIR) \ + abs_srcdir=$(abs_srcdir) + +check-long: all + $(MAKE) check TESTS="$(LONG_TESTS)" SUBDIRS=. + +# END + diff --git a/qpid/cpp/src/tests/legacystore/federation/federation_tests_env.sh b/qpid/cpp/src/tests/legacystore/federation/federation_tests_env.sh new file mode 100755 index 0000000000..bf75056444 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/federation/federation_tests_env.sh @@ -0,0 +1,313 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +# --- Function definitions --- + +func_check_required_env () +#------------------------- +# Check that EITHER: +# QPID_DIR is set (for running against svn QPID) +# OR +# QPID_PREFIX is set (for running against installed QPID +# Will exit with error code 1 if neither of these is defined. +# Params: None +# Returns: 0 if env vars ok, 1 otherwise +{ + if test -z "${QPID_DIR}" -a -z "${QPID_PREFIX}"; then + # Try to find qpidd in the normal installed location + if test -x /usr/sbin/qpidd; then + QPID_PREFIX=/usr + else + echo "ERROR: Could not find installed Qpid" + echo "Either of the following must be set in the environment for this script to run:" + echo " QPID_DIR for running against a Qpid svn build" + echo " QPID_PREFIX for running against an installed Qpid" + return 1 + fi + fi + return 0 +} + + +func_check_clustering () +#----------------------- +# Check openAIS/corosync is running and user has correct privileges +# Params: None +# Returns: 0 if openAIS/corosync is running, 1 otherwise +# Sets env var COROSYNC to 1 if corosync is running, not set otherwise +{ + # Check either aisexec or corosync is running as root + cluster_prog=`ps -u root | grep 'aisexec\|corosync'` + test -n "$cluster_prog" || NODAEMON="Neither aisexec nor corosync is running as root" + if test -z "$NODAEMON"; then + # Test for corosync running + echo $cluster_prog | grep "aisexec" > /dev/null || COROSYNC=1 + if test -n "$COROSYNC"; then + # Corosync auth test + user=`whoami` + ls /etc/corosync/uidgid.d | grep $user > /dev/null || NOAUTH="You are not authorized to use corosync." + else + # OpenAis auth test + id -nG | grep '\<ais\>' >/dev/null || NOAUTH="You are not a member of the ais group." + fi + fi + + if test -n "$NODAEMON" -o -n "$NOAUTH"; then + cat <<EOF + + ========== WARNING: NOT RUNNING CLUSTER TESTS ============ + + Cluster tests will not be run because: + + $NODAEMON + $NOAUTH + + ========================================================== + +EOF + return 1 + fi + CLUSTERING_ENABLED=1 + return 0 +} + + +func_check_qpid_python () +#------------------------ +# Check that Qpid python environment is ok +# Params: None +# Returns: 0 if Python environment is ok; 1 otherwise +{ + if ! python -c "import qpid" ; then + cat <<EOF + + =========== WARNING: PYTHON TESTS DISABLED ============== + + Unable to load python qpid module - skipping python tests. + + PYTHONPATH=${PYTHONPATH} + + =========================================================== + +EOF + return 1 + fi + return 0 +} + +func_set_python_env() +#-------------------- +# Set up the python path +# Params: None +# Returns: Nothing +{ + if test "${QPID_DIR}" -a -d "${QPID_DIR}" ; then + QPID_PYTHON=${QPID_DIR}/python + QPID_TOOLS=${QPID_DIR}/tools/src/py + QMF_LIB=${QPID_DIR}/extras/qmf/src/py + export PYTHONPATH=${QPID_PYTHON}:${QMF_LIB}:${QPID_TOOLS}:$PYTHONPATH + fi +} + +func_set_env () +#-------------- +# Set up the environment based on value of ${QPID_DIR}: if ${QPID_DIR} exists, assume a svn checkout, +# otherwise set up for an installed or prefix test. +# Params: None +# Returns: Nothing +{ + if test "${QPID_DIR}" -a -d "${QPID_DIR}" ; then + # QPID_DIR is defined for source tree builds by the --with-qpid-checkout configure option. + # QPID_BLD is defined as the build directory, either $QPID_DIR/cpp or separately specified with + # the --with-qpid-build option for VPATH builds. + + # Check QPID_BLD is also set + if test -z ${QPID_BLD}; then + QPID_BLD="${QPID_DIR}/cpp" + fi + source $QPID_BLD/src/tests/test_env.sh +# CPP_CLUSTER_EXEC="${QPID_BLD}/src/tests/cluster_test" +# PYTHON_CLUSTER_EXEC="${QPID_DIR}/cpp/src/tests/$PYTHON_TESTNAME" + FEDERATION_SYS_TESTS_FAIL="${QPID_DIR}/cpp/src/tests/federation_sys_tests.fail" + if test -z ${STORE_LIB}; then + STORE_LIB="../../lib/.libs/msgstore.so" + fi +# export STORE_ENABLE=1 + else + # Set up the environment based on value of ${QPID_PREFIX} for testing against an installed qpid + # Alternatively, make sure ${QPID_BIN_DIR}, ${QPID_SBIN_DIR}, ${QPID_LIB_DIR} and ${QPID_LIBEXEC_DIR} are set for + # the installed location. + if test "${QPID_PREFIX}" -a -d "${QPID_PREFIX}" ; then + QPID_BIN_DIR=${QPID_PREFIX}/bin + QPID_SBIN_DIR=${QPID_PREFIX}/sbin + QPID_LIB_DIR=${QPID_PREFIX}/lib + QPID_LIBEXEC_DIR=${QPID_PREFIX}/libexec + export PATH="$QPID_BIN_DIR:$QPID_SBIN_DIR:$QPID_LIBEXEC_DIR/qpid/tests:$PATH" + fi + + # These four env vars must be set prior to calling this script + func_checkpaths QPID_BIN_DIR QPID_SBIN_DIR QPID_LIB_DIR QPID_LIBEXEC_DIR + + # Paths and dirs + export PYTHON_DIR="${QPID_BIN_DIR}" + export PYTHONPATH="${QPID_LIB_DIR}/python:${QPID_LIBEXEC_DIR}/qpid/tests:${QPID_LIB_DIR}/python2.4:${QPID_LIB_DIR}/python2.4/site-packages:${PYTHONPATH}" + # Libraries + export CLUSTER_LIB="${QPID_LIB_DIR}/qpid/daemon/cluster.so" + export ACL_LIB="${QPID_LIB_DIR}/qpid/daemon/acl.so" + export TEST_STORE_LIB="${QPID_LIB_DIR}/qpid/tests/test_store.so" + + # Executables +# CPP_CLUSTER_EXEC="${QPID_LIBEXEC_DIR}/qpid/tests/cluster_test" +# PYTHON_CLUSTER_EXEC="${QPID_LIBEXEC_DIR}/qpid/tests/$PYTHON_TESTNAME" + export QPIDD_EXEC="${QPID_SBIN_DIR}/qpidd" + export QPID_CONFIG_EXEC="${QPID_BIN_DIR}/qpid-config" + export QPID_ROUTE_EXEC="${QPID_BIN_DIR}/qpid-route" + export QPID_CLUSTER_EXEC="${QPID_BIN_DIR}/qpid-cluster" +# export RECEIVER_EXEC="${QPID_LIBEXEC_DIR}/qpid/tests/receiver" +# export SENDER_EXEC="${QPID_LIBEXEC_DIR}/qpid/tests/sender" + export QPID_PYTHON_TEST="${QPID_BIN_DIR}/qpid-python-test" + + # Data + FEDERATION_SYS_TESTS_FAIL="${QPID_LIBEXEC_DIR}/qpid/tests/federation_sys_tests.fail" + fi +} + + +func_mk_data_dir () +#------------------ +# Create a data dir at ${TMP_DATA_DIR} if not present, clear it otherwise. +# Set TMP_DATA_DIR if it is not set. +# Params: None +# Returns: Nothing +{ + if test -z "${TMP_DATA_DIR}"; then + TMP_DATA_DIR=/tmp/federation_sys_tests + echo "TMP_DATA_DIR not set; using ${TMP_DATA_DIR}" + fi + + # Delete old cluster test dirs if they exist + if test -d "${TMP_DATA_DIR}" ; then + rm -rf "${TMP_DATA_DIR}/cluster" + fi + mkdir -p "${TMP_DATA_DIR}/cluster" + export TMP_DATA_DIR +} + + +func_checkvar () +#--------------- +# Check that an environment var is set (ie non-zero length) +# Params: $1 - env var to be checked +# Returns: 0 = env var is set (ie non-zero length) +# 1 = env var is not set +{ + local loc_VAR=$1 + if test -z ${!loc_VAR}; then + echo "WARNING: environment variable ${loc_VAR} not set." + return 1 + fi + return 0 +} + + +func_checkpaths () +#----------------- +# Check a list of paths (each can contain ':'-separated sub-list) is set and valid (ie each path exists as a dir) +# Params: $@ - List of path env vars to be checked +# Returns: Nothing +{ + local loc_PATHS=$@ + for path in ${loc_PATHS}; do + func_checkvar ${path} + if test $? == 0; then + local temp_IFS=${IFS} + IFS=":" + local pl=${!path} + for p in ${pl[@]}; do + if test ! -d ${p}; then + echo "WARNING: Directory ${p} in var ${path} not found." + fi + done + IFS=${temp_IFS} + fi + done +} + + +func_checklibs () +#---------------- +# Check that a list of libs is set and valid (ie each lib exists as an executable file) +# Params: $@ - List of lib values to be checked +# Returns: Nothing +{ + local loc_LIBS=$@ + for lib in ${loc_LIBS[@]}; do + func_checkvar ${lib} + if test $? == 0; then + if test ! -x ${!lib}; then + echo "WARNING: Library ${lib}=${!lib} not found." + fi + fi + done +} + + +func_checkexecs () +#----------------- +# Check that a list of executable is set and valid (ie each exec exists as an executable file) +# Params: $@ - List of exec values to be checked +# Returns: Nothing +{ + local loc_EXECS=$@ + for exec in ${loc_EXECS[@]}; do + func_checkvar ${exec} + if test $? == 0; then + if test ! -x ${!exec}; then + echo "WARNING: Executable ${exec}=${!exec} not found or is not executable." + fi + fi + done +} + + +#--- Start of script --- + +func_set_python_env +func_check_required_env || exit 1 # Cannot run, exit with error +func_check_qpid_python || exit 1 # Cannot run, exit with error +func_check_clustering # Warning + +PYTHON_TESTNAME=federation_sys.py +func_set_env +func_mk_data_dir + +# Check expected environment vars are set +func_checkpaths PYTHON_DIR PYTHONPATH TMP_DATA_DIR +func_checklibs CLUSTER_LIB STORE_LIB +func_checkexecs QPIDD_EXEC QPID_CONFIG_EXEC QPID_ROUTE_EXEC QPID_PYTHON_TEST + +FAILING_PYTHON_TESTS="${abs_srcdir}/../failing_python_tests.txt" +if test -z $1; then + FEDERATION_SYS_TEST="${QPID_PYTHON_TEST} -m cluster_tests -I ${FEDERATION_SYS_TESTS_FAIL}" +else + FEDERATION_SYS_TEST="${QPID_PYTHON_TEST} -m cluster_tests -I ${FEDERATION_SYS_TESTS_FAIL} cluster_tests.LongTests.*" + LONG_TEST=1 +fi + diff --git a/qpid/cpp/src/tests/legacystore/federation/run_federation_sys_tests b/qpid/cpp/src/tests/legacystore/federation/run_federation_sys_tests new file mode 100755 index 0000000000..dff5f12770 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/federation/run_federation_sys_tests @@ -0,0 +1,96 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +# Run the federation tests. +source ${abs_srcdir}/federation_tests_env.sh + +MODULENAME=federation_sys + +# Test for long test +if [[ "$1" == "LONG_TEST" ]]; then + USE_LONG_TEST=1 + shift # get rid of this param so it is not treated as a test name +fi + +trap stop_brokers INT TERM QUIT + +MODULES="--load-module ${STORE_LIB} --jfile-size 12 --num-jfiles 4" +CLUSTER_MODULE="--load-module ${CLUSTER_LIB} " +if [ -z ${USE_LONG_TEST} ]; then + SKIPTESTS="-i federation_sys.A_Long* -i federation_sys.B_Long* -i federation_sys.E_Long* -i federation_sys.F_Long*" +fi +if [ -z ${CLUSTERING_ENABLED} ]; then + SKIPTESTS="${SKIPTESTS} -i federation_sys.C_* -i federation_sys.D_* -i federation_sys.G_* -i federation_sys.H_*" +elif [ -z ${USE_LONG_TEST} ]; then + SKIPTESTS="${SKIPTESTS} -i federation_sys.C_Long* -i federation_sys.D_Long* -i federation_sys.G_Long* -i federation_sys.H_Long*" +fi + +start_brokers() { + clean_or_create_dir() { + if [ -n "$1" -a -d $1 ]; then + rm -rf $1/* + else + mkdir -p $1 + fi + } + start_broker() { + clean_or_create_dir $1 + ${QPIDD_EXEC} --daemon --port 0 --auth no --data-dir $1 $2 > qpidd.port + PORT=`cat qpidd.port` + eval "$3=${PORT}" + } + start_broker ${TMP_DATA_DIR}/local "${MODULES} --log-enable info+ --log-to-file ${TMP_DATA_DIR}/qpidd.log.local" LOCAL_PORT + start_broker ${TMP_DATA_DIR}/remote "${MODULES} --log-enable info+ --log-to-file ${TMP_DATA_DIR}/qpidd.log.remote" REMOTE_PORT + if [ -n "$CLUSTERING_ENABLED" ]; then + start_broker ${TMP_DATA_DIR}/cluster/c1.1 "${MODULES} ${CLUSTER_MODULE} --cluster-name test-cluster-1 --log-enable info+ --log-to-file ${TMP_DATA_DIR}/qpidd.log.cluster1.1" CLUSTER_C1_1 + start_broker ${TMP_DATA_DIR}/cluster/c1.2 "${MODULES} ${CLUSTER_MODULE} --cluster-name test-cluster-1 --log-enable info+ --log-to-file ${TMP_DATA_DIR}/qpidd.log.cluster1.2" CLUSTER_C1_2 + start_broker ${TMP_DATA_DIR}/cluster/c2.1 "${MODULES} ${CLUSTER_MODULE} --cluster-name test-cluster-2 --log-enable info+ --log-to-file ${TMP_DATA_DIR}/qpidd.log.cluster2.1" CLUSTER_C2_1 + start_broker ${TMP_DATA_DIR}/cluster/c2.2 "${MODULES} ${CLUSTER_MODULE} --cluster-name test-cluster-2 --log-enable info+ --log-to-file ${TMP_DATA_DIR}/qpidd.log.cluster2.2" CLUSTER_C2_2 + fi + rm qpidd.port +} + +stop_brokers() { + ${QPIDD_EXEC} -q --port ${LOCAL_PORT} + ${QPIDD_EXEC} -q --port ${REMOTE_PORT} + if [ -n "${CLUSTERING_ENABLED}" ]; then + ${QPID_CLUSTER_EXEC} --all-stop --force localhost:${CLUSTER_C1_1} + ${QPID_CLUSTER_EXEC} --all-stop --force localhost:${CLUSTER_C2_1} + fi +} + +if test -d ${PYTHON_DIR} ; then + start_brokers + if [ -z ${CLUSTERING_ENABLED} ]; then + echo "Running federation tests using brokers on local port ${LOCAL_PORT}, remote port ${REMOTE_PORT} (NOTE: clustering is DISABLED)" + else + echo "Running federation tests using brokers on local port ${LOCAL_PORT}, remote port ${REMOTE_PORT}, local cluster nodes ${CLUSTER_C1_1} ${CLUSTER_C1_2}, remote cluster nodes ${CLUSTER_C2_1} ${CLUSTER_C2_2}" + fi + if [ -z ${USE_LONG_TEST} ]; then + echo "NOTE: To run a full set of federation system tests, use \"make check-long\"." + fi + ${QPID_PYTHON_TEST} -m ${MODULENAME} ${SKIPTESTS} -b localhost:$REMOTE_PORT -Dlocal-port=$LOCAL_PORT -Dremote-port=$REMOTE_PORT -Dlocal-cluster-ports="$CLUSTER_C1_1 $CLUSTER_C1_2" -Dremote-cluster-ports="$CLUSTER_C2_1 $CLUSTER_C2_2" $@ + RETCODE=$? + stop_brokers + if test x$RETCODE != x0; then + echo "FAIL federation tests"; exit 1; + fi +fi diff --git a/qpid/cpp/examples/qmf-agent/example_gen.mak b/qpid/cpp/src/tests/legacystore/federation/run_long_federation_sys_tests index 1d71e77b63..d2b8eec32a 100644..100755 --- a/qpid/cpp/examples/qmf-agent/example_gen.mak +++ b/qpid/cpp/src/tests/legacystore/federation/run_long_federation_sys_tests @@ -1,29 +1,24 @@ -#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-# This nmake file generates the C++ mapping from the example schema.
-#
-# The Visual Studio projects assume the existence of the generated files.
-# These generated files must be created using this makefile for Windows
-# developers working from the source repository.
-
-mgen_dir=..\..\..\cpp\managementgen
-
-all:
- python $(mgen_dir)\qmf-gen -o .\gen\qmf .\schema.xml
+#! /bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +# Run the federation system tests (long version). + +./run_federation_sys_tests LONG_TEST $@ diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_st_auto_expand.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_st_auto_expand.cpp new file mode 100644 index 0000000000..fb5c1f1742 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/_st_auto_expand.cpp @@ -0,0 +1,140 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../unit_test.h" +#include <cmath> +#include <iostream> +#include "qpid/legacystore/jrnl/jcntl.h" + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace std; + +QPID_AUTO_TEST_SUITE(journal_auto_expand) + +const string test_filename("_st_auto_expand"); + +#include "_st_helper_fns.h" + +// === Test suite === + +QPID_AUTO_TEST_CASE(no_ae_threshold) +{ + string test_name = get_test_name(test_filename, "no_ae_threshold"); + try + { + string msg; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_DEFAULT_JFILES, false, 0, DEFAULT_JFSIZE_SBLKS); + unsigned m; + + // Fill journal to just below threshold + u_int32_t t = num_msgs_to_threshold(NUM_DEFAULT_JFILES, + DEFAULT_JFSIZE_SBLKS * JRNL_SBLK_SIZE, LARGE_MSG_REC_SIZE_DBLKS); + for (m=0; m<t; m++) + enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false); + // This enqueue should exceed the threshold + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), t); + enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false, RHM_IORES_ENQCAPTHRESH); + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), t); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(no_ae_threshold_dequeue_some) +{ + string test_name = get_test_name(test_filename, "no_ae_threshold_dequeue_some"); + try + { + string msg; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_DEFAULT_JFILES, false, 0, DEFAULT_JFSIZE_SBLKS); + unsigned m; + + // Fill journal to just below threshold + u_int32_t t = num_msgs_to_threshold(NUM_DEFAULT_JFILES, + DEFAULT_JFSIZE_SBLKS * JRNL_SBLK_SIZE, LARGE_MSG_REC_SIZE_DBLKS); + for (m=0; m<t; m++) + enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false); + // This enqueue should exceed the threshold + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), t); + enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false, RHM_IORES_ENQCAPTHRESH); + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), t); + + // Dequeue 25 msgs + #define NUM_MSGS_DEQ 25 + for (m=0; m<NUM_MSGS_DEQ; m++) + deq_msg(jc, m, m+t); + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), u_int32_t(t-NUM_MSGS_DEQ)); + + // Check we can still enqueue and dequeue + for (m=t+NUM_MSGS_DEQ; m<t+NUM_MSGS_DEQ+NUM_MSGS; m++) + enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false); + for (m=t+NUM_MSGS_DEQ; m<t+NUM_MSGS_DEQ+NUM_MSGS; m++) + deq_msg(jc, m, m+NUM_MSGS_DEQ+NUM_MSGS); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(no_ae_threshold_dequeue_all) +{ + string test_name = get_test_name(test_filename, "no_ae_threshold_dequeue_all"); + try + { + string msg; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_DEFAULT_JFILES, false, 0, DEFAULT_JFSIZE_SBLKS); + unsigned m; + + // Fill journal to just below threshold + u_int32_t t = num_msgs_to_threshold(NUM_DEFAULT_JFILES, + DEFAULT_JFSIZE_SBLKS * JRNL_SBLK_SIZE, LARGE_MSG_REC_SIZE_DBLKS); + for (m=0; m<t; m++) + enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false); + // This enqueue should exceed the threshold + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), t); + enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false, RHM_IORES_ENQCAPTHRESH); + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), t); + + // Dequeue all msgs + for (m=0; m<t; m++) + deq_msg(jc, m, m+t); + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), u_int32_t(0)); + + // Check we can still enqueue and dequeue + for (m=2*t; m<2*t + NUM_MSGS; m++) + enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false); + for (m=2*t; m<2*t + NUM_MSGS; m++) + deq_msg(jc, m, m+2*t+NUM_MSGS); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_st_basic.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_st_basic.cpp new file mode 100644 index 0000000000..4aa6d2e29f --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/_st_basic.cpp @@ -0,0 +1,558 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../unit_test.h" +#include <cmath> +#include <iostream> +#include "qpid/legacystore/jrnl/jcntl.h" + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace std; + +QPID_AUTO_TEST_SUITE(journal_basic) + +const string test_filename("_st_basic"); + +#include "_st_helper_fns.h" + +// === Test suite === + +#ifndef LONG_TEST +/* + * ============================================== + * NORMAL TESTS + * This section contains normal "make check" tests + * for building/packaging. These are built when + * LONG_TEST is _not_ defined. + * ============================================== + */ + +QPID_AUTO_TEST_CASE(instantiation) +{ + string test_name = get_test_name(test_filename, "instantiation"); + try + { + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + BOOST_CHECK_EQUAL(jc.is_ready(), false); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(initialization) +{ + string test_name = get_test_name(test_filename, "initialization"); + try + { + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + BOOST_CHECK_EQUAL(jc.is_ready(), false); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + BOOST_CHECK_EQUAL(jc.is_ready(), true); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_dequeue_block) +{ + string test_name = get_test_name(test_filename, "enqueue_dequeue_block"); + try + { + string msg; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + BOOST_CHECK_EQUAL(enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false), u_int64_t(m)); + for (int m=0; m<NUM_MSGS; m++) + deq_msg(jc, m, m+NUM_MSGS); + + // Again... + for (int m=2*NUM_MSGS; m<3*NUM_MSGS; m++) + BOOST_CHECK_EQUAL(enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false), u_int64_t(m)); + for (int m=2*NUM_MSGS; m<3*NUM_MSGS; m++) + deq_msg(jc, m, m+3*NUM_MSGS); + + // Disjoint rids + for (int m=10*NUM_MSGS; m<11*NUM_MSGS; m++) + BOOST_CHECK_EQUAL(enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false), u_int64_t(m)); + for (int m=10*NUM_MSGS; m<11*NUM_MSGS; m++) + deq_msg(jc, m, m+11*NUM_MSGS); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_dequeue_interleaved) +{ + string test_name = get_test_name(test_filename, "enqueue_dequeue_interleaved"); + try + { + string msg; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<2*NUM_MSGS; m+=2) + { + BOOST_CHECK_EQUAL(enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false), u_int64_t(m)); + deq_msg(jc, m, m+1); + } + + // Again... + for (int m=2*NUM_MSGS; m<4*NUM_MSGS; m+=2) + { + BOOST_CHECK_EQUAL(enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false), u_int64_t(m)); + deq_msg(jc, m, m+1); + } + + // Disjoint rids + for (int m=10*NUM_MSGS; m<12*NUM_MSGS; m+=2) + { + BOOST_CHECK_EQUAL(enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false), u_int64_t(m)); + deq_msg(jc, m, m+1); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_dequeue_interleaved_file_rollover) +{ + string test_name = get_test_name(test_filename, "enqueue_dequeue_interleaved_file_rollover"); + try + { + string msg; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + unsigned n = num_msgs_to_full(NUM_TEST_JFILES, TEST_JFSIZE_SBLKS * JRNL_SBLK_SIZE, + 16*MSG_REC_SIZE_DBLKS, true); + for (unsigned m=0; m<3*2*n; m+=2) // overwrite files 3 times + { + enq_msg(jc, m, create_msg(msg, m, 16*MSG_SIZE), false); + deq_msg(jc, m, m+1); + } + jc.stop(true); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(empty_recover) +{ + string test_name = get_test_name(test_filename, "empty_recover"); + try + { + { + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + BOOST_CHECK_EQUAL(jc.is_ready(), false); + BOOST_CHECK_EQUAL(jc.is_read_only(), false); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + BOOST_CHECK_EQUAL(jc.is_ready(), true); + BOOST_CHECK_EQUAL(jc.is_read_only(), false); + } + { + u_int64_t hrid; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + BOOST_CHECK_EQUAL(jc.is_ready(), false); + BOOST_CHECK_EQUAL(jc.is_read_only(), false); + jc.recover(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS, 0, hrid); + BOOST_CHECK_EQUAL(jc.is_ready(), true); + BOOST_CHECK_EQUAL(jc.is_read_only(), true); + BOOST_CHECK_EQUAL(hrid, u_int64_t(0)); + } + { + u_int64_t hrid; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + BOOST_CHECK_EQUAL(jc.is_ready(), false); + BOOST_CHECK_EQUAL(jc.is_read_only(), false); + jc.recover(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS, 0, hrid); + BOOST_CHECK_EQUAL(jc.is_ready(), true); + BOOST_CHECK_EQUAL(jc.is_read_only(), true); + BOOST_CHECK_EQUAL(hrid, u_int64_t(0)); + jc.recover_complete(); + BOOST_CHECK_EQUAL(jc.is_ready(), true); + BOOST_CHECK_EQUAL(jc.is_read_only(), false); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_recover_dequeue_block) +{ + string test_name = get_test_name(test_filename, "enqueue_recover_dequeue_block"); + try + { + { + string msg; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + } + { + u_int64_t hrid; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.recover(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS, 0, hrid); + BOOST_CHECK_EQUAL(hrid, u_int64_t(NUM_MSGS - 1)); + jc.recover_complete(); + for (int m=0; m<NUM_MSGS; m++) + deq_msg(jc, m, m+NUM_MSGS); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_recover_dequeue_interleaved) +{ + string test_name = get_test_name(test_filename, "enqueue_recover_dequeue_interleaved"); + try + { + string msg; + u_int64_t hrid; + + for (int m=0; m<2*NUM_MSGS; m+=2) + { + { + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + if (m == 0) + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); // First time only + else + { + jc.recover(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS, 0, hrid); + BOOST_CHECK_EQUAL(hrid, u_int64_t(m - 1)); + jc.recover_complete(); + } + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + } + { + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.recover(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS, 0, hrid); + BOOST_CHECK_EQUAL(hrid, u_int64_t(m)); + jc.recover_complete(); + deq_msg(jc, m, m+1); + } + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(header_flags) +{ + string test_name = get_test_name(test_filename, "header_flags"); + try + { + { + string msg; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + // Transient msgs - should not recover + for (int m=0; m<NUM_MSGS; m++) + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), true); + // Persistent msgs + for (int m=NUM_MSGS; m<NUM_MSGS*2; m++) + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + // Transient extern msgs - should not recover + for (int m=NUM_MSGS*2; m<NUM_MSGS*3; m++) + enq_extern_msg(jc, m, MSG_SIZE, true); + // Persistnet extern msgs + for (int m=NUM_MSGS*3; m<NUM_MSGS*4; m++) + enq_extern_msg(jc, m, MSG_SIZE, false); + } + { + string msg; + u_int64_t hrid; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.recover(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS, 0, hrid); + // Recover non-transient msgs + for (int m=NUM_MSGS; m<NUM_MSGS*2; m++) + { + string rmsg; + string xid; + bool transientFlag; + bool externalFlag; + + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + BOOST_CHECK_MESSAGE(transientFlag == false, "Transient message recovered."); + BOOST_CHECK_MESSAGE(externalFlag == false, "External flag incorrect."); + BOOST_CHECK_MESSAGE(create_msg(msg, m, MSG_SIZE).compare(rmsg) == 0, + "Non-transient message corrupt during recover."); + } + // Recover non-transient extern msgs + for (int m=NUM_MSGS*3; m<NUM_MSGS*4; m++) + { + string rmsg; + string xid; + bool transientFlag; + bool externalFlag; + + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + BOOST_CHECK_MESSAGE(transientFlag == false, "Transient message recovered."); + BOOST_CHECK_MESSAGE(externalFlag == true, "External flag incorrect."); + BOOST_CHECK_MESSAGE(rmsg.size() == 0, "External message returned non-zero size."); + } + jc.recover_complete(); + // Read recovered non-transient msgs + for (int m=NUM_MSGS; m<NUM_MSGS*2; m++) + { + string rmsg; + string xid; + bool transientFlag; + bool externalFlag; + + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + BOOST_CHECK_MESSAGE(transientFlag == false, "Transient message recovered."); + BOOST_CHECK_MESSAGE(externalFlag == false, "External flag incorrect."); + BOOST_CHECK_MESSAGE(create_msg(msg, m, MSG_SIZE).compare(rmsg) == 0, + "Non-transient message corrupt during recover."); + } + // Read recovered non-transient extern msgs + for (int m=NUM_MSGS*3; m<NUM_MSGS*4; m++) + { + string rmsg; + string xid; + bool transientFlag; + bool externalFlag; + + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + BOOST_CHECK_MESSAGE(transientFlag == false, "Transient message recovered."); + BOOST_CHECK_MESSAGE(externalFlag == true, "External flag incorrect."); + BOOST_CHECK_MESSAGE(rmsg.size() == 0, "External message returned non-zero size."); + } + // Dequeue recovered messages + for (int m=NUM_MSGS; m<NUM_MSGS*2; m++) + deq_msg(jc, m, m+3*NUM_MSGS); + for (int m=NUM_MSGS*3; m<NUM_MSGS*4; m++) + deq_msg(jc, m, m+2*NUM_MSGS); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(double_dequeue) +{ + string test_name = get_test_name(test_filename, "double_dequeue"); + try + { + string msg; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + enq_msg(jc, 0, create_msg(msg, 0, MSG_SIZE), false); + deq_msg(jc, 0, 1); + try{ deq_msg(jc, 0, 2); BOOST_ERROR("Did not throw exception on second dequeue."); } + catch (const jexception& e){ BOOST_CHECK_EQUAL(e.err_code(), jerrno::JERR_WMGR_DEQRIDNOTENQ); } + enq_msg(jc, 2, create_msg(msg, 1, MSG_SIZE), false); + deq_msg(jc, 2, 3); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +#else +/* + * ============================================== + * LONG TESTS + * This section contains long tests and soak tests, + * and are run using target check-long (ie "make + * check-long"). These are built when LONG_TEST is + * defined. + * ============================================== + */ + +QPID_AUTO_TEST_CASE(journal_overflow) +{ + string test_name = get_test_name(test_filename, "journal_overflow"); + try + { + string msg; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_DEFAULT_JFILES, false, 0, DEFAULT_JFSIZE_SBLKS); + unsigned m; + + // Fill journal to just below threshold + u_int32_t t = num_msgs_to_threshold(NUM_DEFAULT_JFILES, DEFAULT_JFSIZE_SBLKS * JRNL_SBLK_SIZE, + MSG_REC_SIZE_DBLKS); + u_int32_t d = num_dequeues_rem(NUM_DEFAULT_JFILES, DEFAULT_JFSIZE_SBLKS * JRNL_SBLK_SIZE); + for (m=0; m<t; m++) + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + // This enqueue should exceed the threshold + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false, RHM_IORES_ENQCAPTHRESH); + + // Dequeue as many msgs as possible except first + for (m=1; m<=d; m++) + deq_msg(jc, m, m+t); + deq_msg(jc, d+1, d+2, RHM_IORES_FULL); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(file_cycle_block) +{ + string test_name = get_test_name(test_filename, "file_cycle_block"); + try + { + string msg; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_DEFAULT_JFILES, false, 0, DEFAULT_JFSIZE_SBLKS); + + // 5 cycles of enqueue/dequeue blocks of half threshold exception size + u_int32_t t = num_msgs_to_threshold(NUM_DEFAULT_JFILES, DEFAULT_JFSIZE_SBLKS * JRNL_SBLK_SIZE, + LARGE_MSG_REC_SIZE_DBLKS)/2; + for (unsigned i=0; i<5; i++) + { + for (unsigned m=2*i*t; m<(2*i+1)*t; m++) + enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false); + for (unsigned m=2*i*t; m<(2*i+1)*t; m++) + deq_msg(jc, m, m+t); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(file_cycle_interleaved) +{ + string test_name = get_test_name(test_filename, "file_cycle_interleaved"); + try + { + string msg; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_DEFAULT_JFILES, false, 0, DEFAULT_JFSIZE_SBLKS); + + // 5 cycles of enqueue/dequeue blocks of half threshold exception size + u_int32_t t = num_msgs_to_threshold(NUM_DEFAULT_JFILES, DEFAULT_JFSIZE_SBLKS * JRNL_SBLK_SIZE, + LARGE_MSG_REC_SIZE_DBLKS)/2; + for (unsigned m=0; m<5*2*t; m+=2) + { + enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false); + deq_msg(jc, m, m+1); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(recover_file_cycle_block) +{ + string test_name = get_test_name(test_filename, "recover_file_cycle_block"); + try + { + string msg; + u_int64_t hrid; + + // 5 cycles of enqueue/dequeue blocks of half threshold exception size + u_int32_t t = num_msgs_to_threshold(NUM_DEFAULT_JFILES, DEFAULT_JFSIZE_SBLKS * JRNL_SBLK_SIZE, + LARGE_MSG_REC_SIZE_DBLKS)/2; + for (unsigned i=0; i<5; i++) + { + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + if (i) + { + jc.recover(NUM_DEFAULT_JFILES, false, 0, DEFAULT_JFSIZE_SBLKS, 0, hrid); + BOOST_CHECK_EQUAL(hrid, u_int64_t(2*i*t - 1)); + jc.recover_complete(); + } + else + jc.initialize(NUM_DEFAULT_JFILES, false, 0, DEFAULT_JFSIZE_SBLKS); + + for (unsigned m=2*i*t; m<(2*i+1)*t; m++) + enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false); + for (unsigned m=2*i*t; m<(2*i+1)*t; m++) + deq_msg(jc, m, m+t); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(recover_file_cycle_interleaved) +{ + string test_name = get_test_name(test_filename, "recover_file_cycle_interleaved"); + try + { + string msg; + u_int64_t hrid; + + // 5 cycles of enqueue/dequeue blocks of half threshold exception size + u_int32_t t = num_msgs_to_threshold(NUM_DEFAULT_JFILES, DEFAULT_JFSIZE_SBLKS * JRNL_SBLK_SIZE, + LARGE_MSG_REC_SIZE_DBLKS)/2; + for (unsigned i=0; i<5; i++) + { + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + if (i) + { + jc.recover(NUM_DEFAULT_JFILES, false, 0, DEFAULT_JFSIZE_SBLKS, 0, hrid); + BOOST_CHECK_EQUAL(hrid, u_int64_t(2*i*t - 1)); + jc.recover_complete(); + } + else + jc.initialize(NUM_DEFAULT_JFILES, false, 0, DEFAULT_JFSIZE_SBLKS); + + for (unsigned m=2*i*t; m<2*(i+1)*t; m+=2) + { + enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false); + deq_msg(jc, m, m+1); + } + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +#endif + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_st_basic_txn.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_st_basic_txn.cpp new file mode 100644 index 0000000000..aa2d31c2ae --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/_st_basic_txn.cpp @@ -0,0 +1,239 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../unit_test.h" +#include <cmath> +#include <iostream> +#include "qpid/legacystore/jrnl/jcntl.h" + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace std; + +QPID_AUTO_TEST_SUITE(journal_basic_txn) + +const string test_filename("_st_basic_txn"); + +#include "_st_helper_fns.h" + +// === Test suite === + +QPID_AUTO_TEST_CASE(enqueue_commit_dequeue_block) +{ + string test_name = get_test_name(test_filename, "enqueue_commit_dequeue_block"); + try + { + string msg; + string xid; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + create_xid(xid, 0, XID_SIZE); + for (int m=0; m<NUM_MSGS; m++) + BOOST_CHECK_EQUAL(enq_txn_msg(jc, m, create_msg(msg, m, MSG_SIZE), xid, false), u_int64_t(m)); + txn_commit(jc, NUM_MSGS, xid); + for (int m=0; m<NUM_MSGS; m++) + deq_msg(jc, m, m+NUM_MSGS+1); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_abort_dequeue_block) +{ + string test_name = get_test_name(test_filename, "enqueue_abort_dequeue_block"); + try + { + string msg; + string xid; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + create_xid(xid, 0, XID_SIZE); + for (int m=0; m<NUM_MSGS; m++) + BOOST_CHECK_EQUAL(enq_txn_msg(jc, m, create_msg(msg, m, MSG_SIZE), xid, false), u_int64_t(m)); + txn_abort(jc, NUM_MSGS, xid); + for (int m=0; m<NUM_MSGS; m++) + { + try + { + deq_msg(jc, m, m+NUM_MSGS+1); + BOOST_ERROR("Expected dequeue to fail with exception JERR_WMGR_DEQRIDNOTENQ."); + } + catch (const jexception& e) { if (e.err_code() != jerrno::JERR_WMGR_DEQRIDNOTENQ) throw; } + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_commit_dequeue_interleaved) +{ + string test_name = get_test_name(test_filename, "enqueue_commit_dequeue_interleaved"); + try + { + string msg; + string xid; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + { + create_xid(xid, m, XID_SIZE); + BOOST_CHECK_EQUAL(enq_txn_msg(jc, 3*m, create_msg(msg, m, MSG_SIZE), xid, false), u_int64_t(3*m)); + txn_commit(jc, 3*m+1, xid); + deq_msg(jc, 3*m, 3*m+2); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_abort_dequeue_interleaved) +{ + string test_name = get_test_name(test_filename, "enqueue_abort_dequeue_interleaved"); + try + { + string msg; + string xid; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + { + create_xid(xid, m, XID_SIZE); + BOOST_CHECK_EQUAL(enq_txn_msg(jc, 3*m, create_msg(msg, m, MSG_SIZE), xid, false), u_int64_t(3*m)); + txn_abort(jc, 3*m+1, xid); + try + { + deq_msg(jc, 2*m, 2*m+2); + BOOST_ERROR("Expected dequeue to fail with exception JERR_WMGR_DEQRIDNOTENQ."); + } + catch (const jexception& e) { if (e.err_code() != jerrno::JERR_WMGR_DEQRIDNOTENQ) throw; } + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_dequeue_commit_block) +{ + string test_name = get_test_name(test_filename, "enqueue_dequeue_commit_block"); + try + { + string msg; + string xid; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + create_xid(xid, 0, XID_SIZE); + for (int m=0; m<NUM_MSGS; m++) + BOOST_CHECK_EQUAL(enq_txn_msg(jc, m, create_msg(msg, m, MSG_SIZE), xid, false), u_int64_t(m)); + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), u_int32_t(0)); + for (int m=0; m<NUM_MSGS; m++) + deq_txn_msg(jc, m, m+NUM_MSGS, xid); + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), u_int32_t(0)); + txn_commit(jc, 2*NUM_MSGS, xid); + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), u_int32_t(0)); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_dequeue_abort_block) +{ + string test_name = get_test_name(test_filename, "enqueue_dequeue_abort_block"); + try + { + string msg; + string xid; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + create_xid(xid, 0, XID_SIZE); + for (int m=0; m<NUM_MSGS; m++) + BOOST_CHECK_EQUAL(enq_txn_msg(jc, m, create_msg(msg, m, MSG_SIZE), xid, false), u_int64_t(m)); + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), u_int32_t(0)); + for (int m=0; m<NUM_MSGS; m++) + deq_txn_msg(jc, m, m+NUM_MSGS, xid); + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), u_int32_t(0)); + txn_abort(jc, 2*NUM_MSGS, xid); + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), u_int32_t(0)); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_dequeue_commit_interleaved) +{ + string test_name = get_test_name(test_filename, "enqueue_dequeue_commit_interleaved"); + try + { + string msg; + string xid; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + { + create_xid(xid, m, XID_SIZE); + BOOST_CHECK_EQUAL(enq_txn_msg(jc, 3*m, create_msg(msg, m, MSG_SIZE), xid, false), u_int64_t(3*m)); + deq_txn_msg(jc, 3*m, 3*m+1, xid); + txn_commit(jc, 3*m+2, xid); + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), u_int32_t(0)); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_dequeue_abort_interleaved) +{ + string test_name = get_test_name(test_filename, "enqueue_dequeue_abort_interleaved"); + try + { + string msg; + string xid; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + { + create_xid(xid, m, XID_SIZE); + BOOST_CHECK_EQUAL(enq_txn_msg(jc, 3*m, create_msg(msg, m, MSG_SIZE), xid, false), u_int64_t(3*m)); + deq_txn_msg(jc, 3*m, 3*m+1, xid); + txn_abort(jc, 3*m+2, xid); + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), u_int32_t(0)); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_st_helper_fns.h b/qpid/cpp/src/tests/legacystore/jrnl/_st_helper_fns.h new file mode 100644 index 0000000000..923065dd11 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/_st_helper_fns.h @@ -0,0 +1,882 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +// NOTE: This file is included in _st_*.cpp files inside the QPID_AUTO_TEST_SUITE() +// definition. + +#define MAX_AIO_SLEEPS 500 +#define AIO_SLEEP_TIME 1000 +#define NUM_TEST_JFILES 4 +#define NUM_DEFAULT_JFILES 8 +#define JRNL_DEFAULT_FSIZE 24 // Multiples of JRNL_RMGR_PAGE_SIZE +#define TEST_JFSIZE_SBLKS 128 +#define DEFAULT_JFSIZE_SBLKS (JRNL_DEFAULT_FSIZE * JRNL_RMGR_PAGE_SIZE) +#define NUM_MSGS 5 +#define MSG_REC_SIZE_DBLKS 2 +#define MSG_SIZE (MSG_REC_SIZE_DBLKS * JRNL_DBLK_SIZE) - sizeof(enq_hdr) - sizeof(rec_tail) +#define LARGE_MSG_REC_SIZE_DBLKS (JRNL_SBLK_SIZE * JRNL_RMGR_PAGE_SIZE) +#define LARGE_MSG_SIZE (LARGE_MSG_REC_SIZE_DBLKS * JRNL_DBLK_SIZE) - sizeof(enq_hdr) - sizeof(rec_tail) +#define XID_SIZE 64 + +#define XLARGE_MSG_RATIO (1.0 * LARGE_MSG_REC_SIZE / JRNL_DBLK_SIZE / JRNL_SBLK_SIZE / JRNL_RMGR_PAGE_SIZE) +#define XLARGE_MSG_THRESHOLD (int)(JRNL_DEFAULT_FSIZE * NUM_DEFAULT_JFILES * JRNL_ENQ_THRESHOLD / 100 / LARGE_MSG_RATIO) + +#define NUM_JFILES 4 +#define JFSIZE_SBLKS 128 + +const char* tdp = getenv("TMP_DATA_DIR"); +const string test_dir(tdp && strlen(tdp) > 0 ? string(tdp) + "/" + test_filename : "/var/tmp/jrnl_test"); + +class test_dtok : public data_tok +{ +private: + bool flag; +public: + test_dtok() : data_tok(), flag(false) {} + virtual ~test_dtok() {} + bool done() { if (flag || _wstate == NONE) return true; else { flag = true; return false; } } +}; + +class test_jrnl_cb : public aio_callback { + virtual void wr_aio_cb(std::vector<data_tok*>& dtokl) + { + for (std::vector<data_tok*>::const_iterator i=dtokl.begin(); i!=dtokl.end(); i++) + { + test_dtok* dtp = static_cast<test_dtok*>(*i); + if (dtp->done()) + delete dtp; + } + } + virtual void rd_aio_cb(std::vector<u_int16_t>& /*pil*/) {} +}; + +class test_jrnl : public jcntl +{ +test_jrnl_cb* cb; + +public: + test_jrnl(const std::string& jid, const std::string& jdir, const std::string& base_filename, test_jrnl_cb& cb0) : + jcntl(jid, jdir, base_filename), + cb(&cb0) {} + virtual ~test_jrnl() {} + void initialize(const u_int16_t num_jfiles, const bool ae, const u_int16_t ae_max_jfiles, + const u_int32_t jfsize_sblks) + { + jcntl::initialize(num_jfiles, ae, ae_max_jfiles, jfsize_sblks, JRNL_WMGR_DEF_PAGES, JRNL_WMGR_DEF_PAGE_SIZE, + cb); + _jdir.create_dir(); + } + void recover(const u_int16_t num_jfiles, const bool ae, const u_int16_t ae_max_jfiles, const u_int32_t jfsize_sblks, + vector<string>* txn_list, u_int64_t& highest_rid) + { jcntl::recover(num_jfiles, ae, ae_max_jfiles, jfsize_sblks, JRNL_WMGR_DEF_PAGES, JRNL_WMGR_DEF_PAGE_SIZE, cb, + txn_list, highest_rid); } +}; + +/* +* This class is for testing recover functionality by maintaining an internal lfid-pfid map, then creating physical +* journal file stubs (just the fhdr section of the journal) and jinf file. This allows the recover functionality (which +* analyzes these components to determine recover order). +* +* First set up a map or "blueprint" of what the journal should look like for recovery, then have the class create the +* physical files. The jinf object under test then reads and analyzes the created journal, and it's analysis is checked +* against what is expected. +* +* General usage pattern: +* 1. Create instance of lfid_pfid_map. +* 2. Call lfid_pfid_map::journal_create() to simulate initial journal creation. +* 3. (optional) Call lfid_pfid_map::journal_insert() one or more times to simulate the addition of journal files. +* 4. Call lfid_pfid_map::write_journal() to create dummy journal files (files containing only file headers) +* 5. Create and initialize the jinf object under test +* 6. Call jinf::analyze() to determine the pfid order - and thus also first and last lids +* 7. Call lfid_pfid_map::check_analysis() to check the conclusions of the analysis +* 8. Call lfid_pfid_map::destroy_journal() to delete the journal files and reset the lfid_pfid_map object. +* 9. (optional) Back to step 2 for more tests +* +* See the individual methods below for more details. +*/ +class lfid_pfid_map +{ + public: + typedef pair<u_int16_t, file_hdr> lppair; // Used for loading the map + typedef multimap<u_int16_t, file_hdr> lpmap; // Stores the journal "plan" before it is created on-disk + typedef lpmap::const_iterator lpmap_citr; // General purpose iterator + typedef pair<lpmap_citr, lpmap_citr> lpmap_range; // Range of values returned by multimap's equal_range() fn + + private: + string _jid; // Journal id + string _base_filename; // Base filename + lpmap _map; // Stores the journal "blueprint" before it is created on-disk + u_int16_t _num_used_files; // number of files which contain jorunals + u_int16_t _oldest_lfid; // lfid where owi flips; always 0 if !_full + u_int16_t _last_pfid; // last pfid (ie last file added) + + public: + lfid_pfid_map(const string& jid, const string& base_filename) : + _jid(jid), _base_filename(base_filename), _num_used_files(0), _oldest_lfid(0), _last_pfid(0) + {} + virtual ~lfid_pfid_map() {} + + // Mainly used for debugging + void print() + { + int cnt = 0; + for (lpmap_citr i=_map.begin(); i!=_map.end(); i++, cnt++) + { + const file_hdr fh = i->second; + cout << " " << cnt << ": owi=" << (fh.get_owi()?"t":"f") << hex << " frid=0x" << fh._rid; + cout << " pfid=0x" << fh._pfid << " lfid=0x" << fh._lfid << " fro=0x" << fh._fro << dec << endl; + } + } + + std::size_t size() + { + return _map.size(); + } + + /* + * Method journal_create(): Used to simulate the initial creation of a journal before file insertions + * take place. + * + * num_jfiles: The initial journal file count. + * num_used_jfiles: If this number is less than num_jfiles, it indicates a clean journal that has not yet + * completed its first rotation, and some files are empty (ie all null). The first + * num_used_jfiles will contain file headers, the remainder will be blank. + * oldest_lfid: The lfid (==pfid, see note 1 below) at which the owi flag flips. During normal operation, + * each time the journal rotates back to file 0, a flag (called the overwrite indicator or owi) + * is flipped. This flag is saved in the file header. During recovery, if scanning from logical + * file 0 upwards, the file at which this flag reverses from its value in file 0 is the file + * that was to have been overwritten next, and is thus the "oldest" file. Recovery analysis must + * start with this file. oldest_lfid sets the file at which this flag will flip value for the + * simulated recovery analysis. Note that this will be ignored if num_used_jfiles < num_jfiles, + * as it is not possible for an overwrite to have occurred if not all the files have been used. + * first_owi: Sets the value of the owi flag in file 0. If set to false, then the flip will be found with + * a true flag (and visa versa). + * + * NOTES: + * 1. By definition, the lfids and pfids coincide for a journal containing no inserted files. Thus pfid == lfid + * for all journals created after using initial_journal_create() alone. + * 2. By definition, if a journal is not full (num_used_jfiles < num_jfiles), then all owi flags for those files + * that are used must be the same. It is not possible for an overwrite situation to arise if a journal is not + * full. + * 3. This function acts on map _map only, and does not create any test files. Call write_journal() to do that. + * 4. This function must be called on a clean test object or on one where the previous test data has been + * cleared by calling journal_destroy(). Running this function more than once on existing data will + * result in invalid journals which cannot be recovered. + */ + void journal_create(const u_int16_t num_jfiles, // Total number of files + const u_int16_t num_used_jfiles, // Number of used files, rest empty at end + const u_int16_t oldest_lfid = 0, // Fid where owi reverses + const u_int16_t bad_lfid = 0, // Fid where owi reverses again (must be > oldest_lifd), + // used for testing bad owi detection + const bool first_owi = false) // Value of first owi flag (ie pfid=0) + { + const bool full = num_used_jfiles == num_jfiles; + bool owi = first_owi; + _oldest_lfid = full ? oldest_lfid : 0; + for (u_int16_t lfid = 0; lfid < num_jfiles; lfid++) + { + const u_int16_t pfid = lfid; + file_hdr fh; + if (pfid < num_used_jfiles) + { + _num_used_files = num_used_jfiles; + /* + * Invert the owi flag from its current value (initially given by first_owi param) only if: + * 1. The journal is full (ie all files are used) + * AND + * 2. oldest_lfid param is non-zero (this is default, but lfid 0 being inverted is logically + * inconsistent with first_owi parameter being present) + * AND + * 3. Either: + * * current lfid == oldest_lfid (ie we are preparing the oldest lfid) + * OR + * * current lfid == bad_lfid AND bad_lfid > oldest (ie we are past the oldest and preparing the + * bad lfid) + */ + if (full && oldest_lfid > 0 && + (lfid == oldest_lfid || (bad_lfid > oldest_lfid && lfid == bad_lfid))) + owi = !owi; + const u_int64_t frid = u_int64_t(random()); + init_fhdr(fh, frid, pfid, lfid, owi); + } + _map.insert(lppair(lfid, fh)); + } + } + + /* + * Method journal_insert(): Used to simulate the insertion of journal files into an existing journal. + * + * after_lfid: The logical file id (lfid) after which the new file is to be inserted. + * num_files: The number of files to be inserted. + * adjust_lids: Flag indicating that the lids of files _following_ the inserted files are to be adjusted upwards + * by the number of inserted files. Not doing so simulates a recovery immediately after insertion + * but before the following files are overwritten with their new lids. If this is set false, then: + * a) after_lfid MUST be the most recent file (_oldest_lfid-1 ie last lfid before owi changes). + * b) This call must be the last insert call. + * + * NOTES: + * 1. It is not possible to insert before lfid/pfid 0; thus these are always coincidental. This operation is + * logically equivalent to inserting after the last lfid, which is possible. + * 2. It is not possible to insert into a journal that is not full. Doing so will result in an unrecoverable + * journal (one that is logically inconsistent that can never occur in reality). + * 3. If a journal is stopped/interrupted immediately after a file insertion, there could be duplicate lids in + * play at recovery, as the following file lids in their headers are only overwritten when the file is + * eventually written to during normal operation. The owi flags, however, are used to determine which of the + * ambiguous lids are the inserted files. + * 4. This function acts on map _map only, and does not create any test files. Call write_journal() to do that. + */ + void journal_insert(const u_int16_t after_lfid, // Insert files after this lfid + const u_int16_t num_files = 1, // Number of files to insert + const bool adjust_lids = true) // Adjust lids following inserted files + { + if (num_files == 0) return; + _num_used_files += num_files; + const u_int16_t num_jfiles_before_append = _map.size(); + lpmap_citr i = _map.find(after_lfid); + if (i == _map.end()) BOOST_FAIL("Unable to find lfid=" << after_lfid << " in map."); + const file_hdr fh_before = (*i).second; + + // Move overlapping lids (if req'd) + if (adjust_lids && after_lfid < num_jfiles_before_append - 1) + { + for (u_int16_t lfid = num_jfiles_before_append - 1; lfid > after_lfid; lfid--) + { + lpmap_citr itr = _map.find(lfid); + if (itr == _map.end()) BOOST_FAIL("Unable to find lfid=" << after_lfid << " in map."); + file_hdr fh = itr->second; + _map.erase(lfid); + fh._lfid += num_files; + if (lfid == _oldest_lfid) + _oldest_lfid += num_files; + _map.insert(lppair(fh._lfid, fh)); + } + } + + // Add new file headers + u_int16_t pfid = num_jfiles_before_append; + u_int16_t lfid = after_lfid + 1; + while (pfid < num_jfiles_before_append + num_files) + { + const u_int64_t frid = u_int64_t(random()); + const size_t fro = 0x200; + const file_hdr fh(RHM_JDAT_FILE_MAGIC, RHM_JDAT_VERSION, frid, pfid, lfid, fro, fh_before.get_owi(), + true); + _map.insert(lppair(lfid, fh)); + _last_pfid = pfid; + pfid++; + lfid++; + } + } + + /* + * Get the list of pfids in the map in order of lfid. The pfids are appended to the supplied vector. Only + * as many headers as are in the map are appended. + * NOTE: will clear any contents from supplied vector before appending pfid list. + */ + void get_pfid_list(vector<u_int16_t>& pfid_list) + { + pfid_list.clear(); + for (lpmap_citr i = _map.begin(); i != _map.end(); i++) + pfid_list.push_back(i->second._pfid); + } + + /* + * Get the list of lfids in the map. The lfids are appended to the supplied vector in the order they appear + * in the map (which is not necessarily the natural or sorted order). + * NOTE: will clear any contents from supplied vector before appending lfid list. + */ + void get_lfid_list(vector<u_int16_t>& lfid_list) + { + lfid_list.clear(); + lfid_list.assign(_map.size(), 0); + for (lpmap_citr i = _map.begin(); i != _map.end(); i++) + lfid_list[i->second._pfid] = i->first; + } + + /* + * Method check_analysis(): Used to check the result of the test jinf object analysis by comparing the pfid order + * array it produces against the internal map. + * + * ji: A ref to the jinf object under test. + */ + void check_analysis(jinf& ji) // jinf object under test after analyze() has been called + { + BOOST_CHECK_EQUAL(ji.get_first_pfid(), get_first_pfid()); + BOOST_CHECK_EQUAL(ji.get_last_pfid(), get_last_pfid()); + + jinf::pfid_list& pfidl = ji.get_pfid_list(); + const u_int16_t num_jfiles = _map.size(); + const bool all_used = _num_used_files == num_jfiles; + BOOST_CHECK_EQUAL(pfidl.size(), _num_used_files); + + const u_int16_t lfid_start = all_used ? _oldest_lfid : 0; + // Because a simulated failure would leave lfid dups in map and last_fid would not exist in map in this + // case, we must find lfid_stop via pfid instead. Search for pfid == num_files. + lpmap_citr itr = _map.begin(); + while (itr != _map.end() && itr->second._pfid != _num_used_files - 1) itr++; + if (itr == _map.end()) + BOOST_FAIL("check(): Unable to find pfid=" << (_num_used_files - 1) << " in map."); + const u_int16_t lfid_stop = itr->second._lfid; + + std::size_t fidl_index = 0; + for (u_int16_t lfid_cnt = lfid_start; lfid_cnt < lfid_stop; lfid_cnt++, fidl_index++) + { + const u_int16_t lfid = lfid_cnt % num_jfiles; + lpmap_citr itr = _map.find(lfid); + if (itr == _map.end()) + BOOST_FAIL("check(): Unable to find lfid=" << lfid << " in map."); + BOOST_CHECK_EQUAL(itr->second._pfid, pfidl[fidl_index]); + } + } + + /* + * Method get_pfid(): Look up a pfid from a known lfid. + */ + u_int16_t get_pfid(const u_int16_t lfid, const bool initial_owi = false) + { + switch (_map.count(lfid)) + { + case 1: + return _map.find(lfid)->second._pfid; + case 2: + for (lpmap_citr itr = _map.lower_bound(lfid); itr != _map.upper_bound(lfid); itr++) + { + if (itr->second.get_owi() != initial_owi) + return itr->second._pfid; + } + default:; + } + BOOST_FAIL("get_pfid(): lfid=" << lfid << " not found in map."); + return 0xffff; + } + + /* + * Method get_first_pfid(): Look up the first (oldest, or next-to-be-overwritten) pfid in the analysis sequence. + */ + u_int16_t get_first_pfid() + { + return get_pfid(_oldest_lfid); + } + + /* + * Method get_last_pfid(): Look up the last (newest, or most recently written) pfid in the analysis sequence. + */ + u_int16_t get_last_pfid() + { + u_int16_t flfid = 0; + if (_num_used_files == _map.size()) // journal full? + { + if (_oldest_lfid) + { + // if failed insert, cycle past duplicate lids + while (_map.count(_oldest_lfid) == 2) + _oldest_lfid++; + while (_map.find(_oldest_lfid) != _map.end() && _map.find(_oldest_lfid)->second.get_owi() == false) + _oldest_lfid++; + flfid = _oldest_lfid - 1; + } + else + flfid = _map.size() - 1; + } + else + flfid = _num_used_files - 1; + return get_pfid(flfid, true); + } + + /* + * Method write_journal(): Used to create the dummy journal files from the built-up map created by calling + * initial_journal_create() and optionally journal_append() one or more times. Since the jinf object reads the + * jinf file and the file headers only, the create object creates a dummy journal file containing only a file + * header (512 bytes each) and a single jinf file which contains the journal metadata required for recovery + * analysis. + */ + void write_journal(const bool ae, const u_int16_t ae_max_jfiles, const u_int32_t fsize_sblks = JFSIZE_SBLKS) + { + create_jinf(ae, ae_max_jfiles); + u_int16_t pfid = 0; + for (lpmap_citr itr = _map.begin(); itr != _map.end(); itr++, pfid++) + { + if (itr->second._pfid == 0 && itr->second._magic == 0) // empty header, use pfid counter instead + create_journal_file(pfid, itr->second, _base_filename, fsize_sblks); + else + create_journal_file(itr->second._pfid, itr->second, _base_filename, fsize_sblks); + } + } + + /* + * Method destroy_journal(): Destroy the files created by create_journal() and reset the lfid_pfid_map test + * object. A new test may be started using the same lfid_pfid_map test object once this call has been made. + */ + void destroy_journal() + { + for (u_int16_t pfid = 0; pfid < _map.size(); pfid++) + { + string fn = create_journal_filename(pfid, _base_filename); + BOOST_WARN_MESSAGE(::unlink(fn.c_str()) == 0, "destroy_journal(): Failed to remove file " << fn); + } + clean_journal_info_file(_base_filename); + _map.clear(); + _num_used_files = 0; + _oldest_lfid = 0; + _last_pfid = 0; + } + + /* + * Method create_new_jinf(): This static call creates a default jinf file only. This is used to test the read + * constructor of a jinf test object which reads a jinf file at instantiation. + */ + static void create_new_jinf(const string jid, const string base_filename, const bool ae) + { + if (jdir::exists(test_dir)) + jdir::delete_dir(test_dir); + create_jinf(NUM_JFILES, ae, (ae ? 5 * NUM_JFILES : 0), jid, base_filename); + } + + /* + * Method clean_journal_info_file(): This static method deletes only a jinf file without harming any other + * journal file or its directory. This is used to clear those tests which rely only on the existence of a + * jinf file. + */ + static void clean_journal_info_file(const string base_filename) + { + stringstream fn; + fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION; + BOOST_WARN_MESSAGE(::unlink(fn.str().c_str()) == 0, "clean_journal_info_file(): Failed to remove file " << + fn.str()); + } + + static string create_journal_filename(const u_int16_t pfid, const string base_filename) + { + stringstream fn; + fn << test_dir << "/" << base_filename << "."; + fn << setfill('0') << hex << setw(4) << pfid << "." << JRNL_DATA_EXTENSION; + return fn.str(); + } + + private: + static void init_fhdr(file_hdr& fh, + const u_int64_t frid, + const u_int16_t pfid, + const u_int16_t lfid, + const bool owi, + const bool no_enq = false) + { + fh._magic = RHM_JDAT_FILE_MAGIC; + fh._version = RHM_JDAT_VERSION; +#if defined(JRNL_BIG_ENDIAN) + fh._eflag = RHM_BENDIAN_FLAG; +#else + fh._eflag = RHM_LENDIAN_FLAG; +#endif + fh._uflag = owi ? rec_hdr::HDR_OVERWRITE_INDICATOR_MASK : 0; + fh._rid = frid; + fh._pfid = pfid; + fh._lfid = lfid; + fh._fro = no_enq ? 0 : 0x200; + timespec ts; + ::clock_gettime(CLOCK_REALTIME, &ts); + fh._ts_sec = ts.tv_sec; + fh._ts_nsec = ts.tv_nsec; + } + + void create_jinf(const bool ae, const u_int16_t ae_max_jfiles) + { + if (jdir::exists(test_dir)) + jdir::delete_dir(test_dir); + create_jinf(_map.size(), ae, ae_max_jfiles, _jid, _base_filename); + } + + static void create_jinf(u_int16_t num_files, const bool ae, const u_int16_t ae_max_jfiles, const string jid, + const string base_filename) + { + jdir::create_dir(test_dir); // Check test dir exists; create it if not + timespec ts; + ::clock_gettime(CLOCK_REALTIME, &ts); + jinf ji(jid, test_dir, base_filename, num_files, ae, ae_max_jfiles, JFSIZE_SBLKS, JRNL_WMGR_DEF_PAGE_SIZE, + JRNL_WMGR_DEF_PAGES, ts); + ji.write(); + } + + static void create_journal_file(const u_int16_t pfid, + const file_hdr& fh, + const string base_filename, + const u_int32_t fsize_sblks = JFSIZE_SBLKS, + const char fill_char = 0) + { + const std::string filename = create_journal_filename(pfid, base_filename); + ofstream of(filename.c_str(), ofstream::out | ofstream::trunc); + if (!of.good()) + BOOST_FAIL("Unable to open test journal file \"" << filename << "\" for writing."); + + write_file_header(filename, of, fh, fill_char); + write_file_body(of, fsize_sblks, fill_char); + + of.close(); + if (of.fail() || of.bad()) + BOOST_FAIL("Error closing test journal file \"" << filename << "\"."); + } + + static void write_file_header(const std::string& filename, + ofstream& of, + const file_hdr& fh, + const char fill_char) + { + // write file header + u_int32_t cnt = sizeof(file_hdr); + of.write((const char*)&fh, cnt); + if (of.fail() || of.bad()) + BOOST_FAIL("Error writing file header to test journal file \"" << filename << "\"."); + + // fill remaining sblk with fill char + while (cnt++ < JRNL_DBLK_SIZE * JRNL_SBLK_SIZE) + { + of.put(fill_char); + if (of.fail() || of.bad()) + BOOST_FAIL("Error writing filler to test journal file \"" << filename << "\"."); + } + } + + static void write_file_body(ofstream& of, const u_int32_t fsize_sblks, const char fill_char) + { + if (fsize_sblks > 1) + { + std::vector<char> sblk_buffer(JRNL_DBLK_SIZE * JRNL_SBLK_SIZE, fill_char); + u_int32_t fwritten_sblks = 0; // hdr + while (fwritten_sblks++ < fsize_sblks) + of.write(&sblk_buffer[0], JRNL_DBLK_SIZE * JRNL_SBLK_SIZE); + } + } +}; + +const string +get_test_name(const string& file, const string& test_name) +{ + cout << test_filename << "." << test_name << ": " << flush; + return file + "." + test_name; +} + +bool +check_iores(const string& ctxt, const iores ret, const iores exp_ret, test_dtok* dtp) +{ + if (ret != exp_ret) + { + delete dtp; + BOOST_FAIL(ctxt << ": Expected " << iores_str(exp_ret) << "; got " << iores_str(ret)); + } + return false; +} + +bool +handle_jcntl_response(const iores res, jcntl& jc, unsigned& aio_sleep_cnt, const std::string& ctxt, const iores exp_ret, + test_dtok* dtp) +{ + if (res == RHM_IORES_PAGE_AIOWAIT) + { + if (++aio_sleep_cnt <= MAX_AIO_SLEEPS) + { + jc.get_wr_events(0); // *** GEV2 + usleep(AIO_SLEEP_TIME); + } + else + return check_iores(ctxt, res, exp_ret, dtp); + } + else + return check_iores(ctxt, res, exp_ret, dtp); + return true; +} + +u_int64_t +enq_msg(jcntl& jc, + const u_int64_t rid, + const string& msg, + const bool transient, + const iores exp_ret = RHM_IORES_SUCCESS) +{ + ostringstream ctxt; + ctxt << "enq_msg(" << rid << ")"; + test_dtok* dtp = new test_dtok; + BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0)."); + dtp->set_rid(rid); + dtp->set_external_rid(true); + try + { + iores res = jc.enqueue_data_record(msg.c_str(), msg.size(), msg.size(), dtp, transient); + check_iores(ctxt.str(), res, exp_ret, dtp); + u_int64_t dtok_rid = dtp->rid(); + if (dtp->done()) delete dtp; + return dtok_rid; + } + catch (exception& e) { delete dtp; throw; } +} + +u_int64_t +enq_extern_msg(jcntl& jc, const u_int64_t rid, const std::size_t msg_size, const bool transient, + const iores exp_ret = RHM_IORES_SUCCESS) +{ + ostringstream ctxt; + ctxt << "enq_extern_msg(" << rid << ")"; + test_dtok* dtp = new test_dtok; + BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0)."); + dtp->set_rid(rid); + dtp->set_external_rid(true); + try + { + iores res = jc.enqueue_extern_data_record(msg_size, dtp, transient); + check_iores(ctxt.str(), res, exp_ret, dtp); + u_int64_t dtok_rid = dtp->rid(); + if (dtp->done()) delete dtp; + return dtok_rid; + } + catch (exception& e) { delete dtp; throw; } +} + +u_int64_t +enq_txn_msg(jcntl& jc, const u_int64_t rid, const string& msg, const string& xid, const bool transient, + const iores exp_ret = RHM_IORES_SUCCESS) +{ + ostringstream ctxt; + ctxt << "enq_txn_msg(" << rid << ")"; + test_dtok* dtp = new test_dtok; + BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0)."); + dtp->set_rid(rid); + dtp->set_external_rid(true); + try + { + iores res = jc.enqueue_txn_data_record(msg.c_str(), msg.size(), msg.size(), dtp, xid, + transient); + check_iores(ctxt.str(), res, exp_ret, dtp); + u_int64_t dtok_rid = dtp->rid(); + if (dtp->done()) delete dtp; + return dtok_rid; + } + catch (exception& e) { delete dtp; throw; } +} + +u_int64_t +enq_extern_txn_msg(jcntl& jc, const u_int64_t rid, const std::size_t msg_size, const string& xid, const bool transient, + const iores exp_ret = RHM_IORES_SUCCESS) +{ + ostringstream ctxt; + ctxt << "enq_extern_txn_msg(" << rid << ")"; + test_dtok* dtp = new test_dtok; + BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0)."); + dtp->set_rid(rid); + dtp->set_external_rid(true); + try + { + iores res = jc.enqueue_extern_txn_data_record(msg_size, dtp, xid, transient); + check_iores(ctxt.str(), res, exp_ret, dtp); + u_int64_t dtok_rid = dtp->rid(); + if (dtp->done()) delete dtp; + return dtok_rid; + } + catch (exception& e) { delete dtp; throw; } +} + +u_int64_t +deq_msg(jcntl& jc, const u_int64_t drid, const u_int64_t rid, const iores exp_ret = RHM_IORES_SUCCESS) +{ + ostringstream ctxt; + ctxt << "deq_msg(" << drid << ")"; + test_dtok* dtp = new test_dtok; + BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0)."); + dtp->set_rid(rid); + dtp->set_dequeue_rid(drid); + dtp->set_external_rid(true); + dtp->set_wstate(data_tok::ENQ); + try + { + iores res = jc.dequeue_data_record(dtp); + check_iores(ctxt.str(), res, exp_ret, dtp); + u_int64_t dtok_rid = dtp->rid(); + if (dtp->done()) delete dtp; + return dtok_rid; + } + catch (exception& e) { delete dtp; throw; } +} + +u_int64_t +deq_txn_msg(jcntl& jc, const u_int64_t drid, const u_int64_t rid, const string& xid, + const iores exp_ret = RHM_IORES_SUCCESS) +{ + ostringstream ctxt; + ctxt << "deq_txn_msg(" << drid << ")"; + test_dtok* dtp = new test_dtok; + BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0)."); + dtp->set_rid(rid); + dtp->set_dequeue_rid(drid); + dtp->set_external_rid(true); + dtp->set_wstate(data_tok::ENQ); + try + { + iores res = jc.dequeue_txn_data_record(dtp, xid); + check_iores(ctxt.str(), res, exp_ret, dtp); + u_int64_t dtok_rid = dtp->rid(); + if (dtp->done()) delete dtp; + return dtok_rid; + } + catch (exception& e) { delete dtp; throw; } +} + +u_int64_t +txn_abort(jcntl& jc, const u_int64_t rid, const string& xid, const iores exp_ret = RHM_IORES_SUCCESS) +{ + test_dtok* dtp = new test_dtok; + BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0)."); + dtp->set_rid(rid); + dtp->set_external_rid(true); + try + { + iores res = jc.txn_abort(dtp, xid); + check_iores("txn_abort", res, exp_ret, dtp); + u_int64_t dtok_rid = dtp->rid(); + if (dtp->done()) delete dtp; + return dtok_rid; + } + catch (exception& e) { delete dtp; throw; } +} + +u_int64_t +txn_commit(jcntl& jc, const u_int64_t rid, const string& xid, const iores exp_ret = RHM_IORES_SUCCESS) +{ + test_dtok* dtp = new test_dtok; + BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0)."); + dtp->set_rid(rid); + dtp->set_external_rid(true); + try + { + iores res = jc.txn_commit(dtp, xid); + check_iores("txn_commit", res, exp_ret, dtp); + u_int64_t dtok_rid = dtp->rid(); + if (dtp->done()) delete dtp; + return dtok_rid; + } + catch (exception& e) { delete dtp; throw; } +} + +void +read_msg(jcntl& jc, string& msg, string& xid, bool& transient, bool& external, const iores exp_ret = RHM_IORES_SUCCESS) +{ + void* mp = 0; + std::size_t msize = 0; + void* xp = 0; + std::size_t xsize = 0; + test_dtok* dtp = new test_dtok; + BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0)."); + dtp->set_wstate(data_tok::ENQ); + + unsigned aio_sleep_cnt = 0; + try + { + iores res = jc.read_data_record(&mp, msize, &xp, xsize, transient, external, dtp); + while (handle_jcntl_response(res, jc, aio_sleep_cnt, "read_msg", exp_ret, dtp)) + res = jc.read_data_record(&mp, msize, &xp, xsize, transient, external, dtp); + } + catch (exception& e) { delete dtp; throw; } + + if (mp) + msg.assign((char*)mp, msize); + if (xp) + { + xid.assign((char*)xp, xsize); + std::free(xp); + xp = 0; + } + else if (mp) + { + std::free(mp); + mp = 0; + } + delete dtp; +} + +/* + * Returns the number of messages of size msg_rec_size_dblks that will fit into an empty journal with or without + * corresponding dequeues (controlled by include_deq) without a threshold - ie until the journal is full. Assumes + * that dequeue records fit into one dblk. + */ +u_int32_t +num_msgs_to_full(const u_int16_t num_files, const u_int32_t file_size_dblks, const u_int32_t msg_rec_size_dblks, + bool include_deq) +{ + u_int32_t rec_size_dblks = msg_rec_size_dblks; + if (include_deq) + rec_size_dblks++; + return u_int32_t(::floor(1.0 * num_files * file_size_dblks / rec_size_dblks)); +} + +/* + * Returns the number of messages of size msg_rec_size_dblks that will fit into an empty journal before the enqueue + * threshold of JRNL_ENQ_THRESHOLD (%). + */ +u_int32_t +num_msgs_to_threshold(const u_int16_t num_files, const u_int32_t file_size_dblks, const u_int32_t msg_rec_size_dblks) +{ + return u_int32_t(::floor(1.0 * num_files * file_size_dblks * JRNL_ENQ_THRESHOLD / msg_rec_size_dblks / 100)); +} + +/* + * Returns the amount of space reserved in dblks (== num dequeues assuming dequeue size of 1 dblk) for the enqueue + * threshold of JRNL_ENQ_THRESHOLD (%). + */ +u_int32_t +num_dequeues_rem(const u_int16_t num_files, const u_int32_t file_size_dblks) +{ + /* + * Fraction of journal remaining after threshold is used --------------+ + * Total no. dblks in journal ------+ | + * | | + * +------------+------------+ +-----------------+---------------------+ + */ + return u_int32_t(::ceil(num_files * file_size_dblks * (1.0 - (1.0 * JRNL_ENQ_THRESHOLD / 100)))); +} + +string& +create_msg(string& s, const int msg_num, const int len) +{ + ostringstream oss; + oss << "MSG_" << setfill('0') << setw(6) << msg_num << "_"; + for (int i=12; i<=len; i++) + oss << (char)('0' + i%10); + s.assign(oss.str()); + return s; +} + +string& +create_xid(string& s, const int msg_num, const int len) +{ + ostringstream oss; + oss << "XID_" << setfill('0') << setw(6) << msg_num << "_"; + for (int i=11; i<len; i++) + oss << (char)('a' + i%26); + s.assign(oss.str()); + return s; +} + +long +get_seed() +{ + timespec ts; + if (::clock_gettime(CLOCK_REALTIME, &ts)) + BOOST_FAIL("Unable to read clock to generate seed."); + long tenths = ts.tv_nsec / 100000000; + return long(10 * ts.tv_sec + tenths); // time in tenths of a second +} diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_st_read.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_st_read.cpp new file mode 100644 index 0000000000..ff2c39e14c --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/_st_read.cpp @@ -0,0 +1,460 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../unit_test.h" +#include <cmath> +#include <iostream> +#include "qpid/legacystore/jrnl/jcntl.h" + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace std; + +QPID_AUTO_TEST_SUITE(journal_read) + +const string test_filename("_st_read"); + +#include "_st_helper_fns.h" + +// === Test suite === + +#ifndef LONG_TEST +/* + * ============================================== + * NORMAL TESTS + * This section contains normal "make check" tests + * for building/packaging. These are built when + * LONG_TEST is _not_ defined. + * ============================================== + */ + +QPID_AUTO_TEST_CASE(empty_read) +{ + string test_name = get_test_name(test_filename, "empty_read"); + try + { + string msg; + string rmsg; + string xid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_read_dequeue_block) +{ + string test_name = get_test_name(test_filename, "enqueue_read_dequeue_block"); + try + { + string msg; + string rmsg; + string xid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + jc.flush(); + for (int m=0; m<NUM_MSGS; m++) + { + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + BOOST_CHECK_EQUAL(create_msg(msg, m, MSG_SIZE), rmsg); + BOOST_CHECK_EQUAL(xid.size(), std::size_t(0)); + BOOST_CHECK_EQUAL(transientFlag, false); + BOOST_CHECK_EQUAL(externalFlag, false); + } + read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY); + for (int m=0; m<NUM_MSGS; m++) + deq_msg(jc, m, m+NUM_MSGS); + read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_read_dequeue_interleaved) +{ + string test_name = get_test_name(test_filename, "enqueue_read_dequeue_interleaved"); + try + { + string msg; + string rmsg; + string xid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<500*NUM_MSGS; m+=2) + { + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + jc.flush(); + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + BOOST_CHECK_EQUAL(create_msg(msg, m, MSG_SIZE), rmsg); + BOOST_CHECK_EQUAL(xid.size(), std::size_t(0)); + BOOST_CHECK_EQUAL(transientFlag, false); + BOOST_CHECK_EQUAL(externalFlag, false); + deq_msg(jc, m, m+1); + jc.flush(); + read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_recovered_read_dequeue) +{ + string test_name = get_test_name(test_filename, "enqueue_recovered_read_dequeue"); + try + { + { + string msg; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + } + { + string msg; + u_int64_t hrid; + string rmsg; + string xid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.recover(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS, 0, hrid); + BOOST_CHECK_EQUAL(hrid, u_int64_t(NUM_MSGS - 1)); + jc.recover_complete(); + for (int m=0; m<NUM_MSGS; m++) + { + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + BOOST_CHECK_EQUAL(create_msg(msg, m, MSG_SIZE), rmsg); + BOOST_CHECK_EQUAL(xid.size(), std::size_t(0)); + BOOST_CHECK_EQUAL(transientFlag, false); + BOOST_CHECK_EQUAL(externalFlag, false); + } + read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY); + for (int m=0; m<NUM_MSGS; m++) + deq_msg(jc, m, m+NUM_MSGS); + read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(multi_page_enqueue_recovered_read_dequeue_block) +{ + string test_name = get_test_name(test_filename, "multi_page_enqueue_recovered_read_dequeue_block"); + try + { + { + string msg; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(2*NUM_TEST_JFILES, false, 0, 10*TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS*125; m++) + enq_msg(jc, m, create_msg(msg, m, 16*MSG_SIZE), false); + } + { + string msg; + u_int64_t hrid; + string rmsg; + string xid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.recover(2*NUM_TEST_JFILES, false, 0, 10*TEST_JFSIZE_SBLKS, 0, hrid); + BOOST_CHECK_EQUAL(hrid, u_int64_t(NUM_MSGS*125 - 1)); + jc.recover_complete(); + for (int m=0; m<NUM_MSGS*125; m++) + { + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + BOOST_CHECK_EQUAL(create_msg(msg, m, 16*MSG_SIZE), rmsg); + BOOST_CHECK_EQUAL(xid.size(), std::size_t(0)); + BOOST_CHECK_EQUAL(transientFlag, false); + BOOST_CHECK_EQUAL(externalFlag, false); + } + read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY); + for (int m=0; m<NUM_MSGS*125; m++) + deq_msg(jc, m, m+NUM_MSGS*125); + read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_recover_read_recovered_read_dequeue_block) +{ + string test_name = get_test_name(test_filename, "enqueue_recover_read_recovered_read_dequeue_block"); + try + { + { + string msg; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + } + { + string msg; + u_int64_t hrid; + string rmsg; + string xid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.recover(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS, 0, hrid); + BOOST_CHECK_EQUAL(hrid, u_int64_t(NUM_MSGS - 1)); + for (int m=0; m<NUM_MSGS; m++) + { + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + BOOST_CHECK_EQUAL(create_msg(msg, m, MSG_SIZE), rmsg); + BOOST_CHECK_EQUAL(xid.size(), std::size_t(0)); + BOOST_CHECK_EQUAL(transientFlag, false); + BOOST_CHECK_EQUAL(externalFlag, false); + } + read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + { + string msg; + u_int64_t hrid; + string rmsg; + string xid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.recover(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS, 0, hrid); + BOOST_CHECK_EQUAL(hrid, u_int64_t(NUM_MSGS - 1)); + for (int m=0; m<NUM_MSGS; m++) + { + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + BOOST_CHECK_EQUAL(create_msg(msg, m, MSG_SIZE), rmsg); + BOOST_CHECK_EQUAL(xid.size(), std::size_t(0)); + BOOST_CHECK_EQUAL(transientFlag, false); + BOOST_CHECK_EQUAL(externalFlag, false); + } + read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY); + jc.recover_complete(); + for (int m=0; m<NUM_MSGS; m++) + { + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + BOOST_CHECK_EQUAL(create_msg(msg, m, MSG_SIZE), rmsg); + BOOST_CHECK_EQUAL(xid.size(), std::size_t(0)); + BOOST_CHECK_EQUAL(transientFlag, false); + BOOST_CHECK_EQUAL(externalFlag, false); + } + read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY); + for (int m=0; m<NUM_MSGS; m++) + deq_msg(jc, m, m+NUM_MSGS); + read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(delayed_read) +{ + string test_name = get_test_name(test_filename, "delayed_read"); + try + { + string msg; + string rmsg; + string xid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + unsigned m; + for (m=0; m<2*NUM_MSGS; m+=2) + { + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + deq_msg(jc, m, m+1); + } + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + jc.flush(); + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + BOOST_CHECK_EQUAL(msg, rmsg); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(cache_cycled_delayed_read) +{ + string test_name = get_test_name(test_filename, "cache_cycled_delayed_read"); + try + { + string msg; + string rmsg; + string xid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + unsigned m; + unsigned read_buffer_size_dblks = JRNL_RMGR_PAGES * JRNL_RMGR_PAGE_SIZE * JRNL_SBLK_SIZE; + unsigned n = num_msgs_to_full(1, read_buffer_size_dblks, 16*MSG_REC_SIZE_DBLKS, true); + for (m=0; m<2*2*n + 20; m+=2) // fill read buffer twice + 10 msgs + { + enq_msg(jc, m, create_msg(msg, m, 16*MSG_SIZE), false); + deq_msg(jc, m, m+1); + } + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + jc.flush(); + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + BOOST_CHECK_EQUAL(msg, rmsg); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +#else +/* + * ============================================== + * LONG TESTS + * This section contains long tests and soak tests, + * and are run using target check-long (ie "make + * check-long"). These are built when LONG_TEST is + * defined. + * ============================================== + */ + +QPID_AUTO_TEST_CASE(multi_page_enqueue_read_dequeue_block) +{ + string test_name = get_test_name(test_filename, "multi_page_enqueue_read_dequeue_block"); + try + { + string msg; + string rmsg; + string xid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(2*NUM_TEST_JFILES, false, 0, 10*TEST_JFSIZE_SBLKS); + for (int i=0; i<10; i++) + { + for (int m=0; m<NUM_MSGS*125; m++) + enq_msg(jc, m, create_msg(msg, m, 16*MSG_SIZE), false); + jc.flush(); + for (int m=0; m<NUM_MSGS*125; m++) + { + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + BOOST_CHECK_EQUAL(create_msg(msg, m, 16*MSG_SIZE), rmsg); + BOOST_CHECK_EQUAL(xid.size(), std::size_t(0)); + BOOST_CHECK_EQUAL(transientFlag, false); + BOOST_CHECK_EQUAL(externalFlag, false); + } + read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY); + for (int m=0; m<NUM_MSGS*125; m++) + deq_msg(jc, m, m+NUM_MSGS*125); + read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(increasing_interval_delayed_read) +{ + string test_name = get_test_name(test_filename, "increasing_interval_delayed_read"); + try + { + string msg; + string rmsg; + string xid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + unsigned read_buffer_size_dblks = JRNL_RMGR_PAGES * JRNL_RMGR_PAGE_SIZE * JRNL_SBLK_SIZE; + unsigned n = num_msgs_to_full(1, read_buffer_size_dblks, MSG_REC_SIZE_DBLKS, true); + unsigned m = 0; + + // Validate read pipeline + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + jc.flush(); + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + deq_msg(jc, m, m+1); + m += 2; + + // repeat the following multiple times... + for (int i=0; i<10; i++) + { + // Invalidate read pipeline with large write + unsigned t = m + (i*n) + 25; + for (; m<t; m+=2) + { + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + deq_msg(jc, m, m+1); + } + + // Revalidate read pipeline + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + jc.flush(); + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + BOOST_CHECK_EQUAL(msg, rmsg); + deq_msg(jc, m, m+1); + m += 2; + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +#endif + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_st_read_txn.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_st_read_txn.cpp new file mode 100644 index 0000000000..621777d8d3 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/_st_read_txn.cpp @@ -0,0 +1,353 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../unit_test.h" +#include <cmath> +#include <iostream> +#include "qpid/legacystore/jrnl/jcntl.h" + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace std; + +QPID_AUTO_TEST_SUITE(journal_read_txn) + +const string test_filename("_st_read_txn"); + +#include "_st_helper_fns.h" + +// === Test suite === + +QPID_AUTO_TEST_CASE(tx_enqueue_commit_block) +{ + string test_name = get_test_name(test_filename, "tx_enqueue_commit_block"); + try + { + string msg; + string xid; + string rmsg; + string rxid; + bool transientFlag; + bool externalFlag; + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + create_xid(xid, 0, XID_SIZE); + for (int m=0; m<NUM_MSGS; m++) + enq_txn_msg(jc, m, create_msg(msg, m, MSG_SIZE), xid, false); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_TXPENDING); + txn_commit(jc, NUM_MSGS, xid); + jc.flush(); + for (int m=0; m<NUM_MSGS; m++) + { + read_msg(jc, rmsg, rxid, transientFlag, externalFlag); + BOOST_CHECK_EQUAL(create_msg(msg, m, MSG_SIZE), rmsg); + BOOST_CHECK_EQUAL(rxid, xid); + BOOST_CHECK_EQUAL(transientFlag, false); + BOOST_CHECK_EQUAL(externalFlag, false); + } + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(tx_enqueue_commit_interleaved) +{ + string test_name = get_test_name(test_filename, "tx_enqueue_commit_interleaved"); + try + { + string msg; + string xid; + string rmsg; + string rxid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + { + create_xid(xid, 2*m, XID_SIZE); + enq_txn_msg(jc, 2*m, create_msg(msg, 2*m, MSG_SIZE), xid, false); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_TXPENDING); + txn_commit(jc, 2*m+1, xid); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag); + BOOST_CHECK_EQUAL(create_msg(msg, 2*m, MSG_SIZE), rmsg); + BOOST_CHECK_EQUAL(rxid, xid); + BOOST_CHECK_EQUAL(transientFlag, false); + BOOST_CHECK_EQUAL(externalFlag, false); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(tx_enqueue_abort_block) +{ + string test_name = get_test_name(test_filename, "tx_enqueue_abort_block"); + try + { + string msg; + string xid; + string rmsg; + string rxid; + bool transientFlag; + bool externalFlag; + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + create_xid(xid, 1, XID_SIZE); + for (int m=0; m<NUM_MSGS; m++) + enq_txn_msg(jc, m, create_msg(msg, m, MSG_SIZE), xid, false); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_TXPENDING); + txn_abort(jc, NUM_MSGS, xid); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(tx_enqueue_abort_interleaved) +{ + string test_name = get_test_name(test_filename, "tx_enqueue_abort_interleaved"); + try + { + string msg; + string xid; + string rmsg; + string rxid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + { + create_xid(xid, 2*m, XID_SIZE); + enq_txn_msg(jc, 2*m, create_msg(msg, 2*m, MSG_SIZE), xid, false); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_TXPENDING); + txn_abort(jc, 2*m+1, xid); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(tx_enqueue_commit_dequeue_block) +{ + string test_name = get_test_name(test_filename, "tx_enqueue_commit_dequeue_block"); + try + { + string msg; + string xid; + string rmsg; + string rxid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + create_xid(xid, 2, XID_SIZE); + for (int m=0; m<NUM_MSGS; m++) + enq_txn_msg(jc, m, create_msg(msg, m, MSG_SIZE), xid, false); + txn_commit(jc, NUM_MSGS, xid); + for (int m=0; m<NUM_MSGS; m++) + deq_msg(jc, m, m+NUM_MSGS+1); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(tx_enqueue_commit_dequeue_interleaved) +{ + string test_name = get_test_name(test_filename, "tx_enqueue_commit_dequeue_interleaved"); + try + { + string msg; + string xid; + string rmsg; + string rxid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + { + create_xid(xid, 3*m, XID_SIZE); + enq_txn_msg(jc, 3*m, create_msg(msg, m, MSG_SIZE), xid, false); + txn_commit(jc, 3*m+1, xid); + deq_msg(jc, 3*m, 3*m+2); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_tx_dequeue_commit_block) +{ + string test_name = get_test_name(test_filename, "enqueue_tx_dequeue_commit_block"); + try + { + string msg; + string xid; + string rmsg; + string rxid; + bool transientFlag; + bool externalFlag; + + create_xid(xid, 3, XID_SIZE); + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + for (int m=0; m<NUM_MSGS; m++) + deq_txn_msg(jc, m, m+NUM_MSGS, xid); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_TXPENDING); + txn_commit(jc, 2*NUM_MSGS, xid); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_tx_dequeue_commit_interleaved) +{ + string test_name = get_test_name(test_filename, "enqueue_tx_dequeue_commit_interleaved"); + try + { + string msg; + string xid; + string rmsg; + string rxid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + { + enq_msg(jc, 3*m, create_msg(msg, 3*m, MSG_SIZE), false); + create_xid(xid, 3*m, XID_SIZE); + deq_txn_msg(jc, 3*m, 3*m+1, xid); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_TXPENDING); + txn_commit(jc, 3*m+2, xid); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_tx_dequeue_abort_block) +{ + string test_name = get_test_name(test_filename, "enqueue_tx_dequeue_abort_block"); + try + { + string msg; + string xid; + string rmsg; + string rxid; + bool transientFlag; + bool externalFlag; + + create_xid(xid, 4, XID_SIZE); + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + for (int m=0; m<NUM_MSGS; m++) + deq_txn_msg(jc, m, m+NUM_MSGS, xid); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_TXPENDING); + txn_abort(jc, 2*NUM_MSGS, xid); + jc.flush(); + for (int m=0; m<NUM_MSGS; m++) + { + read_msg(jc, rmsg, rxid, transientFlag, externalFlag); + BOOST_CHECK_EQUAL(create_msg(msg, m, MSG_SIZE), rmsg); + BOOST_CHECK_EQUAL(rxid.length(), std::size_t(0)); + BOOST_CHECK_EQUAL(transientFlag, false); + BOOST_CHECK_EQUAL(externalFlag, false); + } + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_tx_dequeue_abort_interleaved) +{ + string test_name = get_test_name(test_filename, "enqueue_tx_dequeue_abort_interleaved"); + try + { + string msg; + string xid; + string rmsg; + string rxid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + { + enq_msg(jc, 3*m, create_msg(msg, 3*m, MSG_SIZE), false); + create_xid(xid, 3*m, XID_SIZE); + deq_txn_msg(jc, 3*m, 3*m+1, xid); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_TXPENDING); + txn_abort(jc, 3*m+2, xid); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_ut_enq_map.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_ut_enq_map.cpp new file mode 100644 index 0000000000..f05dcb824c --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/_ut_enq_map.cpp @@ -0,0 +1,320 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../unit_test.h" + +#include <iostream> +#include "qpid/legacystore/jrnl/enq_map.h" +#include "qpid/legacystore/jrnl/jerrno.h" + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace std; + +QPID_AUTO_TEST_SUITE(enq_map_suite) + +const string test_filename("_ut_enq_map"); + +QPID_AUTO_TEST_CASE(constructor) +{ + cout << test_filename << ".constructor: " << flush; + enq_map e1; + BOOST_CHECK(e1.empty()); + BOOST_CHECK_EQUAL(e1.size(), u_int32_t(0)); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(insert_get) +{ + cout << test_filename << ".insert_get: " << flush; + u_int16_t pfid; + u_int64_t rid; + u_int16_t pfid_start = 0x2000U; + u_int64_t rid_begin = 0xffffffff00000000ULL; + u_int64_t rid_end = 0xffffffff00000200ULL; + + // insert with no dups + u_int64_t rid_incr_1 = 4ULL; + enq_map e2; + e2.set_num_jfiles(pfid_start + (rid_end - rid_begin)/rid_incr_1); + for (rid = rid_begin, pfid = pfid_start; rid < rid_end; rid += rid_incr_1, pfid++) + BOOST_CHECK_EQUAL(e2.insert_pfid(rid, pfid), enq_map::EMAP_OK); + BOOST_CHECK(!e2.empty()); + BOOST_CHECK_EQUAL(e2.size(), u_int32_t(128)); + + // get + u_int64_t rid_incr_2 = 6ULL; + for (u_int64_t rid = rid_begin; rid < rid_end; rid += rid_incr_2) + { + BOOST_CHECK_EQUAL(e2.is_enqueued(rid), (rid%rid_incr_1 ? false : true)); + u_int16_t exp_pfid = pfid_start + (u_int16_t)((rid - rid_begin)/rid_incr_1); + int16_t ret_fid = e2.get_pfid(rid); + if (ret_fid < enq_map::EMAP_OK) // fail + { + BOOST_CHECK_EQUAL(ret_fid, enq_map::EMAP_RID_NOT_FOUND); + BOOST_CHECK(rid%rid_incr_1); + } + else + { + BOOST_CHECK_EQUAL(ret_fid, exp_pfid); + BOOST_CHECK(rid%rid_incr_1 == 0); + } + if ((rid + rid_incr_2)%(8 * rid_incr_2) == 0) + pfid++; + } + + // insert with dups + for (rid = rid_begin, pfid = pfid_start; rid < rid_end; rid += rid_incr_2, pfid++) + { + int16_t res = e2.insert_pfid(rid, pfid); + if (res < enq_map::EMAP_OK) // fail + { + BOOST_CHECK_EQUAL(res, enq_map::EMAP_DUP_RID); + BOOST_CHECK(rid%rid_incr_1 == 0); + } + else + BOOST_CHECK(rid%rid_incr_1); + } + BOOST_CHECK_EQUAL(e2.size(), u_int32_t(171)); + e2.clear(); + BOOST_CHECK(e2.empty()); + BOOST_CHECK_EQUAL(e2.size(), u_int32_t(0)); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(get_remove) +{ + cout << test_filename << ".get_remove: " << flush; + u_int16_t pfid; + u_int64_t rid; + u_int16_t pfid_start = 0x3000U; + u_int64_t rid_begin = 0xeeeeeeee00000000ULL; + u_int64_t rid_end = 0xeeeeeeee00000200ULL; + + u_int64_t rid_incr_1 = 4ULL; + u_int64_t num_incr_1 = (rid_end - rid_begin)/rid_incr_1; + enq_map e3; + e3.set_num_jfiles(pfid_start + (rid_end - rid_begin)/rid_incr_1); + for (rid = rid_begin, pfid = pfid_start; rid < rid_end; rid += rid_incr_1, pfid++) + BOOST_CHECK_EQUAL(e3.insert_pfid(rid, pfid), enq_map::EMAP_OK); + BOOST_CHECK_EQUAL(e3.size(), num_incr_1); + + u_int64_t rid_incr_2 = 6ULL; + for (rid = rid_begin, pfid = pfid_start; rid < rid_end; rid += rid_incr_2, pfid++) + { + u_int16_t exp_pfid = pfid_start + (u_int16_t)((rid - rid_begin)/rid_incr_1); + int16_t ret_fid = e3.get_remove_pfid(rid); + if (ret_fid < enq_map::EMAP_OK) // fail + { + BOOST_CHECK_EQUAL(ret_fid, enq_map::EMAP_RID_NOT_FOUND); + BOOST_CHECK(rid%rid_incr_1); + } + else + { + BOOST_CHECK_EQUAL(ret_fid, exp_pfid); + BOOST_CHECK(rid%rid_incr_1 == 0); + } + } + BOOST_CHECK_EQUAL(e3.size(), u_int32_t(85)); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(lock) +{ + cout << test_filename << ".lock: " << flush; + u_int16_t pfid; + u_int64_t rid; + u_int16_t pfid_start = 0x4000U; + u_int64_t rid_begin = 0xdddddddd00000000ULL; + u_int64_t rid_end = 0xdddddddd00000200ULL; + + // insert, every second entry is locked + u_int64_t rid_incr_1 = 4ULL; + u_int64_t num_incr_1 = (rid_end - rid_begin)/rid_incr_1; + bool locked = false; + enq_map e4; + e4.set_num_jfiles(pfid_start + (rid_end - rid_begin)/rid_incr_1); + for (rid = rid_begin, pfid = pfid_start; rid < rid_end; rid += rid_incr_1, pfid++) + { + BOOST_CHECK_EQUAL(e4.insert_pfid(rid, pfid, locked), enq_map::EMAP_OK); + locked = !locked; + } + BOOST_CHECK_EQUAL(e4.size(), num_incr_1); + + // unlock and lock non-existent rids + int16_t res = e4.lock(1ULL); + if (res < enq_map::EMAP_OK) + BOOST_CHECK_EQUAL(res, enq_map::EMAP_RID_NOT_FOUND); + else + BOOST_ERROR("Failed to detect locking non-existent rid."); + res = e4.unlock(2ULL); + if (res < enq_map::EMAP_OK) + BOOST_CHECK_EQUAL(res, enq_map::EMAP_RID_NOT_FOUND); + else + BOOST_ERROR("Failed to detect unlocking non-existent rid."); + + // get / unlock + for (u_int64_t rid = rid_begin; rid < rid_end; rid += rid_incr_1) + { + int16_t fid = e4.get_pfid(rid); + if (fid < enq_map::EMAP_OK) // fail + { + BOOST_CHECK_EQUAL(fid, enq_map::EMAP_LOCKED); + BOOST_CHECK(rid%(2*rid_incr_1)); + // unlock, read, then relock + BOOST_CHECK_EQUAL(e4.unlock(rid), enq_map::EMAP_OK); + BOOST_CHECK(e4.get_pfid(rid) >= enq_map::EMAP_OK); + BOOST_CHECK_EQUAL(e4.lock(rid), enq_map::EMAP_OK); + fid = e4.get_pfid(rid); + if (fid < enq_map::EMAP_OK) // fail + BOOST_CHECK_EQUAL(fid, enq_map::EMAP_LOCKED); + else + BOOST_ERROR("Failed to prevent getting locked record"); + } + } + + // remove all; if locked, use with txn_flag true; should ignore all locked records + for (u_int64_t rid = rid_begin; rid < rid_end; rid += rid_incr_1) + BOOST_CHECK(e4.get_remove_pfid(rid, true) >= enq_map::EMAP_OK); + BOOST_CHECK(e4.empty()); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(lists) +{ + cout << test_filename << ".lists: " << flush; + u_int16_t pfid; + u_int64_t rid; + u_int16_t pfid_start = 0x5000UL; + u_int64_t rid_begin = 0xdddddddd00000000ULL; + u_int64_t rid_end = 0xdddddddd00000200ULL; + + // insert, every second entry is locked + u_int64_t rid_incr_1 = 4ULL; + u_int64_t num_incr_1 = (rid_end - rid_begin)/rid_incr_1; + vector<u_int64_t> rid_list; + vector<u_int16_t> pfid_list; + enq_map e5; + e5.set_num_jfiles(pfid_start + (rid_end - rid_begin)/rid_incr_1); + for (rid = rid_begin, pfid = pfid_start; rid < rid_end; rid += rid_incr_1, pfid++) + { + BOOST_CHECK_EQUAL(e5.insert_pfid(rid, pfid), enq_map::EMAP_OK); + rid_list.push_back(rid); + pfid_list.push_back(pfid); + } + BOOST_CHECK_EQUAL(e5.size(), num_incr_1); + BOOST_CHECK_EQUAL(rid_list.size(), num_incr_1); + BOOST_CHECK_EQUAL(pfid_list.size(), num_incr_1); + + vector<u_int64_t> ret_rid_list; + e5.rid_list(ret_rid_list); + BOOST_CHECK_EQUAL(ret_rid_list.size(), num_incr_1); + for (unsigned i=0; i<ret_rid_list.size(); i++) + BOOST_CHECK_EQUAL(rid_list[i], ret_rid_list[i]); + + vector<u_int16_t> ret_pfid_list; + e5.pfid_list(ret_pfid_list); + BOOST_CHECK_EQUAL(ret_pfid_list.size(), num_incr_1); + for (unsigned i=0; i<ret_pfid_list.size(); i++) + BOOST_CHECK_EQUAL(pfid_list[i], ret_pfid_list[i]); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enq_count) +{ + cout << test_filename << ".enq_count: " << flush; + + enq_map e6; + + // Check the allocation and cleanup as the file size is set both up and down + e6.set_num_jfiles(24); + e6.set_num_jfiles(0); + e6.set_num_jfiles(100); + e6.set_num_jfiles(4); + + // Add 100 enqueues to file 1, check that the counts match + for (u_int16_t pfid=0; pfid<4; pfid++) + BOOST_CHECK_EQUAL(e6.get_enq_cnt(pfid), u_int32_t(0)); + for (u_int64_t rid=0; rid<100; rid++) + BOOST_CHECK_EQUAL(e6.insert_pfid(rid, 1), enq_map::EMAP_OK); + for (u_int16_t pfid=0; pfid<4; pfid++) + { + if (pfid == 1) + BOOST_CHECK_EQUAL(e6.get_enq_cnt(pfid), u_int32_t(100)); + else + BOOST_CHECK_EQUAL(e6.get_enq_cnt(pfid), u_int32_t(0)); + } + + // Now remove 10 from file 1, check that the counts match + for (u_int64_t rid=0; rid<100; rid+=10) + //e6.Xget_remove_pfid(rid); + BOOST_CHECK(e6.get_remove_pfid(rid) >= enq_map::EMAP_OK); + for (u_int16_t pfid=0; pfid<4; pfid++) + { + if (pfid == 1) + BOOST_CHECK_EQUAL(e6.get_enq_cnt(pfid), u_int32_t(90)); + else + BOOST_CHECK_EQUAL(e6.get_enq_cnt(pfid), u_int32_t(0)); + } + + // Now resize the file up and make sure the count in file 1 still exists + e6.set_num_jfiles(8); + for (u_int16_t pfid=0; pfid<8; pfid++) + { + if (pfid == 1) + BOOST_CHECK_EQUAL(e6.get_enq_cnt(pfid), u_int32_t(90)); + else + BOOST_CHECK_EQUAL(e6.get_enq_cnt(pfid), u_int32_t(0)); + } + + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(stress) +{ + cout << test_filename << ".stress: " << flush; + u_int64_t rid; + u_int64_t rid_cnt; + u_int64_t rid_begin = 0xffffffff00000000ULL; + u_int64_t num_rid = 10; + + enq_map e7; + e7.set_num_jfiles(rid_begin + num_rid); + + // insert even rids with no dups + for (rid = rid_begin, rid_cnt = u_int64_t(0); rid_cnt < num_rid; rid += 2ULL, rid_cnt++) + BOOST_CHECK_EQUAL(e7.insert_pfid(rid, u_int16_t(0)), enq_map::EMAP_OK); + BOOST_CHECK_EQUAL(e7.size(), num_rid); + + // insert odd rids with no dups + for (rid = rid_begin + 1, rid_cnt = u_int64_t(0); rid_cnt < num_rid; rid += 2ULL, rid_cnt++) + BOOST_CHECK_EQUAL(e7.insert_pfid(rid, u_int16_t(0)), enq_map::EMAP_OK); + BOOST_CHECK_EQUAL(e7.size(), num_rid * 2); + + // remove even rids + for (rid = rid_begin, rid_cnt = u_int64_t(0); rid_cnt < num_rid; rid += 2ULL, rid_cnt++) + BOOST_CHECK(e7.get_remove_pfid(rid) >= enq_map::EMAP_OK); + BOOST_CHECK_EQUAL(e7.size(), num_rid); + + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_ut_jdir.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_ut_jdir.cpp new file mode 100644 index 0000000000..842ea5c9ef --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/_ut_jdir.cpp @@ -0,0 +1,416 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../unit_test.h" + +#include <cerrno> +#include <cstring> +#include <dirent.h> +#include <fstream> +#include <iomanip> +#include <iostream> +#include "qpid/legacystore/jrnl/file_hdr.h" +#include "qpid/legacystore/jrnl/jcfg.h" +#include "qpid/legacystore/jrnl/jdir.h" +#include "qpid/legacystore/jrnl/jerrno.h" +#include "qpid/legacystore/jrnl/jexception.h" +#include <sys/stat.h> + +#define NUM_JFILES 4 +#define JFSIZE_SBLKS 128 + +#define ERRORSTR(e) std::strerror(e) << " (" << e << ")" +#define NUM_CLEAR_OPS 20 + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace std; + +QPID_AUTO_TEST_SUITE(jdir_suite) + +const string test_filename("_ut_jdir"); +const char* tdp = getenv("TMP_DATA_DIR"); +const string test_dir(tdp && strlen(tdp) > 0 ? string(tdp) + "/_ut_jdir" : "/var/tmp/_ut_jdir"); + +// === Helper functions === + +void create_file(const char* filename, mode_t fmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) +{ + ofstream of(filename, ofstream::out | ofstream::trunc); + if (!of.good()) + BOOST_FAIL("Unable to open file " << filename << " for writing."); + of.write(filename, std::strlen(filename)); + of.close(); + ::chmod(filename, fmode); +} + +void create_file(const string filename, mode_t fmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) +{ + create_file(filename.c_str(), fmode); +} + +void create_jdat_file(const char* dirname, const char* base_filename, u_int32_t fid, + u_int64_t first_rid) +{ + stringstream fn; + fn << dirname << "/" << base_filename << "."; + fn << setfill('0') << hex << setw(4) << fid << ".jdat"; + file_hdr fh(RHM_JDAT_FILE_MAGIC, RHM_JDAT_VERSION, 0, first_rid, fid, 0x200, true); + ofstream of(fn.str().c_str(), ofstream::out | ofstream::trunc); + if (!of.good()) + BOOST_FAIL("Unable to open journal data file " << fn << " for writing."); + of.write((const char*)&fh, sizeof(file_hdr)); + of.close(); +} + +void create_jinf_file(const char* dirname, const char* base_filename) +{ + timespec ts; + ::clock_gettime(CLOCK_REALTIME, &ts); + jinf ji("test journal id", dirname, base_filename, NUM_JFILES, false, 0, JFSIZE_SBLKS, + JRNL_WMGR_DEF_PAGE_SIZE, JRNL_WMGR_DEF_PAGES, ts); + ji.write(); +} + +void create_jrnl_fileset(const char* dirname, const char* base_filename) +{ + create_jinf_file(dirname, base_filename); + for (u_int32_t fid = 0; fid < NUM_JFILES; fid++) + { + u_int64_t rid = 0x12340000 + (fid * 0x25); + create_jdat_file(dirname, base_filename, fid, rid); + } +} + +unsigned count_dir_contents(const char* dirname, bool incl_files, bool incl_dirs = true) +{ + struct dirent* entry; + struct stat s; + unsigned file_cnt = 0; + unsigned dir_cnt = 0; + unsigned other_cnt = 0; + DIR* dir = ::opendir(dirname); + if (!dir) + BOOST_FAIL("Unable to open directory " << dirname); + while ((entry = ::readdir(dir)) != NULL) + { + // Ignore . and .. + if (std::strcmp(entry->d_name, ".") != 0 && std::strcmp(entry->d_name, "..") != 0) + { + stringstream fn; + fn << dirname << "/" << entry->d_name; + if (::stat(fn.str().c_str(), &s)) + BOOST_FAIL("Unable to stat dir entry " << entry->d_name << "; err=" << + ERRORSTR(errno)); + if (S_ISREG(s.st_mode)) + file_cnt++; + else if (S_ISDIR(s.st_mode)) + dir_cnt++; + else + other_cnt++; + } + } + ::closedir(dir); + if (incl_files) + { + if (incl_dirs) + return file_cnt + dir_cnt; + return file_cnt; + } + else if (incl_dirs) + return dir_cnt; + return other_cnt; +} + +void check_dir_contents(const char* dirname, const char* base_filename, unsigned num_subdirs, + bool jrnl_present) +{ + if (jdir::is_dir(dirname)) + { + // Subdir count + BOOST_CHECK_EQUAL(count_dir_contents(dirname, false, true), num_subdirs); + + // Journal file count + unsigned num_jrnl_files = jrnl_present ? NUM_JFILES + 1 : 0; + BOOST_CHECK_EQUAL(count_dir_contents(dirname, true, false), num_jrnl_files); + + // Check journal files are present + if (jrnl_present) + try { jdir::verify_dir(dirname, base_filename); } + catch(const jexception& e) { BOOST_ERROR(e); } + for (unsigned subdir_num = 1; subdir_num <= num_subdirs; subdir_num++) + { + stringstream subdir_name; + subdir_name << dirname << "/_" << base_filename << ".bak."; + subdir_name << hex << setfill('0') << setw(4) << subdir_num; + try { jdir::verify_dir(subdir_name.str().c_str(), base_filename); } + catch(const jexception& e) { BOOST_ERROR(e); } + } + } + else + BOOST_ERROR(dirname << " is not a directory"); +} + +void check_dir_not_existing(const char* dirname) +{ + if (jdir::exists(dirname) && jdir::is_dir(dirname)) + jdir::delete_dir(dirname); + if (jdir::exists(dirname)) + BOOST_FAIL("Unable to remove directory " << dirname); +} + +void check_dir_not_existing(const string dirname) +{ + check_dir_not_existing(dirname.c_str()); +} + +// === Test suite === + +QPID_AUTO_TEST_CASE(constructor) +{ + cout << test_filename << ".constructor: " << flush; + string dir(test_dir + "/A/B/C/D/E/F"); + string bfn("test_base"); + jdir dir1(dir, bfn); + BOOST_CHECK(dir1.dirname().compare(dir) == 0); + BOOST_CHECK(dir1.base_filename().compare(bfn) == 0); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(create_delete_dir) +{ + cout << test_filename << ".create_delete_dir: " << flush; + // Use instance + string dir_A(test_dir + "/A"); + string dir_Ats(test_dir + "/A/"); // trailing '/' + check_dir_not_existing(test_dir + "/A"); + jdir dir1(dir_A, "test_base"); + dir1.create_dir(); + // check all combos of jdir::exists and jdir::is_dir() + BOOST_CHECK(jdir::exists(dir_A)); + BOOST_CHECK(jdir::exists(dir_Ats)); + BOOST_CHECK(jdir::exists(dir_A.c_str())); + BOOST_CHECK(jdir::exists(dir_Ats.c_str())); + BOOST_CHECK(jdir::is_dir(dir_A)); + BOOST_CHECK(jdir::is_dir(dir_Ats)); + BOOST_CHECK(jdir::is_dir(dir_Ats.c_str())); + BOOST_CHECK(jdir::is_dir(dir_Ats.c_str())); + // do it a second time when dir exists + dir1.create_dir(); + BOOST_CHECK(jdir::is_dir(dir_A)); + dir1.delete_dir(); + BOOST_CHECK(!jdir::exists(dir_A)); + + // Use static fn + check_dir_not_existing(test_dir + "/B"); + jdir::create_dir(test_dir + "/B"); + BOOST_CHECK(jdir::is_dir(test_dir + "/B")); + jdir::create_dir(test_dir + "/B"); + BOOST_CHECK(jdir::is_dir(test_dir + "/B")); + jdir::delete_dir(test_dir + "/B"); + BOOST_CHECK(!jdir::exists(test_dir + "/B")); + + // Non-empty dirs + check_dir_not_existing(test_dir + "/C"); + jdir::create_dir(test_dir + "/C"); + BOOST_CHECK(jdir::is_dir(test_dir + "/C")); + create_file(test_dir + "/C/test_file_1.txt"); // mode 644 (default) + create_file(test_dir + "/C/test_file_2.txt", S_IRWXU | S_IRWXG | S_IRWXO); // mode 777 + create_file(test_dir + "/C/test_file_3.txt", S_IRUSR | S_IRGRP | S_IROTH); // mode 444 (read-only) + create_file(test_dir + "/C/test_file_4.txt", 0); // mode 000 (no permissions) + BOOST_CHECK(jdir::is_dir(test_dir + "/C")); + jdir::create_dir(test_dir + "/C"); + BOOST_CHECK(jdir::is_dir(test_dir + "/C")); + jdir::delete_dir(test_dir + "/C"); + BOOST_CHECK(!jdir::exists(test_dir + "/C")); + + // Check non-existent dirs fail + check_dir_not_existing(test_dir + "/D"); + try + { + jdir::is_dir(test_dir + "/D"); + BOOST_ERROR("jdir::is_dir() failed to throw jexeption for non-existent directory."); + } + catch(const jexception& e) + { + BOOST_CHECK_EQUAL(e.err_code(), jerrno::JERR_JDIR_STAT); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(create_delete_dir_recursive) +{ + cout << test_filename << ".create_delete_dir_recursive: " << flush; + // Use instances + check_dir_not_existing(test_dir + "/E"); + jdir dir1(test_dir + "/E/F/G/H", "test_base"); + dir1.create_dir(); + BOOST_CHECK(jdir::is_dir(test_dir + "/E/F/G/H")); + dir1.delete_dir(); + BOOST_CHECK(!jdir::exists(test_dir + "/E/F/G/H")); // only H deleted, E/F/G remain + BOOST_CHECK(jdir::exists(test_dir + "/E/F/G")); + jdir::delete_dir(test_dir + "/E"); // delete remaining dirs + BOOST_CHECK(!jdir::exists(test_dir + "/E")); + + check_dir_not_existing(test_dir + "/F"); + jdir dir2(test_dir + "/F/G/H/I/", "test_base"); // trailing '/' + dir2.create_dir(); + BOOST_CHECK(jdir::is_dir(test_dir + "/F/G/H/I/")); + dir2.delete_dir(); + BOOST_CHECK(!jdir::exists(test_dir + "/F/G/H/I/")); + BOOST_CHECK(jdir::exists(test_dir + "/F/G/H/")); + jdir::delete_dir(test_dir + "/F"); + BOOST_CHECK(!jdir::exists(test_dir + "/F")); + + check_dir_not_existing(test_dir + "/G"); + jdir dir3(test_dir + "/G/H//I//J", "test_base"); // extra '/' in path + dir3.create_dir(); + BOOST_CHECK(jdir::is_dir(test_dir + "/G/H//I//J")); + dir3.delete_dir(); + BOOST_CHECK(!jdir::exists(test_dir + "/G/H//I//J")); + BOOST_CHECK(jdir::exists(test_dir + "/G/H//I")); + jdir::delete_dir(test_dir + "/F"); + BOOST_CHECK(!jdir::exists(test_dir + "/F")); + + // Use static fn + check_dir_not_existing(test_dir + "/H"); + jdir::create_dir(test_dir + "/H/I/J/K"); + BOOST_CHECK(jdir::is_dir(test_dir + "/H/I/J/K")); + jdir::delete_dir(test_dir + "/H/I/J/K"); + BOOST_CHECK(!jdir::exists(test_dir + "/H/I/J/K")); // only J deleted, H/I/J remain + BOOST_CHECK(jdir::exists(test_dir + "/H/I/J")); + jdir::delete_dir(test_dir + "/H"); + BOOST_CHECK(!jdir::exists(test_dir + "/H")); + + check_dir_not_existing(test_dir + "/I"); + jdir::create_dir(test_dir + "/I/J/K/L/"); // trailing '/' + BOOST_CHECK(jdir::is_dir(test_dir + "/I/J/K/L/")); + jdir::delete_dir(test_dir + "/I/J/K/L/"); + BOOST_CHECK(!jdir::exists(test_dir + "/I/J/K/L/")); + BOOST_CHECK(jdir::exists(test_dir + "/I/J/K/")); + jdir::delete_dir(test_dir + "/I"); + BOOST_CHECK(!jdir::exists(test_dir + "/I")); + + check_dir_not_existing(test_dir + "//J"); + jdir::create_dir(test_dir + "//J//K//L//M"); // extra '/' in path + BOOST_CHECK(jdir::is_dir(test_dir + "//J//K//L//M")); + jdir::delete_dir(test_dir + "//J//K//L//M"); + BOOST_CHECK(!jdir::exists(test_dir + "//J//K//L//M")); + BOOST_CHECK(jdir::exists(test_dir + "//J//K//L")); + jdir::delete_dir(test_dir + "//J"); + BOOST_CHECK(!jdir::exists(test_dir + "//J")); + + // Non-empty dirs + check_dir_not_existing(test_dir + "/K"); + jdir::create_dir(test_dir + "/K/L/M1/N1"); + jdir::create_dir(test_dir + "/K/L/M1/N2"); + jdir::create_dir(test_dir + "/K/L/M1/N3"); + jdir::create_dir(test_dir + "/K/L/M1/N4"); + create_file(test_dir + "/K/L/M1/N4/test_file_1.txt"); // mode 644 (default) + create_file(test_dir + "/K/L/M1/N4/test_file_2.txt", S_IRWXU | S_IRWXG | S_IRWXO); // mode 777 + create_file(test_dir + "/K/L/M1/N4/test_file_3.txt", S_IRUSR | S_IRGRP | S_IROTH); // mode 444 + create_file(test_dir + "/K/L/M1/N4/test_file_4.txt", 0); // mode 000 (no permissions) + jdir::create_dir(test_dir + "/K/L/M2"); + jdir::create_dir(test_dir + "/K/L/M3/N5"); + jdir::create_dir(test_dir + "/K/L/M3/N6"); + BOOST_CHECK(jdir::is_dir(test_dir + "/K/L/M1/N1")); + BOOST_CHECK(jdir::is_dir(test_dir + "/K/L/M1/N2")); + BOOST_CHECK(jdir::is_dir(test_dir + "/K/L/M1/N3")); + BOOST_CHECK(jdir::is_dir(test_dir + "/K/L/M1/N4")); + BOOST_CHECK(jdir::is_dir(test_dir + "/K/L/M2")); + BOOST_CHECK(jdir::is_dir(test_dir + "/K/L/M3/N5")); + BOOST_CHECK(jdir::is_dir(test_dir + "/K/L/M3/N6")); + jdir::delete_dir(test_dir + "/K"); + BOOST_CHECK(!jdir::exists(test_dir + "/K")); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(clear_verify_dir) +{ + cout << test_filename << ".clear_verify_dir: " << flush; + // Use instances + const char* jrnl_dir = "/var/tmp/test_dir_1"; + const char* bfn = "test_base"; + check_dir_not_existing(jrnl_dir); + jdir test_dir_1(jrnl_dir, bfn); + test_dir_1.create_dir(); + BOOST_CHECK(jdir::is_dir(jrnl_dir)); + // add journal files, check they exist, then clear them + unsigned cnt = 0; + while (cnt < NUM_CLEAR_OPS) + { + create_jrnl_fileset(jrnl_dir, bfn); + check_dir_contents(jrnl_dir, bfn, cnt, true); + test_dir_1.clear_dir(); + check_dir_contents(jrnl_dir, bfn, ++cnt, false); + } + // clean up + test_dir_1.delete_dir(); + BOOST_CHECK(!jdir::exists(jrnl_dir)); + + // Non-existent dir with auto-create true + jrnl_dir = "/var/tmp/test_dir_2"; + check_dir_not_existing(jrnl_dir); + jdir test_dir_2(jrnl_dir, bfn); + // clear dir + test_dir_2.clear_dir(); // create flag is true by default + check_dir_contents(jrnl_dir, bfn, 0, false); + // clear empty dir, should not create subdir + test_dir_2.clear_dir(); // create flag is true by default + check_dir_contents(jrnl_dir, bfn, 0, false); + // clean up + test_dir_2.delete_dir(); + BOOST_CHECK(!jdir::exists(jrnl_dir)); + + // non-existent dir with auto-create false + jrnl_dir = "/var/tmp/test_dir_3"; + check_dir_not_existing(jrnl_dir); + jdir test_dir_3(jrnl_dir, bfn); + try + { + test_dir_3.clear_dir(false); + BOOST_ERROR("jdir::clear_dir(flase) failed to throw jexeption for non-existent directory."); + } + catch(const jexception& e) + { + BOOST_CHECK_EQUAL(e.err_code(), jerrno::JERR_JDIR_OPENDIR); + } + + // Use static fn + jrnl_dir = "/var/tmp/test_dir_4"; + check_dir_not_existing(jrnl_dir); + jdir::clear_dir(jrnl_dir, bfn); // should create dir if it does not exist + // add journal files, check they exist, then clear them + cnt = 0; + while (cnt < NUM_CLEAR_OPS) + { + create_jrnl_fileset(jrnl_dir, bfn); + check_dir_contents(jrnl_dir, bfn, cnt, true); + jdir::clear_dir(jrnl_dir, bfn); + check_dir_contents(jrnl_dir, bfn, ++cnt, false); + } + // clean up + jdir::delete_dir(jrnl_dir); + BOOST_CHECK(!jdir::exists(jrnl_dir)); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_ut_jerrno.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_ut_jerrno.cpp new file mode 100644 index 0000000000..1ae1138355 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/_ut_jerrno.cpp @@ -0,0 +1,47 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../unit_test.h" + +#include <cstring> +#include <iostream> +#include "qpid/legacystore/jrnl/jerrno.h" + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace std; + +QPID_AUTO_TEST_SUITE(jerrno_suite) +using namespace mrg::journal; + +const string test_filename("_ut_jerrno"); + +QPID_AUTO_TEST_CASE(jerrno_val) +{ + cout << test_filename << ".jerrno_val: " << flush; + const char* m = "JERR__MALLOC"; + string malloc_msg = string(jerrno::err_msg(jerrno::JERR__MALLOC)); + BOOST_CHECK(malloc_msg.substr(0, std::strlen(m)).compare(m) == 0); + BOOST_CHECK(std::strcmp(jerrno::err_msg(0), "<Unknown error code>") == 0); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_ut_jexception.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_ut_jexception.cpp new file mode 100644 index 0000000000..8b9e876aa6 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/_ut_jexception.cpp @@ -0,0 +1,346 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../unit_test.h" + +#include <cstring> +#include <iostream> +#include "qpid/legacystore/jrnl/jerrno.h" +#include "qpid/legacystore/jrnl/jexception.h" + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace std; + +QPID_AUTO_TEST_SUITE(jexception_suite) + +const string test_filename("_ut_jexception"); + +// === Helper functions === + +void throw_exception(const jexception& e, std::size_t what_len, std::size_t ai_len, + std::size_t tc_len, std::size_t tf_len) +{ + try { throw e; } + catch (const jexception& e) + { + BOOST_CHECK_EQUAL(std::strlen(e.what()), what_len); + BOOST_CHECK_EQUAL(e.additional_info().size(), ai_len); + BOOST_CHECK_EQUAL(e.throwing_class().size(), tc_len); + BOOST_CHECK_EQUAL(e.throwing_fn().size(), tf_len); + } +} + +void throw_exception(const jexception& e, std::size_t what_len, std::size_t ai_len) +{ + throw_exception(e, what_len, ai_len, 0, 0); +} + +void throw_exception(const jexception& e, std::size_t what_len, std::size_t tc_len, + std::size_t tf_len) +{ + throw_exception(e, what_len, 0, tc_len, tf_len); +} + +// === Test suite === + +QPID_AUTO_TEST_CASE(constructor_1) +{ + cout << test_filename << ".constructor_1: " << flush; + try + { + jexception e1; + BOOST_CHECK_EQUAL(e1.err_code(), (u_int32_t)0); + BOOST_CHECK(e1.additional_info().size() == 0); + BOOST_CHECK(e1.throwing_class().size() == 0); + BOOST_CHECK(e1.throwing_fn().size() == 0); + BOOST_CHECK(std::strlen(e1.what()) > 0); + throw e1; + } + catch (const jexception& e) + { + BOOST_CHECK_EQUAL(e.err_code(), (u_int32_t)0); + BOOST_CHECK(e.additional_info().size() == 0); + BOOST_CHECK(e.throwing_class().size() == 0); + BOOST_CHECK(e.throwing_fn().size() == 0); + BOOST_CHECK(std::strlen(e.what()) > 0); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(constructor_2) +{ + cout << test_filename << ".constructor_2: " << flush; + const u_int32_t err_code = 2; + try + { + jexception e2(err_code); + BOOST_CHECK_EQUAL(e2.err_code(), err_code); + BOOST_CHECK(e2.additional_info().size() == 0); + BOOST_CHECK(e2.throwing_class().size() == 0); + BOOST_CHECK(e2.throwing_fn().size() == 0); + BOOST_CHECK(std::strlen(e2.what()) > 0); + throw e2; + } + catch (const jexception& e) + { + BOOST_CHECK_EQUAL(e.err_code(), err_code); + BOOST_CHECK(e.additional_info().size() == 0); + BOOST_CHECK(e.throwing_class().size() == 0); + BOOST_CHECK(e.throwing_fn().size() == 0); + BOOST_CHECK(std::strlen(e.what()) > 0); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(constructor_3a) +{ + cout << test_filename << ".constructor_3a: " << flush; + const char* err_msg = "exception3"; + try + { + jexception e3(err_msg); + BOOST_CHECK_EQUAL(e3.err_code(), (u_int32_t)0); + BOOST_CHECK(e3.additional_info().compare(err_msg) == 0); + BOOST_CHECK(e3.throwing_class().size() == 0); + BOOST_CHECK(e3.throwing_fn().size() == 0); + BOOST_CHECK(std::strlen(e3.what()) > 0); + throw e3; + } + catch (const jexception& e) + { + BOOST_CHECK_EQUAL(e.err_code(), (u_int32_t)0); + BOOST_CHECK(e.additional_info().compare(err_msg) == 0); + BOOST_CHECK(e.throwing_class().size() == 0); + BOOST_CHECK(e.throwing_fn().size() == 0); + BOOST_CHECK(std::strlen(e.what()) > 0); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(constructor_3b) +{ + cout << test_filename << ".constructor_3b: " << flush; + const string err_msg("exception3"); + try + { + jexception e3(err_msg); + BOOST_CHECK_EQUAL(e3.err_code(), (u_int32_t)0); + BOOST_CHECK(e3.additional_info().compare(err_msg) == 0); + BOOST_CHECK(e3.throwing_class().size() == 0); + BOOST_CHECK(e3.throwing_fn().size() == 0); + BOOST_CHECK(std::strlen(e3.what()) > 0); + throw e3; + } + catch (const jexception& e) + { + BOOST_CHECK_EQUAL(e.err_code(), (u_int32_t)0); + BOOST_CHECK(e.additional_info().compare(err_msg) == 0); + BOOST_CHECK(e.throwing_class().size() == 0); + BOOST_CHECK(e.throwing_fn().size() == 0); + BOOST_CHECK(std::strlen(e.what()) > 0); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(constructor_4a) +{ + cout << test_filename << ".constructor_4a: " << flush; + const u_int32_t err_code = 4; + const char* err_msg = "exception4"; + try + { + jexception e4(err_code, err_msg); + BOOST_CHECK_EQUAL(e4.err_code(), err_code); + BOOST_CHECK(e4.additional_info().compare(err_msg) == 0); + BOOST_CHECK(e4.throwing_class().size() == 0); + BOOST_CHECK(e4.throwing_fn().size() == 0); + BOOST_CHECK(std::strlen(e4.what()) > 0); + throw e4; + } + catch (const jexception& e) + { + BOOST_CHECK_EQUAL(e.err_code(), err_code); + BOOST_CHECK(e.additional_info().compare(err_msg) == 0); + BOOST_CHECK(e.throwing_class().size() == 0); + BOOST_CHECK(e.throwing_fn().size() == 0); + BOOST_CHECK(std::strlen(e.what()) > 0); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(constructor_4b) +{ + cout << test_filename << ".constructor_4b: " << flush; + const u_int32_t err_code = 4; + const string err_msg("exception4"); + try + { + jexception e4(err_code, err_msg); + BOOST_CHECK_EQUAL(e4.err_code(), err_code); + BOOST_CHECK(e4.additional_info().compare(err_msg) == 0); + BOOST_CHECK(e4.throwing_class().size() == 0); + BOOST_CHECK(e4.throwing_fn().size() == 0); + BOOST_CHECK(std::strlen(e4.what()) > 0); + throw e4; + } + catch (const jexception& e) + { + BOOST_CHECK_EQUAL(e.err_code(), err_code); + BOOST_CHECK(e.additional_info().compare(err_msg) == 0); + BOOST_CHECK(e.throwing_class().size() == 0); + BOOST_CHECK(e.throwing_fn().size() == 0); + BOOST_CHECK(std::strlen(e.what()) > 0); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(constructor_5a) +{ + cout << test_filename << ".constructor_5a: " << flush; + const u_int32_t err_code = 5; + const char* err_class = "class5"; + const char* err_fn = "fn5"; + try + { + jexception e5(err_code, err_class, err_fn); + BOOST_CHECK_EQUAL(e5.err_code(), err_code); + BOOST_CHECK(e5.additional_info().size() == 0); + BOOST_CHECK(e5.throwing_class().compare(err_class) == 0); + BOOST_CHECK(e5.throwing_fn().compare(err_fn) == 0); + BOOST_CHECK(std::strlen(e5.what()) > 0); + throw e5; + } + catch (const jexception& e) + { + BOOST_CHECK_EQUAL(e.err_code(), err_code); + BOOST_CHECK(e.additional_info().size() == 0); + BOOST_CHECK(e.throwing_class().compare(err_class) == 0); + BOOST_CHECK(e.throwing_fn().compare(err_fn) == 0); + BOOST_CHECK(std::strlen(e.what()) > 0); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(constructor_5b) +{ + cout << test_filename << ".constructor_5b: " << flush; + const u_int32_t err_code = 5; + const string err_class("class5"); + const string err_fn("fn5"); + try + { + jexception e5(err_code, err_class, err_fn); + BOOST_CHECK_EQUAL(e5.err_code(), err_code); + BOOST_CHECK(e5.additional_info().size() == 0); + BOOST_CHECK(e5.throwing_class().compare(err_class) == 0); + BOOST_CHECK(e5.throwing_fn().compare(err_fn) == 0); + BOOST_CHECK(std::strlen(e5.what()) > 0); + throw e5; + } + catch (const jexception& e) + { + BOOST_CHECK_EQUAL(e.err_code(), err_code); + BOOST_CHECK(e.additional_info().size() == 0); + BOOST_CHECK(e.throwing_class().compare(err_class) == 0); + BOOST_CHECK(e.throwing_fn().compare(err_fn) == 0); + BOOST_CHECK(std::strlen(e.what()) > 0); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(constructor_6a) +{ + cout << test_filename << ".constructor_6a: " << flush; + const u_int32_t err_code = 6; + const char* err_msg = "exception6"; + const char* err_class = "class6"; + const char* err_fn = "fn6"; + try + { + jexception e6(err_code, err_msg, err_class, err_fn); + BOOST_CHECK_EQUAL(e6.err_code(), err_code); + BOOST_CHECK(e6.additional_info().compare(err_msg) == 0); + BOOST_CHECK(e6.throwing_class().compare(err_class) == 0); + BOOST_CHECK(e6.throwing_fn().compare(err_fn) == 0); + BOOST_CHECK(std::strlen(e6.what()) > 0); + throw e6; + } + catch (const jexception& e) + { + BOOST_CHECK_EQUAL(e.err_code(), err_code); + BOOST_CHECK(e.additional_info().compare(err_msg) == 0); + BOOST_CHECK(e.throwing_class().compare(err_class) == 0); + BOOST_CHECK(e.throwing_fn().compare(err_fn) == 0); + BOOST_CHECK(std::strlen(e.what()) > 0); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(constructor_6b) +{ + cout << test_filename << ".constructor_6b: " << flush; + const u_int32_t err_code = 6; + const string err_msg("exception6"); + const string err_class("class6"); + const string err_fn("fn6"); + try + { + jexception e6(err_code, err_msg, err_class, err_fn); + BOOST_CHECK_EQUAL(e6.err_code(), err_code); + BOOST_CHECK(e6.additional_info().compare(err_msg) == 0); + BOOST_CHECK(e6.throwing_class().compare(err_class) == 0); + BOOST_CHECK(e6.throwing_fn().compare(err_fn) == 0); + BOOST_CHECK(std::strlen(e6.what()) > 0); + throw e6; + } + catch (const jexception& e) + { + BOOST_CHECK_EQUAL(e.err_code(), err_code); + BOOST_CHECK(e.additional_info().compare(err_msg) == 0); + BOOST_CHECK(e.throwing_class().compare(err_class) == 0); + BOOST_CHECK(e.throwing_fn().compare(err_fn) == 0); + BOOST_CHECK(std::strlen(e.what()) > 0); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(msg_scope) +{ + cout << test_filename << ".msg_scope: " << flush; + try + { + // These will go out of scope as soon as jexception is thrown... + const string msg("Error message"); + const string cls("class"); + const string fn("function"); + throw jexception(100, msg, cls, fn); + } + catch (const jexception& e) + { + stringstream ss; + ss << e; + BOOST_CHECK(ss.str().size() > 0); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_ut_jinf.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_ut_jinf.cpp new file mode 100644 index 0000000000..c6377c2287 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/_ut_jinf.cpp @@ -0,0 +1,402 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../unit_test.h" + +#include <cmath> +#include <iostream> +#include "qpid/legacystore/jrnl/jcntl.h" + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace std; + +QPID_AUTO_TEST_SUITE(jinf_suite) + +const string test_filename("_ut_jinf"); + +#include "_st_helper_fns.h" + +timespec ts; + +QPID_AUTO_TEST_CASE(write_constructor) +{ + string test_name = get_test_name(test_filename, "write_constructor"); + const string jid = test_name + "_jid"; + const string base_filename = test_name + "_bfn"; + jdir::create_dir(test_dir); // Check test dir exists; create it if not + ::clock_gettime(CLOCK_REALTIME, &ts); + jinf ji(jid, test_dir, base_filename, NUM_JFILES, false, 0, JFSIZE_SBLKS, JRNL_WMGR_DEF_PAGE_SIZE, JRNL_WMGR_DEF_PAGES, ts); + BOOST_CHECK_EQUAL(ji.jver(), RHM_JDAT_VERSION); + BOOST_CHECK(ji.jid().compare(jid) == 0); + BOOST_CHECK(ji.jdir().compare(test_dir) == 0); + BOOST_CHECK(ji.base_filename().compare(base_filename) == 0); + const timespec this_ts = ji.ts(); + BOOST_CHECK_EQUAL(this_ts.tv_sec, ts.tv_sec); + BOOST_CHECK_EQUAL(this_ts.tv_nsec, ts.tv_nsec); + BOOST_CHECK_EQUAL(ji.num_jfiles(), u_int16_t(NUM_JFILES)); + BOOST_CHECK_EQUAL(ji.is_ae(), false); + BOOST_CHECK_EQUAL(ji.ae_max_jfiles(), u_int16_t(0)); + BOOST_CHECK_EQUAL(ji.jfsize_sblks(), u_int32_t(JFSIZE_SBLKS)); + BOOST_CHECK_EQUAL(ji.sblk_size_dblks(), u_int16_t(JRNL_SBLK_SIZE)); + BOOST_CHECK_EQUAL(ji.dblk_size(), u_int32_t(JRNL_DBLK_SIZE)); + BOOST_CHECK_EQUAL(ji.wcache_pgsize_sblks(), u_int32_t(JRNL_WMGR_DEF_PAGE_SIZE)); + BOOST_CHECK_EQUAL(ji.wcache_num_pages(), u_int16_t(JRNL_WMGR_DEF_PAGES)); + BOOST_CHECK_EQUAL(ji.rcache_pgsize_sblks(), u_int32_t(JRNL_RMGR_PAGE_SIZE)); + BOOST_CHECK_EQUAL(ji.rcache_num_pages(), u_int16_t(JRNL_RMGR_PAGES)); + ji.write(); + cout << "done" << endl; +} + +QPID_AUTO_TEST_CASE(read_constructor) +{ + string test_name = get_test_name(test_filename, "read_constructor"); + const string jid = test_name + "_jid"; + const string base_filename = test_name + "_bfn"; + lfid_pfid_map::create_new_jinf(jid, base_filename, false); + + stringstream fn; + fn << test_dir << "/" <<base_filename << "." << JRNL_INFO_EXTENSION; + jinf ji(fn.str(), false); + BOOST_CHECK_EQUAL(ji.jver(), RHM_JDAT_VERSION); + BOOST_CHECK(ji.jid().compare(jid) == 0); + BOOST_CHECK(ji.jdir().compare(test_dir) == 0); + BOOST_CHECK(ji.base_filename().compare(base_filename) == 0); +// const timespec this_ts = ji.ts(); +// BOOST_CHECK_EQUAL(this_ts.tv_sec, ts.tv_sec); +// BOOST_CHECK_EQUAL(this_ts.tv_nsec, ts.tv_nsec); + BOOST_CHECK_EQUAL(ji.num_jfiles(), u_int16_t(NUM_JFILES)); + BOOST_CHECK_EQUAL(ji.is_ae(), false); + BOOST_CHECK_EQUAL(ji.ae_max_jfiles(), u_int16_t(0)); + BOOST_CHECK_EQUAL(ji.jfsize_sblks(), u_int32_t(JFSIZE_SBLKS)); + BOOST_CHECK_EQUAL(ji.sblk_size_dblks(), u_int16_t(JRNL_SBLK_SIZE)); + BOOST_CHECK_EQUAL(ji.dblk_size(), u_int32_t(JRNL_DBLK_SIZE)); + BOOST_CHECK_EQUAL(ji.wcache_pgsize_sblks(), u_int32_t(JRNL_WMGR_DEF_PAGE_SIZE)); + BOOST_CHECK_EQUAL(ji.wcache_num_pages(), u_int16_t(JRNL_WMGR_DEF_PAGES)); + BOOST_CHECK_EQUAL(ji.rcache_pgsize_sblks(), u_int32_t(JRNL_RMGR_PAGE_SIZE)); + BOOST_CHECK_EQUAL(ji.rcache_num_pages(), u_int16_t(JRNL_RMGR_PAGES)); + + cout << "done" << endl; +} + +QPID_AUTO_TEST_CASE(set_functions) +{ + string test_name = get_test_name(test_filename, "set_functions"); + const string jid = test_name + "_jid"; + const string base_filename = test_name + "_bfn"; + lfid_pfid_map::create_new_jinf(jid, base_filename, false); + + stringstream fn; + fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION; + jinf ji(fn.str(), false); + + ji.set_jdir("abc123"); + BOOST_CHECK(ji.jdir().compare("abc123") == 0); + ji.set_jdir(test_dir); + BOOST_CHECK(ji.jdir().compare(test_dir) == 0); + ji.incr_num_jfiles(); + BOOST_CHECK_EQUAL(ji.num_jfiles(), u_int16_t(NUM_JFILES+1)); + ji.incr_num_jfiles(); + BOOST_CHECK_EQUAL(ji.num_jfiles(), u_int16_t(NUM_JFILES+2)); + + lfid_pfid_map::clean_journal_info_file(test_dir); + cout << "done" << endl; +} + +QPID_AUTO_TEST_CASE(validate) +{ + string test_name = get_test_name(test_filename, "validate"); + const string jid = test_name + "_jid"; + const string base_filename = test_name + "_bfn"; + lfid_pfid_map::create_new_jinf(jid, base_filename, false); + + stringstream fn; + fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION; + jinf ji(fn.str(), true); + // TODO: Check validation picks up conflict, but need to be friend to jinf to do it + + lfid_pfid_map::clean_journal_info_file(test_dir); + cout << "done" << endl; +} + +QPID_AUTO_TEST_CASE(analyze_empty_journal) +{ + string test_name = get_test_name(test_filename, "analyze_empty_journal"); + const string jid = test_name + "_jid"; + const string base_filename = test_name + "_bfn"; + jdir::create_dir(test_dir); // Check test dir exists; create it if not + + lfid_pfid_map m(jid, base_filename); + m.journal_create(NUM_JFILES, 0, 0); + m.write_journal(false, 0); + + stringstream fn; + fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION; + jinf ji(fn.str(), false); + try { ji.analyze(); } + catch (const jexception& e) + { + if (e.err_code() != jerrno::JERR_JINF_JDATEMPTY) + BOOST_ERROR("Failed to throw expected exception jerrno::JERR_JINF_JDATEMPTY"); + } + + m.destroy_journal(); + cout << "done" << endl; +} + +QPID_AUTO_TEST_CASE(analyze_part_full_journal) +{ + string test_name = get_test_name(test_filename, "analyze_part_full_journal"); + const string jid = test_name + "_jid"; + const string base_filename = test_name + "_bfn"; + lfid_pfid_map m(jid, base_filename); + for (u_int16_t num_files = 1; num_files < NUM_JFILES; num_files++) + { + m.journal_create(NUM_JFILES, num_files, 0); + m.write_journal(false, 0); + + stringstream fn; + fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION; + jinf ji(fn.str(), false); + ji.analyze(); + m.check_analysis(ji); + + m.destroy_journal(); + } + cout << "done" << endl; +} + +QPID_AUTO_TEST_CASE(analyze_full_journal) +{ + string test_name = get_test_name(test_filename, "analyze_full_journal"); + const string jid = test_name + "_jid"; + const string base_filename = test_name + "_bfn"; + lfid_pfid_map m(jid, base_filename); + for (u_int16_t file_num = 0; file_num < NUM_JFILES; file_num++) + { + m.journal_create(NUM_JFILES, NUM_JFILES, file_num); + m.write_journal(false, 0); + + stringstream fn; + fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION; + jinf ji(fn.str(), false); + ji.analyze(); + m.check_analysis(ji); + + m.destroy_journal(); + } + cout << "done" << endl; +} + +QPID_AUTO_TEST_CASE(analyze_single_appended_journal) +{ + string test_name = get_test_name(test_filename, "analyze_single_appended_journal"); + const string jid = test_name + "_jid"; + const string base_filename = test_name + "_bfn"; + lfid_pfid_map m(jid, base_filename); + for (u_int16_t oldest_lid = 0; oldest_lid < NUM_JFILES; oldest_lid++) + for (u_int16_t after_lid = 0; after_lid < NUM_JFILES; after_lid++) + for (u_int16_t num_files = 1; num_files <= 5; num_files++) + { + m.journal_create(NUM_JFILES, NUM_JFILES, oldest_lid); + m.journal_insert(after_lid, num_files); + m.write_journal(true, 16); + + stringstream fn; + fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION; + jinf ji(fn.str(), false); + ji.analyze(); + m.check_analysis(ji); + + m.destroy_journal(); + } + cout << "done" << endl; +} + +QPID_AUTO_TEST_CASE(analyze_multi_appended_journal) +{ + string test_name = get_test_name(test_filename, "analyze_multi_appended_journal"); + const string jid = test_name + "_jid"; + const string base_filename = test_name + "_bfn"; + lfid_pfid_map m(jid, base_filename); + ::srand48(1); + + for (u_int16_t num_appends = 1; num_appends <= 2*NUM_JFILES; num_appends++) + { + const u_int16_t oldest_lid = u_int16_t(NUM_JFILES * ::drand48()); + m.journal_create(NUM_JFILES, NUM_JFILES, oldest_lid); + for (u_int16_t a = 0; a < num_appends; a++) + { + const u_int16_t num_files = u_int16_t(1 + (NUM_JFILES * ::drand48())); + const u_int16_t after_lid = u_int16_t(m.size() * ::drand48()); + m.journal_insert(after_lid, num_files); + } + m.write_journal(true, 24); + + stringstream fn; + fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION; + jinf ji(fn.str(), false); + ji.analyze(); + m.check_analysis(ji); + + m.destroy_journal(); + } + + cout << "done" << endl; +} + +QPID_AUTO_TEST_CASE(analyze_multi_appended_then_failed_journal) +{ + string test_name = get_test_name(test_filename, "analyze_multi_appended_then_failed_journal"); + const string jid = test_name + "_jid"; + const string base_filename = test_name + "_bfn"; + lfid_pfid_map m(jid, base_filename); + ::srand48(1); + + // As this test relies on repeatable but random sequences, use many iterations for coverage + for (int c = 1; c <= 100; c++) + { + for (u_int16_t num_appends = 1; num_appends <= 2*NUM_JFILES; num_appends++) + { + u_int16_t oldest_lid = u_int16_t(NUM_JFILES * ::drand48()); + m.journal_create(NUM_JFILES, NUM_JFILES, oldest_lid); + for (u_int16_t a = 0; a < num_appends-1; a++) + { + const u_int16_t num_files = u_int16_t(1 + (NUM_JFILES * ::drand48())); + const u_int16_t after_lid = u_int16_t(m.size() * ::drand48()); + m.journal_insert(after_lid, num_files); + if (after_lid < oldest_lid) + oldest_lid += num_files; + } + const u_int16_t num_files = u_int16_t(1 + (NUM_JFILES * ::drand48())); + const u_int16_t after_lid = oldest_lid == 0 ? m.size() - 1 : oldest_lid - 1; + m.journal_insert(after_lid, num_files, false); + m.write_journal(true, 32); + + stringstream fn; + fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION; + jinf ji(fn.str(), false); + ji.analyze(); + m.check_analysis(ji); + + m.destroy_journal(); + } + } + + cout << "done" << endl; +} + +QPID_AUTO_TEST_CASE(analyze_inconsistent_jdat_file_size_in_journal) +{ + string test_name = get_test_name(test_filename, "analyze_inconsistent_jdat_file_size_in_journal"); + const string jid = test_name + "_jid"; + const string base_filename = test_name + "_bfn"; + lfid_pfid_map m(jid, base_filename); + ::srand48(1); + + for (u_int16_t pfid = 1; pfid < NUM_JFILES; pfid++) + { + m.journal_create(NUM_JFILES, NUM_JFILES, 0); + m.write_journal(false, 0); + + const std::string filename = m.create_journal_filename(pfid, base_filename); + std::ofstream of(filename.c_str(), ofstream::out | ofstream::app); + if (!of.good()) + BOOST_FAIL("Unable to open test journal file \"" << filename << "\" for writing."); + std::size_t expand_size = std::size_t(10 * JRNL_DBLK_SIZE * JRNL_SBLK_SIZE * ::drand48()); + std::vector<char> sblk_buffer(expand_size, 0); + of.write(&sblk_buffer[0], expand_size); + of.close(); + + stringstream fn; + fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION; + jinf ji(fn.str(), false); + try + { + ji.analyze(); + BOOST_FAIL("Failed to detect irregular journal file size in file \"" << filename << "\""); + } + catch (const jexception& e) {} // ignore - expected + + m.destroy_journal(); + } + cout << "done" << endl; +} + +QPID_AUTO_TEST_CASE(analyze_owi_in_non_ae_journal) +{ + string test_name = get_test_name(test_filename, "analyze_owi_in_non_ae_journal"); + const string jid = test_name + "_jid"; + const string base_filename = test_name + "_bfn"; + lfid_pfid_map m(jid, base_filename); + for (u_int16_t oldest_file = 1; oldest_file < NUM_DEFAULT_JFILES-1; oldest_file++) + { + for (u_int16_t bad_owi_file = oldest_file + 1; bad_owi_file < NUM_DEFAULT_JFILES; bad_owi_file++) + { + m.journal_create(NUM_DEFAULT_JFILES, NUM_DEFAULT_JFILES, oldest_file, bad_owi_file); + m.write_journal(false, 0); + + stringstream fn; + fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION; + jinf ji(fn.str(), false); + try + { + ji.analyze(); + BOOST_FAIL("Failed to detect irregular OWI flag in non-ae journal file \"" << fn << "\""); + } + catch (const jexception& e) {} // ignore - expected + + m.destroy_journal(); + } + } + cout << "done" << endl; +} + +QPID_AUTO_TEST_CASE(analyze_owi_in_ae_min_size_journal) +{ + string test_name = get_test_name(test_filename, "analyze_owi_in_ae_min_size_journal"); + const string jid = test_name + "_jid"; + const string base_filename = test_name + "_bfn"; + lfid_pfid_map m(jid, base_filename); + for (u_int16_t oldest_file = 1; oldest_file < NUM_JFILES-1; oldest_file++) + { + for (u_int16_t bad_owi_file = oldest_file + 1; bad_owi_file < NUM_JFILES; bad_owi_file++) + { + m.journal_create(NUM_JFILES, NUM_JFILES, oldest_file, bad_owi_file); + m.write_journal(true, 16); + + stringstream fn; + fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION; + jinf ji(fn.str(), false); + try + { + ji.analyze(); + BOOST_FAIL("Failed to detect irregular OWI flag in min-sized ae journal file \"" << fn << "\""); + } + catch (const jexception& e) {} // ignore - expected + + m.destroy_journal(); + } + } + cout << "done" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_ut_lpmgr.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_ut_lpmgr.cpp new file mode 100644 index 0000000000..2dc20ffa7c --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/_ut_lpmgr.cpp @@ -0,0 +1,886 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../unit_test.h" +#include <cmath> +#include <iostream> +#include "qpid/legacystore/jrnl/jcntl.h" +#include "qpid/legacystore/jrnl/lpmgr.h" + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace std; + +QPID_AUTO_TEST_SUITE(arr_cnt_suite) + +const string test_filename("_ut_lpmgr"); + +#include "_st_helper_fns.h" + +// === Helper functions and definitions === + +typedef vector<u_int16_t> flist; +typedef flist::const_iterator flist_citr; + +class lpmgr_test_helper +{ + lpmgr_test_helper() {} + virtual ~lpmgr_test_helper() {} + +public: + static void check_pfids_lfids(const lpmgr& lm, const u_int16_t pfids[], const u_int16_t lfids[], + const size_t pfid_lfid_size) + { + vector<u_int16_t> res; + lm.get_pfid_list(res); + vectors_equal(lm, pfids, pfid_lfid_size, res, true); + lm.get_lfid_list(res); + vectors_equal(lm, lfids, pfid_lfid_size, res, false); + } + + static void check_pfids_lfids(const lpmgr& lm, const flist& pfids, const flist lfids) + { + vector<u_int16_t> res; + lm.get_pfid_list(res); + vectors_equal(lm, pfids, res, true); + lm.get_lfid_list(res); + vectors_equal(lm, lfids, res, false); + } + + static void check_linear_pfids_lfids(const lpmgr& lm, const size_t pfid_lfid_size) + { + vector<u_int16_t> res; + lm.get_pfid_list(res); + linear_vectors_equal(lm, pfid_lfid_size, res, true); + lm.get_lfid_list(res); + linear_vectors_equal(lm, pfid_lfid_size, res, false); + } + + static void rcvdat_init(rcvdat& rd, const u_int16_t num_jfiles, const bool ae, const u_int16_t ae_max_jfiles, + const u_int16_t pfids[]) + { + rd.reset(num_jfiles, ae, ae_max_jfiles); + load_vector(pfids, num_jfiles, rd._fid_list); + rd._jempty = false; + rd._lfid = pfids[num_jfiles - 1]; + rd._eo = 100 * JRNL_DBLK_SIZE * JRNL_SBLK_SIZE; + } + + static void rcvdat_init(rcvdat& rd, const flist& pfidl, const bool ae, const u_int16_t ae_max_jfiles) + { + const u_int16_t num_jfiles = pfidl.size(); + rd.reset(num_jfiles, ae, ae_max_jfiles); + load_vector(pfidl, rd._fid_list); + rd._jempty = false; + rd._lfid = pfidl[num_jfiles - 1]; + rd._eo = 100 * JRNL_DBLK_SIZE * JRNL_SBLK_SIZE; + } + + static void initialize(lpmgr& lm, test_jrnl& jc, const u_int16_t num_jfiles, const bool ae, + const u_int16_t ae_max_jfiles) + { + lm.initialize(num_jfiles, ae, ae_max_jfiles, &jc, &jc.new_fcntl); + BOOST_CHECK_EQUAL(lm.is_init(), true); + BOOST_CHECK_EQUAL(lm.is_ae(), ae); + BOOST_CHECK_EQUAL(lm.ae_max_jfiles(), ae_max_jfiles); + BOOST_CHECK_EQUAL(lm.num_jfiles(), num_jfiles); + if (num_jfiles) + check_linear_pfids_lfids(lm, num_jfiles); + else + BOOST_CHECK_EQUAL(lm.get_fcntlp(0), (void*)0); + } + + // version which sets up the lfid_pfid_map for later manipulation by insert tests + static void initialize(lfid_pfid_map& lfm, lpmgr& lm, test_jrnl& jc, const u_int16_t num_jfiles, const bool ae, + const u_int16_t ae_max_jfiles) + { + lfm.journal_create(num_jfiles, num_jfiles); + initialize(lm, jc, num_jfiles, ae, ae_max_jfiles); + } + + static void prepare_recover(lfid_pfid_map& lfm, const u_int16_t size) + { + if (size < 4) BOOST_FAIL("prepare_recover(): size parameter (" << size << ") too small."); + lfm.journal_create(4, 4); // initial journal of size 4 + u_int16_t s = 4; // cumulative size + while (s < size) + { + const u_int16_t ins_posn = u_int16_t(s * ::drand48()); // this insert posn + if (3.0 * ::drand48() > 1.0 || size - s < 2) // 2:1 chance of single insert when >= 2 still to insert + { + lfm.journal_insert(ins_posn); // single insert + s++; + } + else + { + // multiple insert, either 2 - 5 + const u_int16_t max_ins_size = size - s >5 ? 5 : size - s; + const u_int16_t ins_size = 2 + u_int16_t((max_ins_size - 2) * ::drand48()); // this insert size + lfm.journal_insert(ins_posn, ins_size); + s += ins_size; + } + } + } + + static void recover(lfid_pfid_map& lfm, lpmgr& lm, test_jrnl& jc, const bool ae, const u_int16_t ae_max_jfiles) + { + flist pfidl; + flist lfidl; + rcvdat rd; + const u_int16_t num_jfiles = lfm.size(); + + lfm.get_pfid_list(pfidl); + lfm.get_lfid_list(lfidl); + lm.finalize(); // clear all file handles before erasing old journal files + lfm.write_journal(ae, ae_max_jfiles, JFSIZE_SBLKS); + + lpmgr_test_helper::rcvdat_init(rd, pfidl, ae, ae_max_jfiles); + lm.recover(rd, &jc, &jc.new_fcntl); + BOOST_CHECK_EQUAL(lm.is_init(), true); + BOOST_CHECK_EQUAL(lm.is_ae(), ae); + BOOST_CHECK_EQUAL(lm.ae_max_jfiles(), ae_max_jfiles); + BOOST_CHECK_EQUAL(lm.num_jfiles(), num_jfiles); + if (num_jfiles) + check_pfids_lfids(lm, pfidl, lfidl); + else + BOOST_CHECK_EQUAL(lm.get_fcntlp(0), (void*)0); + } + + static void finalize(lpmgr& lm) + { + lm.finalize(); + BOOST_CHECK_EQUAL(lm.is_init(), false); + BOOST_CHECK_EQUAL(lm.is_ae(), false); + BOOST_CHECK_EQUAL(lm.ae_max_jfiles(), u_int16_t(0)); + BOOST_CHECK_EQUAL(lm.num_jfiles(), u_int16_t(0)); + BOOST_CHECK_EQUAL(lm.get_fcntlp(0), (void*)0); + vector<u_int16_t> res; + lm.get_pfid_list(res); + BOOST_CHECK_EQUAL(res.size(), u_int16_t(0)); + lm.get_lfid_list(res); + BOOST_CHECK_EQUAL(res.size(), u_int16_t(0)); + } + + static void insert(lfid_pfid_map& lfm, lpmgr& lm, test_jrnl& jc, const u_int16_t after_lfid, const u_int16_t incr = 1) + { + flist pfidl; + flist lfidl; + const u_int16_t num_jfiles = lm.num_jfiles(); + lfm.journal_insert(after_lfid, incr); + lfm.get_pfid_list(pfidl); + lfm.get_lfid_list(lfidl); + lm.insert(after_lfid, &jc, &jc.new_fcntl, incr); + BOOST_CHECK_EQUAL(lm.num_jfiles(), num_jfiles + incr); + lpmgr_test_helper::check_pfids_lfids(lm, pfidl, lfidl); + } + + static void check_ae_max_jfiles(lpmgr& lm, const u_int16_t num_jfiles, const u_int16_t ae_max_jfiles) + { + bool legal = ae_max_jfiles > num_jfiles || ae_max_jfiles == 0; + + lm.set_ae(false); + BOOST_CHECK(!lm.is_ae()); + if (legal) + { + lm.set_ae_max_jfiles(ae_max_jfiles); + BOOST_CHECK_EQUAL(lm.ae_max_jfiles(), ae_max_jfiles); + lm.set_ae(true); + BOOST_CHECK(lm.is_ae()); + BOOST_CHECK_EQUAL(lm.ae_jfiles_rem(), ae_max_jfiles + ? ae_max_jfiles - num_jfiles + : JRNL_MAX_NUM_FILES - num_jfiles); + } + else + { + lm.set_ae_max_jfiles(ae_max_jfiles); + BOOST_CHECK_EQUAL(lm.ae_max_jfiles(), ae_max_jfiles); + try + { + lm.set_ae(true); // should raise exception + BOOST_ERROR("Auto-expand enabled with out-of-range ae_max_jfiles"); + } + catch (const jexception& e) { BOOST_CHECK_EQUAL(e.err_code(), jerrno::JERR_LFMGR_BADAEFNUMLIM); } + BOOST_CHECK(!lm.is_ae()); + BOOST_CHECK_EQUAL(lm.ae_jfiles_rem(), 0); + } + BOOST_CHECK_EQUAL(lm.ae_max_jfiles(), ae_max_jfiles); + } + + static void check_multiple_initialization_recover(lfid_pfid_map& lfm, test_jrnl& jc, + const u_int16_t num_jfiles_arr[][2], const bool init_flag_0, const bool finalize_flag, + const bool init_flag_1) + { + unsigned i_njf = 0; + while (num_jfiles_arr[i_njf][0] && num_jfiles_arr[i_njf][1]) // cycle through each entry in num_jfiles_arr + { + for (unsigned i1_njf = 0; i1_njf <= 1; i1_njf++) // cycle through the two numbers in each entry of num_jfiles_arr + { + const u_int16_t num_jfiles_0 = num_jfiles_arr[i_njf][i1_njf == 0]; // first number in pair + const u_int16_t num_jfiles_1 = num_jfiles_arr[i_njf][i1_njf != 0]; // second number in pair + + for (unsigned i_ae = 0; i_ae < 4; i_ae++) // cycle through combinations of enabling AE + { + const bool ae_0 = i_ae & 0x1; // first bit: enable AE on first init + const bool ae_1 = i_ae & 0x2; // second bit: enable AE on second init + for (unsigned i_aemjf = 0; i_aemjf < 4; i_aemjf++) // cycle through combinations of enabling/disabling ae limit + { + const u_int16_t ae_max_jfiles_0 = i_aemjf & 0x1 ? 3 * num_jfiles_0 : 0; // max ae files, 0 = disable max + const u_int16_t ae_max_jfiles_1 = i_aemjf & 0x2 ? 4 * num_jfiles_1 : 0; // max ae files, 0 = disable max + + lpmgr lm; // DUT + + if (init_flag_0) + initialize(lm, jc, num_jfiles_0, ae_0, ae_max_jfiles_0); + else + { + prepare_recover(lfm, num_jfiles_0); + recover(lfm, lm, jc, ae_1, ae_max_jfiles_0); + lfm.destroy_journal(); + } + + if (finalize_flag) finalize(lm); + + if (init_flag_1) + initialize(lm, jc, num_jfiles_1, ae_1, ae_max_jfiles_1); + else + { + prepare_recover(lfm, num_jfiles_1); + recover(lfm, lm, jc, ae_1, ae_max_jfiles_1); + lfm.destroy_journal(); + } + } + } + } + i_njf++; + } + } + + static void check_insert(lfid_pfid_map& lfm, lpmgr& lm, test_jrnl& jc, const u_int16_t after_lfid, + const u_int16_t incr = 1) + { + const u_int16_t num_jfiles = lm.num_jfiles(); + const u_int16_t ae_max_jfiles = lm.ae_max_jfiles(); + const u_int16_t effective_ae_max_jfiles = ae_max_jfiles ? ae_max_jfiles : JRNL_MAX_NUM_FILES; + BOOST_CHECK_EQUAL(lm.ae_jfiles_rem(), effective_ae_max_jfiles - num_jfiles); + bool legal = lm.is_ae() && num_jfiles + incr <= effective_ae_max_jfiles; + if (legal) + { + insert(lfm, lm, jc, after_lfid, incr); + BOOST_CHECK_EQUAL(lm.num_jfiles(), num_jfiles + incr); + BOOST_CHECK_EQUAL(lm.ae_jfiles_rem(), effective_ae_max_jfiles - num_jfiles - incr); + } + else + { + try + { + insert(lfm, lm, jc, after_lfid, incr); + if (lm.is_ae()) + BOOST_ERROR("lpmgr::insert() succeeded and exceeded limit"); + else + BOOST_ERROR("lpmgr::insert() succeeded with auto-expand disabled"); + } + catch (const jexception& e) + { + if (lm.is_ae()) + BOOST_CHECK_EQUAL(e.err_code(), jerrno::JERR_LFMGR_AEFNUMLIMIT); + else + BOOST_CHECK_EQUAL(e.err_code(), jerrno::JERR_LFMGR_AEDISABLED); + } + BOOST_CHECK_EQUAL(lm.num_jfiles(), num_jfiles); + BOOST_CHECK_EQUAL(lm.ae_jfiles_rem(), effective_ae_max_jfiles - num_jfiles); + } + } + + static void check_limit(lfid_pfid_map& lfm, test_jrnl& jc, const bool ae, const u_int16_t num_jfiles, + const u_int16_t ae_max_jfiles) + { + lpmgr lm; + + for (unsigned i = 0; i < 2; i++) + { + if (i) + initialize(lfm, lm, jc, num_jfiles, ae, ae_max_jfiles); + else + { + prepare_recover(lfm, num_jfiles); + recover(lfm, lm, jc, ae, ae_max_jfiles); + } + + // use up all available files + unsigned j = ae_max_jfiles ? ae_max_jfiles : JRNL_MAX_NUM_FILES; + while (ae && j > num_jfiles) + { + const u_int16_t posn = static_cast<u_int16_t>((lm.num_jfiles() - 1) * ::drand48()); + const u_int16_t incr = 1 + static_cast<u_int16_t>((lm.ae_jfiles_rem() > 4 + ? 3 : lm.ae_jfiles_rem() - 1) * ::drand48()); + check_insert(lfm, lm, jc, posn, incr); + j -= incr; + } + // these should be over the limit or illegal + check_insert(lfm, lm, jc, 0); + check_insert(lfm, lm, jc, 2, 2); + lfm.destroy_journal(); + } + } + +private: + static void load_vector(const u_int16_t a[], const size_t n, flist& v) + { + for (size_t i = 0; i < n; i++) + v.push_back(a[i]); + } + + static void load_vector(const flist& a, flist& b) + { + for (flist_citr i = a.begin(); i < a.end(); i++) + b.push_back(*i); + } + + static void vectors_equal(const lpmgr& lm, const u_int16_t a[], const size_t n, const flist& b, + const bool pfid_check) + { + BOOST_CHECK_EQUAL(n, b.size()); + for (size_t i = 0; i < n; i++) + { + BOOST_CHECK_EQUAL(a[i], b[i]); + fcntl* fp = lm.get_fcntlp(i); + BOOST_CHECK_MESSAGE(fp != (void*)0, "Unexpected void pointer returned by lpmgr::get_fcntlp()"); + if (fp) BOOST_CHECK_EQUAL(pfid_check ? fp->pfid() : fp->lfid(), pfid_check ? a[i] : i); + } + } + + static void vectors_equal(const lpmgr& lm, const flist& a, const flist& b, const bool pfid_check) + { + BOOST_CHECK_EQUAL(a.size(), b.size()); + for (size_t i = 0; i < a.size(); i++) + { + BOOST_CHECK_EQUAL(a[i], b[i]); + fcntl* fp = lm.get_fcntlp(i); + BOOST_CHECK_MESSAGE(fp != (void*)0, "Unexpected void pointer returned by lpmgr::get_fcntlp()"); + if (fp) BOOST_CHECK_EQUAL(pfid_check ? fp->pfid() : fp->lfid(), pfid_check ? a[i] : i); + } + } + + static void linear_vectors_equal(const lpmgr& lm, const size_t n, const flist& f, const bool pfid_check) + { + BOOST_CHECK_EQUAL(n, f.size()); + for (size_t i = 0; i < n; i++) + { + BOOST_CHECK_EQUAL(i, f[i]); + fcntl* fp = lm.get_fcntlp(i); + BOOST_CHECK_MESSAGE(fp != (void*)0, "Unexpected void pointer returned by lpmgr::get_fcntlp()"); + if (fp) BOOST_CHECK_EQUAL(pfid_check ? fp->pfid() : fp->lfid(), i); + } + } +}; + +// === Tests === + +#ifndef LONG_TEST +/* + * ============================================== + * NORMAL TESTS + * This section contains normal "make check" tests + * for building/packaging. These are built when + * LONG_TEST is _not_ defined. + * ============================================== + */ + +/* + * Check that after construction, the fcntl array _fcntl_arr is empty and the is_init() function returns false. + */ +QPID_AUTO_TEST_CASE(default_constructor) +{ + string test_name = get_test_name(test_filename, "default_constructor"); + try + { + lpmgr lm; + BOOST_CHECK_EQUAL(lm.is_init(), false); + BOOST_CHECK_EQUAL(lm.is_ae(), false); + BOOST_CHECK_EQUAL(lm.ae_max_jfiles(), u_int16_t(0)); + BOOST_CHECK_EQUAL(lm.num_jfiles(), u_int16_t(0)); + BOOST_CHECK_EQUAL(lm.get_fcntlp(0), (void*)0); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "done" << endl; +} + +/* + * Check that initialize() correctly creates an ordered fcntl array _fcntl_arr. + */ +QPID_AUTO_TEST_CASE(initialize) +{ + string test_name = get_test_name(test_filename, "initialize"); + const u_int16_t num_jfiles = 8; + try + { + jdir::create_dir(test_dir); // Check test dir exists; create it if not + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + { + lpmgr lm; + lpmgr_test_helper::initialize(lm, jc, num_jfiles, false, 0); + } + { + lpmgr lm; + lpmgr_test_helper::initialize(lm, jc, num_jfiles, true, 0); + } + { + lpmgr lm; + lpmgr_test_helper::initialize(lm, jc, num_jfiles, true, 5 * num_jfiles); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "done" << endl; +} + +/* + * Check that recover() correctly sets up the specified pfid list order. + */ +QPID_AUTO_TEST_CASE(recover) +{ + string test_name = get_test_name(test_filename, "recover"); + ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover() + try + { + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + lfid_pfid_map lfm(test_name, test_name); + + { + lpmgr lm; + lpmgr_test_helper::prepare_recover(lfm, 8); + lpmgr_test_helper::recover(lfm, lm, jc, false, 0); + lfm.destroy_journal(); + } + { + lpmgr lm; + lpmgr_test_helper::prepare_recover(lfm, 8); + lpmgr_test_helper::recover(lfm, lm, jc, true, 0); + lfm.destroy_journal(); + } + { + lpmgr lm; + lpmgr_test_helper::prepare_recover(lfm, 8); + lpmgr_test_helper::recover(lfm, lm, jc, true, 5 * lfm.size()); + lfm.destroy_journal(); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "done" << endl; +} + +/* + * Check that finalize() after an initialize() empties _fcntl_arr and that afterwards is_init() returns false. + */ +QPID_AUTO_TEST_CASE(initialize_finalize) +{ + string test_name = get_test_name(test_filename, "initialize_finalize"); + const u_int16_t num_jfiles = 8; + try + { + jdir::create_dir(test_dir); // Check test dir exists; create it if not + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + { + lpmgr lm; + lpmgr_test_helper::initialize(lm, jc, num_jfiles, false, 0); + lpmgr_test_helper::finalize(lm); + } + { + lpmgr lm; + lpmgr_test_helper::initialize(lm, jc, num_jfiles, true, 0); + lpmgr_test_helper::finalize(lm); + } + { + lpmgr lm; + lpmgr_test_helper::initialize(lm, jc, num_jfiles, true, 5 * num_jfiles); + lpmgr_test_helper::finalize(lm); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "done" << endl; +} + +/* + * Check that finalize() after a recover() empties _fcntl_arr and that afterwards is_init() returns false. + */ +QPID_AUTO_TEST_CASE(recover_finalize) +{ + string test_name = get_test_name(test_filename, "recover_finalize"); + const u_int16_t num_jfiles = 8; + ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover() + try + { + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + lfid_pfid_map lfm(test_name, test_name); + + { + lpmgr lm; + lpmgr_test_helper::prepare_recover(lfm, num_jfiles); + lpmgr_test_helper::recover(lfm, lm, jc, false, 0); + lpmgr_test_helper::finalize(lm); + lfm.destroy_journal(); + } + { + lpmgr lm; + lpmgr_test_helper::prepare_recover(lfm, num_jfiles); + lpmgr_test_helper::recover(lfm, lm, jc, true, 0); + lpmgr_test_helper::finalize(lm); + lfm.destroy_journal(); + } + { + lpmgr lm; + lpmgr_test_helper::prepare_recover(lfm, num_jfiles); + lpmgr_test_helper::recover(lfm, lm, jc, true, 5 * lfm.size()); + lpmgr_test_helper::finalize(lm); + lfm.destroy_journal(); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "done" << endl; +} + +/* + * Check that 0 and/or null and other extreme/boundary parameters behave as expected. + */ +QPID_AUTO_TEST_CASE(zero_null_params) +{ + string test_name = get_test_name(test_filename, "zero_null_params"); + const u_int16_t num_jfiles = 8; + try + { + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + lfid_pfid_map lfm(test_name, test_name); + lpmgr lm; + lpmgr_test_helper::initialize(lfm, lm, jc, num_jfiles, true, 0); + + // Check that inserting 0 files works ok + lpmgr_test_helper::insert(lfm, lm, jc, 0, 0); + lpmgr_test_helper::insert(lfm, lm, jc, 2, 0); + lpmgr_test_helper::insert(lfm, lm, jc, num_jfiles - 1, 0); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "done" << endl; +} + +/* + * Check that initialize()/recover() works correctly after a previous initialize()/recover() with/without an intervening + * finalize(). + */ +QPID_AUTO_TEST_CASE(multiple_initialization_recover) +{ + string test_name = get_test_name(test_filename, "multiple_initialization_recover"); + ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover() + + // Set combinations of value pairs to be used for number of journal files in first and second init + u_int16_t num_jfiles_arr[][2] = {{8, 12}, {4, 7}, {0, 0}}; // end with zeros + try + { + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + lfid_pfid_map lfm(test_name, test_name); + for (unsigned p = 0; p < 8; p++) + { + const bool i_0 = p & 0x01; // first bit + const bool i_1 = p & 0x02; // second bit + const bool f = p & 0x04; // third bit + lpmgr_test_helper::check_multiple_initialization_recover(lfm, jc, num_jfiles_arr, i_0, f, i_1); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "done" << endl; +} + +/* + * Check that insert() works correctly after initialize() and shifts the pfid sequence beyond the insert point correctly: + * + * The following sequence is tested: + * initialize 4 pfids=[0,1,2,3] lfids=[0,1,2,3] + * insert 1 after lfid 0 pfids=[0,4,1,2,3] lfids=[0,2,3,4,1] + * insert 2 after lfid 2 pfids=[0,4,1,5,6,2,3] lfids=[0,2,5,6,1,3,4] + * insert 1 after lfid 6 pfids=[0,4,1,5,6,2,3,7] lfids=[0,2,5,6,1,3,4,7] + * issert 1 after lfid 3 pfids=[0,4,1,5,8,6,2,3,7] lfids=[0,2,6,7,1,3,5,8,4] + */ +QPID_AUTO_TEST_CASE(initialize_insert) +{ + string test_name = get_test_name(test_filename, "initialize_insert"); + const u_int16_t initial_num_jfiles = 8; + try + { + jdir::create_dir(test_dir); // Check test dir exists; create it if not + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + lfid_pfid_map lfm(test_name, test_name); + lpmgr lm; + lpmgr_test_helper::initialize(lfm, lm, jc, initial_num_jfiles, true, 0); + + lpmgr_test_helper::insert(lfm, lm, jc, 0); + lpmgr_test_helper::insert(lfm, lm, jc, 2, 2); + lpmgr_test_helper::insert(lfm, lm, jc, 6); + lpmgr_test_helper::insert(lfm, lm, jc, 3); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "done" << endl; +} + +/* + * Check that insert() works correctly after recover() and shifts the pfid sequence beyond the insert point correctly: + * + * The following sequence is tested: + * recover 4 pfids=[0,2,3,1] lfids=[0,3,1,2] + * insert 1 after lfid 0 pfids=[0,4,2,3,1] lfids=[0,4,2,3,1] + * insert 2 after lfid 2 pfids=[0,4,2,5,6,3,1] lfids=[0,6,2,5,1,3,4] + * insert 1 after lfid 6 pfids=[0,4,2,5,6,3,1,7] lfids=[0,6,2,5,1,3,4,7] + * issert 1 after lfid 3 pfids=[0,4,2,5,8,6,3,1,7] lfids=[0,7,2,6,1,3,5,8,4] + */ +QPID_AUTO_TEST_CASE(recover_insert) +{ + string test_name = get_test_name(test_filename, "recover_insert"); + const u_int16_t initial_num_jfiles = 4; + ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover() + try + { + jdir::create_dir(test_dir); // Check test dir exists; create it if not + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + lfid_pfid_map lfm(test_name, test_name); + lpmgr lm; + lpmgr_test_helper::prepare_recover(lfm, initial_num_jfiles); + lpmgr_test_helper::recover(lfm, lm, jc, true, 0); + + lpmgr_test_helper::insert(lfm, lm, jc, 0); + lpmgr_test_helper::insert(lfm, lm, jc, 2, 2); + lpmgr_test_helper::insert(lfm, lm, jc, 6); + lpmgr_test_helper::insert(lfm, lm, jc, 3); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "done" << endl; +} + +/* + * Check that illegal ae parameter combinations are caught and result in an exception being thrown. + */ +QPID_AUTO_TEST_CASE(ae_parameters) +{ + string test_name = get_test_name(test_filename, "ae_parameters"); + ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover() + try + { + jdir::create_dir(test_dir); // Check test dir exists; create it if not + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + lfid_pfid_map lfm(test_name, test_name); + const u_int16_t num_jfiles = 8; + lpmgr lm; + + for (unsigned i = 0; i < 2; i++) + { + if (i) + lpmgr_test_helper::initialize(lfm, lm, jc, num_jfiles, false, 0); + else + { + lpmgr_test_helper::prepare_recover(lfm, num_jfiles); + lpmgr_test_helper::recover(lfm, lm, jc, false, 0); + } + + lpmgr_test_helper::check_ae_max_jfiles(lm, num_jfiles, num_jfiles - 2); + lpmgr_test_helper::check_ae_max_jfiles(lm, num_jfiles, 0); + lpmgr_test_helper::check_ae_max_jfiles(lm, num_jfiles, 2 * num_jfiles); + lpmgr_test_helper::check_ae_max_jfiles(lm, num_jfiles, num_jfiles); + lfm.destroy_journal(); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "done" << endl; +} + +/* + * Check that initialized or recovered journals with auto-expand disabled will not allow either inserts or appends. + */ +QPID_AUTO_TEST_CASE(ae_disabled) +{ + string test_name = get_test_name(test_filename, "ae_disabled"); + ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover() + try + { + jdir::create_dir(test_dir); // Check test dir exists; create it if not + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + lfid_pfid_map lfm(test_name, test_name); + lpmgr_test_helper::check_limit(lfm, jc, false, 8, 0); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "done" << endl; +} + +/* + * Check that initialized or recovered journals with auto-expand enabled and a file limit set will enforce the correct + * limits on inserts and appends. + */ +QPID_AUTO_TEST_CASE(ae_enabled_limit) +{ + string test_name = get_test_name(test_filename, "ae_enabled_limit"); + ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover() + try + { + jdir::create_dir(test_dir); // Check test dir exists; create it if not + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + lfid_pfid_map lfm(test_name, test_name); + lpmgr_test_helper::check_limit(lfm, jc, true, 8, 32); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "done" << endl; +} + +/* + * Check that initialized or recovered journals with auto-expand enabled and no file limit set (0) will allow inserts and + * appends up to the file limit JRNL_MAX_NUM_FILES. + */ +QPID_AUTO_TEST_CASE(ae_enabled_unlimited) +{ + string test_name = get_test_name(test_filename, "ae_enabled_unlimited"); + ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover() + try + { + jdir::create_dir(test_dir); // Check test dir exists; create it if not + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + lfid_pfid_map lfm(test_name, test_name); + lpmgr_test_helper::check_limit(lfm, jc, true, 8, 0); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "done" << endl; +} + +#else +/* + * ============================================== + * LONG TESTS + * This section contains long tests and soak tests, + * and are run using target check-long (ie "make + * check-long"). These are built when LONG_TEST is + * defined. + * ============================================== + */ + +/* + * Tests randomized combinations of initialization/recovery, initial size, number, size and location of inserts. + * + * To reproduce a specific test, comment out the get_seed() statement and uncomment the literal below, adjusting the seed + * value to that required. + */ +QPID_AUTO_TEST_CASE(randomized_tests) +{ + string test_name = get_test_name(test_filename, "randomized_tests"); + const long seed = get_seed(); + // const long seed = 0x2d9b69d32; + cout << "seed=0x" << hex << seed << dec << " " << flush; + ::srand48(seed); + + lfid_pfid_map lfm(test_name, test_name); + flist pfidl; + flist lfidl; + rcvdat rd; + u_int16_t curr_ae_max_jfiles = 0; + jdir::create_dir(test_dir); // Check test dir exists; create it if not + + for (int test_num = 0; test_num < 250; test_num++) + { + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + lpmgr lm; + // 50% chance of recovery except first run and if there is still ae space left + const bool recover_flag = test_num > 0 && + curr_ae_max_jfiles > lfm.size() && + 2.0 * ::drand48() < 1.0; + if (recover_flag) + { + // Recover from previous iteration + lfm.get_pfid_list(pfidl); + lfm.get_lfid_list(lfidl); + lfm.write_journal(true, curr_ae_max_jfiles, JFSIZE_SBLKS); + lpmgr_test_helper::rcvdat_init(rd, pfidl, true, curr_ae_max_jfiles); + lm.recover(rd, &jc, &jc.new_fcntl); + lpmgr_test_helper::check_pfids_lfids(lm, pfidl, lfidl); + } + else + { + // Initialize from scratch + const u_int16_t num_jfiles = 4 + u_int16_t(21.0 * ::drand48()); // size: 4 - 25 files + curr_ae_max_jfiles = u_int16_t(4 * num_jfiles * ::drand48()); // size: 0 - 100 files + if (curr_ae_max_jfiles > JRNL_MAX_NUM_FILES) curr_ae_max_jfiles = JRNL_MAX_NUM_FILES; + else if (curr_ae_max_jfiles <= num_jfiles) curr_ae_max_jfiles = 0; + lfm.destroy_journal(); + lfm.journal_create(num_jfiles, num_jfiles); + lfm.get_pfid_list(pfidl); + lfm.get_lfid_list(lfidl); + lm.initialize(num_jfiles, true, curr_ae_max_jfiles, &jc, &jc.new_fcntl); + lpmgr_test_helper::check_linear_pfids_lfids(lm, num_jfiles); + } + + // Loop to insert pfids + const int num_inserts = 1 + int(lfm.size() * ::drand48()); + for (int i = 0; i < num_inserts; i++) + { + const u_int16_t size = lm.num_jfiles(); + const u_int16_t after_lfid = u_int16_t(1.0 * size * ::drand48()); + const u_int16_t num_jfiles = 1 + u_int16_t(4.0 * ::drand48()); + const bool legal = lm.ae_max_jfiles() + ? size + num_jfiles <= lm.ae_max_jfiles() + : size + num_jfiles <= JRNL_MAX_NUM_FILES; + if (legal) + { + lfm.journal_insert(after_lfid, num_jfiles); + lfm.get_pfid_list(pfidl); + lfm.get_lfid_list(lfidl); + + lm.insert(after_lfid, &jc, &jc.new_fcntl, num_jfiles); + lpmgr_test_helper::check_pfids_lfids(lm, pfidl, lfidl); + } + else + { + try + { + lm.insert(after_lfid, &jc, &jc.new_fcntl, num_jfiles); + BOOST_FAIL("lpmgr::insert() succeeded and exceeded limit"); + } + catch (const jexception& e) + { + BOOST_CHECK_EQUAL(e.err_code(), jerrno::JERR_LFMGR_AEFNUMLIMIT); + break; // no more inserts... + } + } + } + lm.finalize(); + BOOST_CHECK_EQUAL(lm.is_init(), false); + BOOST_CHECK_EQUAL(lm.num_jfiles(), u_int16_t(0)); + BOOST_CHECK_EQUAL(lm.get_fcntlp(0), (void*)0); + } + cout << "done" << endl; +} + +#endif + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_ut_rec_hdr.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_ut_rec_hdr.cpp new file mode 100644 index 0000000000..099e576bbd --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/_ut_rec_hdr.cpp @@ -0,0 +1,438 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../unit_test.h" + +#include <ctime> +#include <iostream> +#include "qpid/legacystore/jrnl/deq_hdr.h" +#include "qpid/legacystore/jrnl/enq_hdr.h" +#include "qpid/legacystore/jrnl/file_hdr.h" +#include "qpid/legacystore/jrnl/jcfg.h" +#include "qpid/legacystore/jrnl/rec_tail.h" +#include "qpid/legacystore/jrnl/txn_hdr.h" + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace std; + +QPID_AUTO_TEST_SUITE(rec_hdr_suite) + +const string test_filename("_ut_rec_hdr"); + +QPID_AUTO_TEST_CASE(hdr_class) +{ + cout << test_filename << ".hdr_class: " << flush; + rec_hdr h1; + BOOST_CHECK_EQUAL(h1._magic, 0UL); + BOOST_CHECK_EQUAL(h1._version, 0); + BOOST_CHECK_EQUAL(h1._eflag, 0); + BOOST_CHECK_EQUAL(h1._uflag, 0); + BOOST_CHECK_EQUAL(h1._rid, 0ULL); + BOOST_CHECK(!h1.get_owi()); + + const u_int32_t magic = 0x89abcdefUL; + const u_int16_t uflag = 0x5537; + const u_int8_t version = 0xef; + const u_int64_t rid = 0x123456789abcdef0ULL; + const bool owi = true; + + rec_hdr h2(magic, version, rid, owi); + BOOST_CHECK_EQUAL(h2._magic, magic); + BOOST_CHECK_EQUAL(h2._version, version); +#ifdef JRNL_LITTLE_ENDIAN + BOOST_CHECK_EQUAL(h2._eflag, RHM_LENDIAN_FLAG); +#else + BOOST_CHECK_EQUAL(h2._eflag, RHM_BENDIAN_FLAG); +#endif + BOOST_CHECK_EQUAL(h2._uflag, (const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK); + BOOST_CHECK_EQUAL(h2._rid, rid); + BOOST_CHECK_EQUAL(h2.get_owi(), owi); + h2._uflag = uflag; + BOOST_CHECK(h2.get_owi()); + h2.set_owi(true); + BOOST_CHECK(h2.get_owi()); + BOOST_CHECK_EQUAL(h2._uflag, uflag); + h2.set_owi(false); + BOOST_CHECK(!h2.get_owi()); + BOOST_CHECK_EQUAL(h2._uflag, (uflag & ~(const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK)); + h2.set_owi(true); + BOOST_CHECK(h2.get_owi()); + BOOST_CHECK_EQUAL(h2._uflag, uflag); + + h1.hdr_copy(h2); + BOOST_CHECK_EQUAL(h1._magic, magic); + BOOST_CHECK_EQUAL(h1._version, version); +#ifdef JRNL_LITTLE_ENDIAN + BOOST_CHECK_EQUAL(h1._eflag, RHM_LENDIAN_FLAG); +#else + BOOST_CHECK_EQUAL(h1._eflag, RHM_BENDIAN_FLAG); +#endif + BOOST_CHECK_EQUAL(h1._uflag, uflag); + BOOST_CHECK_EQUAL(h1._rid, rid); + BOOST_CHECK(h1.get_owi()); + BOOST_CHECK_EQUAL(h1._uflag, uflag); + + h1.reset(); + BOOST_CHECK_EQUAL(h1._magic, 0UL); + BOOST_CHECK_EQUAL(h1._version, 0); + BOOST_CHECK_EQUAL(h1._eflag, 0); + BOOST_CHECK_EQUAL(h1._uflag, 0); + BOOST_CHECK_EQUAL(h1._rid, 0ULL); + BOOST_CHECK(!h1.get_owi()); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(rec_tail_class) +{ + cout << test_filename << ".rec_tail_class: " << flush; + const u_int32_t magic = 0xfedcba98; + const u_int64_t rid = 0xfedcba9876543210ULL; + const u_int32_t xmagic = ~magic; + + { + rec_tail rt1; + BOOST_CHECK_EQUAL(rt1._xmagic, 0xffffffffUL); + BOOST_CHECK_EQUAL(rt1._rid, 0ULL); + } + + { + rec_tail rt2(magic, rid); + BOOST_CHECK_EQUAL(rt2._xmagic, magic); + BOOST_CHECK_EQUAL(rt2._rid, rid); + } + + { + rec_hdr h(magic, RHM_JDAT_VERSION, rid, true); + rec_tail rt3(h); + BOOST_CHECK_EQUAL(rt3._xmagic, xmagic); + BOOST_CHECK_EQUAL(rt3._rid, rid); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(file_hdr_class) +{ + cout << test_filename << ".file_hdr_class: " << flush; + const u_int32_t magic = 0xfedcba98UL; + const u_int8_t version = 0xa5; + const u_int16_t uflag = 0x5537; + const u_int64_t rid = 0xfedcba9876543210ULL; + const u_int16_t pfid = 0xfedcU; + const u_int16_t lfid = 0xf0e1U; +#ifdef JRNL_32_BIT + const std::size_t fro = 0xfedcba98UL; +#else + const std::size_t fro = 0xfedcba9876543210ULL; +#endif + timespec ts; + const bool owi = true; + + { + file_hdr fh1; + BOOST_CHECK_EQUAL(fh1._magic, 0UL); + BOOST_CHECK_EQUAL(fh1._version, 0); + BOOST_CHECK_EQUAL(fh1._eflag, 0); + BOOST_CHECK_EQUAL(fh1._uflag, 0); + BOOST_CHECK_EQUAL(fh1._rid, 0ULL); + BOOST_CHECK_EQUAL(fh1._pfid, 0UL); + BOOST_CHECK_EQUAL(fh1._lfid, 0U); + BOOST_CHECK_EQUAL(fh1._fro, std::size_t(0)); + BOOST_CHECK_EQUAL(fh1._ts_sec, std::time_t(0)); + BOOST_CHECK_EQUAL(fh1._ts_nsec, u_int32_t(0)); + BOOST_CHECK(!fh1.get_owi()); + } + + { + file_hdr fh2(magic, version, rid, pfid, lfid, fro, owi, false); + BOOST_CHECK_EQUAL(fh2._magic, magic); + BOOST_CHECK_EQUAL(fh2._version, version); +#ifdef JRNL_LITTLE_ENDIAN + BOOST_CHECK_EQUAL(fh2._eflag, RHM_LENDIAN_FLAG); +#else + BOOST_CHECK_EQUAL(fh2._eflag, RHM_BENDIAN_FLAG); +#endif + BOOST_CHECK_EQUAL(fh2._uflag, (const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK); + BOOST_CHECK_EQUAL(fh2._rid, rid); + BOOST_CHECK_EQUAL(fh2._pfid, pfid ); + BOOST_CHECK_EQUAL(fh2._lfid, lfid); + BOOST_CHECK_EQUAL(fh2._fro, fro); + BOOST_CHECK_EQUAL(fh2._ts_sec, std::time_t(0)); + BOOST_CHECK_EQUAL(fh2._ts_nsec, u_int32_t(0)); + ::clock_gettime(CLOCK_REALTIME, &ts); + fh2.set_time(ts); + BOOST_CHECK_EQUAL(fh2._ts_sec, ts.tv_sec); + BOOST_CHECK_EQUAL(fh2._ts_nsec, u_int32_t(ts.tv_nsec)); + BOOST_CHECK(fh2.get_owi()); + + fh2._uflag = uflag; + BOOST_CHECK(fh2.get_owi()); + + fh2.set_owi(false); + BOOST_CHECK(!fh2.get_owi()); + BOOST_CHECK_EQUAL(fh2._uflag, + (uflag & ~(const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK)); + + fh2.set_owi(true); + BOOST_CHECK(fh2.get_owi()); + BOOST_CHECK_EQUAL(fh2._uflag, uflag); + } + + { + file_hdr fh3(magic, version, rid, pfid, lfid, fro, owi, true); + BOOST_CHECK_EQUAL(fh3._magic, magic); + BOOST_CHECK_EQUAL(fh3._version, version); +#ifdef JRNL_LITTLE_ENDIAN + BOOST_CHECK_EQUAL(fh3._eflag, RHM_LENDIAN_FLAG); +#else + BOOST_CHECK_EQUAL(fh3._eflag, RHM_BENDIAN_FLAG); +#endif + BOOST_CHECK_EQUAL(fh3._uflag, (const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK); + BOOST_CHECK_EQUAL(fh3._rid, rid); + BOOST_CHECK_EQUAL(fh3._pfid, pfid); + BOOST_CHECK_EQUAL(fh3._lfid, lfid); + BOOST_CHECK_EQUAL(fh3._fro, fro); + BOOST_CHECK(fh3._ts_sec - ts.tv_sec <= 1); // No more than 1 sec difference + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enq_hdr_class) +{ + cout << test_filename << ".enq_hdr_class: " << flush; + const u_int32_t magic = 0xfedcba98UL; + const u_int8_t version = 0xa5; + const u_int64_t rid = 0xfedcba9876543210ULL; + const u_int16_t uflag = 0x5537; +#ifdef JRNL_32_BIT + const std::size_t xidsize = 0xfedcba98UL; + const std::size_t dsize = 0x76543210UL; +#else + const std::size_t xidsize = 0xfedcba9876543210ULL; + const std::size_t dsize = 0x76543210fedcba98ULL; +#endif + const bool owi = true; + + { + enq_hdr eh1; + BOOST_CHECK_EQUAL(eh1._magic, 0UL); + BOOST_CHECK_EQUAL(eh1._version, 0); + BOOST_CHECK_EQUAL(eh1._eflag, 0); + BOOST_CHECK_EQUAL(eh1._uflag, 0); + BOOST_CHECK_EQUAL(eh1._rid, 0ULL); + BOOST_CHECK_EQUAL(eh1._xidsize, std::size_t(0)); + BOOST_CHECK_EQUAL(eh1._dsize, std::size_t(0)); + BOOST_CHECK(!eh1.get_owi()); + } + + { + enq_hdr eh2(magic, version, rid, xidsize, dsize, owi, false); + BOOST_CHECK_EQUAL(eh2._magic, magic); + BOOST_CHECK_EQUAL(eh2._version, version); +#ifdef JRNL_LITTLE_ENDIAN + BOOST_CHECK_EQUAL(eh2._eflag, RHM_LENDIAN_FLAG); +#else + BOOST_CHECK_EQUAL(eh2._eflag, RHM_BENDIAN_FLAG); +#endif + BOOST_CHECK_EQUAL(eh2._uflag, (const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK); + BOOST_CHECK_EQUAL(eh2._rid, rid); + BOOST_CHECK_EQUAL(eh2._xidsize, xidsize); + BOOST_CHECK_EQUAL(eh2._dsize, dsize); + BOOST_CHECK(eh2.get_owi()); + BOOST_CHECK(!eh2.is_transient()); + BOOST_CHECK(!eh2.is_external()); + + eh2._uflag = uflag; + BOOST_CHECK(eh2.get_owi()); + BOOST_CHECK(eh2.is_transient()); + BOOST_CHECK(eh2.is_external()); + + eh2.set_owi(false); + BOOST_CHECK(!eh2.get_owi()); + BOOST_CHECK(eh2.is_transient()); + BOOST_CHECK(eh2.is_external()); + BOOST_CHECK_EQUAL(eh2._uflag, + (uflag & ~(const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK)); + + eh2.set_owi(true); + BOOST_CHECK(eh2.get_owi()); + BOOST_CHECK(eh2.is_transient()); + BOOST_CHECK(eh2.is_external()); + BOOST_CHECK_EQUAL(eh2._uflag, uflag); + + eh2.set_transient(false); + BOOST_CHECK(eh2.get_owi()); + BOOST_CHECK(!eh2.is_transient()); + BOOST_CHECK(eh2.is_external()); + BOOST_CHECK_EQUAL(eh2._uflag, uflag & ~(const u_int16_t)enq_hdr::ENQ_HDR_TRANSIENT_MASK); + + eh2.set_transient(true); + BOOST_CHECK(eh2.get_owi()); + BOOST_CHECK(eh2.is_transient()); + BOOST_CHECK(eh2.is_external()); + BOOST_CHECK_EQUAL(eh2._uflag, uflag); + + eh2.set_external(false); + BOOST_CHECK(eh2.get_owi()); + BOOST_CHECK(eh2.is_transient()); + BOOST_CHECK(!eh2.is_external()); + BOOST_CHECK_EQUAL(eh2._uflag, uflag & ~(const u_int16_t)enq_hdr::ENQ_HDR_EXTERNAL_MASK); + + eh2.set_external(true); + BOOST_CHECK(eh2.get_owi()); + BOOST_CHECK(eh2.is_transient()); + BOOST_CHECK(eh2.is_external()); + BOOST_CHECK_EQUAL(eh2._uflag, uflag); + } + + { + enq_hdr eh3(magic, version, rid, xidsize, dsize, owi, true); + BOOST_CHECK_EQUAL(eh3._magic, magic); + BOOST_CHECK_EQUAL(eh3._version, version); +#ifdef JRNL_LITTLE_ENDIAN + BOOST_CHECK_EQUAL(eh3._eflag, RHM_LENDIAN_FLAG); +#else + BOOST_CHECK_EQUAL(eh3._eflag, RHM_BENDIAN_FLAG); +#endif + BOOST_CHECK_EQUAL(eh3._uflag, (const u_int16_t)enq_hdr::ENQ_HDR_TRANSIENT_MASK | + (const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK); + BOOST_CHECK_EQUAL(eh3._rid, rid); + BOOST_CHECK_EQUAL(eh3._xidsize, xidsize); + BOOST_CHECK_EQUAL(eh3._dsize, dsize); + BOOST_CHECK(eh3.get_owi()); + BOOST_CHECK(eh3.is_transient()); + BOOST_CHECK(!eh3.is_external()); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(deq_hdr_class) +{ + cout << test_filename << ".deq_hdr_class: " << flush; + const u_int32_t magic = 0xfedcba98UL; + const u_int8_t version = 0xa5; + const u_int16_t uflag = 0x5537; + const u_int64_t rid = 0xfedcba9876543210ULL; + const u_int64_t drid = 0x76543210fedcba98ULL; +#ifdef JRNL_32_BIT + const std::size_t xidsize = 0xfedcba98UL; +#else + const std::size_t xidsize = 0xfedcba9876543210ULL; +#endif + const bool owi = true; + + { + deq_hdr dh1; + BOOST_CHECK_EQUAL(dh1._magic, 0UL); + BOOST_CHECK_EQUAL(dh1._version, 0); + BOOST_CHECK_EQUAL(dh1._eflag, 0); + BOOST_CHECK_EQUAL(dh1._uflag, 0); + BOOST_CHECK_EQUAL(dh1._rid, 0ULL); + BOOST_CHECK_EQUAL(dh1._deq_rid, 0ULL); + BOOST_CHECK_EQUAL(dh1._xidsize, std::size_t(0)); + BOOST_CHECK(!dh1.get_owi()); + } + + { + deq_hdr dh2(magic, version, rid, drid, xidsize, owi); + BOOST_CHECK_EQUAL(dh2._magic, magic); + BOOST_CHECK_EQUAL(dh2._version, version); +#ifdef JRNL_LITTLE_ENDIAN + BOOST_CHECK_EQUAL(dh2._eflag, RHM_LENDIAN_FLAG); +#else + BOOST_CHECK_EQUAL(dh2._eflag, RHM_BENDIAN_FLAG); +#endif + BOOST_CHECK_EQUAL(dh2._uflag, (const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK); + BOOST_CHECK_EQUAL(dh2._rid, rid); + BOOST_CHECK_EQUAL(dh2._deq_rid, drid); + BOOST_CHECK_EQUAL(dh2._xidsize, xidsize); + BOOST_CHECK(dh2.get_owi()); + + dh2._uflag = uflag; + BOOST_CHECK(dh2.get_owi()); + + dh2.set_owi(false); + BOOST_CHECK(!dh2.get_owi()); + BOOST_CHECK_EQUAL(dh2._uflag, + (uflag & ~(const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK)); + + dh2.set_owi(true); + BOOST_CHECK(dh2.get_owi()); + BOOST_CHECK_EQUAL(dh2._uflag, uflag); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(txn_hdr_class) +{ + cout << test_filename << ".txn_hdr_class: " << flush; + const u_int32_t magic = 0xfedcba98UL; + const u_int8_t version = 0xa5; + const u_int16_t uflag = 0x5537; + const u_int64_t rid = 0xfedcba9876543210ULL; +#ifdef JRNL_32_BIT + const std::size_t xidsize = 0xfedcba98UL; +#else + const std::size_t xidsize = 0xfedcba9876543210ULL; +#endif + const bool owi = true; + + { + txn_hdr th1; + BOOST_CHECK_EQUAL(th1._magic, 0UL); + BOOST_CHECK_EQUAL(th1._version, 0); + BOOST_CHECK_EQUAL(th1._eflag, 0); + BOOST_CHECK_EQUAL(th1._uflag, 0); + BOOST_CHECK_EQUAL(th1._rid, 0ULL); + BOOST_CHECK_EQUAL(th1._xidsize, std::size_t(0)); + BOOST_CHECK(!th1.get_owi()); + } + + { + txn_hdr th2(magic, version, rid, xidsize, owi); + BOOST_CHECK_EQUAL(th2._magic, magic); + BOOST_CHECK_EQUAL(th2._version, version); +#ifdef JRNL_LITTLE_ENDIAN + BOOST_CHECK_EQUAL(th2._eflag, RHM_LENDIAN_FLAG); +#else + BOOST_CHECK_EQUAL(th2._eflag, RHM_BENDIAN_FLAG); +#endif + BOOST_CHECK_EQUAL(th2._uflag, (const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK); + BOOST_CHECK_EQUAL(th2._rid, rid); + BOOST_CHECK_EQUAL(th2._xidsize, xidsize); + BOOST_CHECK(th2.get_owi()); + + th2._uflag = uflag; + BOOST_CHECK(th2.get_owi()); + + th2.set_owi(false); + BOOST_CHECK(!th2.get_owi()); + BOOST_CHECK_EQUAL(th2._uflag, + (uflag & ~(const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK)); + + th2.set_owi(true); + BOOST_CHECK(th2.get_owi()); + BOOST_CHECK_EQUAL(th2._uflag, uflag); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_ut_time_ns.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_ut_time_ns.cpp new file mode 100644 index 0000000000..f1b53bb97b --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/_ut_time_ns.cpp @@ -0,0 +1,163 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../unit_test.h" + +#include <ctime> +#include <iostream> +#include "qpid/legacystore/jrnl/time_ns.h" + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace std; + +QPID_AUTO_TEST_SUITE(time_ns_suite) + +const string test_filename("_ut_time_ns"); + +QPID_AUTO_TEST_CASE(constructors) +{ + cout << test_filename << ".constructors: " << flush; + const std::time_t sec = 123; + const long nsec = 123456789; + + time_ns t1; + BOOST_CHECK_EQUAL(t1.tv_sec, 0); + BOOST_CHECK_EQUAL(t1.tv_nsec, 0); + BOOST_CHECK_EQUAL(t1.is_zero(), true); + time_ns t2(sec, nsec); + BOOST_CHECK_EQUAL(t2.tv_sec, sec); + BOOST_CHECK_EQUAL(t2.tv_nsec, nsec); + BOOST_CHECK_EQUAL(t2.is_zero(), false); + time_ns t3(t1); + BOOST_CHECK_EQUAL(t3.tv_sec, 0); + BOOST_CHECK_EQUAL(t3.tv_nsec, 0); + BOOST_CHECK_EQUAL(t3.is_zero(), true); + time_ns t4(t2); + BOOST_CHECK_EQUAL(t4.tv_sec, sec); + BOOST_CHECK_EQUAL(t4.tv_nsec, nsec); + BOOST_CHECK_EQUAL(t4.is_zero(), false); + t4.set_zero(); + BOOST_CHECK_EQUAL(t4.tv_sec, 0); + BOOST_CHECK_EQUAL(t4.tv_nsec, 0); + BOOST_CHECK_EQUAL(t4.is_zero(), true); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(operators) +{ + cout << test_filename << ".operators: " << flush; + const std::time_t sec1 = 123; + const long nsec1 = 123456789; + const std::time_t sec2 = 1; + const long nsec2 = 999999999; + const std::time_t sec_sum = sec1 + sec2 + 1; + const long nsec_sum = nsec1 + nsec2 - 1000000000; + const std::time_t sec_1_minus_2 = sec1 - sec2 - 1; + const long nsec_1_minus_2 = nsec1 - nsec2 + 1000000000; + const std::time_t sec_2_minus_1 = sec2 - sec1; + const long nsec_2_minus_1 = nsec2 - nsec1; + time_ns z; + time_ns t1(sec1, nsec1); + time_ns t2(sec2, nsec2); + + time_ns t3 = z; + BOOST_CHECK_EQUAL(t3.tv_sec, 0); + BOOST_CHECK_EQUAL(t3.tv_nsec, 0); + BOOST_CHECK_EQUAL(t3 == z, true); + BOOST_CHECK_EQUAL(t3 != z, false); + BOOST_CHECK_EQUAL(t3 > z, false); + BOOST_CHECK_EQUAL(t3 >= z, true); + BOOST_CHECK_EQUAL(t3 < z, false); + BOOST_CHECK_EQUAL(t3 <= z, true); + + t3 = t1; + BOOST_CHECK_EQUAL(t3.tv_sec, sec1); + BOOST_CHECK_EQUAL(t3.tv_nsec, nsec1); + BOOST_CHECK_EQUAL(t3 == t1, true); + BOOST_CHECK_EQUAL(t3 != t1, false); + BOOST_CHECK_EQUAL(t3 > t1, false); + BOOST_CHECK_EQUAL(t3 >= t1, true); + BOOST_CHECK_EQUAL(t3 < t1, false); + BOOST_CHECK_EQUAL(t3 <= t1, true); + + t3 += z; + BOOST_CHECK_EQUAL(t3.tv_sec, sec1); + BOOST_CHECK_EQUAL(t3.tv_nsec, nsec1); + + t3 = t2; + BOOST_CHECK_EQUAL(t3.tv_sec, sec2); + BOOST_CHECK_EQUAL(t3.tv_nsec, nsec2); + BOOST_CHECK_EQUAL(t3 == t2, true); + BOOST_CHECK_EQUAL(t3 != t2, false); + BOOST_CHECK_EQUAL(t3 > t2, false); + BOOST_CHECK_EQUAL(t3 >= t2, true); + BOOST_CHECK_EQUAL(t3 < t2, false); + BOOST_CHECK_EQUAL(t3 <= t2, true); + + t3 += z; + BOOST_CHECK_EQUAL(t3.tv_sec, sec2); + BOOST_CHECK_EQUAL(t3.tv_nsec, nsec2); + + t3 = t1; + t3 += t2; + BOOST_CHECK_EQUAL(t3.tv_sec, sec_sum); + BOOST_CHECK_EQUAL(t3.tv_nsec, nsec_sum); + + t3 = t1; + t3 -= t2; + BOOST_CHECK_EQUAL(t3.tv_sec, sec_1_minus_2); + BOOST_CHECK_EQUAL(t3.tv_nsec, nsec_1_minus_2); + + t3 = t2; + t3 -= t1; + BOOST_CHECK_EQUAL(t3.tv_sec, sec_2_minus_1); + BOOST_CHECK_EQUAL(t3.tv_nsec, nsec_2_minus_1); + + t3 = t1 + t2; + BOOST_CHECK_EQUAL(t3.tv_sec, sec_sum); + BOOST_CHECK_EQUAL(t3.tv_nsec, nsec_sum); + + t3 = t1 - t2; + BOOST_CHECK_EQUAL(t3.tv_sec, sec_1_minus_2); + BOOST_CHECK_EQUAL(t3.tv_nsec, nsec_1_minus_2); + + t3 = t2 - t1; + BOOST_CHECK_EQUAL(t3.tv_sec, sec_2_minus_1); + BOOST_CHECK_EQUAL(t3.tv_nsec, nsec_2_minus_1); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(str) +{ + cout << test_filename << ".str: " << flush; + time_ns t1(123, 123456789); + BOOST_CHECK_EQUAL(t1.str(), "123.123457"); + BOOST_CHECK_EQUAL(t1.str(9), "123.123456789"); + BOOST_CHECK_EQUAL(t1.str(0), "123"); + time_ns t2(1, 1); + BOOST_CHECK_EQUAL(t2.str(9), "1.000000001"); + time_ns t3(-12, 345); + BOOST_CHECK_EQUAL(t3.str(9), "-11.999999655"); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_ut_txn_map.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_ut_txn_map.cpp new file mode 100644 index 0000000000..595ce0f6c6 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/_ut_txn_map.cpp @@ -0,0 +1,106 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../unit_test.h" + +#include <iomanip> +#include <iostream> +#include "qpid/legacystore/jrnl/txn_map.h" +#include <sstream> + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace std; + +QPID_AUTO_TEST_SUITE(txn_map_suite) + +const string test_filename("_ut_txn_map"); + +// === Helper functions === + +const string make_xid(u_int64_t rid) +{ + stringstream ss; + ss << "XID-" << setfill('0') << setw(16) << hex << rid; + ss << "-0123456789abcdef"; + return ss.str(); +} + +void check_td_equal(txn_data& td1, txn_data& td2) +{ + BOOST_CHECK_EQUAL(td1._rid, td2._rid); + BOOST_CHECK_EQUAL(td1._drid, td2._drid); + BOOST_CHECK_EQUAL(td1._pfid, td2._pfid); + BOOST_CHECK_EQUAL(td1._enq_flag, td2._enq_flag); + BOOST_CHECK_EQUAL(td1._aio_compl, td2._aio_compl); +} + +// === Test suite === + +QPID_AUTO_TEST_CASE(constructor) +{ + cout << test_filename << ".constructor: " << flush; + const u_int64_t rid = 0x123456789abcdef0ULL; + const u_int64_t drid = 0xfedcba9876543210ULL; + const u_int16_t pfid = 0xfedcU; + const bool enq_flag = true; + txn_data td(rid, drid, pfid, enq_flag); + BOOST_CHECK_EQUAL(td._rid, rid); + BOOST_CHECK_EQUAL(td._drid, drid); + BOOST_CHECK_EQUAL(td._pfid, pfid); + BOOST_CHECK_EQUAL(td._enq_flag, enq_flag); + BOOST_CHECK_EQUAL(td._aio_compl, false); + + txn_map t1; + BOOST_CHECK(t1.empty()); + BOOST_CHECK_EQUAL(t1.size(), u_int32_t(0)); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(insert_get) +{ + cout << test_filename << ".insert_get: " << flush; + u_int16_t fid; + u_int64_t rid; + u_int16_t pfid_start = 0x2000U; + u_int64_t rid_begin = 0xffffffff00000000ULL; + u_int64_t rid_end = 0xffffffff00000200ULL; + + // insert with no dups + u_int64_t rid_incr_1 = 4ULL; + txn_map t2; + t2.set_num_jfiles(pfid_start + (rid_end - rid_begin)/rid_incr_1); + for (rid = rid_begin, fid = pfid_start; rid < rid_end; rid += rid_incr_1, fid++) + t2.insert_txn_data(make_xid(rid), txn_data(rid, ~rid, fid, false)); + BOOST_CHECK(!t2.empty()); + BOOST_CHECK_EQUAL(t2.size(), u_int32_t(128)); + + // get + u_int64_t rid_incr_2 = 6ULL; + for (u_int64_t rid = rid_begin; rid < rid_end; rid += rid_incr_2) + { + string xid = make_xid(rid); + BOOST_CHECK_EQUAL(t2.in_map(xid), (rid%rid_incr_1 ? false : true)); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/chk_jdata b/qpid/cpp/src/tests/legacystore/jrnl/chk_jdata new file mode 100755 index 0000000000..67b322af59 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/chk_jdata @@ -0,0 +1,32 @@ +#!/bin/bash + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +JRNL_BLK_SIZE=512 # Block size in bytes +JRNL_PAGE_SIZE=256 # Journal page size in blocks +JRNL_FILE_SIZE=12 # Journal file size in pages +let END_OFFSET=${JRNL_BLK_SIZE}*${JRNL_PAGE_SIZE}*${JRNL_FILE_SIZE} +for f in jdata/test.*.jdat; do + echo $f + hexdump -C -n 1024 $f + hexdump -C -s ${END_OFFSET} $f + echo "============" +done diff --git a/qpid/cpp/src/tests/legacystore/jrnl/cp_rtest_jrnl b/qpid/cpp/src/tests/legacystore/jrnl/cp_rtest_jrnl new file mode 100755 index 0000000000..6d5171ae71 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/cp_rtest_jrnl @@ -0,0 +1,59 @@ +#!/bin/bash + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +JDATA_DIR=jdata +TAR_DIR=rd_test_jrnls + +function get_filename +{ + local prefix=$1 + local file_num=$2 + local suffix=$3 + + if (( file_num < 10 )); then + local num="000${file_num}" + elif (( file_num < 100 )); then + local num="00${file_num}" + elif (( file_num < 1000 )); then + local num="0${file_num}" + else + local num="${file_num}" + fi + FILENAME=${prefix}${num}${suffix} + return 0 +} + +if (( $# != 1 )); then + echo "Incorrect args, expected 1 arg (usage: \"prep <testnum>\")" + exit +fi + +get_filename "t" $1 ".tar.gz" +if [[ -d ${JDATA_DIR} ]]; then + rm -rf ${JDATA_DIR}/* +else + mkdir -p ${JDATA_DIR} +fi +if [[ -f "${TAR_DIR}/${FILENAME}" ]]; then + tar -C ${JDATA_DIR} -xzf "${TAR_DIR}/${FILENAME}" +else + echo "Error: file \"${TAR_DIR}/${FILENAME}\" not found." +fi diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jhexdump b/qpid/cpp/src/tests/legacystore/jrnl/jhexdump new file mode 100755 index 0000000000..2d4c8a4afb --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jhexdump @@ -0,0 +1,41 @@ +#!/bin/bash + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +if [ -z "$1" ]; then + echo "No directory specified." + exit +fi + +JDIR=$1 +echo "Target directory: ${JDIR}" + +rm -f j*.txt + +if [ -d "${JDIR}" ]; then + n=0 + for f in "${JDIR}"/*.jdat; do + echo "$f -> j$n.txt" + hexdump -C "$f" > j$n.txt + (( n += 1 )) + done +else + echo "This directory does not exist." +fi diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_data_src.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_data_src.cpp new file mode 100644 index 0000000000..e4656ef83f --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_data_src.cpp @@ -0,0 +1,207 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../../unit_test.h" +#include <cstddef> +#include "data_src.h" +#include <iomanip> +#include <iostream> + +using namespace boost::unit_test; +using namespace mrg::jtt; +using namespace std; + +QPID_AUTO_TEST_SUITE(jtt_data_src) + +const string test_filename("_ut_data_src"); + +long +get_seed() +{ + timespec ts; + if (::clock_gettime(CLOCK_REALTIME, &ts)) + BOOST_FAIL("Unable to read clock to generate seed."); + long tenths = ts.tv_nsec / 100000000; + return long(10 * ts.tv_sec + tenths); // time in tenths of a second +} + +#ifndef LONG_TEST +/* + * ============================================== + * NORMAL TESTS + * This section contains normal "make check" tests + * for building/packaging. These are built when + * LONG_TEST is _not_ defined. + * ============================================== + */ + +QPID_AUTO_TEST_CASE(data) +{ + cout << test_filename << ".data: " << flush; + BOOST_CHECK(data_src::max_dsize > 0); + for (std::size_t i=0; i<1024; i++) + { + const char* dp = data_src::get_data(i); + BOOST_CHECK_EQUAL(*dp, static_cast<char>('0' + ((i + 1) % 10))); + } + for (std::size_t i=data_src::max_dsize-1024; i<data_src::max_dsize; i++) + { + const char* dp = data_src::get_data(i); + BOOST_CHECK_EQUAL(*dp, static_cast<char>('0' + ((i + 1) % 10))); + } + const char* dp1 = data_src::get_data(data_src::max_dsize); + BOOST_CHECK_EQUAL(dp1,(char*) 0); + const char* dp2 = data_src::get_data(data_src::max_dsize + 0x1000); + BOOST_CHECK_EQUAL(dp2, (char*)0); + cout << "ok" << endl; +} + +// There is a long version of this test in _ut_long_data_src.cpp +QPID_AUTO_TEST_CASE(xid_data_xid) +{ + const std::size_t num = 64; + cout << test_filename << ".xid_data_xid: " << flush; + BOOST_CHECK_EQUAL(data_src::get_xid(1), "0"); + BOOST_CHECK_EQUAL(data_src::get_xid(2), "01"); + BOOST_CHECK_EQUAL(data_src::get_xid(3), "002"); + BOOST_CHECK_EQUAL(data_src::get_xid(4), "0003"); + BOOST_CHECK_EQUAL(data_src::get_xid(5), "00004"); + BOOST_CHECK_EQUAL(data_src::get_xid(6), "000005"); + BOOST_CHECK_EQUAL(data_src::get_xid(7), "0000006"); + BOOST_CHECK_EQUAL(data_src::get_xid(8), "00000007"); + BOOST_CHECK_EQUAL(data_src::get_xid(9), "xid:00008"); + BOOST_CHECK_EQUAL(data_src::get_xid(10), "xid:000009"); + BOOST_CHECK_EQUAL(data_src::get_xid(11), "xid:0000010"); + BOOST_CHECK_EQUAL(data_src::get_xid(12), "xid:00000011"); + BOOST_CHECK_EQUAL(data_src::get_xid(13), "xid:00000012:"); + BOOST_CHECK_EQUAL(data_src::get_xid(14), "xid:00000013:n"); + BOOST_CHECK_EQUAL(data_src::get_xid(15), "xid:00000014:no"); + std::size_t i = 15; + for (; i<num; i++) + { + string xid(data_src::get_xid(i)); + + ostringstream oss; + oss << setfill('0') << "xid:" << setw(8) << i << ":"; + + BOOST_CHECK_EQUAL(xid.size(), i); + BOOST_CHECK_EQUAL(xid.substr(0, 13), oss.str()); + BOOST_CHECK_EQUAL(xid[13], 'n'); + BOOST_CHECK_EQUAL(xid[i-1], (char)('a' + ((i-1)%26))); + } + for (std::size_t j=data_src::max_xsize-num; j<data_src::max_xsize; j++,i++) + { + string xid(data_src::get_xid(j)); + + ostringstream oss; + oss << setfill('0') << "xid:" << setw(8) << i << ":"; + + BOOST_CHECK_EQUAL(xid.size(), j); + BOOST_CHECK_EQUAL(xid.substr(0, 13), oss.str()); + BOOST_CHECK_EQUAL(xid[13], 'n'); + BOOST_CHECK_EQUAL(xid[j-1], (char)('a' + ((j-1)%26))); + } + cout << "ok" << endl; +} + +#else +/* + * ============================================== + * LONG TESTS + * This section contains long tests and soak tests, + * and are run using target check-long (ie "make + * check-long"). These are built when LONG_TEST is + * defined. + * ============================================== + */ + +/* + * To reproduce a specific test, comment out the get_seed() statement and uncomment the literal below, adjusting the seed + * value to that required. + */ +QPID_AUTO_TEST_CASE(xid_data_xid) +{ + const long seed = get_seed(); + // const long seed = 0x2d9b69d32; + ::srand48(seed); + + const std::size_t num = 1024; + cout << test_filename << ".xid_data_xid seed=0x" << std::hex << seed << std::dec << ": " << flush; + BOOST_CHECK_EQUAL(data_src::get_xid(1), "0"); + BOOST_CHECK_EQUAL(data_src::get_xid(2), "01"); + BOOST_CHECK_EQUAL(data_src::get_xid(3), "002"); + BOOST_CHECK_EQUAL(data_src::get_xid(4), "0003"); + BOOST_CHECK_EQUAL(data_src::get_xid(5), "00004"); + BOOST_CHECK_EQUAL(data_src::get_xid(6), "000005"); + BOOST_CHECK_EQUAL(data_src::get_xid(7), "0000006"); + BOOST_CHECK_EQUAL(data_src::get_xid(8), "00000007"); + BOOST_CHECK_EQUAL(data_src::get_xid(9), "xid:00008"); + BOOST_CHECK_EQUAL(data_src::get_xid(10), "xid:000009"); + BOOST_CHECK_EQUAL(data_src::get_xid(11), "xid:0000010"); + BOOST_CHECK_EQUAL(data_src::get_xid(12), "xid:00000011"); + BOOST_CHECK_EQUAL(data_src::get_xid(13), "xid:00000012:"); + BOOST_CHECK_EQUAL(data_src::get_xid(14), "xid:00000013:n"); + BOOST_CHECK_EQUAL(data_src::get_xid(15), "xid:00000014:no"); + std::size_t i = 15; + for (; i<num; i++) + { + string xid(data_src::get_xid(i)); + + ostringstream oss; + oss << setfill('0') << "xid:" << setw(8) << i << ":"; + + BOOST_CHECK_EQUAL(xid.size(), i); + BOOST_CHECK_EQUAL(xid.substr(0, 13), oss.str()); + BOOST_CHECK_EQUAL(xid[13], 'n'); + BOOST_CHECK_EQUAL(xid[i-1], (char)('a' + ((i-1)%26))); + } + for (std::size_t j=data_src::max_xsize-num; j<data_src::max_xsize; j++,i++) + { + string xid(data_src::get_xid(j)); + + ostringstream oss; + oss << setfill('0') << "xid:" << setw(8) << i << ":"; + + BOOST_CHECK_EQUAL(xid.size(), j); + BOOST_CHECK_EQUAL(xid.substr(0, 13), oss.str()); + BOOST_CHECK_EQUAL(xid[13], 'n'); + BOOST_CHECK_EQUAL(xid[j-1], (char)('a' + ((j-1)%26))); + } + std::srand(seed); + for (int cnt=0; cnt<1000; cnt++,i++) + { + std::size_t k = 1 + ::lrand48() % (data_src::max_xsize - 1); + string xid(data_src::get_xid(k)); + + ostringstream oss; + oss << setfill('0') << "xid:" << setw(8) << i << ":"; + + BOOST_CHECK_EQUAL(xid.size(), k); + BOOST_CHECK_EQUAL(xid.substr(0, 13), oss.str()); + BOOST_CHECK_EQUAL(xid[13], 'n'); + BOOST_CHECK_EQUAL(xid[k-1], (char)('a' + ((k-1)%26))); + } + cout << "ok" << endl; +} + +#endif + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_jrnl_init_params.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_jrnl_init_params.cpp new file mode 100644 index 0000000000..9fefe25105 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_jrnl_init_params.cpp @@ -0,0 +1,100 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../../unit_test.h" +#include "jrnl_init_params.h" +#include <iostream> + +using namespace boost::unit_test; +using namespace mrg::jtt; +using namespace std; + +QPID_AUTO_TEST_SUITE(jtt_jrnl_init_params) + +const string test_filename("_ut_jrnl_init_params"); + +QPID_AUTO_TEST_CASE(constructor) +{ + cout << test_filename << ".constructor: " << flush; + const string jid = "jid"; + const string jdir = "jdir"; + const string bfn = "base filename"; + const u_int16_t num_jfiles = 123; + const bool ae = false; + const u_int16_t ae_max_jfiles = 456; + const u_int32_t jfsize_sblks = 789; + jrnl_init_params jip(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks); + BOOST_CHECK_EQUAL(jip.jid(), jid); + BOOST_CHECK_EQUAL(jip.jdir(), jdir); + BOOST_CHECK_EQUAL(jip.base_filename(), bfn); + BOOST_CHECK_EQUAL(jip.num_jfiles(), num_jfiles); + BOOST_CHECK_EQUAL(jip.is_ae(), ae); + BOOST_CHECK_EQUAL(jip.ae_max_jfiles(), ae_max_jfiles); + BOOST_CHECK_EQUAL(jip.jfsize_sblks(), jfsize_sblks); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(copy_constructor_1) +{ + cout << test_filename << ".copy_constructor_1: " << flush; + const string jid = "jid"; + const string jdir = "jdir"; + const string bfn = "base filename"; + const u_int16_t num_jfiles = 123; + const bool ae = false; + const u_int16_t ae_max_jfiles = 456; + const u_int32_t jfsize_sblks = 789; + jrnl_init_params jip1(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks); + jrnl_init_params jip2(jip1); + BOOST_CHECK_EQUAL(jip2.jid(), jid); + BOOST_CHECK_EQUAL(jip2.jdir(), jdir); + BOOST_CHECK_EQUAL(jip2.base_filename(), bfn); + BOOST_CHECK_EQUAL(jip2.num_jfiles(), num_jfiles); + BOOST_CHECK_EQUAL(jip2.is_ae(), ae); + BOOST_CHECK_EQUAL(jip2.ae_max_jfiles(), ae_max_jfiles); + BOOST_CHECK_EQUAL(jip2.jfsize_sblks(), jfsize_sblks); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(copy_constructor_2) +{ + cout << test_filename << ".copy_constructor_2: " << flush; + const string jid = "jid"; + const string jdir = "jdir"; + const string bfn = "base filename"; + const u_int16_t num_jfiles = 123; + const bool ae = false; + const u_int16_t ae_max_jfiles = 456; + const u_int32_t jfsize_sblks = 789; + jrnl_init_params::shared_ptr p(new jrnl_init_params(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks)); + jrnl_init_params jip2(p.get()); + BOOST_CHECK_EQUAL(jip2.jid(), jid); + BOOST_CHECK_EQUAL(jip2.jdir(), jdir); + BOOST_CHECK_EQUAL(jip2.base_filename(), bfn); + BOOST_CHECK_EQUAL(jip2.num_jfiles(), num_jfiles); + BOOST_CHECK_EQUAL(jip2.is_ae(), ae); + BOOST_CHECK_EQUAL(jip2.ae_max_jfiles(), ae_max_jfiles); + BOOST_CHECK_EQUAL(jip2.jfsize_sblks(), jfsize_sblks); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() + diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_jrnl_instance.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_jrnl_instance.cpp new file mode 100644 index 0000000000..12f1c542d6 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_jrnl_instance.cpp @@ -0,0 +1,178 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../../unit_test.h" + +#include <iostream> +#include "jrnl_init_params.h" +#include "jrnl_instance.h" +#include "qpid/legacystore/jrnl/jdir.h" +#include "qpid/legacystore/jrnl/jerrno.h" + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace mrg::jtt; +using namespace std; + +QPID_AUTO_TEST_SUITE(jtt_jrnl_instance) + +const string test_filename("_ut_jrnl_instance"); +const char* tdp = getenv("TMP_DATA_DIR"); +const string test_dir(tdp && strlen(tdp) > 0 ? tdp : "/var/tmp/JttTest"); + +QPID_AUTO_TEST_CASE(constructor_1) +{ + cout << test_filename << ".constructor_1: " << flush; + const string jid = "jid1"; + const string jdir = test_dir + "/test1"; + const string bfn = "test"; + const u_int16_t num_jfiles = 20; + const bool ae = false; + const u_int16_t ae_max_jfiles = 45; + const u_int32_t jfsize_sblks = 128; + + args a("a1"); + using mrg::jtt::test_case; + test_case::shared_ptr p(new test_case(1, 0, 0, 0, false, 0, 0, test_case::JTT_PERSISTNET, test_case::JDL_INTERNAL, + "t1")); + jrnl_instance ji(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks); + ji.init_tc(p, &a); + ji.run_tc(); + ji.tc_wait_compl(); + try { jdir::verify_dir(jdir, bfn); } + catch (const jexception& e) { BOOST_ERROR(e.what()); } + jdir::delete_dir(jdir); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(constructor_2) +{ + cout << test_filename << ".constructor_2: " << flush; + const string jid = "jid2"; + const string jdir = test_dir + "/test2"; + const string bfn = "test"; + const u_int16_t num_jfiles = 20; + const bool ae = false; + const u_int16_t ae_max_jfiles = 45; + const u_int32_t jfsize_sblks = 128; + + args a("a2"); + using mrg::jtt::test_case; + test_case::shared_ptr p(new test_case(2, 0, 0, 0, false, 0, 0, test_case::JTT_PERSISTNET, test_case::JDL_INTERNAL, + "t2")); + jrnl_init_params::shared_ptr jpp(new jrnl_init_params(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks)); + jrnl_instance ji(jpp); + ji.init_tc(p, &a); + ji.run_tc(); + ji.tc_wait_compl(); + try { jdir::verify_dir(jdir, bfn); } + catch (const jexception& e) { BOOST_ERROR(e.what()); } + jdir::delete_dir(jdir); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(constructor_3) +{ + cout << test_filename << ".constructor_3: " << flush; + const string jid = "jid3"; + const string jdir = test_dir + "/test3"; + const string bfn = "test"; + const u_int16_t num_jfiles = 20; + const bool ae = false; + const u_int16_t ae_max_jfiles = 45; + const u_int32_t jfsize_sblks = 128; + + args a("a3"); + using mrg::jtt::test_case; + test_case::shared_ptr p(new test_case(3, 0, 0, 0, false, 0, 0, test_case::JTT_PERSISTNET, test_case::JDL_INTERNAL, + "t3")); + jrnl_init_params::shared_ptr jpp(new jrnl_init_params(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks)); + jrnl_instance ji(jpp); + ji.init_tc(p, &a); + ji.run_tc(); + ji.tc_wait_compl(); + try { jdir::verify_dir(jdir, bfn); } + catch (const jexception& e) { BOOST_ERROR(e.what()); } + jdir::delete_dir(jdir); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(recover) +{ + cout << test_filename << ".recover: " << flush; + const string jid = "jid5"; + const string jdir = test_dir + "/test5"; + const string bfn = "test"; + const u_int16_t num_jfiles = 20; + const bool ae = false; + const u_int16_t ae_max_jfiles = 0; + const u_int32_t jfsize_sblks = 128; + + args a("a4"); + using mrg::jtt::test_case; + test_case::shared_ptr p(new test_case(5, 0, 0, 0, false, 0, 0, test_case::JTT_PERSISTNET, test_case::JDL_INTERNAL, + "t5")); + jrnl_init_params::shared_ptr jpp(new jrnl_init_params(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks)); + jrnl_instance ji(jpp); + ji.init_tc(p, &a); + ji.run_tc(); + ji.tc_wait_compl(); + try { jdir::verify_dir(jdir, bfn); } + catch (const jexception& e) { BOOST_ERROR(e.what()); } + a.recover_mode = true; + ji.init_tc(p, &a); + ji.run_tc(); + ji.tc_wait_compl(); + try { jdir::verify_dir(jdir, bfn); } + catch (const jexception& e) { BOOST_ERROR(e.what()); } + jdir::delete_dir(jdir); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(recover_no_files) +{ + cout << test_filename << ".recover_no_files: " << flush; + const string jid = "jid6"; + const string jdir = test_dir + "/test6"; + const string bfn = "test"; + const u_int16_t num_jfiles = 20; + const bool ae = false; + const u_int16_t ae_max_jfiles = 0; + const u_int32_t jfsize_sblks = 128; + + args a("a5"); + a.recover_mode = true; + using mrg::jtt::test_case; + test_case::shared_ptr p(new test_case(6, 0, 0, 0, false, 0, 0, test_case::JTT_PERSISTNET, test_case::JDL_INTERNAL, + "t6")); + jrnl_init_params::shared_ptr jpp(new jrnl_init_params(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks)); + jrnl_instance ji(jpp); + ji.init_tc(p, &a); + ji.run_tc(); + ji.tc_wait_compl(); + try { jdir::verify_dir(jdir, bfn); } + catch (const jexception& e) { BOOST_ERROR(e.what()); } + jdir::delete_dir(jdir); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() + diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_read_arg.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_read_arg.cpp new file mode 100644 index 0000000000..0d2025270d --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_read_arg.cpp @@ -0,0 +1,146 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../../unit_test.h" +#include <boost/test/unit_test_log.hpp> +#include "read_arg.h" +#include <iostream> + +#include <boost/program_options.hpp> +namespace po = boost::program_options; +using namespace mrg::jtt; +using namespace boost::unit_test; +using namespace std; + +QPID_AUTO_TEST_SUITE(jtt_read_arg) + +const string test_filename("_ut_read_arg"); + +QPID_AUTO_TEST_CASE(constructor) +{ + cout << test_filename << ".constructor: " << flush; + read_arg ra1; + BOOST_CHECK_EQUAL(ra1.val(), read_arg::NONE); + BOOST_CHECK_EQUAL(ra1.str(), "NONE"); + read_arg ra2(read_arg::NONE); + BOOST_CHECK_EQUAL(ra2.val(), read_arg::NONE); + BOOST_CHECK_EQUAL(ra2.str(), "NONE"); + read_arg ra3(read_arg::ALL); + BOOST_CHECK_EQUAL(ra3.val(), read_arg::ALL); + BOOST_CHECK_EQUAL(ra3.str(), "ALL"); + read_arg ra4(read_arg::RANDOM); + BOOST_CHECK_EQUAL(ra4.val(), read_arg::RANDOM); + BOOST_CHECK_EQUAL(ra4.str(), "RANDOM"); + read_arg ra5(read_arg::LAZYLOAD); + BOOST_CHECK_EQUAL(ra5.val(), read_arg::LAZYLOAD); + BOOST_CHECK_EQUAL(ra5.str(), "LAZYLOAD"); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(set_val) +{ + cout << test_filename << ".set_val: " << flush; + read_arg ra; + BOOST_CHECK_EQUAL(ra.val(), read_arg::NONE); + BOOST_CHECK_EQUAL(ra.str(), "NONE"); + ra.set_val(read_arg::ALL); + BOOST_CHECK_EQUAL(ra.val(), read_arg::ALL); + BOOST_CHECK_EQUAL(ra.str(), "ALL"); + ra.set_val(read_arg::RANDOM); + BOOST_CHECK_EQUAL(ra.val(), read_arg::RANDOM); + BOOST_CHECK_EQUAL(ra.str(), "RANDOM"); + ra.set_val(read_arg::LAZYLOAD); + BOOST_CHECK_EQUAL(ra.val(), read_arg::LAZYLOAD); + BOOST_CHECK_EQUAL(ra.str(), "LAZYLOAD"); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(parse) +{ + cout << test_filename << ".parse: " << flush; + read_arg ra; + ra.parse("LAZYLOAD"); + BOOST_CHECK_EQUAL(ra.val(), read_arg::LAZYLOAD); + BOOST_CHECK_EQUAL(ra.str(), "LAZYLOAD"); + ra.parse("ALL"); + BOOST_CHECK_EQUAL(ra.val(), read_arg::ALL); + BOOST_CHECK_EQUAL(ra.str(), "ALL"); + BOOST_CHECK_THROW(ra.parse(""), po::invalid_option_value) + BOOST_CHECK_EQUAL(ra.val(), read_arg::ALL); + BOOST_CHECK_EQUAL(ra.str(), "ALL"); + BOOST_CHECK_THROW(ra.parse("abc123"), po::invalid_option_value) + BOOST_CHECK_EQUAL(ra.val(), read_arg::ALL); + BOOST_CHECK_EQUAL(ra.str(), "ALL"); + ra.parse("NONE"); + BOOST_CHECK_EQUAL(ra.val(), read_arg::NONE); + BOOST_CHECK_EQUAL(ra.str(), "NONE"); + ra.parse("RANDOM"); + BOOST_CHECK_EQUAL(ra.val(), read_arg::RANDOM); + BOOST_CHECK_EQUAL(ra.str(), "RANDOM"); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(istream_) +{ + cout << test_filename << ".istream_: " << flush; + read_arg ra; + istringstream ss1("LAZYLOAD", ios::in); + ss1 >> ra; + BOOST_CHECK_EQUAL(ra.val(), read_arg::LAZYLOAD); + BOOST_CHECK_EQUAL(ra.str(), "LAZYLOAD"); + istringstream ss2("ALL", ios::in); + ss2 >> ra; + BOOST_CHECK_EQUAL(ra.val(), read_arg::ALL); + BOOST_CHECK_EQUAL(ra.str(), "ALL"); + istringstream ss3("NONE", ios::in); + ss3 >> ra; + BOOST_CHECK_EQUAL(ra.val(), read_arg::NONE); + BOOST_CHECK_EQUAL(ra.str(), "NONE"); + istringstream ss4("RANDOM", ios::in); + ss4 >> ra; + BOOST_CHECK_EQUAL(ra.val(), read_arg::RANDOM); + BOOST_CHECK_EQUAL(ra.str(), "RANDOM"); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(ostream_) +{ + cout << test_filename << ".ostream_: " << flush; + ostringstream s1; + read_arg ra(read_arg::LAZYLOAD); + s1 << ra; + BOOST_CHECK_EQUAL(s1.str(), "LAZYLOAD"); + ra.set_val(read_arg::ALL); + ostringstream s2; + s2 << ra; + BOOST_CHECK_EQUAL(s2.str(), "ALL"); + ra.set_val(read_arg::NONE); + ostringstream s3; + s3 << ra; + BOOST_CHECK_EQUAL(s3.str(), "NONE"); + ra.set_val(read_arg::RANDOM); + ostringstream s4; + s4 << ra; + BOOST_CHECK_EQUAL(s4.str(), "RANDOM"); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case.cpp new file mode 100644 index 0000000000..3a7d0f951c --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case.cpp @@ -0,0 +1,113 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../../unit_test.h" +#include <cstddef> +#include <iomanip> +#include <iostream> +#include "test_case.h" +#include "test_case_result.h" + +using namespace boost::unit_test; +using namespace mrg::jtt; +using namespace std; + +QPID_AUTO_TEST_SUITE(jtt_test_case) + +const string test_filename("_ut_test_case"); + +QPID_AUTO_TEST_CASE(constructor) +{ + cout << test_filename << ".constructor: " << flush; + const unsigned test_case_num = 0x12345; + const u_int32_t num_msgs = 0x100; + const std::size_t min_data_size = 0x1000; + const std::size_t max_data_size = 0; + const bool auto_deq = true; + const std::size_t min_xid_size = 0x200; + const std::size_t max_xid_size = 0x200; + using mrg::jtt::test_case; + const test_case::transient_t transient = test_case::JTT_PERSISTNET; + const test_case::external_t external = test_case::JDL_INTERNAL; + const string comment = "This is a test"; + + test_case tc(test_case_num, num_msgs, min_data_size, max_data_size, auto_deq, + min_xid_size, max_xid_size, transient, external, comment); + BOOST_CHECK_EQUAL(tc.test_case_num(), test_case_num); + BOOST_CHECK_EQUAL(tc.num_msgs(), num_msgs); + BOOST_CHECK_EQUAL(tc.min_data_size(), min_data_size); + BOOST_CHECK_EQUAL(tc.max_data_size(), max_data_size); + BOOST_CHECK_EQUAL(tc.auto_deq(), auto_deq); + BOOST_CHECK_EQUAL(tc.min_xid_size(), min_xid_size); + BOOST_CHECK_EQUAL(tc.max_xid_size(), max_xid_size); + BOOST_CHECK_EQUAL(tc.transient(), transient); + BOOST_CHECK_EQUAL(tc.external(), external); + BOOST_CHECK_EQUAL(tc.comment(), comment); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(results) +{ + cout << test_filename << ".results: " << flush; + const unsigned test_case_num = 0x12345; + const u_int32_t num_msgs = 0x100; + const std::size_t min_data_size = 0x1000; + const std::size_t max_data_size = 0; + const bool auto_deq = true; + const std::size_t min_xid_size = 0x200; + const std::size_t max_xid_size = 0x200; + using mrg::jtt::test_case; + const test_case::transient_t transient = test_case::JTT_PERSISTNET; + const test_case::external_t external = test_case::JDL_INTERNAL; + const string comment = "This is a test"; + const unsigned num_results = 20; + + test_case tc(test_case_num, num_msgs, min_data_size, max_data_size, auto_deq, + min_xid_size, max_xid_size, transient, external, comment); + for (unsigned i=0; i<num_results; i++) + { + ostringstream oss; + oss << "JID_" << setfill('0') << setw(2) << i; + test_case_result::shared_ptr p(new test_case_result(oss.str())); + tc.add_result(p); + } + BOOST_CHECK_EQUAL(tc.num_results(), num_results); + test_case_result_agregation ave = tc.average(); + unsigned i=0; + for (test_case_result_agregation::tcrp_list_citr j=ave.rlist_begin(); j!=ave.rlist_end(); + i++,j++) + { + ostringstream oss; + oss << "JID_" << setfill('0') << setw(2) << i; + BOOST_CHECK_EQUAL((*j)->jid(), oss.str()); + } + for (unsigned i=0; i<num_results; i++) + { + ostringstream oss; + oss << "JID_" << setfill('0') << setw(2) << i; + BOOST_CHECK_EQUAL(ave[i]->jid(), oss.str()); + } + tc.clear(); + BOOST_CHECK_EQUAL(tc.num_results(), unsigned(0)); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_result.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_result.cpp new file mode 100644 index 0000000000..dd83dbee69 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_result.cpp @@ -0,0 +1,206 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../../unit_test.h" + +#include <iostream> +#include "qpid/legacystore/jrnl/jexception.h" +#include "test_case_result.h" + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace mrg::jtt; +using namespace std; + +QPID_AUTO_TEST_SUITE(jtt_test_case_result) + +const string test_filename("_ut_test_case_result"); + +QPID_AUTO_TEST_CASE(constructor) +{ + cout << test_filename << ".constructor: " << flush; + const string jid("journal id 1"); + test_case_result tcr(jid); + BOOST_CHECK_EQUAL(tcr.jid(), jid); + BOOST_CHECK_EQUAL(tcr.exception(), false); + BOOST_CHECK_EQUAL(tcr.exception_count(), 0U); + const time_ns& ts1 = tcr.start_time(); + BOOST_CHECK(ts1.is_zero()); + const time_ns& ts2 = tcr.stop_time(); + BOOST_CHECK(ts2.is_zero()); + const time_ns& ts3 = tcr.test_time(); + BOOST_CHECK(ts3.is_zero()); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(start_stop) +{ + cout << test_filename << ".start_stop: " << flush; + const string jid("journal id 2"); + test_case_result tcr(jid); + BOOST_CHECK_EQUAL(tcr.exception(), false); + BOOST_CHECK_EQUAL(tcr.exception_count(), 0U); + const time_ns& ts1 = tcr.start_time(); + BOOST_CHECK(ts1.is_zero()); + const time_ns& ts2 = tcr.stop_time(); + BOOST_CHECK(ts2.is_zero()); + const time_ns& ts3 = tcr.test_time(); + BOOST_CHECK(ts3.is_zero()); + + tcr.set_start_time(); + BOOST_CHECK_EQUAL(tcr.exception(), false); + BOOST_CHECK_EQUAL(tcr.exception_count(), 0U); + const time_ns& ts4 = tcr.start_time(); + BOOST_CHECK(!ts4.is_zero()); + const time_ns& ts5 = tcr.stop_time(); + BOOST_CHECK(ts5.is_zero()); + const time_ns& ts6 = tcr.test_time(); + BOOST_CHECK(ts6.is_zero()); + + ::usleep(1100000); // 1.1 sec in microseconds + tcr.set_stop_time(); + BOOST_CHECK_EQUAL(tcr.exception(), false); + BOOST_CHECK_EQUAL(tcr.exception_count(), 0U); + const time_ns& ts7 = tcr.stop_time(); + BOOST_CHECK(!ts7.is_zero()); + const time_ns& ts8 = tcr.test_time(); + BOOST_CHECK(ts8.tv_sec == 1); + BOOST_CHECK(ts8.tv_nsec > 100000000); // 0.1 sec in nanoseconds + BOOST_CHECK(ts8.tv_nsec < 200000000); // 0.2 sec in nanoseconds + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(start_exception_stop_1) +{ + cout << test_filename << ".start_exception_stop_1: " << flush; + const string jid("journal id 3"); + test_case_result tcr(jid); + const u_int32_t err_code = 0x321; + const string err_msg = "exception message"; + const jexception e(err_code, err_msg); + tcr.set_start_time(); + ::usleep(1100000); // 1.1 sec in microseconds + tcr.add_exception(e); + BOOST_CHECK_EQUAL(tcr.exception(), true); + BOOST_CHECK_EQUAL(tcr.exception_count(), 1U); + BOOST_CHECK_EQUAL(tcr[0], e.what()); + const time_ns& ts1 = tcr.stop_time(); + BOOST_CHECK(!ts1.is_zero()); + const time_ns& ts2 = tcr.test_time(); + BOOST_CHECK(ts2.tv_sec == 1); + BOOST_CHECK(ts2.tv_nsec > 100000000); // 0.1 sec in nanoseconds + BOOST_CHECK(ts2.tv_nsec < 200000000); // 0.2 sec in nanoseconds + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(start_exception_stop_2) +{ + cout << test_filename << ".start_exception_stop_2: " << flush; + const string jid("journal id 4"); + test_case_result tcr(jid); + const string err_msg = "exception message"; + tcr.set_start_time(); + ::usleep(1100000); // 1.1 sec in microseconds + tcr.add_exception(err_msg); + BOOST_CHECK_EQUAL(tcr.exception(), true); + BOOST_CHECK_EQUAL(tcr.exception_count(), 1U); + BOOST_CHECK_EQUAL(tcr[0], err_msg); + const time_ns& ts1 = tcr.stop_time(); + BOOST_CHECK(!ts1.is_zero()); + const time_ns& ts2 = tcr.test_time(); + BOOST_CHECK(ts2.tv_sec == 1); + BOOST_CHECK(ts2.tv_nsec > 100000000); // 0.1 sec in nanoseconds + BOOST_CHECK(ts2.tv_nsec < 200000000); // 0.2 sec in nanoseconds + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(start_exception_stop_3) +{ + cout << test_filename << ".start_exception_stop_3: " << flush; + const string jid("journal id 5"); + test_case_result tcr(jid); + const char* err_msg = "exception message"; + tcr.set_start_time(); + ::usleep(1100000); // 1.1 sec in microseconds + tcr.add_exception(err_msg); + BOOST_CHECK_EQUAL(tcr.exception(), true); + BOOST_CHECK_EQUAL(tcr.exception_count(), 1U); + BOOST_CHECK_EQUAL(tcr[0], err_msg); + const time_ns& ts1 = tcr.stop_time(); + BOOST_CHECK(!ts1.is_zero()); + const time_ns& ts2 = tcr.test_time(); + BOOST_CHECK(ts2.tv_sec == 1); + BOOST_CHECK(ts2.tv_nsec > 100000000); // 0.1 sec in nanoseconds + BOOST_CHECK(ts2.tv_nsec < 200000000); // 0.2 sec in nanoseconds + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(start_exception) +{ + cout << test_filename << ".start_exception: " << flush; + const string jid("journal id 6"); + test_case_result tcr(jid); + u_int32_t err_code = 0x654; + const string err_msg = "exception message"; + const jexception e(err_code, err_msg); + tcr.set_start_time(); + ::usleep(1100000); // 1.1 sec in microseconds + tcr.add_exception(e, false); + BOOST_CHECK_EQUAL(tcr.exception(), true); + BOOST_CHECK_EQUAL(tcr.exception_count(), 1U); + BOOST_CHECK_EQUAL(tcr[0], e.what()); + const time_ns& ts1 = tcr.stop_time(); + BOOST_CHECK(ts1.is_zero()); + const time_ns& ts2 = tcr.test_time(); + BOOST_CHECK(ts2.is_zero()); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(counters) +{ + cout << test_filename << ".counters: " << flush; + const u_int32_t num_enq = 125; + const u_int32_t num_deq = 64; + const u_int32_t num_read = 22; + const string jid("journal id 7"); + test_case_result tcr(jid); + BOOST_CHECK_EQUAL(tcr.num_enq(), u_int32_t(0)); + BOOST_CHECK_EQUAL(tcr.num_deq(), u_int32_t(0)); + BOOST_CHECK_EQUAL(tcr.num_read(), u_int32_t(0)); + for (unsigned i=0; i<num_enq; i++) + tcr.incr_num_enq(); + BOOST_CHECK_EQUAL(tcr.num_enq(), num_enq); + BOOST_CHECK_EQUAL(tcr.num_deq(), u_int32_t(0)); + BOOST_CHECK_EQUAL(tcr.num_read(), u_int32_t(0)); + for (unsigned j=0; j<num_deq; j++) + tcr.incr_num_deq(); + BOOST_CHECK_EQUAL(tcr.num_enq(), num_enq); + BOOST_CHECK_EQUAL(tcr.num_deq(), num_deq); + BOOST_CHECK_EQUAL(tcr.num_read(), u_int32_t(0)); + for (unsigned k=0; k<num_read; k++) + tcr.incr_num_read(); + BOOST_CHECK_EQUAL(tcr.num_enq(), num_enq); + BOOST_CHECK_EQUAL(tcr.num_deq(), num_deq); + BOOST_CHECK_EQUAL(tcr.num_read(), num_read); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_result_agregation.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_result_agregation.cpp new file mode 100644 index 0000000000..aa01bf833d --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_result_agregation.cpp @@ -0,0 +1,178 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../../unit_test.h" +#include <ctime> +#include <iostream> +#include "test_case_result_agregation.h" + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace mrg::jtt; +using namespace std; + +QPID_AUTO_TEST_SUITE(jtt_test_case_result_agregation) + +const string test_filename("_ut_test_case_result_agregation"); + +// === Helper functions === + +void check_agregate(const test_case_result_agregation& tcra, const u_int32_t num_enq, + const u_int32_t num_deq, const u_int32_t num_reads, const u_int32_t num_results, + const u_int32_t num_exceptions, const std::time_t secs, const long nsec) +{ + BOOST_CHECK_EQUAL(tcra.num_enq(), num_enq); + BOOST_CHECK_EQUAL(tcra.num_deq(), num_deq); + BOOST_CHECK_EQUAL(tcra.num_read(), num_reads); + BOOST_CHECK_EQUAL(tcra.num_results(), num_results); + BOOST_CHECK_EQUAL(tcra.exception_count(), num_exceptions); + BOOST_CHECK_EQUAL(tcra.exception(), num_exceptions > 0); + const time_ns& ts1 = tcra.test_time(); + BOOST_CHECK_EQUAL(ts1.tv_sec, secs); + BOOST_CHECK_EQUAL(ts1.tv_nsec, nsec); +} + +test_case_result::shared_ptr make_result(const string& jid, const u_int32_t num_enq, + const u_int32_t num_deq, const u_int32_t num_reads, const std::time_t secs, const long nsec) +{ + test_case_result::shared_ptr tcrp(new test_case_result(jid)); + for (unsigned i=0; i<num_enq; i++) + tcrp->incr_num_enq(); + for (unsigned i=0; i<num_deq; i++) + tcrp->incr_num_deq(); + for (unsigned i=0; i<num_reads; i++) + tcrp->incr_num_read(); + time_ns ts(secs, nsec); + tcrp->set_test_time(ts); + return tcrp; +} + +// === Test suite === + +QPID_AUTO_TEST_CASE(constructor_1) +{ + cout << test_filename << ".constructor_1: " << flush; + test_case_result_agregation tcra; + BOOST_CHECK_EQUAL(tcra.tc_average_mode(), true); + BOOST_CHECK_EQUAL(tcra.jid(), "Average"); + BOOST_CHECK_EQUAL(tcra.exception(), false); + BOOST_CHECK_EQUAL(tcra.exception_count(), 0U); + const time_ns& ts1 = tcra.start_time(); + BOOST_CHECK(ts1.is_zero()); + const time_ns& ts2 = tcra.stop_time(); + BOOST_CHECK(ts2.is_zero()); + const time_ns& ts3 = tcra.test_time(); + BOOST_CHECK(ts3.is_zero()); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(constructor_2) +{ + cout << test_filename << ".constructor_2: " << flush; + string jid("journal id"); + test_case_result_agregation tcra(jid); + BOOST_CHECK_EQUAL(tcra.tc_average_mode(), false); + BOOST_CHECK_EQUAL(tcra.jid(), jid); + BOOST_CHECK_EQUAL(tcra.exception(), false); + BOOST_CHECK_EQUAL(tcra.exception_count(), 0U); + const time_ns& ts1 = tcra.start_time(); + BOOST_CHECK(ts1.is_zero()); + const time_ns& ts2 = tcra.stop_time(); + BOOST_CHECK(ts2.is_zero()); + const time_ns& ts3 = tcra.test_time(); + BOOST_CHECK(ts3.is_zero()); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(add_test_case) +{ + cout << test_filename << ".add_test_case: " << flush; + string jid("jid1"); + test_case_result::shared_ptr tcrp1 = make_result("jid1", 10, 10, 0, 1, 101010101L); + test_case_result::shared_ptr tcrp2 = make_result("jid1", 25, 0, 35, 10, 20202020L); + test_case_result::shared_ptr tcrp3 = make_result("jid1", 0, 15, 5, 2, 555555555L); + test_case_result::shared_ptr tcrp4 = make_result("jid2", 100, 100, 100, 100, 323232324L); + test_case_result::shared_ptr tcrp5 = make_result("jid1", 5, 0, 0, 0, 100L); + tcrp5->add_exception(string("error 1"), false); + test_case_result::shared_ptr tcrp6 = make_result("jid3", 0, 5, 0, 0, 100L); + jexception e(0x123, "exception 2"); + tcrp6->add_exception(e, false); + test_case_result::shared_ptr tcrp7 = make_result("jid1", 0, 0, 0, 0, 0L); + test_case_result::shared_ptr tcrp8 = make_result("jid1", 200, 100, 300, 12, 323232224L); + + test_case_result_agregation tcra(jid); + check_agregate(tcra, 0, 0, 0, 0, 0, 0, 0L); + tcra.add_test_result(tcrp1); + check_agregate(tcra, 10, 10, 0, 1, 0, 1, 101010101L); + tcra.add_test_result(tcrp2); + check_agregate(tcra, 35, 10, 35, 2, 0, 11, 121212121L); + tcra.add_test_result(tcrp3); + check_agregate(tcra, 35, 25, 40, 3, 0, 13, 676767676L); + tcra.add_test_result(tcrp4); + check_agregate(tcra, 35, 25, 40, 3, 0, 13, 676767676L); + tcra.add_test_result(tcrp5); + check_agregate(tcra, 40, 25, 40, 4, 1, 13, 676767776L); + tcra.add_test_result(tcrp6); + check_agregate(tcra, 40, 25, 40, 4, 1, 13, 676767776L); + tcra.add_test_result(tcrp7); + check_agregate(tcra, 40, 25, 40, 5, 1, 13, 676767776L); + tcra.add_test_result(tcrp8); + check_agregate(tcra, 240, 125, 340, 6, 1, 26, 0L); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(add_test_case_average) +{ + cout << test_filename << ".add_test_case_average: " << flush; + test_case_result::shared_ptr tcrp1 = make_result("jid1", 10, 10, 0, 1, 101010101L); + test_case_result::shared_ptr tcrp2 = make_result("jid2", 25, 0, 35, 10, 20202020L); + test_case_result::shared_ptr tcrp3 = make_result("jid3", 0, 15, 5, 2, 555555555L); + test_case_result::shared_ptr tcrp4 = make_result("jid4", 100, 100, 100, 100, 323232324L); + test_case_result::shared_ptr tcrp5 = make_result("jid5", 5, 0, 0, 0, 100L); + tcrp5->add_exception(string("error 1"), false); + test_case_result::shared_ptr tcrp6 = make_result("jid6", 0, 5, 0, 0, 100L); + jexception e(0x123, "exception 2"); + tcrp6->add_exception(e, false); + test_case_result::shared_ptr tcrp7 = make_result("jid7", 0, 0, 0, 0, 0L); + test_case_result::shared_ptr tcrp8 = make_result("jid8", 200, 100, 300, 12, 222222022L); + + test_case_result_agregation tcra; + check_agregate(tcra, 0, 0, 0, 0, 0, 0, 0L); + tcra.add_test_result(tcrp1); + check_agregate(tcra, 10, 10, 0, 1, 0, 1, 101010101L); + tcra.add_test_result(tcrp2); + check_agregate(tcra, 35, 10, 35, 2, 0, 11, 121212121L); + tcra.add_test_result(tcrp3); + check_agregate(tcra, 35, 25, 40, 3, 0, 13, 676767676L); + tcra.add_test_result(tcrp4); + check_agregate(tcra, 135, 125, 140, 4, 0, 114, 0L); + tcra.add_test_result(tcrp5); + check_agregate(tcra, 140, 125, 140, 5, 1, 114, 100L); + tcra.add_test_result(tcrp6); + check_agregate(tcra, 140, 130, 140, 6, 2, 114, 200L); + tcra.add_test_result(tcrp7); + check_agregate(tcra, 140, 130, 140, 7, 2, 114, 200L); + tcra.add_test_result(tcrp8); + check_agregate(tcra, 340, 230, 440, 8, 2, 126, 222222222L); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_set.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_set.cpp new file mode 100644 index 0000000000..adbdf6884b --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_set.cpp @@ -0,0 +1,147 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../../unit_test.h" +#include <cstddef> +#include <iostream> +#include <sys/stat.h> +#include "test_case.h" +#include "test_case_set.h" + +using namespace boost::unit_test; +using namespace mrg::jtt; +using namespace std; + +QPID_AUTO_TEST_SUITE(jtt_test_case_set) + +const string csv_file("_ut_test_case_set.csv"); +const string test_filename("_ut_test_case_set"); + +// === Helper functions === + +bool check_csv_file(const char* filename) +{ + struct stat s; + if (::stat(filename, &s)) + return false; + if (S_ISREG(s.st_mode)) + return true; + return false; +} + +// === Test suite === + +QPID_AUTO_TEST_CASE(constructor) +{ + cout << test_filename << ".constructor: " << flush; + test_case_set tcs; + BOOST_CHECK(tcs.empty()); + BOOST_CHECK_EQUAL(tcs.size(), unsigned(0)); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(append_1) +{ + cout << test_filename << ".append_1: " << flush; + const unsigned test_case_num = 0x12345; + const u_int32_t num_msgs = 0x100; + const std::size_t min_data_size = 0x1000; + const std::size_t max_data_size = 0; + const bool auto_deq = true; + const std::size_t min_xid_size = 0x200; + const std::size_t max_xid_size = 0x200; + using mrg::jtt::test_case; + const test_case::transient_t transient = test_case::JTT_PERSISTNET; + const test_case::external_t external = test_case::JDL_INTERNAL; + const string comment = "This is a test"; + + test_case_set tcs; + tcs.append(test_case_num, num_msgs, min_data_size, max_data_size, auto_deq, min_xid_size, + max_xid_size, transient, external, comment); + BOOST_CHECK(!tcs.empty()); + BOOST_CHECK_EQUAL(tcs.size(), unsigned(1)); + test_case::shared_ptr tcp = tcs[0]; + BOOST_CHECK_EQUAL(tcp->test_case_num(), test_case_num); + BOOST_CHECK_EQUAL(tcp->num_msgs(), num_msgs); + BOOST_CHECK_EQUAL(tcp->min_data_size(), min_data_size); + BOOST_CHECK_EQUAL(tcp->max_data_size(), max_data_size); + BOOST_CHECK_EQUAL(tcp->min_xid_size(), min_xid_size); + BOOST_CHECK_EQUAL(tcp->max_xid_size(), max_xid_size); + BOOST_CHECK_EQUAL(tcp->transient(), transient); + BOOST_CHECK_EQUAL(tcp->external(), external); + BOOST_CHECK_EQUAL(tcp->comment(), comment); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(append_2) +{ + cout << test_filename << ".append_2: " << flush; + const unsigned test_case_num = 0x12345; + const u_int32_t num_msgs = 0x100; + const std::size_t min_data_size = 0x1000; + const std::size_t max_data_size = 0; + const bool auto_deq = true; + const std::size_t min_xid_size = 0x200; + const std::size_t max_xid_size = 0x200; + using mrg::jtt::test_case; + const test_case::transient_t transient = test_case::JTT_PERSISTNET; + const test_case::external_t external = test_case::JDL_INTERNAL; + const string comment = "This is a test"; + + test_case::shared_ptr tcp(new test_case(test_case_num, num_msgs, min_data_size, max_data_size, + auto_deq, min_xid_size, max_xid_size, transient, external, comment)); + test_case_set tcs; + tcs.append(tcp); + BOOST_CHECK(!tcs.empty()); + BOOST_CHECK_EQUAL(tcs.size(), unsigned(1)); + tcp = tcs[0]; + BOOST_CHECK_EQUAL(tcp->test_case_num(), test_case_num); + BOOST_CHECK_EQUAL(tcp->num_msgs(), num_msgs); + BOOST_CHECK_EQUAL(tcp->min_data_size(), min_data_size); + BOOST_CHECK_EQUAL(tcp->max_data_size(), max_data_size); + BOOST_CHECK_EQUAL(tcp->min_xid_size(), min_xid_size); + BOOST_CHECK_EQUAL(tcp->max_xid_size(), max_xid_size); + BOOST_CHECK_EQUAL(tcp->transient(), transient); + BOOST_CHECK_EQUAL(tcp->external(), external); + BOOST_CHECK_EQUAL(tcp->comment(), comment); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(append_from_csv) +{ + cout << test_filename << ".append_from_csv: " << flush; + test_case_set tcs; + BOOST_REQUIRE_MESSAGE(check_csv_file(csv_file.c_str()), "Test CSV file \"" << csv_file << + "\" is missing."); + tcs.append_from_csv(csv_file, false); + BOOST_CHECK(!tcs.empty()); + BOOST_CHECK_EQUAL(tcs.size(), unsigned(44)); + BOOST_CHECK_EQUAL(tcs.ignored(), unsigned(0)); + tcs.clear(); + BOOST_CHECK(tcs.empty()); + tcs.append_from_csv(csv_file, true); + BOOST_CHECK(!tcs.empty()); + BOOST_CHECK_EQUAL(tcs.size(), unsigned(18)); + BOOST_CHECK_EQUAL(tcs.ignored(), unsigned(26)); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_set.csv b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_set.csv new file mode 100644 index 0000000000..f886186275 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_set.csv @@ -0,0 +1,74 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +,,,,,,,"Msg size",,"Xid size",,,,,"enq-size",,"deq-size",,"txn-size",, +"Col. 0","1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20" +"Test #","tf","pf","amn","mn incr","#msgs","ms incr","Min","Max","Min","Max","auto-deq","transient","extern","bytes","dblks","bytes","dblks","bytes","dblks","comment" +,,,,,,,,,,,,,,,,,,,, +"Initialize only",,,,,,,,,,,,,,,,,,,, +0,"L",0,0,0,0,0,0,0,0,0,FALSE,FALSE,FALSE,44,1,0,0,0,0,"No messages - journal creation/initialization only" +,,,,,,,,,,,,,,,,,,,, +"Simple message combinations of persistent/deq transientueued/non-dequeued, transactional/non-transactional",,,,,,,,,,,,,,,,,,,, +1,"L",1,1,0,1,0,10,10,0,0,FALSE,FALSE,FALSE,54,1,0,0,0,0,"1 * 10-byte message" +2,"L",1,10,0,10,0,10,10,0,0,FALSE,FALSE,FALSE,54,1,0,0,0,0,"10 * 10-byte message" +3,"L",1,1,0,1,0,10,10,0,0,FALSE,TRUE,FALSE,54,1,0,0,0,0,"1 * 10-byte message [transient]" +4,"L",1,10,0,10,0,10,10,0,0,FALSE,TRUE,FALSE,54,1,0,0,0,0,"10 * 10-byte message [transient]" +5,"L",1,1,0,1,0,10,10,10,10,FALSE,FALSE,FALSE,64,1,0,0,0,0,"1 * 10-byte message [txn]" +6,"L",1,10,0,10,0,10,10,10,10,FALSE,FALSE,FALSE,64,1,0,0,0,0,"10 * 10-byte message [txn]" +7,"L",1,1,0,1,0,10,10,10,10,FALSE,TRUE,FALSE,64,1,0,0,0,0,"1 * 10-byte message [txn transient]" +8,"L",1,10,0,10,0,10,10,10,10,FALSE,TRUE,FALSE,64,1,0,0,0,0,"10 * 10-byte message [txn transient]" +9,"L",1,1,0,1,0,10,10,0,0,TRUE,FALSE,FALSE,54,1,32,1,0,0,"1 * 10-byte message [deq]" +10,"L",1,10,0,10,0,10,10,0,0,TRUE,FALSE,FALSE,54,1,32,1,0,0,"10 * 10-byte message [deq]" +11,"L",1,1,0,1,0,10,10,0,0,TRUE,TRUE,FALSE,54,1,32,1,0,0,"1 * 10-byte message [deq transient]" +12,"L",1,10,0,10,0,10,10,0,0,TRUE,TRUE,FALSE,54,1,32,1,0,0,"10 * 10-byte message [deq transient]" +13,"L",1,1,0,1,0,10,10,10,10,TRUE,FALSE,FALSE,64,1,54,1,46,1,"1 * 10-byte message [deq txn]" +14,"L",1,10,0,10,0,10,10,10,10,TRUE,FALSE,FALSE,64,1,54,1,46,1,"10 * 10-byte message [deq txn]" +15,"L",1,1,0,1,0,10,10,10,10,TRUE,TRUE,FALSE,64,1,54,1,46,1,"1 * 10-byte message [txn deq transient]" +16,"L",1,10,0,10,0,10,10,10,10,TRUE,TRUE,FALSE,64,1,54,1,46,1,"10 * 10-byte message [txn deq transient]" +17,"L",1,1,0,1,0,10,10,0,0,FALSE,FALSE,TRUE,54,1,0,0,0,0,"1 * 10-byte message [extern]" +18,"L",1,10,0,10,0,10,10,0,0,FALSE,FALSE,TRUE,54,1,0,0,0,0,"10 * 10-byte message [extern]" +19,"L",1,1,0,1,0,10,10,0,0,FALSE,TRUE,TRUE,54,1,0,0,0,0,"1 * 10-byte message [transient extern]" +20,"L",1,10,0,10,0,10,10,0,0,FALSE,TRUE,TRUE,54,1,0,0,0,0,"10 * 10-byte message [transient extern]" +21,"L",1,1,0,1,0,10,10,10,10,FALSE,FALSE,TRUE,64,1,0,0,0,0,"1 * 10-byte message [txn extern]" +22,"L",1,10,0,10,0,10,10,10,10,FALSE,FALSE,TRUE,64,1,0,0,0,0,"10 * 10-byte message [txn extern]" +23,"L",1,1,0,1,0,10,10,10,10,FALSE,TRUE,TRUE,64,1,0,0,0,0,"1 * 10-byte message [txn transient extern]" +24,"L",1,10,0,10,0,10,10,10,10,FALSE,TRUE,TRUE,64,1,0,0,0,0,"10 * 10-byte message [txn transient extern]" +25,"L",1,1,0,1,0,10,10,0,0,TRUE,FALSE,TRUE,54,1,32,1,0,0,"1 * 10-byte message [deq extern]" +26,"L",1,10,0,10,0,10,10,0,0,TRUE,FALSE,TRUE,54,1,32,1,0,0,"10 * 10-byte message [deq extern]" +27,"L",1,1,0,1,0,10,10,0,0,TRUE,TRUE,TRUE,54,1,32,1,0,0,"1 * 10-byte message [deq transient extern]" +28,"L",1,10,0,10,0,10,10,0,0,TRUE,TRUE,TRUE,54,1,32,1,0,0,"10 * 10-byte message [deq transient extern]" +29,"L",1,1,0,1,0,10,10,10,10,TRUE,FALSE,TRUE,64,1,54,1,46,1,"1 * 10-byte message [deq txn extern]" +30,"L",1,10,0,10,0,10,10,10,10,TRUE,FALSE,TRUE,64,1,54,1,46,1,"10 * 10-byte message [deq txn extern]" +31,"L",1,1,0,1,0,10,10,10,10,TRUE,TRUE,TRUE,64,1,54,1,46,1,"1 * 10-byte message [txn deq transient extern]" +32,"L",1,10,0,10,0,10,10,10,10,TRUE,TRUE,TRUE,64,1,54,1,46,1,"10 * 10-byte message [txn deq transient extern]" +,,,,,,,,,,,,,,,,,,,, +"High volume tests of random message lengths - RHM_WRONLY req'd for auto-dequeue == FALSE",,,,,,,,,,,,,,,,,,,, +33,"M",1,5000000,0,5000000,0,0,100,1,100,FALSE,RANDOM,RANDOM,244,2,0,0,0,0,"100 bytes xid max + 100 bytes data max [txn]" +34,"M",3,3000000,0,3000000,0,0,300,1,300,FALSE,RANDOM,RANDOM,644,6,0,0,0,0,"300 bytes xid max + 300 bytes data max [txn]" +35,"M",10,1600000,0,1600000,0,0,1000,1,1000,FALSE,RANDOM,RANDOM,2044,16,0,0,0,0,"1000 bytes xid max + 1000 bytes data max [txn]" +36,"M",30,600000,0,600000,0,0,3000,1,3000,FALSE,RANDOM,RANDOM,6044,48,0,0,0,0,"3000 bytes xid max + 3000 bytes data max [txn]" +37,"M",100,200000,0,200000,0,0,10000,1,10000,FALSE,RANDOM,RANDOM,20044,157,0,0,0,0,"10000 bytes xid max + 10000 bytes data max [txn]" +38,"M",300,60000,0,60000,0,0,30000,1,30000,FALSE,RANDOM,RANDOM,60044,470,0,0,0,0,"30000 bytes xid max + 30000 bytes data max [txn]" +39,"M",1000,20000,0,20000,0,0,100000,1,100000,FALSE,RANDOM,RANDOM,200044,1563,0,0,0,0,"100000 bytes xid max + 100000 bytes data max [txn]" +,,,,,,,,,,,,,,,,,,,, +"STANDARD PERFORMANCE BENCHMARK: 10,000,000 writes, data=212b (2 dblks)",,,,,,,,,,,,,,,,,,,, +40,"M",1,10000000,0,10000000,0,212,212,0,0,FALSE,FALSE,FALSE,256,2,0,0,0,0,"212 bytes data (2 dblks enq)" +41,"M",1,10000000,0,10000000,0,148,148,64,64,FALSE,FALSE,FALSE,256,2,0,0,0,0,"148 bytes data + 64 bytes xid (2 dblks enq)" +42,"M",1,10000000,0,10000000,0,212,212,0,0,TRUE,FALSE,FALSE,256,2,32,1,0,0,"212 bytes data (2 dblks enq + 1 dblk deq)" +43,"M",1,10000000,0,10000000,0,148,148,64,64,TRUE,FALSE,FALSE,256,2,108,1,100,1,"148 bytes data + 64 bytes xid (2 dblks enq + 1 dblks deq + 1 dblks txn)" diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/args.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/args.cpp new file mode 100644 index 0000000000..0f041c380e --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/args.cpp @@ -0,0 +1,226 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "args.h" + +#include <cstddef> +#include <iostream> + +namespace po = boost::program_options; + +namespace mrg +{ +namespace jtt +{ + +args::args(std::string opt_title): + _options_descr(opt_title), + format_chk(false), + keep_jrnls(false), + lld_rd_num(10), + lld_skip_num(100), + num_jrnls(1), + pause_secs(0), + randomize(false), + read_mode(), + read_prob(50), + recover_mode(false), + repeat_flag(false), + reuse_instance(false), + seed(0) +{ + _options_descr.add_options() + ("csv-file,c", + po::value<std::string>(&test_case_csv_file_name)->default_value("jtt.csv"), + "CSV file containing test cases.") + + ("format-chk", + po::value<bool>(&format_chk)->zero_tokens(), + "Check the format of each journal file.") + + ("help,h", "This help message.") + + ("jrnl-dir", + po::value<std::string>(&journal_dir)->default_value("/var/tmp/jtt"), + "Directory in which journal files will be placed.") + + ("keep-jrnls", + po::value<bool>(&keep_jrnls)->zero_tokens(), + "Keep all test journals.") + + ("lld-rd-num", + po::value<unsigned>(&lld_rd_num)->default_value(10), + "Number of consecutive messages to read after only dequeueing lld-skip-num " + "messages during lazy-loading. Ignored if read-mode is not set to LAZYLOAD.") + + ("lld-skip-num", + po::value<unsigned>(&lld_skip_num)->default_value(100), + "Number of consecutive messages to dequeue only (without reading) prior to " + "reading lld-rd-num messages. Ignored if read-mode is not set to LAZYLOAD.") + + ("num-jrnls", + po::value<unsigned>(&num_jrnls)->default_value(1), + "Number of simultaneous journal instances to test.") + + ("pause", + po::value<unsigned>(&pause_secs)->default_value(0), + "Pause in seconds between test cases (allows disk to catch up).") + + ("randomize", + po::value<bool>(&randomize)->zero_tokens(), + "Randomize the order of the tests.") + + ("read-mode", + po::value<read_arg>(&read_mode)->default_value(read_arg::NONE), + read_arg::descr().c_str()) + + ("read-prob", + po::value<unsigned>(&read_prob)->default_value(50), + "Read probability (percent) for each message when read-mode is set to RANDOM.") + + ("recover-mode", + po::value<bool>(&recover_mode)->zero_tokens(), + "Recover journal from the previous test for each test case.") + + ("repeat", + po::value<bool>(&repeat_flag)->zero_tokens(), + "Repeat all test cases indefinitely.") + + ("reuse-instance", + po::value<bool>(&reuse_instance)->zero_tokens(), + "Reuse journal instance for all test cases.") + + ("seed", + po::value<unsigned>(&seed)->default_value(0), + "Seed for use in random number generator.") + + ("analyzer", + po::value<std::string>(&jfile_analyzer)->default_value("./file_chk.py"), + "Journal file analyzer program to use when the --format-chk option is used, ignored otherwise.") + + ; +} + +bool +args::parse(int argc, char** argv) // return true if error, false if ok +{ + try + { + po::store(po::parse_command_line(argc, argv, _options_descr), _vmap); + po::notify(_vmap); + } + catch (const std::exception& e) + { + std::cout << "ERROR: " << e.what() << std::endl; + return usage(); + } + if (_vmap.count("help")) + return usage(); + if (num_jrnls == 0) + { + std::cout << "ERROR: num-jrnls must be 1 or more." << std::endl; + return usage(); + } + if (read_prob > 100) // read_prob is unsigned, so no need to check < 0 + { + std::cout << "ERROR: read-prob must be between 0 and 100 inclusive." << std::endl; + return usage(); + } + if (repeat_flag && keep_jrnls) + { + std::string resp; + std::cout << "WARNING: repeat and keep-jrnls: Monitor disk usage as test journals will" + " accumulate." << std::endl; + std::cout << "Continue? <y/n> "; + std::cin >> resp; + if (resp.size() == 1) + { + if (resp[0] != 'y' && resp[0] != 'Y') + return true; + } + else if (resp.size() == 3) // any combo of lower- and upper-case + { + if (resp[0] != 'y' && resp[0] != 'Y') + return true; + if (resp[1] != 'e' && resp[1] != 'E') + return true; + if (resp[2] != 's' && resp[2] != 'S') + return true; + } + else + return true; + } + return false; +} + +bool +args::usage() const +{ + std::cout << _options_descr << std::endl; + return true; +} + +void +args::print_args() const +{ + std::cout << "Number of journals: " << num_jrnls << std::endl; + std::cout << "Read mode: " << read_mode << std::endl; + if (read_mode.val() == read_arg::RANDOM) + std::cout << "Read probability: " << read_prob << " %" << std::endl; + if (read_mode.val() == read_arg::LAZYLOAD) + { + std::cout << "Lazy-load skips: " << lld_skip_num << std::endl; + std::cout << "Lazy-load reads: " << lld_rd_num << std::endl; + } + if (pause_secs) + std::cout << "Pause between test cases: " << pause_secs << " sec." << std::endl; + if (seed) + std::cout << "Randomize seed: " << seed << std::endl; + print_flags(); +} + +void +args::print_flags() const +{ + if (format_chk || keep_jrnls || randomize || recover_mode || repeat_flag || + reuse_instance) + { + std::cout << "Flag options:"; + // TODO: Get flag args and their strings directly from _options_descr. + if (format_chk) + std::cout << " format-chk"; + if (keep_jrnls) + std::cout << " keep-jrnls"; + if (randomize) + std::cout << " randomize"; + if (recover_mode) + std::cout << " recover-mode"; + if (repeat_flag) + std::cout << " repeat-flag"; + if (reuse_instance) + std::cout << " reuse-instance"; + std::cout << std::endl; + } + std::cout << std::endl; +} + +} // namespace jtt +} // namespace mrg diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/args.h b/qpid/cpp/src/tests/legacystore/jrnl/jtt/args.h new file mode 100644 index 0000000000..b6f7fb4a79 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/args.h @@ -0,0 +1,66 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef mrg_jtt_args_hpp +#define mrg_jtt_args_hpp + +#include <boost/program_options.hpp> +#include "read_arg.h" + +namespace mrg +{ +namespace jtt +{ + + struct args + { + boost::program_options::options_description _options_descr; + boost::program_options::variables_map _vmap; + + // Add args here + std::string jfile_analyzer; + std::string test_case_csv_file_name; + std::string journal_dir; + bool format_chk; + bool keep_jrnls; + unsigned lld_rd_num; + unsigned lld_skip_num; + unsigned num_jrnls; + unsigned pause_secs; + bool randomize; + read_arg read_mode; + unsigned read_prob; + bool recover_mode; + bool repeat_flag; + bool reuse_instance; + unsigned seed; + + args(std::string opt_title); + bool parse(int argc, char** argv); // return true if error, false if ok + bool usage() const; // return true + void print_args() const; + void print_flags() const; + }; + +} // namespace jtt +} // namespace mrg + +#endif // ifndef mrg_jtt_args_hpp diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/data_src.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/data_src.cpp new file mode 100644 index 0000000000..3530e0b223 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/data_src.cpp @@ -0,0 +1,87 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "data_src.h" + +#include <cstddef> +#include <iomanip> +#include <sstream> + +namespace mrg +{ +namespace jtt +{ + +char data_src::_data_src[data_src::max_dsize]; +char data_src::_xid_src[data_src::max_xsize]; +bool data_src::_initialized = data_src::__init(); +u_int64_t data_src::_xid_cnt = 0ULL; +mrg::journal::smutex data_src::_sm; + +data_src::data_src() +{} + +bool +data_src::__init() +{ + for (unsigned i=0; i<max_dsize; i++) + _data_src[i] = '0' + ((i + 1) % 10); // 123456789012345... + for (unsigned j=0; j<max_xsize; j++) + _xid_src[j] = 'a' + (j % 26); // abc...xyzabc... + return true; +} + +const char* +data_src::get_data(const std::size_t offs) +{ + if (offs >= max_dsize) return 0; + return _data_src + offs; +} + +std::string +data_src::get_xid(const std::size_t xid_size) +{ + if (xid_size == 0) + return ""; + std::ostringstream oss; + oss << std::setfill('0'); + if (xid_size < 9) + oss << std::setw(xid_size) << get_xid_cnt(); + else if (xid_size < 13) + oss << "xid:" << std::setw(xid_size - 4) << get_xid_cnt(); + else + { + oss << "xid:" << std::setw(8) << get_xid_cnt() << ":"; + oss.write(get_xid_content(13), xid_size - 13); + } + return oss.str(); +} + +const char* +data_src::get_xid_content(const std::size_t offs) +{ + if (offs >= max_xsize) return 0; + return _xid_src + offs; +} + +} // namespace jtt +} // namespace mrg + diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/data_src.h b/qpid/cpp/src/tests/legacystore/jrnl/jtt/data_src.h new file mode 100644 index 0000000000..66dc613787 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/data_src.h @@ -0,0 +1,66 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef mrg_jtt_data_src_hpp +#define mrg_jtt_data_src_hpp + +#include <cstddef> +#include "qpid/legacystore/jrnl/slock.h" +#include "qpid/legacystore/jrnl/smutex.h" +#include <pthread.h> +#include <string> +#include <sys/types.h> + +#define DATA_SIZE 1024 * 1024 +#define XID_SIZE 1024 * 1024 + +namespace mrg +{ +namespace jtt +{ + class data_src + { + public: + static const std::size_t max_dsize = DATA_SIZE; + static const std::size_t max_xsize = XID_SIZE; + + private: + static char _data_src[]; + static char _xid_src[]; + static u_int64_t _xid_cnt; + static bool _initialized; + static mrg::journal::smutex _sm; + + public: + static const char* get_data(const std::size_t offs); + static std::string get_xid(const std::size_t xid_size); + + private: + data_src(); + static u_int64_t get_xid_cnt() { mrg::journal::slock s(_sm); return _xid_cnt++; } + static const char* get_xid_content(const std::size_t offs); + static bool __init(); + }; + +} // namespace jtt +} // namespace mrg + +#endif // ifndef mrg_jtt_data_src_hpp diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/jfile_chk.py b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jfile_chk.py new file mode 100755 index 0000000000..36ef511f5c --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jfile_chk.py @@ -0,0 +1,838 @@ +#!/usr/bin/env python + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +import sys +import getopt +import string +import xml.parsers.expat +from struct import unpack, calcsize +from time import gmtime, strftime + +dblk_size = 128 +sblk_size = 4 * dblk_size +jfsize = None +hdr_ver = 1 + +TEST_NUM_COL = 0 +NUM_MSGS_COL = 5 +MIN_MSG_SIZE_COL = 7 +MAX_MSG_SIZE_COL = 8 +MIN_XID_SIZE_COL = 9 +MAX_XID_SIZE_COL = 10 +AUTO_DEQ_COL = 11 +TRANSIENT_COL = 12 +EXTERN_COL = 13 +COMMENT_COL = 20 + +owi_mask = 0x01 +transient_mask = 0x10 +extern_mask = 0x20 + +printchars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ ' + + + +#== global functions =========================================================== + +def load(f, klass): + args = load_args(f, klass) + subclass = klass.discriminate(args) + result = subclass(*args) + if subclass != klass: + result.init(f, *load_args(f, subclass)) + result.skip(f) + return result; + +def load_args(f, klass): + size = calcsize(klass.format) + foffs = f.tell(), + bin = f.read(size) + if len(bin) != size: + raise Exception("end of file") + return foffs + unpack(klass.format, bin) + +def size_blks(size, blk_size): + return (size + blk_size - 1)/blk_size + +def rem_in_blk(f, blk_size): + foffs = f.tell() + return (size_blks(f.tell(), blk_size) * blk_size) - foffs; + +def file_full(f): + return f.tell() >= jfsize + +def isprintable(s): + return s.strip(printchars) == '' + +def print_xid(xidsize, xid): + if xid == None: + if xidsize > 0: + raise Exception('Inconsistent XID size: xidsize=%d, xid=None' % xidsize) + return '' + if isprintable(xid): + xidstr = split_str(xid) + else: + xidstr = hex_split_str(xid) + if xidsize != len(xid): + raise Exception('Inconsistent XID size: xidsize=%d, xid(%d)=\"%s\"' % (xidsize, len(xid), xidstr)) + return 'xid(%d)=\"%s\" ' % (xidsize, xidstr) + +def print_data(dsize, data): + if data == None: + return '' + if isprintable(data): + datastr = split_str(data) + else: + datastr = hex_split_str(data) + if dsize != len(data): + raise Exception('Inconsistent data size: dsize=%d, data(%d)=\"%s\"' % (dsize, len(data), datastr)) + return 'data(%d)=\"%s\" ' % (dsize, datastr) + +def hex_split_str(s, split_size = 50): + if len(s) <= split_size: + return hex_str(s, 0, len(s)) + if len(s) > split_size + 25: + return hex_str(s, 0, 10) + ' ... ' + hex_str(s, 55, 65) + ' ... ' + hex_str(s, len(s)-10, len(s)) + return hex_str(s, 0, 10) + ' ... ' + hex_str(s, len(s)-10, len(s)) + +def hex_str(s, b, e): + o = '' + for i in range(b, e): + if isprintable(s[i]): + o += s[i] + else: + o += '\\%02x' % ord(s[i]) + return o + +def split_str(s, split_size = 50): + if len(s) < split_size: + return s + return s[:25] + ' ... ' + s[-25:] + +def inv_str(s): + si = '' + for i in range(0,len(s)): + si += chr(~ord(s[i]) & 0xff) + return si + +def load_file_data(f, size, data): + if size == 0: + return (data, True) + if data == None: + loaded = 0 + else: + loaded = len(data) + foverflow = f.tell() + size - loaded > jfsize + if foverflow: + rsize = jfsize - f.tell() + else: + rsize = size - loaded + bin = f.read(rsize) + if data == None: + data = unpack('%ds' % (rsize), bin)[0] + else: + data = data + unpack('%ds' % (rsize), bin)[0] + return (data, not foverflow) + +def exit(code, qflag): + if code != 0 or not qflag: + print out.getvalue() + out.close() + sys.exit(code) + +#== class Sizeable ============================================================= + +class Sizeable: + + def size(self): + classes = [self.__class__] + + size = 0 + while classes: + cls = classes.pop() + if hasattr(cls, "format"): + size += calcsize(cls.format) + classes.extend(cls.__bases__) + + return size + + +#== class Hdr ================================================================== + +class Hdr(Sizeable): + + format = '=4sBBHQ' + + def discriminate(args): + return CLASSES.get(args[1][-1], Hdr) + discriminate = staticmethod(discriminate) + + def __init__(self, foffs, magic, ver, end, flags, rid): + self.foffs = foffs + self.magic = magic + self.ver = ver + self.end = end + self.flags = flags + self.rid = rid + if self.magic[-1] not in ['0x00', 'a', 'c', 'd', 'e', 'f', 'x']: + error = 3 + + def __str__(self): + if self.empty(): + return '0x%08x: <empty>' % (self.foffs) + if self.magic[-1] == 'x': + return '0x%08x: [\"%s\"]' % (self.foffs, self.magic) + if self.magic[-1] in ['a', 'c', 'd', 'e', 'f', 'x']: + return '0x%08x: [\"%s\" v=%d e=%d f=0x%04x rid=0x%x]' % (self.foffs, self.magic, self.ver, self.end, self.flags, self.rid) + return '0x%08x: <error, unknown magic \"%s\" (possible overwrite boundary?)>' % (self.foffs, self.magic) + + def empty(self): + return self.magic == '\x00'*4 + + def owi(self): + return self.flags & owi_mask != 0 + + def skip(self, f): + f.read(rem_in_blk(f, dblk_size)) + + def check(self): + if self.empty() or self.magic[:3] != 'RHM' or self.magic[3] not in ['a', 'c', 'd', 'e', 'f', 'x']: + return True + if self.ver != hdr_ver and self.magic[-1] != 'x': + raise Exception('%s: Invalid header version: found %d, expected %d.' % (self, self.ver, hdr_ver)) + return False + + +#== class FileHdr ============================================================== + +class FileHdr(Hdr): + + format = '=2H4x3Q' + + def init(self, f, foffs, fid, lid, fro, time_sec, time_ns): + self.fid = fid + self.lid = lid + self.fro = fro + self.time_sec = time_sec + self.time_ns = time_ns + + def __str__(self): + return '%s fid=%d lid=%d fro=0x%08x t=%s' % (Hdr.__str__(self), self.fid, self.lid, self.fro, self.timestamp_str()) + + def skip(self, f): + f.read(rem_in_blk(f, sblk_size)) + + def timestamp(self): + return (self.time_sec, self.time_ns) + + def timestamp_str(self): + ts = gmtime(self.time_sec) + fstr = '%%a %%b %%d %%H:%%M:%%S.%09d %%Y' % (self.time_ns) + return strftime(fstr, ts) + + +#== class DeqHdr =============================================================== + +class DeqHdr(Hdr): + + format = '=QQ' + + def init(self, f, foffs, deq_rid, xidsize): + self.deq_rid = deq_rid + self.xidsize = xidsize + self.xid = None + self.deq_tail = None + self.xid_complete = False + self.tail_complete = False + self.tail_bin = None + self.tail_offs = 0 + self.load(f) + + def load(self, f): + if self.xidsize == 0: + self.xid_complete = True + self.tail_complete = True + else: + if not self.xid_complete: + ret = load_file_data(f, self.xidsize, self.xid) + self.xid = ret[0] + self.xid_complete = ret[1] + if self.xid_complete and not self.tail_complete: + ret = load_file_data(f, calcsize(RecTail.format), self.tail_bin) + self.tail_bin = ret[0] + if ret[1]: + self.enq_tail = RecTail(self.tail_offs, *unpack(RecTail.format, self.tail_bin)) + if self.enq_tail.magic_inv != inv_str(self.magic) or self.enq_tail.rid != self.rid: + print " > %s" % self + raise Exception('Invalid dequeue record tail (magic=%s; rid=%d) at 0x%08x' % (self.enq_tail, self.enq_tail.rid, self.enq_tail.foffs)) + self.enq_tail.skip(f) + self.tail_complete = ret[1] + return self.complete() + + def complete(self): + return self.xid_complete and self.tail_complete + + def __str__(self): + return '%s %sdrid=0x%x' % (Hdr.__str__(self), print_xid(self.xidsize, self.xid), self.deq_rid) + + +#== class TxnHdr =============================================================== + +class TxnHdr(Hdr): + + format = '=Q' + + def init(self, f, foffs, xidsize): + self.xidsize = xidsize + self.xid = None + self.tx_tail = None + self.xid_complete = False + self.tail_complete = False + self.tail_bin = None + self.tail_offs = 0 + self.load(f) + + def load(self, f): + if not self.xid_complete: + ret = load_file_data(f, self.xidsize, self.xid) + self.xid = ret[0] + self.xid_complete = ret[1] + if self.xid_complete and not self.tail_complete: + ret = load_file_data(f, calcsize(RecTail.format), self.tail_bin) + self.tail_bin = ret[0] + if ret[1]: + self.enq_tail = RecTail(self.tail_offs, *unpack(RecTail.format, self.tail_bin)) + if self.enq_tail.magic_inv != inv_str(self.magic) or self.enq_tail.rid != self.rid: + print " > %s" % self + raise Exception('Invalid transaction record tail (magic=%s; rid=%d) at 0x%08x' % (self.enq_tail, self.enq_tail.rid, self.enq_tail.foffs)) + self.enq_tail.skip(f) + self.tail_complete = ret[1] + return self.complete() + + def complete(self): + return self.xid_complete and self.tail_complete + + def __str__(self): + return '%s %s' % (Hdr.__str__(self), print_xid(self.xidsize, self.xid)) + + +#== class RecTail ============================================================== + +class RecTail(Sizeable): + + format = '=4sQ' + + def __init__(self, foffs, magic_inv, rid): + self.foffs = foffs + self.magic_inv = magic_inv + self.rid = rid + + def __str__(self): + magic = inv_str(self.magic_inv) + return '[\"%s\" rid=0x%x]' % (magic, self.rid) + + def skip(self, f): + f.read(rem_in_blk(f, dblk_size)) + + +#== class EnqRec =============================================================== + +class EnqRec(Hdr): + + format = '=QQ' + + def init(self, f, foffs, xidsize, dsize): + self.xidsize = xidsize + self.dsize = dsize + self.transient = self.flags & transient_mask > 0 + self.extern = self.flags & extern_mask > 0 + self.xid = None + self.data = None + self.enq_tail = None + self.xid_complete = False + self.data_complete = False + self.tail_complete = False + self.tail_bin = None + self.tail_offs = 0 + self.load(f) + + def load(self, f): + if not self.xid_complete: + ret = load_file_data(f, self.xidsize, self.xid) + self.xid = ret[0] + self.xid_complete = ret[1] + if self.xid_complete and not self.data_complete: + if self.extern: + self.data_complete = True + else: + ret = load_file_data(f, self.dsize, self.data) + self.data = ret[0] + self.data_complete = ret[1] + if self.data_complete and not self.tail_complete: + ret = load_file_data(f, calcsize(RecTail.format), self.tail_bin) + self.tail_bin = ret[0] + if ret[1]: + self.enq_tail = RecTail(self.tail_offs, *unpack(RecTail.format, self.tail_bin)) + if self.enq_tail.magic_inv != inv_str(self.magic) or self.enq_tail.rid != self.rid: + print " > %s" % self + raise Exception('Invalid enqueue record tail (magic=%s; rid=%d) at 0x%08x' % (self.enq_tail, self.enq_tail.rid, self.enq_tail.foffs)) + self.enq_tail.skip(f) + self.tail_complete = ret[1] + return self.complete() + + def complete(self): + return self.xid_complete and self.data_complete and self.tail_complete + + def print_flags(self): + s = '' + if self.transient: + s = '*TRANSIENT' + if self.extern: + if len(s) > 0: + s += ',EXTERNAL' + else: + s = '*EXTERNAL' + if len(s) > 0: + s += '*' + return s + + def __str__(self): + return '%s %s%s %s %s' % (Hdr.__str__(self), print_xid(self.xidsize, self.xid), print_data(self.dsize, self.data), self.enq_tail, self.print_flags()) + + +#== class Main ================================================================= + +class Main: + def __init__(self, argv): + self.bfn = None + self.csvfn = None + self.jdir = None + self.aflag = False + self.hflag = False + self.qflag = False + self.tnum = None + self.num_jfiles = None + self.num_msgs = None + self.msg_len = None + self.auto_deq = None + self.xid_len = None + self.transient = None + self.extern = None + + self.file_start = 0 + self.file_num = 0 + self.fro = 0x200 + self.emap = {} + self.tmap = {} + self.rec_cnt = 0 + self.msg_cnt = 0 + self.txn_msg_cnt = 0 + self.fhdr = None + self.f = None + self.first_rec = False + self.last_file = False + self.last_rid = -1 + self.fhdr_owi_at_msg_start = None + + self.proc_args(argv) + self.proc_csv() + self.read_jinf() + + def run(self): + try: + start_info = self.analyze_files() + stop = self.advance_file(*start_info) + except Exception: + print 'WARNING: All journal files are empty.' + if self.num_msgs > 0: + raise Exception('All journal files are empty, but %d msgs expectd.' % self.num_msgs) + else: + stop = True + while not stop: + warn = '' + if file_full(self.f): + stop = self.advance_file() + if stop: + break + hdr = load(self.f, Hdr) + if hdr.empty(): + stop = True; + break + if hdr.check(): + stop = True; + else: + self.rec_cnt += 1 + self.fhdr_owi_at_msg_start = self.fhdr.owi() + if self.first_rec: + if self.fhdr.fro != hdr.foffs: + raise Exception('File header first record offset mismatch: fro=0x%08x; rec_offs=0x%08x' % (self.fhdr.fro, hdr.foffs)) + else: + if not self.qflag: print ' * fro ok: 0x%08x' % self.fhdr.fro + self.first_rec = False + if isinstance(hdr, EnqRec) and not stop: + while not hdr.complete(): + stop = self.advance_file() + if stop: + break + hdr.load(self.f) + if self.extern != None: + if hdr.extern: + if hdr.data != None: + raise Exception('Message data found on external record') + else: + if self.msg_len > 0 and len(hdr.data) != self.msg_len: + raise Exception('Message length (%d) incorrect; expected %d' % (len(hdr.data), self.msg_len)) + else: + if self.msg_len > 0 and len(hdr.data) != self.msg_len: + raise Exception('Message length (%d) incorrect; expected %d' % (len(hdr.data), self.msg_len)) + if self.xid_len > 0 and len(hdr.xid) != self.xid_len: + print ' ERROR: XID length (%d) incorrect; expected %d' % (len(hdr.xid), self.xid_len) + sys.exit(1) + #raise Exception('XID length (%d) incorrect; expected %d' % (len(hdr.xid), self.xid_len)) + if self.transient != None: + if self.transient: + if not hdr.transient: + raise Exception('Expected transient record, found persistent') + else: + if hdr.transient: + raise Exception('Expected persistent record, found transient') + stop = not self.check_owi(hdr) + if stop: + warn = ' (WARNING: OWI mismatch - could be overwrite boundary.)' + else: + self.msg_cnt += 1 + if self.aflag or self.auto_deq: + if hdr.xid == None: + self.emap[hdr.rid] = (self.fhdr.fid, hdr, False) + else: + self.txn_msg_cnt += 1 + if hdr.xid in self.tmap: + self.tmap[hdr.xid].append((self.fhdr.fid, hdr)) #Append tuple to existing list + else: + self.tmap[hdr.xid] = [(self.fhdr.fid, hdr)] # Create new list + elif isinstance(hdr, DeqHdr) and not stop: + while not hdr.complete(): + stop = self.advance_file() + if stop: + break + hdr.load(self.f) + stop = not self.check_owi(hdr) + if stop: + warn = ' (WARNING: OWI mismatch - could be overwrite boundary.)' + else: + if self.auto_deq != None: + if not self.auto_deq: + warn = ' WARNING: Dequeue record rid=%d found in non-dequeue test - ignoring.' % hdr.rid + if self.aflag or self.auto_deq: + if hdr.xid == None: + if hdr.deq_rid in self.emap: + if self.emap[hdr.deq_rid][2]: + warn = ' (WARNING: dequeue rid 0x%x dequeues locked enqueue record 0x%x)' % (hdr.rid, hdr.deq_rid) + del self.emap[hdr.deq_rid] + else: + warn = ' (WARNING: rid being dequeued 0x%x not found in enqueued records)' % hdr.deq_rid + else: + if hdr.deq_rid in self.emap: + t = self.emap[hdr.deq_rid] + self.emap[hdr.deq_rid] = (t[0], t[1], True) # Lock enq record + if hdr.xid in self.tmap: + self.tmap[hdr.xid].append((self.fhdr.fid, hdr)) #Append to existing list + else: + self.tmap[hdr.xid] = [(self.fhdr.fid, hdr)] # Create new list + elif isinstance(hdr, TxnHdr) and not stop: + while not hdr.complete(): + stop = self.advance_file() + if stop: + break + hdr.load(self.f) + stop = not self.check_owi(hdr) + if stop: + warn = ' (WARNING: OWI mismatch - could be overwrite boundary.)' + else: + if hdr.xid in self.tmap: + mismatched_rids = [] + if hdr.magic[-1] == 'c': # commit + for rec in self.tmap[hdr.xid]: + if isinstance(rec[1], EnqRec): + self.emap[rec[1].rid] = (rec[0], rec[1], False) # Transfer enq to emap + elif isinstance(rec[1], DeqHdr): + if rec[1].deq_rid in self.emap: + del self.emap[rec[1].deq_rid] # Delete from emap + else: + mismatched_rids.append('0x%x' % rec[1].deq_rid) + else: + raise Exception('Unknown header found in txn map: %s' % rec[1]) + elif hdr.magic[-1] == 'a': # abort + for rec in self.tmap[hdr.xid]: + if isinstance(rec[1], DeqHdr): + if rec[1].deq_rid in self.emap: + t = self.emap[rec[1].deq_rid] + self.emap[rec[1].deq_rid] = (t[0], t[1], False) # Unlock enq record + del self.tmap[hdr.xid] + if len(mismatched_rids) > 0: + warn = ' (WARNING: transactional dequeues not found in enqueue map; rids=%s)' % mismatched_rids + else: + warn = ' (WARNING: %s not found in transaction map)' % print_xid(len(hdr.xid), hdr.xid) + if not self.qflag: print ' > %s%s' % (hdr, warn) + if not stop: + stop = (self.last_file and hdr.check()) or hdr.empty() or self.fhdr.empty() + + def analyze_files(self): + fname = '' + fnum = -1 + rid = -1 + fro = -1 + tss = '' + if not self.qflag: print 'Analyzing journal files:' + owi_found = False + for i in range(0, self.num_jfiles): + jfn = self.jdir + '/' + self.bfn + '.%04d.jdat' % i + f = open(jfn) + fhdr = load(f, Hdr) + if fhdr.empty(): + if not self.qflag: + print ' %s: file empty' % jfn + break + if i == 0: + init_owi = fhdr.owi() + fname = jfn + fnum = i + rid = fhdr.rid + fro = fhdr.fro + tss = fhdr.timestamp_str() + elif fhdr.owi() != init_owi and not owi_found: + fname = jfn + fnum = i + rid = fhdr.rid + fro = fhdr.fro + tss = fhdr.timestamp_str() + owi_found = True + if not self.qflag: + print ' %s: owi=%s rid=0x%x, fro=0x%08x ts=%s' % (jfn, fhdr.owi(), fhdr.rid, fhdr.fro, fhdr.timestamp_str()) + if fnum < 0 or rid < 0 or fro < 0: + raise Exception('All journal files empty') + if not self.qflag: print ' Oldest complete file: %s: rid=%d, fro=0x%08x ts=%s' % (fname, rid, fro, tss) + return (fnum, rid, fro) + + def advance_file(self, *start_info): + seek_flag = False + if len(start_info) == 3: + self.file_start = self.file_num = start_info[0] + self.fro = start_info[2] + seek_flag = True + if self.f != None and file_full(self.f): + self.file_num = self.incr_fnum() + if self.file_num == self.file_start: + return True + if self.file_start == 0: + self.last_file = self.file_num == self.num_jfiles - 1 + else: + self.last_file = self.file_num == self.file_start - 1 + if self.file_num < 0 or self.file_num >= self.num_jfiles: + raise Exception('Bad file number %d' % self.file_num) + jfn = self.jdir + '/' + self.bfn + '.%04d.jdat' % self.file_num + self.f = open(jfn) + self.fhdr = load(self.f, Hdr) + if seek_flag and self.f.tell() != self.fro: + self.f.seek(self.fro) + self.first_rec = True + if not self.qflag: print jfn, ": ", self.fhdr + return False + + def incr_fnum(self): + self.file_num += 1 + if self.file_num >= self.num_jfiles: + self.file_num = 0; + return self.file_num + + def check_owi(self, hdr): + return self.fhdr_owi_at_msg_start == hdr.owi() + + def check_rid(self, hdr): + if self.last_rid != -1 and hdr.rid <= self.last_rid: + return False + self.last_rid = hdr.rid + return True + + def read_jinf(self): + filename = self.jdir + '/' + self.bfn + '.jinf' + try: + f = open(filename, 'r') + except IOError: + print 'ERROR: Unable to open jinf file %s' % filename + sys.exit(1) + p = xml.parsers.expat.ParserCreate() + p.StartElementHandler = self.handleStartElement + p.CharacterDataHandler = self.handleCharData + p.EndElementHandler = self.handleEndElement + p.ParseFile(f) + if self.num_jfiles == None: + print 'ERROR: number_jrnl_files not found in jinf file "%s"!' % filename + if jfsize == None: + print 'ERROR: jrnl_file_size_sblks not found in jinf file "%s"!' % filename + if self.num_jfiles == None or jfsize == None: + sys.exit(1) + + def handleStartElement(self, name, attrs): + global jfsize + if name == 'number_jrnl_files': + self.num_jfiles = int(attrs['value']) + if name == 'jrnl_file_size_sblks': + jfsize = (int(attrs['value']) + 1) * sblk_size + + def handleCharData(self, data): pass + + def handleEndElement(self, name): pass + + def proc_csv(self): + if self.csvfn != None and self.tnum != None: + tparams = self.get_test(self.csvfn, self.tnum) + if tparams == None: + print 'ERROR: Test %d not found in CSV file "%s"' % (self.tnum, self.csvfn) + sys.exit(1) + self.num_msgs = tparams['num_msgs'] + if tparams['min_size'] == tparams['max_size']: + self.msg_len = tparams['max_size'] + else: + self.msg_len = 0 + self.auto_deq = tparams['auto_deq'] + if tparams['xid_min_size'] == tparams['xid_max_size']: + self.xid_len = tparams['xid_max_size'] + else: + self.xid_len = 0 + self.transient = tparams['transient'] + self.extern = tparams['extern'] + + def get_test(self, filename, tnum): + try: + f=open(filename, 'r') + except IOError: + print 'ERROR: Unable to open CSV file "%s"' % filename + sys.exit(1) + for l in f: + sl = l.strip().split(',') + if len(sl[0]) > 0 and sl[0][0] != '"': + try: + if (int(sl[TEST_NUM_COL]) == tnum): + return { 'num_msgs':int(sl[NUM_MSGS_COL]), + 'min_size':int(sl[MIN_MSG_SIZE_COL]), + 'max_size':int(sl[MAX_MSG_SIZE_COL]), + 'auto_deq':not (sl[AUTO_DEQ_COL] == 'FALSE' or sl[AUTO_DEQ_COL] == '0'), + 'xid_min_size':int(sl[MIN_XID_SIZE_COL]), + 'xid_max_size':int(sl[MAX_XID_SIZE_COL]), + 'transient':not (sl[TRANSIENT_COL] == 'FALSE' or sl[TRANSIENT_COL] == '0'), + 'extern':not (sl[EXTERN_COL] == 'FALSE' or sl[EXTERN_COL] == '0'), + 'comment':sl[COMMENT_COL] } + except Exception: + pass + return None + + def proc_args(self, argv): + try: + opts, args = getopt.getopt(sys.argv[1:], "ab:c:d:hqt:", ["analyse", "base-filename=", "csv-filename=", "dir=", "help", "quiet", "test-num="]) + except getopt.GetoptError: + self.usage() + sys.exit(2) + for o, a in opts: + if o in ("-h", "--help"): + self.usage() + sys.exit() + if o in ("-a", "--analyze"): + self.aflag = True + if o in ("-b", "--base-filename"): + self.bfn = a + if o in ("-c", "--csv-filename"): + self.csvfn = a + if o in ("-d", "--dir"): + self.jdir = a + if o in ("-q", "--quiet"): + self.qflag = True + if o in ("-t", "--test-num"): + if not a.isdigit(): + print 'ERROR: Illegal test-num argument. Must be a non-negative number' + sys.exit(2) + self.tnum = int(a) + if self.bfn == None or self.jdir == None: + print 'ERROR: Missing requred args.' + self.usage() + sys.exit(2) + if self.tnum != None and self.csvfn == None: + print 'ERROR: Test number specified, but not CSV file' + self.usage() + sys.exit(2) + + def usage(self): + print 'Usage: %s opts' % sys.argv[0] + print ' where opts are in either short or long format (*=req\'d):' + print ' -a --analyze Analyze enqueue/dequeue records' + print ' -b --base-filename [string] * Base filename for journal files' + print ' -c --csv-filename [string] CSV filename containing test parameters' + print ' -d --dir [string] * Journal directory containing journal files' + print ' -h --help Print help' + print ' -q --quiet Quiet (reduced output)' + print ' -t --test-num [int] Test number from CSV file - only valid if CSV file named' + + def report(self): + if not self.qflag: + print + print ' === REPORT ====' + if self.num_msgs > 0 and self.msg_cnt != self.num_msgs: + print 'WARNING: Found %d messages; %d expected.' % (self.msg_cnt, self.num_msgs) + if len(self.emap) > 0: + print + print 'Remaining enqueued records (sorted by rid): ' + keys = sorted(self.emap.keys()) + for k in keys: + if self.emap[k][2] == True: # locked + locked = ' (locked)' + else: + locked = '' + print " fid=%d %s%s" % (self.emap[k][0], self.emap[k][1], locked) + print 'WARNING: Enqueue-Dequeue mismatch, %d enqueued records remain.' % len(self.emap) + if len(self.tmap) > 0: + txn_rec_cnt = 0 + print + print 'Remaining transactions: ' + for t in self.tmap: + print_xid(len(t), t) + for r in self.tmap[t]: + print " fid=%d %s" % (r[0], r[1]) + print " Total: %d records for xid %s" % (len(self.tmap[t]), t) + txn_rec_cnt += len(self.tmap[t]) + print 'WARNING: Incomplete transactions, %d xids remain containing %d records.' % (len(self.tmap), txn_rec_cnt) + print '%d enqueues, %d journal records processed.' % (self.msg_cnt, self.rec_cnt) + + +#=============================================================================== + +CLASSES = { + "a": TxnHdr, + "c": TxnHdr, + "d": DeqHdr, + "e": EnqRec, + "f": FileHdr +} + +m = Main(sys.argv) +m.run() +m.report() + +sys.exit(None) diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_init_params.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_init_params.cpp new file mode 100644 index 0000000000..1bc04110af --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_init_params.cpp @@ -0,0 +1,77 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "jrnl_init_params.h" + +namespace mrg +{ +namespace jtt +{ + +jrnl_init_params::jrnl_init_params(const std::string& jid, const std::string& jdir, const std::string& base_filename, + const u_int16_t num_jfiles, const bool ae, const u_int16_t ae_max_jfiles, const u_int32_t jfsize_sblks, + const u_int16_t wcache_num_pages, const u_int32_t wcache_pgsize_sblks): + _jid(jid), + _jdir(jdir), + _base_filename(base_filename), + _num_jfiles(num_jfiles), + _ae(ae), + _ae_max_jfiles(ae_max_jfiles), + _jfsize_sblks(jfsize_sblks), + _wcache_num_pages(wcache_num_pages), + _wcache_pgsize_sblks(wcache_pgsize_sblks) +{} + +jrnl_init_params::jrnl_init_params(const jrnl_init_params& jp): + _jid(jp._jid), + _jdir(jp._jdir), + _base_filename(jp._base_filename), + _num_jfiles(jp._num_jfiles), + _ae(jp._ae), + _ae_max_jfiles(jp._ae_max_jfiles), + _jfsize_sblks(jp._jfsize_sblks), + _wcache_num_pages(jp._wcache_num_pages), + _wcache_pgsize_sblks(jp._wcache_pgsize_sblks) +{} + +jrnl_init_params::jrnl_init_params(const jrnl_init_params* const jp_ptr): + _jid(jp_ptr->_jid), + _jdir(jp_ptr->_jdir), + _base_filename(jp_ptr->_base_filename), + _num_jfiles(jp_ptr->_num_jfiles), + _ae(jp_ptr->_ae), + _ae_max_jfiles(jp_ptr->_ae_max_jfiles), + _jfsize_sblks(jp_ptr->_jfsize_sblks), + _wcache_num_pages(jp_ptr->_wcache_num_pages), + _wcache_pgsize_sblks(jp_ptr->_wcache_pgsize_sblks) +{} + +// static initializers + +const u_int16_t jrnl_init_params::def_num_jfiles = 8; +const bool jrnl_init_params::def_ae = false; +const u_int16_t jrnl_init_params::def_ae_max_jfiles = 0; +const u_int32_t jrnl_init_params::def_jfsize_sblks = 0xc00; +const u_int16_t jrnl_init_params::def_wcache_num_pages = 32; +const u_int32_t jrnl_init_params::def_wcache_pgsize_sblks = 64; + +} // namespace jtt +} // namespace mrg diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_init_params.h b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_init_params.h new file mode 100644 index 0000000000..ece87f8e03 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_init_params.h @@ -0,0 +1,80 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef mrg_jtt_jrnl_init_params_hpp +#define mrg_jtt_jrnl_init_params_hpp + +#include <boost/shared_ptr.hpp> +#include <string> +#include <sys/types.h> + +namespace mrg +{ +namespace jtt +{ + + class jrnl_init_params + { + public: + static const u_int16_t def_num_jfiles; + static const bool def_ae; + static const u_int16_t def_ae_max_jfiles; + static const u_int32_t def_jfsize_sblks; + static const u_int16_t def_wcache_num_pages; + static const u_int32_t def_wcache_pgsize_sblks; + + typedef boost::shared_ptr<jrnl_init_params> shared_ptr; + + private: + std::string _jid; + std::string _jdir; + std::string _base_filename; + u_int16_t _num_jfiles; + bool _ae; + u_int16_t _ae_max_jfiles; + u_int32_t _jfsize_sblks; + u_int16_t _wcache_num_pages; + u_int32_t _wcache_pgsize_sblks; + + public: + jrnl_init_params(const std::string& jid, const std::string& jdir, const std::string& base_filename, + const u_int16_t num_jfiles = def_num_jfiles, const bool ae = def_ae, + const u_int16_t ae_max_jfiles = def_ae_max_jfiles, const u_int32_t jfsize_sblks = def_jfsize_sblks, + const u_int16_t wcache_num_pages = def_wcache_num_pages, + const u_int32_t wcache_pgsize_sblks = def_wcache_pgsize_sblks); + jrnl_init_params(const jrnl_init_params& jp); + jrnl_init_params(const jrnl_init_params* const jp_ptr); + + inline const std::string& jid() const { return _jid; } + inline const std::string& jdir() const { return _jdir; } + inline const std::string& base_filename() const { return _base_filename; } + inline u_int16_t num_jfiles() const { return _num_jfiles; } + inline bool is_ae() const { return _ae; } + inline u_int16_t ae_max_jfiles() const { return _ae_max_jfiles; } + inline u_int32_t jfsize_sblks() const { return _jfsize_sblks; } + inline u_int16_t wcache_num_pages() const { return _wcache_num_pages; } + inline u_int32_t wcache_pgsize_sblks() const { return _wcache_pgsize_sblks; } + }; + +} // namespace jtt +} // namespace mrg + +#endif // ifndef mrg_jtt_jrnl_init_params_hpp diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_instance.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_instance.cpp new file mode 100644 index 0000000000..339dc1b52c --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_instance.cpp @@ -0,0 +1,439 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "jrnl_instance.h" + +#include <cstdlib> +#include "data_src.h" +#include "qpid/legacystore/jrnl/data_tok.h" +#include "qpid/legacystore/jrnl/jerrno.h" +#include "test_case_result.h" + +#define MAX_WR_WAIT 10 // in ms +#define MAX_RD_WAIT 100 // in ms +#define MAX_ENQCAPTHRESH_CNT 1000 // 10s if MAX_WR_WAIT is 10 ms + +namespace mrg +{ +namespace jtt +{ + +jrnl_instance::jrnl_instance(const std::string& jid, const std::string& jdir, const std::string& base_filename, + const u_int16_t num_jfiles, const bool ae, const u_int16_t ae_max_jfiles, const u_int32_t jfsize_sblks, + const u_int16_t wcache_num_pages, const u_int32_t wcache_pgsize_sblks): + mrg::journal::jcntl(jid, jdir, base_filename), + _jpp(new jrnl_init_params(jid, jdir, base_filename, num_jfiles, ae, ae_max_jfiles, jfsize_sblks, + wcache_num_pages, wcache_pgsize_sblks)), + _args_ptr(0), + _dtok_master_enq_list(), + _dtok_master_txn_list(), + _dtok_rd_list(), + _dtok_deq_list(), + _rd_aio_cv(_rd_aio_mutex), + _wr_full_cv(_wr_full_mutex), + _rd_list_cv(_rd_list_mutex), + _deq_list_cv(_deq_list_mutex), + _tcp(), + _tcrp() +{} + +jrnl_instance::jrnl_instance(const jrnl_init_params::shared_ptr& p): + mrg::journal::jcntl(p->jid(), p->jdir(), p->base_filename()), + _jpp(p), + _args_ptr(0), + _dtok_master_enq_list(), + _dtok_master_txn_list(), + _dtok_rd_list(), + _dtok_deq_list(), + _rd_aio_cv(_rd_aio_mutex), + _wr_full_cv(_wr_full_mutex), + _rd_list_cv(_rd_list_mutex), + _deq_list_cv(_deq_list_mutex), + _tcp(), + _tcrp() +{} + +jrnl_instance::~jrnl_instance() {} + + +void +jrnl_instance::init_tc(test_case::shared_ptr& tcp, const args* const args_ptr) throw () +{ + test_case_result::shared_ptr p(new test_case_result(_jpp->jid())); + _tcrp = p; + _args_ptr = args_ptr; + try + { + _tcp = tcp; + _dtok_master_enq_list.clear(); + _dtok_master_txn_list.clear(); + _dtok_rd_list.clear(); + _dtok_deq_list.clear(); + + if (_args_ptr->recover_mode) + { + try + { + u_int64_t highest_rid; + recover(_jpp->num_jfiles(), _jpp->is_ae(), _jpp->ae_max_jfiles(), _jpp->jfsize_sblks(), + _jpp->wcache_num_pages(), _jpp->wcache_pgsize_sblks(), this, + 0, highest_rid); + recover_complete(); + } + catch (const mrg::journal::jexception& e) + { + if (e.err_code() == mrg::journal::jerrno::JERR_JDIR_STAT) + initialize(_jpp->num_jfiles(), _jpp->is_ae(), _jpp->ae_max_jfiles(), _jpp->jfsize_sblks(), + _jpp->wcache_num_pages(), _jpp->wcache_pgsize_sblks(), this); + else + throw; + } + } + else + initialize(_jpp->num_jfiles(), _jpp->is_ae(), _jpp->ae_max_jfiles(), _jpp->jfsize_sblks(), + _jpp->wcache_num_pages(), _jpp->wcache_pgsize_sblks(), this); + } + catch (const mrg::journal::jexception& e) { _tcrp->add_exception(e); } + catch (const std::exception& e) { _tcrp->add_exception(e.what()); } + catch (...) { _tcrp->add_exception("Unknown exception"); } +} + +void +jrnl_instance::run_tc() throw () +{ + _tcrp->set_start_time(); + ::pthread_create(&_enq_thread, 0, run_enq, this); + ::pthread_create(&_read_thread, 0, run_read, this); + ::pthread_create(&_deq_thread, 0, run_deq, this); +} + +void +jrnl_instance::tc_wait_compl() throw () +{ + try + { + ::pthread_join(_deq_thread, 0); + ::pthread_join(_read_thread, 0); + ::pthread_join(_enq_thread, 0); + stop(true); + } + catch (const mrg::journal::jexception& e) { _tcrp->add_exception(e); panic(); } + catch (const std::exception& e) { _tcrp->add_exception(e.what()); panic(); } + catch (...) { _tcrp->add_exception("Unknown exception"); panic(); } + _lpmgr.finalize(); + _tcrp->set_stop_time(); + _tcp->add_result(_tcrp); +} + +void +jrnl_instance::run_enq() throw () +{ + try + { + unsigned sleep_cnt = 0U; + while(_tcrp->num_enq() < _tcp->num_msgs() && !_tcrp->exception()) + { + dtok_ptr p(new mrg::journal::data_tok); + _dtok_master_enq_list.push_back(p); + const char* msgp = data_src::get_data(_tcrp->num_enq() % 10); + const std::size_t msg_size = _tcp->this_data_size(); + const std::size_t xid_size = _tcp->this_xid_size(); + const std::string xid(data_src::get_xid(xid_size)); + const bool external = _tcp->this_external(); + const bool transient = _tcp->this_transience(); + mrg::journal::iores res; + if (xid_size) + { + if (external) + res = enqueue_extern_txn_data_record(msg_size, p.get(), xid, transient); + else + res = enqueue_txn_data_record(msgp, msg_size, msg_size, p.get(), xid, + transient); + } + else + { + if (external) + res = enqueue_extern_data_record(msg_size, p.get(), transient); + else + res = enqueue_data_record(msgp, msg_size, msg_size, p.get(), transient); + } + switch (res) + { + case mrg::journal::RHM_IORES_SUCCESS: + sleep_cnt = 0U; + _tcrp->incr_num_enq(); + if (p->has_xid() && !_tcp->auto_deq()) + commit(p.get()); + break; + case mrg::journal::RHM_IORES_ENQCAPTHRESH: + if (++sleep_cnt > MAX_ENQCAPTHRESH_CNT) + { + _tcrp->add_exception("Timeout waiting for RHM_IORES_ENQCAPTHRESH to clear."); + panic(); + } + else if (get_wr_events(0) == 0) // *** GEV2 + { + mrg::journal::slock sl(_wr_full_mutex); + _wr_full_cv.waitintvl(MAX_WR_WAIT * 1000000); // MAX_WR_WAIT in ms + } + break; + default: + std::ostringstream oss; + oss << "ERROR: enqueue operation in journal \"" << _jid << "\" returned "; + oss << mrg::journal::iores_str(res) << "."; + _tcrp->add_exception(oss.str()); + } + } + flush(true); + } + catch (const mrg::journal::jexception& e) { _tcrp->add_exception(e); panic(); } + catch (const std::exception& e) { _tcrp->add_exception(e.what()); panic(); } + catch (...) { _tcrp->add_exception("Unknown exception"); panic(); } +} + +void +jrnl_instance::run_read() throw () +{ + try + { + read_arg::read_mode_t rd_mode = _args_ptr->read_mode.val(); + if (rd_mode != read_arg::NONE) + { + while (_tcrp->num_rproc() < _tcp->num_msgs() && !_tcrp->exception()) + { + journal::data_tok* dtokp = 0; + { + mrg::journal::slock sl(_rd_list_mutex); + if (_dtok_rd_list.empty()) + _rd_list_cv.wait(); + if (!_dtok_rd_list.empty()) + { + dtokp = _dtok_rd_list.front(); + _dtok_rd_list.pop_front(); + } + } + if (dtokp) + { + _tcrp->incr_num_rproc(); + + bool do_read = true; + if (rd_mode == read_arg::RANDOM) + do_read = 1.0 * std::rand() / RAND_MAX < _args_ptr->read_prob / 100.0; + else if (rd_mode == read_arg::LAZYLOAD) + do_read = _tcrp->num_rproc() >= _args_ptr->lld_skip_num && + _tcrp->num_read() < _args_ptr->lld_rd_num; + bool read_compl = false; + while (do_read && !read_compl && !_tcrp->exception()) + { + void* dptr = 0; + std::size_t dsize = 0; + void* xptr = 0; + std::size_t xsize = 0; + bool tr = false; + bool ext = false; + mrg::journal::iores res = read_data_record(&dptr, dsize, &xptr, xsize, tr, + ext, dtokp); + switch (res) + { + case mrg::journal::RHM_IORES_SUCCESS: + { + mrg::journal::slock sl(_deq_list_mutex); + _dtok_deq_list.push_back(dtokp); + _deq_list_cv.broadcast(); + } + read_compl = true; + _tcrp->incr_num_read(); + + // clean up + if (xsize) + std::free(xptr); + else if (dsize) + std::free(dptr); + dptr = 0; + xptr = 0; + break; + case mrg::journal::RHM_IORES_PAGE_AIOWAIT: + if (get_rd_events(0) == 0) + { + mrg::journal::slock sl(_rd_aio_mutex); + _rd_aio_cv.waitintvl(MAX_RD_WAIT * 1000000); // MAX_RD_WAIT in ms + } + break; + default: + std::ostringstream oss; + oss << "ERROR: read operation in journal \"" << _jid; + oss << "\" returned " << mrg::journal::iores_str(res) << "."; + _tcrp->add_exception(oss.str()); + { + mrg::journal::slock sl(_deq_list_mutex); + _deq_list_cv.broadcast(); // wake up deq thread + } + } + } + } + } + } + } + catch (const mrg::journal::jexception& e) { _tcrp->add_exception(e); panic(); } + catch (const std::exception& e) { _tcrp->add_exception(e.what()); panic(); } + catch (...) { _tcrp->add_exception("Unknown exception"); panic(); } +} + +void +jrnl_instance::run_deq() throw () +{ + try + { + if (_tcp->auto_deq()) + { + while(_tcrp->num_deq() < _tcp->num_msgs() && !_tcrp->exception()) + { + journal::data_tok* dtokp = 0; + { + mrg::journal::slock sl(_deq_list_mutex); + if (_dtok_deq_list.empty()) + _deq_list_cv.wait(); + if (!_dtok_deq_list.empty()) + { + dtokp = _dtok_deq_list.front(); + _dtok_deq_list.pop_front(); + } + } + if (dtokp) + { + mrg::journal::iores res; + if (dtokp->has_xid()) + res = dequeue_txn_data_record(dtokp, dtokp->xid()); + else + res = dequeue_data_record(dtokp); + if (res == mrg::journal::RHM_IORES_SUCCESS) + { + _tcrp->incr_num_deq(); + commit(dtokp); + } + else + { + std::ostringstream oss; + oss << "ERROR: dequeue operation in journal \"" << _jid; + oss << "\" returned " << mrg::journal::iores_str(res) << "."; + _tcrp->add_exception(oss.str()); + } + } + } + flush(true); + } + } + catch (const mrg::journal::jexception& e) { _tcrp->add_exception(e); panic(); } + catch (const std::exception& e) { _tcrp->add_exception(e.what()); panic(); } + catch (...) { _tcrp->add_exception("Unknown exception"); panic(); } +} + +void +jrnl_instance::abort(const mrg::journal::data_tok* dtokp) +{ + txn(dtokp, false); +} + +void +jrnl_instance::commit(const mrg::journal::data_tok* dtokp) +{ + txn(dtokp, true); +} + +void +jrnl_instance::txn(const mrg::journal::data_tok* dtokp, const bool commit) +{ + if (dtokp->has_xid()) + { + mrg::journal::data_tok* p = prep_txn_dtok(dtokp); + mrg::journal::iores res = commit ? txn_commit(p, p->xid()) : txn_abort(p, p->xid()); + if (res != mrg::journal::RHM_IORES_SUCCESS) + { + std::ostringstream oss; + oss << "ERROR: " << (commit ? "commit" : "abort") << " operation in journal \""; + oss << _jid << "\" returned " << mrg::journal::iores_str(res) << "."; + _tcrp->add_exception(oss.str()); + } + } +} + +mrg::journal::data_tok* +jrnl_instance::prep_txn_dtok(const mrg::journal::data_tok* dtokp) +{ + dtok_ptr p(new mrg::journal::data_tok); + _dtok_master_txn_list.push_back(p); + p->set_xid(dtokp->xid()); + return p.get(); +} + +void +jrnl_instance::panic() +{ + // In the event of a panic or exception condition, release all waiting CVs + _rd_aio_cv.broadcast(); + _wr_full_cv.broadcast(); + _rd_list_cv.broadcast(); + _deq_list_cv.broadcast(); +} + +// AIO callbacks + +void +jrnl_instance::wr_aio_cb(std::vector<journal::data_tok*>& dtokl) +{ + for (std::vector<journal::data_tok*>::const_iterator i=dtokl.begin(); i!=dtokl.end(); i++) + { + if ((*i)->wstate() == journal::data_tok::ENQ || (*i)->wstate() == journal::data_tok::DEQ) + { + journal::data_tok* dtokp = *i; + if (dtokp->wstate() == journal::data_tok::ENQ) + { + if (_args_ptr->read_mode.val() == read_arg::NONE) + { + mrg::journal::slock sl(_deq_list_mutex); + _dtok_deq_list.push_back(dtokp); + _deq_list_cv.broadcast(); + } + else + { + mrg::journal::slock sl(_rd_list_mutex); + _dtok_rd_list.push_back(dtokp); + _rd_list_cv.broadcast(); + } + } + else // DEQ + { + mrg::journal::slock sl(_wr_full_mutex); + _wr_full_cv.broadcast(); + } + } + } +} + +void +jrnl_instance::rd_aio_cb(std::vector<u_int16_t>& /*pil*/) +{ + mrg::journal::slock sl(_rd_aio_mutex); + _rd_aio_cv.broadcast(); +} + +} // namespace jtt +} // namespace mrg diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_instance.h b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_instance.h new file mode 100644 index 0000000000..5003f39b24 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_instance.h @@ -0,0 +1,121 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef mrg_jtt_jrnl_instance_hpp +#define mrg_jtt_jrnl_instance_hpp + +#include "args.h" +#include "jrnl_init_params.h" +#include "test_case.h" + +#include <boost/shared_ptr.hpp> +#include "qpid/legacystore/jrnl/cvar.h" +#include "qpid/legacystore/jrnl/data_tok.h" +#include "qpid/legacystore/jrnl/jcntl.h" +#include "qpid/legacystore/jrnl/slock.h" +#include "qpid/legacystore/jrnl/smutex.h" +#include <list> +#include <vector> + +namespace mrg +{ +namespace jtt +{ + + class jrnl_instance : public mrg::journal::jcntl, public virtual mrg::journal::aio_callback + { + public: + typedef boost::shared_ptr<jrnl_instance> shared_ptr; + typedef boost::shared_ptr<journal::data_tok> dtok_ptr; + + private: + jrnl_init_params::shared_ptr _jpp; + const args* _args_ptr; + std::vector<dtok_ptr> _dtok_master_enq_list; + std::vector<dtok_ptr> _dtok_master_txn_list; + std::list<journal::data_tok*> _dtok_rd_list; + std::list<journal::data_tok*> _dtok_deq_list; + mrg::journal::smutex _rd_aio_mutex; ///< Mutex for read aio wait conditions + mrg::journal::cvar _rd_aio_cv; ///< Condition var for read aio wait conditions + mrg::journal::smutex _wr_full_mutex; ///< Mutex for write full conditions + mrg::journal::cvar _wr_full_cv; ///< Condition var for write full conditions + mrg::journal::smutex _rd_list_mutex; ///< Mutex for _dtok_rd_list + mrg::journal::cvar _rd_list_cv; ///< Condition var for _dtok_rd_list + mrg::journal::smutex _deq_list_mutex; ///< Mutex for _dtok_deq_list + mrg::journal::cvar _deq_list_cv; ///< Condition var for _dtok_deq_list + pthread_t _enq_thread; + pthread_t _deq_thread; + pthread_t _read_thread; + test_case::shared_ptr _tcp; + test_case_result::shared_ptr _tcrp; + + public: + jrnl_instance(const std::string& jid, const std::string& jdir, + const std::string& base_filename, + const u_int16_t num_jfiles = jrnl_init_params::def_num_jfiles, + const bool ae = jrnl_init_params::def_ae, + const u_int16_t ae_max_jfiles = jrnl_init_params::def_ae_max_jfiles, + const u_int32_t jfsize_sblks = jrnl_init_params::def_jfsize_sblks, + const u_int16_t wcache_num_pages = jrnl_init_params::def_wcache_num_pages, + const u_int32_t wcache_pgsize_sblks = jrnl_init_params::def_wcache_pgsize_sblks); + jrnl_instance(const jrnl_init_params::shared_ptr& params); + virtual ~jrnl_instance(); + + inline const jrnl_init_params::shared_ptr& params() const { return _jpp; } + inline const std::string& jid() const { return _jpp->jid(); } + + void init_tc(test_case::shared_ptr& tcp, const args* const args_ptr) throw (); + void run_tc() throw (); + void tc_wait_compl() throw (); + + // AIO callbacks + virtual void wr_aio_cb(std::vector<journal::data_tok*>& dtokl); + virtual void rd_aio_cb(std::vector<u_int16_t>& pil); + + private: + void run_enq() throw (); + inline static void* run_enq(void* p) + { static_cast<jrnl_instance*>(p)->run_enq(); return 0; } + + void run_read() throw (); + inline static void* run_read(void* p) + { static_cast<jrnl_instance*>(p)->run_read(); return 0; } + + void run_deq() throw (); + inline static void* run_deq(void* p) + { static_cast<jrnl_instance*>(p)->run_deq(); return 0; } + + void abort(const mrg::journal::data_tok* dtokp); + void commit(const mrg::journal::data_tok* dtokp); + void txn(const mrg::journal::data_tok* dtokp, const bool commit); + mrg::journal::data_tok* prep_txn_dtok(const mrg::journal::data_tok* dtokp); + + void panic(); + +// // static callbacks +// static void aio_rd_callback(jcntl* journal, std::vector<u_int16_t>& pil); +// static void aio_wr_callback(jcntl* journal, std::vector<journal::data_tok*>& dtokl); + }; + +} // namespace jtt +} // namespace mrg + +#endif // ifndef mrg_jtt_jrnl_instance_hpp diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/jtt.csv b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jtt.csv new file mode 100644 index 0000000000..df523e3f97 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jtt.csv @@ -0,0 +1,234 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +,,,,,,,"Msg size",,"Xid size",,,,,"enq-size",,"deq-size",,"txn-size",, +"Test #","tf","pf","amn","mn incr","#msgs","ms incr","Min","Max","Min","Max","auto-deq","transient","extern","bytes","dblks","bytes","dblks","bytes","dblks","comment" +,,,,,,,,,,,,,,,,,,,, +"Initialize only",,,,,,,,,,,,,,,,,,,, +0,"L",0,0,0,0,0,0,0,0,0,FALSE,FALSE,FALSE,44,1,0,0,0,0,"No messages - journal creation/initialization only" +,,,,,,,,,,,,,,,,,,,, +"Simple message combinations of persistent/deq transientueued/non-dequeued, transactional/non-transactional",,,,,,,,,,,,,,,,,,,, +1,"L",1,1,0,1,0,10,10,0,0,FALSE,FALSE,FALSE,54,1,0,0,0,0,"1 * 10-byte message" +2,"L",1,10,0,10,0,10,10,0,0,FALSE,FALSE,FALSE,54,1,0,0,0,0,"10 * 10-byte message" +3,"L",1,1,0,1,0,10,10,0,0,FALSE,TRUE,FALSE,54,1,0,0,0,0,"1 * 10-byte message [transient]" +4,"L",1,10,0,10,0,10,10,0,0,FALSE,TRUE,FALSE,54,1,0,0,0,0,"10 * 10-byte message [transient]" +5,"L",1,1,0,1,0,10,10,10,10,FALSE,FALSE,FALSE,64,1,0,0,0,0,"1 * 10-byte message [txn]" +6,"L",1,10,0,10,0,10,10,10,10,FALSE,FALSE,FALSE,64,1,0,0,0,0,"10 * 10-byte message [txn]" +7,"L",1,1,0,1,0,10,10,10,10,FALSE,TRUE,FALSE,64,1,0,0,0,0,"1 * 10-byte message [txn transient]" +8,"L",1,10,0,10,0,10,10,10,10,FALSE,TRUE,FALSE,64,1,0,0,0,0,"10 * 10-byte message [txn transient]" +9,"L",1,1,0,1,0,10,10,0,0,TRUE,FALSE,FALSE,54,1,32,1,0,0,"1 * 10-byte message [deq]" +10,"L",1,10,0,10,0,10,10,0,0,TRUE,FALSE,FALSE,54,1,32,1,0,0,"10 * 10-byte message [deq]" +11,"L",1,1,0,1,0,10,10,0,0,TRUE,TRUE,FALSE,54,1,32,1,0,0,"1 * 10-byte message [deq transient]" +12,"L",1,10,0,10,0,10,10,0,0,TRUE,TRUE,FALSE,54,1,32,1,0,0,"10 * 10-byte message [deq transient]" +13,"L",1,1,0,1,0,10,10,10,10,TRUE,FALSE,FALSE,64,1,54,1,46,1,"1 * 10-byte message [deq txn]" +14,"L",1,10,0,10,0,10,10,10,10,TRUE,FALSE,FALSE,64,1,54,1,46,1,"10 * 10-byte message [deq txn]" +15,"L",1,1,0,1,0,10,10,10,10,TRUE,TRUE,FALSE,64,1,54,1,46,1,"1 * 10-byte message [txn deq transient]" +16,"L",1,10,0,10,0,10,10,10,10,TRUE,TRUE,FALSE,64,1,54,1,46,1,"10 * 10-byte message [txn deq transient]" +17,"L",1,1,0,1,0,10,10,0,0,FALSE,FALSE,TRUE,54,1,0,0,0,0,"1 * 10-byte message [extern]" +18,"L",1,10,0,10,0,10,10,0,0,FALSE,FALSE,TRUE,54,1,0,0,0,0,"10 * 10-byte message [extern]" +19,"L",1,1,0,1,0,10,10,0,0,FALSE,TRUE,TRUE,54,1,0,0,0,0,"1 * 10-byte message [transient extern]" +20,"L",1,10,0,10,0,10,10,0,0,FALSE,TRUE,TRUE,54,1,0,0,0,0,"10 * 10-byte message [transient extern]" +21,"L",1,1,0,1,0,10,10,10,10,FALSE,FALSE,TRUE,64,1,0,0,0,0,"1 * 10-byte message [txn extern]" +22,"L",1,10,0,10,0,10,10,10,10,FALSE,FALSE,TRUE,64,1,0,0,0,0,"10 * 10-byte message [txn extern]" +23,"L",1,1,0,1,0,10,10,10,10,FALSE,TRUE,TRUE,64,1,0,0,0,0,"1 * 10-byte message [txn transient extern]" +24,"L",1,10,0,10,0,10,10,10,10,FALSE,TRUE,TRUE,64,1,0,0,0,0,"10 * 10-byte message [txn transient extern]" +25,"L",1,1,0,1,0,10,10,0,0,TRUE,FALSE,TRUE,54,1,32,1,0,0,"1 * 10-byte message [deq extern]" +26,"L",1,10,0,10,0,10,10,0,0,TRUE,FALSE,TRUE,54,1,32,1,0,0,"10 * 10-byte message [deq extern]" +27,"L",1,1,0,1,0,10,10,0,0,TRUE,TRUE,TRUE,54,1,32,1,0,0,"1 * 10-byte message [deq transient extern]" +28,"L",1,10,0,10,0,10,10,0,0,TRUE,TRUE,TRUE,54,1,32,1,0,0,"10 * 10-byte message [deq transient extern]" +29,"L",1,1,0,1,0,10,10,10,10,TRUE,FALSE,TRUE,64,1,54,1,46,1,"1 * 10-byte message [deq txn extern]" +30,"L",1,10,0,10,0,10,10,10,10,TRUE,FALSE,TRUE,64,1,54,1,46,1,"10 * 10-byte message [deq txn extern]" +31,"L",1,1,0,1,0,10,10,10,10,TRUE,TRUE,TRUE,64,1,54,1,46,1,"1 * 10-byte message [txn deq transient extern]" +32,"L",1,10,0,10,0,10,10,10,10,TRUE,TRUE,TRUE,64,1,54,1,46,1,"10 * 10-byte message [txn deq transient extern]" +,,,,,,,,,,,,,,,,,,,, +"Transition from one d-block to two per message",,,,,,,,,,,,,,,,,,,, +33,"L",1,10,0,10,0,84,84,0,0,FALSE,FALSE,FALSE,128,1,0,0,0,0,"1 dblk exact fit" +34,"L",1,10,0,10,1,85,85,0,0,FALSE,FALSE,FALSE,129,2,0,0,0,0,"1 dblk + 1 byte" +35,"L",1,10,0,10,0,58,58,26,26,FALSE,FALSE,FALSE,128,1,0,0,0,0,"1 dblk exact fit [txn]" +36,"L",1,10,0,10,1,59,59,26,26,FALSE,FALSE,FALSE,129,2,0,0,0,0,"1 dblk + 1 byte [txn]" +,,,,,,,,,,,,,,,,,,,, +"Transition from one s-block to two per message",,,,,,,,,,,,,,,,,,,, +37,"L",1,10,0,10,0,468,468,0,0,FALSE,FALSE,FALSE,512,4,0,0,0,0,"1 sblk exact fit" +38,"L",1,10,0,10,1,469,469,0,0,FALSE,FALSE,FALSE,513,5,0,0,0,0,"1 sblk + 1 byte" +39,"L",1,10,0,10,0,442,442,26,26,FALSE,FALSE,FALSE,512,4,0,0,0,0,"1 sblk exact fit [txn]" +40,"L",1,10,0,10,1,443,443,26,26,FALSE,FALSE,FALSE,513,5,0,0,0,0,"1 sblk + 1 byte [txn]" +,,,,,,,,,,,,,,,,,,,, +"Transition from first page to second",,,,,,,,,,,,,,,,,,,, +41,"L",1,8,0,8,0,4052,4052,0,0,FALSE,FALSE,FALSE,4096,32,0,0,0,0,"1/8 page" +42,"L",1,8,1,9,0,4052,4052,0,0,FALSE,FALSE,FALSE,4096,32,0,0,0,0,"1/8 page" +43,"L",1,8,0,8,1,4053,4053,0,0,FALSE,FALSE,FALSE,4097,33,0,0,0,0,"1/8 page + 1 byte" +44,"L",1,8,0,8,0,3796,3796,256,256,FALSE,FALSE,FALSE,4096,32,0,0,0,0,"1/8 page [txn]" +45,"L",1,8,1,9,0,3796,3796,256,256,FALSE,FALSE,FALSE,4096,32,0,0,0,0,"1/8 page [txn]" +46,"L",1,8,0,8,1,3797,3797,256,256,FALSE,FALSE,FALSE,4097,33,0,0,0,0,"1/8 page + 1 byte [txn]" +47,"L",1,8,0,8,0,3924,3924,0,0,TRUE,FALSE,FALSE,3968,31,32,1,0,0,"1/8 page incl deq [deq]" +48,"L",1,8,1,9,0,3924,3924,0,0,TRUE,FALSE,FALSE,3968,31,32,1,0,0,"1/8 page incl deq [deq]" +49,"L",1,8,0,8,1,3925,3925,0,0,TRUE,FALSE,FALSE,3969,32,32,1,0,0,"1/8 page incl deq + 1 byte [deq]" +50,"L",1,8,0,8,0,3028,3028,256,256,TRUE,FALSE,FALSE,3328,26,300,3,292,3,"1/8 page incl deq & txn [deq txn]" +51,"L",1,8,1,9,0,3028,3028,256,256,TRUE,FALSE,FALSE,3328,26,300,3,292,3,"1/8 page incl deq & txn [deq txn]" +52,"L",1,8,0,8,1,3029,3029,256,256,TRUE,FALSE,FALSE,3329,27,300,3,292,3,"1/8 page incl deq & txn + 1 byte [deq txn]" +,,,,,,,,,,,,,,,,,,,, +"Page cache rollover (from page 32 back to page 0)",,,,,,,,,,,,,,,,,,,, +53,"L",1,32,0,32,0,32724,32724,0,0,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page" +54,"L",1,32,1,33,0,32724,32724,0,0,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page" +55,"L",1,32,0,32,1,32725,32725,0,0,FALSE,FALSE,FALSE,32769,257,0,0,0,0,"1 page + 1 byte" +56,"L",1.5,22,0,22,0,49108,49108,0,0,FALSE,FALSE,FALSE,49152,384,0,0,0,0,"1.5 pages" +57,"L",1,32,0,32,0,32468,32468,256,256,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page [txn]" +58,"L",1,32,1,33,0,32468,32468,256,256,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page [txn]" +59,"L",1,32,0,32,1,32469,32469,256,256,FALSE,FALSE,FALSE,32769,257,0,0,0,0,"1 page + 1 byte [txn]" +60,"L",1.5,22,0,22,0,48852,48852,256,256,FALSE,FALSE,FALSE,49152,384,0,0,0,0,"1.5 pages [txn]" +61,"L",1,32,0,32,0,32596,32596,0,0,TRUE,FALSE,FALSE,32640,255,32,1,0,0,"1 page incl deq [deq]" +62,"L",1,32,1,33,0,32596,32596,0,0,TRUE,FALSE,FALSE,32640,255,32,1,0,0,"1 page incl deq [deq]" +63,"L",1,32,0,32,1,32597,32597,0,0,TRUE,FALSE,FALSE,32641,256,32,1,0,0,"1 page incl deq + 1 byte [deq]" +64,"L",1.5,22,0,22,0,48980,48980,0,0,TRUE,FALSE,FALSE,49024,383,32,1,0,0,"1.5 pages incl deq [deq]" +65,"L",1,32,0,32,0,31700,31700,256,256,TRUE,FALSE,FALSE,32000,250,300,3,292,3,"1 page incl deq & txn [deq txn]" +66,"L",1,32,1,33,0,31700,31700,256,256,TRUE,FALSE,FALSE,32000,250,300,3,292,3,"1 page incl deq & txn [deq txn]" +67,"L",1,32,0,32,1,31701,31701,256,256,TRUE,FALSE,FALSE,32001,251,300,3,292,3,"1 page incl deq & txn + 1 byte [deq txn]" +68,"L",1.5,22,0,22,0,48084,48084,256,256,TRUE,FALSE,FALSE,48384,378,300,3,292,3,"1.5 pages incl deq & txn [deq txn]" +,,,,,,,,,,,,,,,,,,,, +"File transition (from file 0000 to 0001)",,,,,,,,,,,,,,,,,,,, +69,"L",1,48,0,48,0,32724,32724,0,0,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page" +70,"L",1,48,1,49,0,32724,32724,0,0,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page" +71,"L",1,48,0,48,1,32725,32725,0,0,FALSE,FALSE,FALSE,32769,257,0,0,0,0,"1 page + 1 byte" +72,"L",2.5,20,0,20,0,81876,81876,0,0,FALSE,FALSE,FALSE,81920,640,0,0,0,0,"2.5 pages" +73,"L",1,48,0,48,0,32468,32468,256,256,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page [txn]" +74,"L",1,48,1,49,0,32468,32468,256,256,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page [txn]" +75,"L",1,48,0,48,1,32469,32469,256,256,FALSE,FALSE,FALSE,32769,257,0,0,0,0,"1 page + 1 byte [txn]" +76,"L",2.5,20,0,20,0,81620,81620,256,256,FALSE,FALSE,FALSE,81920,640,0,0,0,0,"2.5 pages [txn]" +77,"L",1,48,0,48,0,32596,32596,0,0,TRUE,FALSE,FALSE,32640,255,32,1,0,0,"1 page incl deq [deq]" +78,"L",1,48,1,49,0,32596,32596,0,0,TRUE,FALSE,FALSE,32640,255,32,1,0,0,"1 page incl deq [deq]" +79,"L",1,48,0,48,1,32597,32597,0,0,TRUE,FALSE,FALSE,32641,256,32,1,0,0,"1 page incl deq + 1 byte [deq]" +80,"L",2.5,20,0,20,0,81748,81748,0,0,TRUE,FALSE,FALSE,81792,639,32,1,0,0,"2.5 pages incl deq [deq]" +81,"L",1,48,0,48,0,31700,31700,256,256,TRUE,FALSE,FALSE,32000,250,300,3,292,3,"1 page incl deq & txn [deq txn]" +82,"L",1,48,1,49,0,31700,31700,256,256,TRUE,FALSE,FALSE,32000,250,300,3,292,3,"1 page incl deq & txn [deq txn]" +83,"L",1,48,0,48,1,31701,31701,256,256,TRUE,FALSE,FALSE,32001,251,300,3,292,3,"1 page incl deq & txn + 1 byte [deq txn]" +84,"L",2.5,20,0,20,0,80852,80852,256,256,TRUE,FALSE,FALSE,81152,634,300,3,292,3,"2.5 pages incl deq & txn [deq txn]" +,,,,,,,,,,,,,,,,,,,, +"File rollover (from file 0007 to 0000) - RHM_WRONLY req'd for auto-dequeue == FALSE",,,,,,,,,,,,,,,,,,,, +85,"L",0.5,16,0,16,0,786260,786260,0,0,TRUE,FALSE,FALSE,786304,6143,32,1,0,0,"24 pages incl deq = ½ file [deq]" +86,"L",0.5,16,1,17,0,786260,786260,0,0,TRUE,FALSE,FALSE,786304,6143,32,1,0,0,"24 pages incl deq = ½ file [deq]" +87,"L",0.5,16,0,16,1,786261,786261,0,0,TRUE,FALSE,FALSE,786305,6144,32,1,0,0,"24 pages incl deq + 1 byte [deq]" +88,"L",0.5,16,0,16,0,785364,785364,256,256,TRUE,FALSE,FALSE,785664,6138,300,3,292,3,"24 pages incl deq & txn = ½ file [deq txn]" +89,"L",0.5,16,1,17,0,785364,785364,256,256,TRUE,FALSE,FALSE,785664,6138,300,3,292,3,"24 pages incl deq & txn = ½ file [deq txn]" +90,"L",0.5,16,0,16,1,785365,785365,256,256,TRUE,FALSE,FALSE,785665,6139,300,3,292,3,"24 pages incl deq & txn + 1 byte [deq txn]" +91,"L",0.25,32,0,32,0,786260,786260,0,0,TRUE,FALSE,FALSE,786304,6143,32,1,0,0,"24 pages incl deq = ½ file [deq]" +92,"L",0.25,32,1,33,0,786260,786260,0,0,TRUE,FALSE,FALSE,786304,6143,32,1,0,0,"24 pages incl deq = ½ file [deq]" +93,"L",0.25,32,0,32,1,786261,786261,0,0,TRUE,FALSE,FALSE,786305,6144,32,1,0,0,"24 pages incl deq + 1 byte [deq]" +94,"L",0.25,32,0,32,0,785364,785364,256,256,TRUE,FALSE,FALSE,785664,6138,300,3,292,3,"24 pages incl deq & txn = ½ file [deq txn]" +95,"L",0.25,32,1,33,0,785364,785364,256,256,TRUE,FALSE,FALSE,785664,6138,300,3,292,3,"24 pages incl deq & txn = ½ file [deq txn]" +96,"L",0.25,32,0,32,1,785365,785365,256,256,TRUE,FALSE,FALSE,785665,6139,300,3,292,3,"24 pages incl deq & txn + 1 byte [deq txn]" +,,,,,,,,,,,,,,,,,,,, +"Multi-page messages (large messages) - tests various paths in encoder.",,,,,,,,,,,,,,,,,,,, +97,"L",1,16,0,16,0,32724,32724,0,0,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"data 1 page" +98,"L",1,16,0,16,1,32725,32725,0,0,FALSE,FALSE,FALSE,32769,257,0,0,0,0,"data 1 page + 1 byte (tail split; 1 byte over page boundary)" +99,"L",1,16,0,16,11,32735,32735,0,0,FALSE,FALSE,FALSE,32779,257,0,0,0,0,"data 1 page + 11 bytes (tail split; 11 bytes over page boundary)" +100,"L",1,16,0,16,12,32736,32736,0,0,FALSE,FALSE,FALSE,32780,257,0,0,0,0,"data 1 page + 12 bytes (tail separated exactly onto next page)" +101,"L",1,16,0,16,13,32737,32737,0,0,FALSE,FALSE,FALSE,32781,257,0,0,0,0,"data 1 page + 13 bytes (data split; 1 byte over page boundary)" +102,"L",1,16,0,16,0,32468,32468,256,256,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"data 1 page [txn]" +103,"L",1,16,0,16,1,32469,32469,256,256,FALSE,FALSE,FALSE,32769,257,0,0,0,0,"data 1 page + 1 byte (tail split; 1 byte over page boundary) [txn]" +104,"L",1,16,0,16,11,32479,32479,256,256,FALSE,FALSE,FALSE,32779,257,0,0,0,0,"data 1 page + 11 bytes (tail split; 11 bytes over page boundary) [txn]" +105,"L",1,16,0,16,12,32480,32480,256,256,FALSE,FALSE,FALSE,32780,257,0,0,0,0,"data 1 page + 12 bytes (tail separated exactly onto next page) [txn]" +106,"L",1,16,0,16,13,32481,32481,256,256,FALSE,FALSE,FALSE,32781,257,0,0,0,0,"data 1 page + 13 bytes (data split; 1 byte over page boundary) [txn]" +107,"L",2,16,0,16,0,65492,65492,0,0,FALSE,FALSE,FALSE,65536,512,0,0,0,0,"data 2 pages" +108,"L",2,16,0,16,1,65493,65493,0,0,FALSE,FALSE,FALSE,65537,513,0,0,0,0,"data 2 pages + 1 byte (tail split; 1 byte over page boundary)" +109,"L",2,16,0,16,11,65503,65503,0,0,FALSE,FALSE,FALSE,65547,513,0,0,0,0,"data 2 pages + 11 bytes (tail split; 11 bytes over page boundary)" +110,"L",2,16,0,16,12,65504,65504,0,0,FALSE,FALSE,FALSE,65548,513,0,0,0,0,"data 2 pages + 12 bytes (tail separated exactly onto next page)" +111,"L",2,16,0,16,13,65505,65505,0,0,FALSE,FALSE,FALSE,65549,513,0,0,0,0,"data 2 pages + 13 bytes (data split; 1 byte over page boundary)" +112,"L",2,16,0,16,0,65236,65236,256,256,FALSE,FALSE,FALSE,65536,512,0,0,0,0,"data 2 pages [txn]" +113,"L",2,16,0,16,1,65237,65237,256,256,FALSE,FALSE,FALSE,65537,513,0,0,0,0,"data 2 pages + 1 byte (tail split; 1 byte over page boundary) [txn]" +114,"L",2,16,0,16,11,65247,65247,256,256,FALSE,FALSE,FALSE,65547,513,0,0,0,0,"data 2 pages + 11 bytes (tail split; 11 bytes over page boundary) [txn]" +115,"L",2,16,0,16,12,65248,65248,256,256,FALSE,FALSE,FALSE,65548,513,0,0,0,0,"data 2 pages + 12 bytes (tail separated exactly onto next page) [txn]" +116,"L",2,16,0,16,13,65249,65249,256,256,FALSE,FALSE,FALSE,65549,513,0,0,0,0,"data 2 pages + 13 bytes (data split; 1 byte over page boundary) [txn]" +117,"L",4,16,0,16,0,131028,131028,0,0,FALSE,FALSE,FALSE,131072,1024,0,0,0,0,"data 4 pages" +118,"L",4,16,0,16,1,131029,131029,0,0,FALSE,FALSE,FALSE,131073,1025,0,0,0,0,"data 4 pages + 1 byte (tail split; 1 byte over page boundary)" +119,"L",4,16,0,16,11,131039,131039,0,0,FALSE,FALSE,FALSE,131083,1025,0,0,0,0,"data 4 pages + 11 bytes (tail split; 11 bytes over page boundary)" +120,"L",4,16,0,16,12,131040,131040,0,0,FALSE,FALSE,FALSE,131084,1025,0,0,0,0,"data 4 pages + 12 bytes (tail separated exactly onto next page)" +121,"L",4,16,0,16,13,131041,131041,0,0,FALSE,FALSE,FALSE,131085,1025,0,0,0,0,"data 4 pages + 13 bytes (data split; 1 byte over page boundary)" +122,"L",4,16,0,16,0,130772,130772,256,256,FALSE,FALSE,FALSE,131072,1024,0,0,0,0,"data 4 pages [txn]" +123,"L",4,16,0,16,1,130773,130773,256,256,FALSE,FALSE,FALSE,131073,1025,0,0,0,0,"data 4 pages + 1 byte (tail split; 1 byte over page boundary) [txn]" +124,"L",4,16,0,16,11,130783,130783,256,256,FALSE,FALSE,FALSE,131083,1025,0,0,0,0,"data 4 pages + 11 bytes (tail split; 11 bytes over page boundary) [txn]" +125,"L",4,16,0,16,12,130784,130784,256,256,FALSE,FALSE,FALSE,131084,1025,0,0,0,0,"data 4 pages + 12 bytes (tail separated exactly onto next page) [txn]" +126,"L",4,16,0,16,13,130785,130785,256,256,FALSE,FALSE,FALSE,131085,1025,0,0,0,0,"data 4 pages + 13 bytes (data split; 1 byte over page boundary) [txn]" +127,"L",3.5,16,0,16,0,114644,114644,0,0,FALSE,FALSE,FALSE,114688,896,0,0,0,0,"data 3.5 pages" +128,"L",3.5,16,0,16,1,114645,114645,0,0,FALSE,FALSE,FALSE,114689,897,0,0,0,0,"data 3.5 pages + 1 byte" +129,"L",3.5,16,0,16,0,114388,114388,256,256,FALSE,FALSE,FALSE,114688,896,0,0,0,0,"data 3.5 pages [txn]" +130,"L",3.5,16,0,16,1,114389,114389,256,256,FALSE,FALSE,FALSE,114689,897,0,0,0,0,"data 3.5 pages + 1 byte [txn]" +131,"L",1,16,0,16,-1,10,10,32735,32735,FALSE,FALSE,FALSE,32789,257,0,0,0,0,"xid 1 page – 1 byte; data 10 bytes (exact fit) [txn]" +132,"L",1,16,0,16,0,10,10,32736,32736,FALSE,FALSE,FALSE,32790,257,0,0,0,0,"xid 1 page; data 10 bytes (exact fit) [txn]" +133,"L",1,16,0,16,1,10,10,32737,32737,FALSE,FALSE,FALSE,32791,257,0,0,0,0,"xid 1 page + 1 byte; data 10 bytes (exact fit) [txn]" +134,"L",2,16,0,16,-1,10,10,65503,65503,FALSE,FALSE,FALSE,65557,513,0,0,0,0,"xid 2 pages – 1 byte; data 10 bytes (exact fit) [txn]" +135,"L",2,16,0,16,0,10,10,65504,65504,FALSE,FALSE,FALSE,65558,513,0,0,0,0,"xid 2 pages; data 10 bytes (exact fit) [txn]" +136,"L",2,16,0,16,1,10,10,65505,65505,FALSE,FALSE,FALSE,65559,513,0,0,0,0,"xid 2 pages + 1 byte; data 10 bytes (exact fit) [txn]" +137,"L",4,16,0,16,-1,10,10,131039,131039,FALSE,FALSE,FALSE,131093,1025,0,0,0,0,"xid 4 pages – 1 byte; data 10 bytes (exact fit) [txn]" +138,"L",4,16,0,16,0,10,10,131040,131040,FALSE,FALSE,FALSE,131094,1025,0,0,0,0,"xid 4 pages; data 10 bytes (exact fit) [txn]" +139,"L",4,16,0,16,1,10,10,131041,131041,FALSE,FALSE,FALSE,131095,1025,0,0,0,0,"xid 4 pages + 1 byte; data 10 bytes (exact fit) [txn]" +140,"L",3.5,16,0,16,0,10,10,114656,114656,FALSE,FALSE,FALSE,114710,897,0,0,0,0,"xid 3.5 pages; data 10 bytes (exact fit) [txn]" +141,"L",3.5,16,0,16,1,10,10,114657,114657,FALSE,FALSE,FALSE,114711,897,0,0,0,0,"xid 3.5 pages + 1 byte; data 10 bytes (exact fit) [txn]" +142,"L",1,16,0,16,-1,10,10,32735,32735,TRUE,FALSE,FALSE,32789,257,32779,257,32771,257,"xid 1 page – 1 byte for enq rec; data 10 bytes (exact fit) [deq, txn]" +143,"L",1,16,0,16,0,10,10,32736,32736,TRUE,FALSE,FALSE,32790,257,32780,257,32772,257,"xid 1 page for enq rec; data 10 bytes (exact fit) [deq, txn]" +144,"L",1,16,0,16,1,10,10,32737,32737,TRUE,FALSE,FALSE,32791,257,32781,257,32773,257,"xid 1 page + 1 byte for enq rec; data 10 bytes (exact fit) [deq, txn]" +145,"L",2,16,0,16,-1,10,10,65503,65503,TRUE,FALSE,FALSE,65557,513,65547,513,65539,513,"xid 2 pages – 1 byte for enq rec; data 10 bytes (exact fit) [deq, txn]" +146,"L",2,16,0,16,0,10,10,65504,65504,TRUE,FALSE,FALSE,65558,513,65548,513,65540,513,"xid 2 pages for enq rec; data 10 bytes (exact fit) [deq, txn]" +147,"L",2,16,0,16,1,10,10,65505,65505,TRUE,FALSE,FALSE,65559,513,65549,513,65541,513,"xid 2 pages + 1 byte for enq rec; data 10 bytes (exact fit) [deq, txn]" +148,"L",4,16,0,16,-1,10,10,131039,131039,TRUE,FALSE,FALSE,131093,1025,131083,1025,131075,1025,"xid 4 pages – 1 byte for enq rec; data 10 bytes (exact fit) [deq, txn]" +149,"L",4,16,0,16,0,10,10,131040,131040,TRUE,FALSE,FALSE,131094,1025,131084,1025,131076,1025,"xid 4 pages for enq rec; data 10 bytes (exact fit) [deq, txn]" +150,"L",4,16,0,16,1,10,10,131041,131041,TRUE,FALSE,FALSE,131095,1025,131085,1025,131077,1025,"xid 4 pages + 1 byte for enq rec; data 10 bytes (exact fit) [deq, txn]" +151,"L",3.5,16,0,16,0,10,10,114656,114656,TRUE,FALSE,FALSE,114710,897,114700,897,114692,897,"xid 3.5 pages for enq rec; data 10 bytes (exact fit) [deq, txn]" +152,"L",3.5,16,0,16,1,10,10,114657,114657,TRUE,FALSE,FALSE,114711,897,114701,897,114693,897,"xid 3.5 pages + 1 byte for enq rec; data 10 bytes (exact fit) [deq, txn]" +153,"L",1,16,0,16,-1,10,10,32735,32735,TRUE,FALSE,FALSE,32789,257,32779,257,32771,257,"xid 1 page – 1 byte for deq rec; data 10 bytes (exact fit) [deq, txn]" +154,"L",1,16,0,16,0,10,10,32736,32736,TRUE,FALSE,FALSE,32790,257,32780,257,32772,257,"xid 1 page for deq rec; data 10 bytes (exact fit) [deq, txn]" +155,"L",1,16,0,16,1,10,10,32737,32737,TRUE,FALSE,FALSE,32791,257,32781,257,32773,257,"xid 1 page + 1 byte for deq rec; data 10 bytes (exact fit) [deq, txn]" +156,"L",2,16,0,16,-1,10,10,65503,65503,TRUE,FALSE,FALSE,65557,513,65547,513,65539,513,"xid 2 pages – 1 byte for deq rec; data 10 bytes (exact fit) [deq, txn]" +157,"L",2,16,0,16,0,10,10,65504,65504,TRUE,FALSE,FALSE,65558,513,65548,513,65540,513,"xid 2 pages for deq rec; data 10 bytes (exact fit) [deq, txn]" +158,"L",2,16,0,16,1,10,10,65505,65505,TRUE,FALSE,FALSE,65559,513,65549,513,65541,513,"xid 2 pages + 1 byte for deq rec; data 10 bytes (exact fit) [deq, txn]" +159,"L",4,16,0,16,-1,10,10,131039,131039,TRUE,FALSE,FALSE,131093,1025,131083,1025,131075,1025,"xid 4 pages – 1 byte for deq rec; data 10 bytes (exact fit) [deq, txn]" +160,"L",4,16,0,16,0,10,10,131040,131040,TRUE,FALSE,FALSE,131094,1025,131084,1025,131076,1025,"xid 4 pages for deq rec; data 10 bytes (exact fit) [deq, txn]" +161,"L",4,16,0,16,1,10,10,131041,131041,TRUE,FALSE,FALSE,131095,1025,131085,1025,131077,1025,"xid 4 pages + 1 byte for deq rec; data 10 bytes (exact fit) [deq, txn]" +162,"L",3.5,16,0,16,0,10,10,114656,114656,TRUE,FALSE,FALSE,114710,897,114700,897,114692,897,"xid 3.5 pages for deq rec; data 10 bytes (exact fit) [deq, txn]" +163,"L",3.5,16,0,16,1,10,10,114657,114657,TRUE,FALSE,FALSE,114711,897,114701,897,114693,897,"xid 3.5 pages + 1 byte for deq rec; data 10 bytes (exact fit) [deq, txn]" +164,"L",1,16,0,16,-1,10,10,32743,32743,TRUE,FALSE,FALSE,32797,257,32787,257,32779,257,"xid 1 page – 1 byte for txn rec; data 10 bytes (exact fit) [deq, txn]" +165,"L",1,16,0,16,0,10,10,32744,32744,TRUE,FALSE,FALSE,32798,257,32788,257,32780,257,"xid 1 page for txn rec; data 10 bytes (exact fit) [deq, txn]" +166,"L",1,16,0,16,1,10,10,32745,32745,TRUE,FALSE,FALSE,32799,257,32789,257,32781,257,"xid 1 page + 1 byte for txn rec; data 10 bytes (exact fit) [deq, txn]" +167,"L",2,16,0,16,-1,10,10,65511,65511,TRUE,FALSE,FALSE,65565,513,65555,513,65547,513,"xid 2 pages – 1 byte for txn rec; data 10 bytes (exact fit) [deq, txn]" +168,"L",2,16,0,16,0,10,10,65512,65512,TRUE,FALSE,FALSE,65566,513,65556,513,65548,513,"xid 2 pages for txn rec; data 10 bytes (exact fit) [deq, txn]" +169,"L",2,16,0,16,1,10,10,65513,65513,TRUE,FALSE,FALSE,65567,513,65557,513,65549,513,"xid 2 pages + 1 byte for txn rec; data 10 bytes (exact fit) [deq, txn]" +170,"L",4,16,0,16,-1,10,10,131047,131047,TRUE,FALSE,FALSE,131101,1025,131091,1025,131083,1025,"xid 4 pages – 1 byte for txn rec; data 10 bytes (exact fit) [deq, txn]" +171,"L",4,16,0,16,0,10,10,131048,131048,TRUE,FALSE,FALSE,131102,1025,131092,1025,131084,1025,"xid 4 pages for txn rec; data 10 bytes (exact fit) [deq, txn]" +172,"L",4,16,0,16,1,10,10,131049,131049,TRUE,FALSE,FALSE,131103,1025,131093,1025,131085,1025,"xid 4 pages + 1 byte for txn rec; data 10 bytes (exact fit) [deq, txn]" +173,"L",3.5,16,0,16,0,10,10,114664,114664,TRUE,FALSE,FALSE,114718,897,114708,897,114700,897,"xid 3.5 pages for txn rec; data 10 bytes (exact fit) [deq, txn]" +174,"L",3.5,16,0,16,1,10,10,114665,114665,TRUE,FALSE,FALSE,114719,897,114709,897,114701,897,"xid 3.5 pages + 1 byte for txn rec; data 10 bytes (exact fit) [deq, txn]" +,,,,,,,,,,,,,,,,,,,, +"High volume tests of random message lengths - RHM_WRONLY req'd for auto-dequeue == FALSE",,,,,,,,,,,,,,,,,,,, +#175,"M",1,5000000,0,5000000,0,0,84,0,0,TRUE,FALSE,FALSE,128,1,32,1,0,0,"1 dblk max [deq]" +#176,"M",3,3000000,0,3000000,0,0,340,0,0,TRUE,FALSE,FALSE,384,3,32,1,0,0,"3 dblks max [deq]" +#177,"M",10,1600000,0,1600000,0,0,1236,0,0,TRUE,FALSE,FALSE,1280,10,32,1,0,0,"10 dblks max [deq]" +#178,"M",30,600000,0,600000,0,0,3796,0,0,TRUE,FALSE,FALSE,3840,30,32,1,0,0,"30 dblks max [deq]" +#179,"M",100,200000,0,200000,0,0,12756,0,0,TRUE,FALSE,FALSE,12800,100,32,1,0,0,"100 dblks max [deq]" +#180,"M",300,60000,0,60000,0,0,38356,0,0,TRUE,FALSE,FALSE,38400,300,32,1,0,0,"300 dblks max [deq]" +#181,"M",1000,20000,0,20000,0,0,127956,0,0,TRUE,FALSE,FALSE,128000,1000,32,1,0,0,"1000 dblks max [deq]" +#182,"M",1,5000000,0,5000000,0,0,100,1,100,TRUE,FALSE,FALSE,244,2,144,2,136,2,"100 bytes xid max + 100 bytes data max [deq txn]" +#183,"M",3,3000000,0,3000000,0,0,300,1,300,TRUE,FALSE,FALSE,644,6,344,3,336,3,"300 bytes xid max + 300 bytes data max [deq txn]" +#184,"M",10,1600000,0,1600000,0,0,1000,1,1000,TRUE,FALSE,FALSE,2044,16,1044,9,1036,9,"1000 bytes xid max + 1000 bytes data max [deq txn]" +#185,"M",30,600000,0,600000,0,0,3000,1,3000,TRUE,FALSE,FALSE,6044,48,3044,24,3036,24,"3000 bytes xid max + 3000 bytes data max [deq txn]" +#186,"M",100,200000,0,200000,0,0,10000,1,10000,TRUE,FALSE,FALSE,20044,157,10044,79,10036,79,"10000 bytes xid max + 10000 bytes data max [deq txn]" +#187,"M",300,60000,0,60000,0,0,30000,1,30000,TRUE,FALSE,FALSE,60044,470,30044,235,30036,235,"30000 bytes xid max + 30000 bytes data max [deq txn]" +#188,"M",1000,20000,0,20000,0,0,100000,1,100000,TRUE,FALSE,FALSE,200044,1563,100044,782,100036,782,"100000 bytes xid max + 100000 bytes data max [deq txn]" +,,,,,,,,,,,,,,,,,,,, +"STANDARD PERFORMANCE BENCHMARK: 10,000,000 writes, data=212b (2 dblks)",,,,,,,,,,,,,,,,,,,, +#189,"M",1,10000000,0,10000000,0,212,212,0,0,TRUE,FALSE,FALSE,256,2,32,1,0,0,"212 bytes data (2 dblks enq + 1 dblk deq)" +#190,"M",1,10000000,0,10000000,0,148,148,64,64,TRUE,FALSE,FALSE,256,2,108,1,100,1,"148 bytes data + 64 bytes xid (2 dblks enq + 1 dblks deq + 1 dblks txn)" diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/main.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/main.cpp new file mode 100644 index 0000000000..c8a4642b1c --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/main.cpp @@ -0,0 +1,57 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "test_mgr.h" + +#include "args.h" +#include <csignal> +#include <iostream> + +#define PACKAGE_NAME "Journal Test Tool" +#define VERSION "0.1" + +namespace po = boost::program_options; + +int main(int argc, char** argv) +{ + std::signal(SIGINT, mrg::jtt::test_mgr::signal_handler); + std::signal(SIGTERM, mrg::jtt::test_mgr::signal_handler); + + std::cout << PACKAGE_NAME << " v." << VERSION << std::endl; + + std::ostringstream oss; + oss << PACKAGE_NAME << " options"; + mrg::jtt::args args(oss.str()); + if (args.parse(argc, argv)) return 1; + + try + { + mrg::jtt::test_mgr tm(args); + tm.run(); + if (tm.error()) return 2; // One or more tests threw exceptions + } + catch (const std::exception& e) + { + std::cerr << e.what() << std::endl; + return 3; + } + return 0; +} diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/read_arg.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/read_arg.cpp new file mode 100644 index 0000000000..94a07c7005 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/read_arg.cpp @@ -0,0 +1,93 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "read_arg.h" + +#include <cassert> +#include <boost/program_options.hpp> +namespace po = boost::program_options; + +namespace mrg +{ +namespace jtt +{ +std::map<std::string, read_arg::read_mode_t> read_arg::_map; +std::string read_arg::_description; +const bool read_arg::init = __init(); + +// static init fn +bool +read_arg::__init() +{ + // Set string versions of each enum option here + _map["NONE"] = NONE; + _map["ALL"] = ALL; + _map["RANDOM"] = RANDOM; + _map["LAZYLOAD"] = LAZYLOAD; + _description = "Determines if and when messages will be read prior to dequeueing. " + "Values: (NONE | ALL | RANDOM | LAZYLOAD)"; + return true; +} + +void +read_arg::parse(const std::string& str) +{ + std::map<std::string, read_arg::read_mode_t>::const_iterator i = _map.find(str); + if (i == _map.end()) + throw po::invalid_option_value(str); + _rm = i->second; +} + +// static fn +const std::string& +read_arg::str(const read_mode_t rm) +{ + std::map<std::string, read_mode_t>::const_iterator i = _map.begin(); + while (i->second != rm && i != _map.end()) i++; + assert(i != _map.end()); + return i->first; +} + +// static fn +const std::string& +read_arg::descr() +{ + return _description; +} + +std::ostream& +operator<<(std::ostream& os, const read_arg& ra) +{ + os << ra.str(); + return os; +} + +std::istream& +operator>>(std::istream& is, read_arg& ra) +{ + std::string s; + is >> s; + ra.parse(s); + return is; +} + +} // namespace jtt +} // namespace mrg diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/read_arg.h b/qpid/cpp/src/tests/legacystore/jrnl/jtt/read_arg.h new file mode 100644 index 0000000000..a8fd6f198e --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/read_arg.h @@ -0,0 +1,62 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef mrg_jtt_read_arg_hpp +#define mrg_jtt_read_arg_hpp + +#include <string> +#include <map> + +namespace mrg +{ +namespace jtt +{ + +class read_arg +{ + public: + enum read_mode_t { NONE, ALL, RANDOM, LAZYLOAD}; + private: + static std::map<std::string, read_mode_t> _map; + static std::string _description; + static const bool init; + static bool __init(); + read_mode_t _rm; + public: + inline read_arg() : _rm(NONE) {} + inline read_arg(read_mode_t rm) : _rm(rm) {} + + inline read_mode_t val() const { return _rm; } + inline void set_val(const read_mode_t rm) { _rm = rm; } + void parse(const std::string& str); + + inline const std::string& str() const { return str(_rm); } + static const std::string& str(const read_mode_t rm); + static const std::string& descr(); + + friend std::ostream& operator<<(std::ostream& os, const read_arg& ra); + friend std::istream& operator>>(std::istream& is, read_arg& ra); +}; + +} // namespace jtt +} // namespace mrg + +#endif // ifndef mrg_jtt_read_arg_hpp diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case.cpp new file mode 100644 index 0000000000..e06e053504 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case.cpp @@ -0,0 +1,179 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "test_case.h" + +#include <cstdlib> +#include <iomanip> +#include <sstream> + +namespace mrg +{ +namespace jtt +{ + +test_case::test_case(const unsigned test_case_num, const u_int32_t num_msgs, + const std::size_t min_data_size, const std::size_t max_data_size, const bool auto_deq, + const std::size_t min_xid_size, const std::size_t max_xid_size, const transient_t transient, + const external_t external, const std::string& comment): + _test_case_num(test_case_num), + _num_msgs(num_msgs), + _min_data_size(min_data_size), + _max_data_size(max_data_size), + _auto_dequeue(auto_deq), + _min_xid_size(min_xid_size), + _max_xid_size(max_xid_size), + _transient(transient), + _external(external), + _comment(comment), + _result_average(), + _result_jmap() +{} + +test_case::~test_case() +{} + +std::size_t +test_case::this_data_size() const +{ + if (_min_data_size == _max_data_size) + return _max_data_size; + std::size_t size_diff = _max_data_size - _min_data_size; + return _min_data_size + std::size_t(1.0 * std::rand() * size_diff/(RAND_MAX + 1.0)); +} + +std::size_t +test_case::this_xid_size() const +{ + // TODO: rework when probabilities are introduced. Assume 50% if _min_xid_size = 0 + if (_max_xid_size == 0) + return std::size_t(0); + if (_min_xid_size == 0) + { + if (1.0 * std::rand() / RAND_MAX < 0.5) + return std::size_t(0); + } + std::size_t size_diff = _max_xid_size - _min_xid_size; + return _min_xid_size + std::size_t(1.0 * std::rand() * size_diff/(RAND_MAX + 1.0)); +} + +bool +test_case::this_transience() const +{ + // TODO: rework when probabilities are introduced. Assume 50% if JTT_RANDOM + if (_transient == JTT_TRANSIENT) + return false; + if (_transient == JTT_PERSISTNET) + return true; + return 1.0 * std::rand() / RAND_MAX < 0.5; +} + +bool +test_case::this_external() const +{ + // TODO: rework when probabilities are introduced. Assume 50% if JDL_RANDOM + if (_external == JDL_INTERNAL) + return false; + if (_external == JDL_EXTERNAL) + return true; + return 1.0 * std::rand() / RAND_MAX < 0.5; +} + +void +test_case::add_result(test_case_result::shared_ptr& tcrp) +{ + _result_average.add_test_result(tcrp); + res_map_citr ari = _result_jmap.find(tcrp->jid()); + if (ari == _result_jmap.end()) + { + test_case_result_agregation::shared_ptr p(new test_case_result_agregation(tcrp->jid())); + p->add_test_result(tcrp); + _result_jmap.insert(res_map_pair(tcrp->jid(), p)); + } + else + ari->second->add_test_result(tcrp); +} + +void +test_case::set_fmt_chk_res(const bool res, const std::string& jid) +{ + _result_average.set_fmt_chk_res(res); + res_map_citr ari = _result_jmap.find(jid); + if (ari != _result_jmap.end()) + ari->second->set_fmt_chk_res(res); +} + +const test_case_result::shared_ptr +test_case::jmap_last(std::string& jid) const +{ + res_map_citr i = _result_jmap.find(jid); + if (i == _result_jmap.end()) + return test_case_result::shared_ptr(); + u_int32_t num_res = (*i).second->num_results(); + if (num_res) + return (*(*i).second)[num_res - 1]; + return test_case_result::shared_ptr(); +} + +void +test_case::clear() +{ + _result_average.clear(); + _result_jmap.clear(); +} + +const std::string +test_case::str() const +{ + std::ostringstream oss; + oss << "Test Parameters: Test case no. " << _test_case_num << ":" << std::endl; + oss << " Comment: " << _comment << std::endl; + oss << " Number of messages: " << _num_msgs << std::endl; + oss << " Data size: " << _min_data_size; + if (_min_data_size == _max_data_size) + oss << " bytes (fixed)" << std::endl; + else + oss << " - " << _max_data_size << " bytes" << std::endl; + oss << " XID size: " << _min_xid_size; + if (_min_xid_size == _max_xid_size) + oss << " bytes (fixed)" << std::endl; + else + oss << " - " << _max_xid_size << " bytes" << std::endl; + oss << " Auto-dequeue: " << (_auto_dequeue ? "true" : "false") << std::endl; + oss << " Persistence: "; + switch (_transient) + { + case JTT_TRANSIENT: oss << "TRANSIENT" << std::endl; break; + case JTT_PERSISTNET: oss << "PERSISTNET" << std::endl; break; + case JTT_RANDOM: oss << "RANDOM" << std::endl; break; + } + oss << " Message Data: "; + switch (_external) + { + case JDL_INTERNAL: oss << "INTERNAL"; break; + case JDL_EXTERNAL: oss << "EXTERNAL"; break; + case JDL_RANDOM: oss << "RANDOM"; break; + } + return oss.str(); +} + +} // namespace jtt +} // namespace mrg diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case.h b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case.h new file mode 100644 index 0000000000..f72dd05f0c --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case.h @@ -0,0 +1,110 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef mrg_jtt_test_case_hpp +#define mrg_jtt_test_case_hpp + +#include <boost/shared_ptr.hpp> +#include <cstddef> +#include <map> +#include "test_case_result.h" +#include "test_case_result_agregation.h" +#include <vector> + +namespace mrg +{ +namespace jtt +{ + + class test_case + { + public: + enum transient_type { JTT_TRANSIENT = 0, JTT_PERSISTNET, JTT_RANDOM }; + typedef transient_type transient_t; + + enum data_location { JDL_INTERNAL = 0, JDL_EXTERNAL, JDL_RANDOM }; + typedef data_location external_t; + + typedef boost::shared_ptr<test_case> shared_ptr; + + typedef std::map<std::string, test_case_result_agregation::shared_ptr> res_map; + typedef std::pair<std::string, test_case_result_agregation::shared_ptr> res_map_pair; + typedef res_map::const_iterator res_map_citr; + + private: + unsigned _test_case_num; + u_int32_t _num_msgs; + std::size_t _min_data_size; + std::size_t _max_data_size; + bool _auto_dequeue; + // TODO: add probability of transaction to these params + std::size_t _min_xid_size; + std::size_t _max_xid_size; + // TODO: change these enums (transient_t & external_t) to probabilities + transient_t _transient; + external_t _external; + std::string _comment; + + test_case_result_agregation _result_average; // overall average result + res_map _result_jmap; // map of per-journal averages + + public: + test_case(const unsigned test_case_num, const u_int32_t num_msgs, + const std::size_t min_data_size, const std::size_t max_data_size, + const bool auto_deq, const std::size_t min_xid_size, + const std::size_t max_xid_size, const transient_t transient, + const external_t external, const std::string& comment); + virtual ~test_case(); + + inline unsigned test_case_num() const { return _test_case_num; } + inline u_int32_t num_msgs() const { return _num_msgs; } + inline std::size_t min_data_size() const { return _min_data_size; } + inline std::size_t max_data_size() const { return _max_data_size; } + std::size_t this_data_size() const; + inline bool auto_deq() const { return _auto_dequeue; } + inline std::size_t min_xid_size() const { return _min_xid_size; } + inline std::size_t max_xid_size() const { return _max_xid_size; } + std::size_t this_xid_size() const; + inline transient_t transient() const { return _transient; } + bool this_transience() const; + inline external_t external() const { return _external; } + bool this_external() const; + inline const std::string& comment() const { return _comment; } + + void add_result(test_case_result::shared_ptr& p); + void set_fmt_chk_res(const bool res, const std::string& jid); + + inline const test_case_result_agregation& average() const { return _result_average; } + inline u_int32_t num_results() const { return _result_average.num_results(); } + inline unsigned num_jrnls() const { return _result_jmap.size(); } + inline res_map_citr jrnl_average(std::string& jid) const { return _result_jmap.find(jid); } + inline res_map_citr jmap_begin() const { return _result_jmap.begin(); } + inline res_map_citr jmap_end() const { return _result_jmap.end(); } + const test_case_result::shared_ptr jmap_last(std::string& jid) const; + + void clear(); + const std::string str() const; + }; + +} // namespace jtt +} // namespace mrg + +#endif // ifndef mrg_jtt_test_case_hpp diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result.cpp new file mode 100644 index 0000000000..2f88f265a5 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result.cpp @@ -0,0 +1,201 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "test_case_result.h" + +#include <iomanip> +#include <sstream> + +namespace mrg +{ +namespace jtt +{ + +test_case_result::test_case_result(const std::string& jid): + _jid(jid), + _num_enq(0), + _num_deq(0), + _num_read(0), + _num_rproc(0), + _start_time(), + _stop_time(), + _stopped(false), + _test_time(), + _exception_list() +{} + +test_case_result::~test_case_result() +{} + +const std::string +test_case_result::test_time_str() const +{ + return _test_time.str(9); +} + +void +test_case_result::add_exception(const journal::jexception& e, const bool set_stop_time_flag) +{ + if (!_stopped && set_stop_time_flag) + { + set_stop_time(); + _stopped = true; + } + _exception_list.push_back(e.what()); +} + +void +test_case_result::add_exception(const std::string& err_str, const bool set_stop_time_flag) +{ + if (!_stopped && set_stop_time_flag) + { + set_stop_time(); + _stopped = true; + } + _exception_list.push_back(err_str); +} + +void +test_case_result::add_exception(const char* err_str, const bool set_stop_time_flag) +{ + if (!_stopped && set_stop_time_flag) + { + set_stop_time(); + _stopped = true; + } + _exception_list.push_back(err_str); +} + +void +test_case_result::clear() +{ + _num_enq = 0; + _num_deq = 0; + _num_read = 0; + _start_time.set_zero(); + _stop_time.set_zero(); + _test_time.set_zero(); + _exception_list.clear(); +} + +const std::string +test_case_result::str(const bool summary) const +{ + std::ostringstream oss; + if (summary) + { + oss << _jid << ":"; + oss << str_summary(); + if (_exception_list.size()) + oss << "; fail: " << _exception_list[0] << std::endl; + else + oss << "; ok" << std::endl; + } + else + { + oss << "--- Journal instance: jid=\"" << _jid << "\" ---" << std::endl; + oss << str_full(); + if (_exception_list.size()) + oss << " exception/error:" << _exception_list[0] << std::endl; + } + return oss.str(); +} + +const std::string +test_case_result::str_full() const +{ + const double t = _test_time.tv_sec + (_test_time.tv_nsec/1e9); + const bool no_exception = _exception_list.empty(); + std::ostringstream oss; + oss.setf(std::ios::fixed, std::ios::floatfield); + oss.precision(2); + if (no_exception) + { + oss.precision(6); + oss << " total test time: " << t << "s" << std::endl; + } + oss.precision(3); + oss << " total number enqueues: " << _num_enq; + if (no_exception) + oss << " (" << (_num_enq / t) << " enq/sec)"; + oss << std::endl; + oss << " total number dequeues: " << _num_deq; + if (no_exception) + oss << " (" << (_num_deq / t) << " deq/sec)"; + oss << std::endl; + oss << "total write operations: " << (_num_enq + _num_deq); + if (no_exception) + oss << " (" << ((_num_enq + _num_deq) / t) << " wrops/sec)"; + oss << std::endl; + oss << " total number reads: " << _num_read; + if (no_exception) + oss << " (" << (_num_read / t) << " rd/sec)"; + oss << std::endl; + oss << " total operations: " << (_num_enq + _num_deq + _num_read); + if (no_exception) + oss << " (" << ((_num_enq + _num_deq + _num_read) / t) << " ops/sec)"; + oss << std::endl; + oss << " overall result: " << (no_exception ? "PASS" : "*** FAIL ***") << std::endl; + return oss.str(); +} + +const std::string +test_case_result::str_summary() const +{ + const double t = _test_time.tv_sec + (_test_time.tv_nsec/1e9); + const bool no_exception = _exception_list.empty(); + std::ostringstream oss; + oss.setf(std::ios::fixed, std::ios::floatfield); + if (no_exception) + { + oss.precision(6); + oss << " t=" << t << "s;"; + } + else + oss << " exception"; + oss.precision(3); + oss << " enq=" << _num_enq; + if (no_exception) + oss << " (" << (_num_enq / t) << ")"; + oss << "; deq=" << _num_deq; + if (no_exception) + oss << " (" << (_num_deq / t) << ")"; + oss << "; wr=" << (_num_enq + _num_deq); + if (no_exception) + oss << " (" << ((_num_enq + _num_deq) / t) << ")"; + oss << "; rd=" << _num_read; + if (no_exception) + oss << " (" << (_num_read / t) << ")"; + oss << "; tot=" << (_num_enq + _num_deq + _num_read); + if (no_exception) + oss << " (" << ((_num_enq + _num_deq + _num_read) / t) << ")"; + return oss.str(); +} + +void +test_case_result::calc_test_time() +{ + if (!_start_time.is_zero() && _stop_time >= _start_time) + _test_time = _stop_time - _start_time; +} + +} // namespace jtt +} // namespace mrg diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result.h b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result.h new file mode 100644 index 0000000000..d15f9d021d --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result.h @@ -0,0 +1,100 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef mrg_jtt_test_case_result_hpp +#define mrg_jtt_test_case_result_hpp + +#include <boost/shared_ptr.hpp> +#include <deque> +#include "qpid/legacystore/jrnl/jexception.h" +#include "qpid/legacystore/jrnl/time_ns.h" +#include <string> + +namespace mrg +{ +namespace jtt +{ + + class test_case_result + { + public: + typedef boost::shared_ptr<test_case_result> shared_ptr; + + typedef std::deque<std::string> elist; + typedef elist::const_iterator elist_citr; + + protected: + std::string _jid; + u_int32_t _num_enq; + u_int32_t _num_deq; + u_int32_t _num_read; // Messages actually read + u_int32_t _num_rproc; // Messages handled by read thread (not all are read) + journal::time_ns _start_time; + journal::time_ns _stop_time; + bool _stopped; + journal::time_ns _test_time; + elist _exception_list; + + public: + test_case_result(const std::string& jid); + virtual ~test_case_result(); + + inline const std::string& jid() const { return _jid; } + inline u_int32_t num_enq() const { return _num_enq; } + inline u_int32_t incr_num_enq() { return ++_num_enq; } + inline u_int32_t num_deq() const { return _num_deq; } + inline u_int32_t incr_num_deq() { return ++_num_deq; } + inline u_int32_t num_read() const { return _num_read; } + inline u_int32_t incr_num_read() { return ++_num_read; } + inline u_int32_t num_rproc() const { return _num_rproc; } + inline u_int32_t incr_num_rproc() { return ++_num_rproc; } + + inline const journal::time_ns& start_time() const { return _start_time; } + inline void set_start_time() { ::clock_gettime(CLOCK_REALTIME, &_start_time); } + inline const journal::time_ns& stop_time() const { return _stop_time; } + inline void set_stop_time() + { ::clock_gettime(CLOCK_REALTIME, &_stop_time); calc_test_time(); } + inline void set_test_time(const journal::time_ns& ts) { _test_time = ts; } + inline const journal::time_ns& test_time() const { return _test_time; } + const std::string test_time_str() const; + + void add_exception(const journal::jexception& e, const bool set_stop_time_flag = true); + void add_exception(const std::string& err_str, const bool set_stop_time_flag = true); + void add_exception(const char* err_str, const bool set_stop_time_flag = true); + inline bool exception() const { return _exception_list.size() > 0; } + inline unsigned exception_count() const { return _exception_list.size(); } + inline elist_citr begin() { return _exception_list.begin(); } + inline elist_citr end() { return _exception_list.end(); } + inline const std::string& operator[](unsigned i) { return _exception_list[i]; } + + void clear(); + const std::string str(const bool summary) const; + + protected: + const std::string str_full() const; + const std::string str_summary() const; + void calc_test_time(); + }; + +} // namespace jtt +} // namespace mrg + +#endif // ifndef mrg_jtt_test_case_result_hpp diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result_agregation.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result_agregation.cpp new file mode 100644 index 0000000000..da439e71e8 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result_agregation.cpp @@ -0,0 +1,185 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "test_case_result_agregation.h" + +#include <iomanip> +#include <sstream> + +namespace mrg +{ +namespace jtt +{ + +test_case_result_agregation::test_case_result_agregation(): + test_case_result("Average"), + _tc_average(true), + _fmt_chk_done(false), + _fmt_chk_err(false), + _res_list() +{ +} + +test_case_result_agregation::test_case_result_agregation(const std::string& jid): + test_case_result(jid), + _tc_average(false), + _fmt_chk_done(false), + _fmt_chk_err(false), + _res_list() +{} + +test_case_result_agregation::~test_case_result_agregation() +{} + +void +test_case_result_agregation::add_test_result(const test_case_result::shared_ptr& tcrp) +{ + if (_tc_average || _jid.compare(tcrp->jid()) == 0) + { + _num_enq += tcrp->num_enq(); + _num_deq += tcrp->num_deq(); + _num_read += tcrp->num_read(); + add_test_time(tcrp->test_time()); + _exception_list.insert(_exception_list.end(), tcrp->begin(), tcrp->end()); + _res_list.push_back(tcrp); + } +} + +bool +test_case_result_agregation::exception() const +{ + for (tcrp_list_citr i = _res_list.begin(); i < _res_list.end(); i++) + if ((*i)->exception()) + return true; + return false; +} + +unsigned +test_case_result_agregation::exception_count() const +{ + unsigned cnt = 0; + for (tcrp_list_citr i = _res_list.begin(); i < _res_list.end(); i++) + cnt += (*i)->exception_count(); + return cnt; +} + +void +test_case_result_agregation::clear() +{ + test_case_result::clear(); + _res_list.clear(); +} + +const std::string +test_case_result_agregation::str(const bool last_only, const bool summary) const +{ + std::ostringstream oss; + if (last_only) + oss << " " << _res_list.at(_res_list.size()-1)->str(summary); + else + { + for (tcrp_list_citr i=_res_list.begin(); i!=_res_list.end(); i++) + oss << " " << (*i)->str(summary); + } + if (_res_list.size() > 1) + oss << " " << (summary ? str_summary(last_only) : str_full(last_only)); + return oss.str(); +} + +const std::string +test_case_result_agregation::str_full(const bool /*last_only*/) const +{ + std::ostringstream oss; + oss.precision(2); + if (_tc_average) + oss << "Average across all journal instances:" << std::endl; + else + oss << "Average for jid=\"" << _jid << "\":" << std::endl; + oss << " total number results: " << _res_list.size() << std::endl; + oss << " number exceptions: " << _exception_list.size() << " (" << + (100.0 * _res_list.size() / _exception_list.size()) << "%)" << std::endl; + + oss << test_case_result::str_full(); + + if (_exception_list.size()) + { + unsigned n = 0; + oss << "List of exceptions/errors:" << std::endl; + for (elist_citr i = _exception_list.begin(); i != _exception_list.end(); i++, n++) + oss << " " << n << ". " << (*i) << std::endl; + } + + if (!_tc_average && _res_list.size() > 1) + { + oss << "Individual results:" << std::endl; + for (tcrp_list_citr i=_res_list.begin(); i!=_res_list.end(); i++) + oss << " " << (*i)->str(false) << std::endl; + oss << std::endl; + } + + return oss.str(); +} + +const std::string +test_case_result_agregation::str_summary(const bool /*last_only*/) const +{ + std::ostringstream oss; + if (_tc_average) + oss << "overall average [" << _res_list.size() << "]:"; + else + oss << "average (" << _res_list.size() << "):"; + + oss << test_case_result::str_summary(); + if (_fmt_chk_done) + oss << " fmt-chk=" << (_fmt_chk_err ? "fail" : "ok"); + + if (_exception_list.size()) + { + if (_tc_average) + oss << " fail: " << _exception_list.size() << " exception" + << (_exception_list.size()>1?"s":"") << std::endl; + else + { + if (_exception_list.size() == 1) + oss << " fail: " << *_exception_list.begin() << std::endl; + else + { + oss << std::endl; + unsigned n = 0; + for (elist_citr i = _exception_list.begin(); i != _exception_list.end(); i++, n++) + oss << " " << n << ". " << (*i) << std::endl; + } + } + } + else + oss << " ok" << std::endl; + return oss.str(); +} + +const journal::time_ns& +test_case_result_agregation::add_test_time(const journal::time_ns& t) +{ + _test_time += t; + return _test_time; +} + +} // namespace jtt +} // namespace mrg diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result_agregation.h b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result_agregation.h new file mode 100644 index 0000000000..0b3998176c --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result_agregation.h @@ -0,0 +1,81 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef mrg_jtt_test_case_result_agregation_hpp +#define mrg_jtt_test_case_result_agregation_hpp + +#include "test_case_result.h" + +#include <iostream> +#include <vector> + +namespace mrg +{ +namespace jtt +{ + + class test_case_result_agregation : public test_case_result + { + public: + typedef boost::shared_ptr<test_case_result_agregation> shared_ptr; + + typedef std::vector<test_case_result::shared_ptr> tcrp_list; + typedef tcrp_list::const_iterator tcrp_list_citr; + + private: + bool _tc_average; + bool _fmt_chk_done; + bool _fmt_chk_err; + tcrp_list _res_list; + + public: + test_case_result_agregation(); // used for average across jrnl instances + test_case_result_agregation(const std::string& jid); + virtual ~test_case_result_agregation(); + + void add_test_result(const test_case_result::shared_ptr& tcrp); + + inline bool tc_average_mode() const { return _tc_average; } + inline bool fmt_chk_done() const { return _fmt_chk_done; } + inline bool fmt_chk_res() const { return _fmt_chk_err; } + inline void set_fmt_chk_res(const bool err) + { _fmt_chk_done = true; _fmt_chk_err |= err; if (err) add_exception("Journal format error"); } + inline u_int32_t num_results() const { return _res_list.size(); } + inline tcrp_list_citr rlist_begin() const { return _res_list.begin(); } + inline tcrp_list_citr rlist_end() const { return _res_list.end(); } + inline const test_case_result::shared_ptr& operator[](unsigned i) const + { return _res_list[i]; } + bool exception() const; + unsigned exception_count() const; + + void clear(); + const std::string str(const bool last_only, const bool summary) const; + + private: + const std::string str_full(const bool last_only) const; + const std::string str_summary(const bool last_only) const; + const journal::time_ns& add_test_time(const journal::time_ns& t); + }; + +} // namespace jtt +} // namespace mrg + +#endif // ifndef mrg_jtt_test_case_result_agregation_hpp diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_set.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_set.cpp new file mode 100644 index 0000000000..b818d6c7ae --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_set.cpp @@ -0,0 +1,169 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "test_case_set.h" + +#include <cstdlib> +#include <fstream> +#include <iostream> + +namespace mrg +{ +namespace jtt +{ + +test_case_set::test_case_set(): + _tc_list(), + _csv_ignored(0) +{} + +test_case_set::test_case_set(const std::string& csv_filename, const bool recover_mode, + const csv_map& cols): + _tc_list(), + _csv_ignored(0) +{ + append_from_csv(csv_filename, recover_mode, cols); +} + +test_case_set::~test_case_set() +{} + +void +test_case_set::append(const unsigned test_case_num, const u_int32_t num_msgs, + const std::size_t min_data_size, const std::size_t max_data_size, const bool auto_deq, + const std::size_t min_xid_size, const std::size_t max_xid_size, + const test_case::transient_t transient, const test_case::external_t external, + const std::string& comment) +{ + test_case::shared_ptr tcp(new test_case(test_case_num, num_msgs, min_data_size, + max_data_size, auto_deq, min_xid_size, max_xid_size, transient, external, comment)); + append(tcp); +} + + +#define CSV_BUFF_SIZE 2048 +void +test_case_set::append_from_csv(const std::string& csv_filename, const bool recover_mode, + const csv_map& cols) +{ + char buff[CSV_BUFF_SIZE]; + std::ifstream ifs(csv_filename.c_str()); + while (ifs.good()) + { + ifs.getline(buff, (std::streamsize)CSV_BUFF_SIZE); + if (ifs.gcount()) + { + test_case::shared_ptr tcp = get_tc_from_csv(buff, cols); + if (tcp.get()) + { + if (!recover_mode || tcp->auto_deq()) + append(tcp); + else + _csv_ignored++; + } + } + } +} + +test_case::shared_ptr +test_case_set::get_tc_from_csv(const std::string& csv_line, const csv_map& cols) +{ + unsigned test_case_num = 0; + u_int32_t num_msgs = 0; + std::size_t min_data_size = 0; + std::size_t max_data_size = 0; + bool auto_deq = false; + std::size_t min_xid_size = 0; + std::size_t max_xid_size = 0; + test_case::transient_t transient = test_case::JTT_TRANSIENT; + test_case::external_t external = test_case::JDL_INTERNAL; + std::string comment; + + csv_tok t(csv_line); + unsigned col_num = 0; + for (csv_tok_citr t_itr = t.begin(); t_itr != t.end(); ++t_itr, ++col_num) + { + const std::string& tok = *t_itr; + csv_map_citr m_citr = cols.find(col_num); + if (m_citr != cols.end()) + { + switch (m_citr->second) + { + case CSV_TC_NUM: + if (!tok.size() || tok[0] < '0' || tok[0] > '9') + return test_case::shared_ptr(); + test_case_num = unsigned(std::atol(tok.c_str())); + break; + case CSV_TC_NUM_MSGS: num_msgs = u_int32_t(std::atol(tok.c_str())); break; + case CSV_TC_MIN_DATA_SIZE: min_data_size = std::size_t(std::atol(tok.c_str())); break; + case CSV_TC_MAX_DATA_SIZE: max_data_size = std::size_t(std::atol(tok.c_str())); break; + case CSV_TC_AUTO_DEQ: + if (tok == "TRUE" || tok == "1") + auto_deq = true; + break; + case CSV_TC_MIN_XID_SIZE: min_xid_size = std::size_t(std::atol(tok.c_str())); break; + case CSV_TC_MAX_XID_SIZE: max_xid_size = std::size_t(std::atol(tok.c_str())); break; + case CSV_TC_TRANSIENT: + if (tok == "TRUE" || tok == "1") + transient = test_case::JTT_PERSISTNET; + else if (tok == "RANDOM" || tok == "-1") + transient = test_case::JTT_RANDOM; + break; + case CSV_TC_EXTERNAL: + if (tok == "TRUE" || tok == "1") + external = test_case::JDL_EXTERNAL; + else if (tok == "RANDOM" || tok == "-1") + external = test_case::JDL_RANDOM; + break; + case CSV_TC_COMMENT: comment = *t_itr; break; + } + } + } + if (col_num) + return test_case::shared_ptr(new test_case(test_case_num, num_msgs, min_data_size, + max_data_size, auto_deq, min_xid_size, max_xid_size, transient, external, comment)); + else + return test_case::shared_ptr(); +} + +// Static member initializations +// This csv_map is for use on the standard spreadsheet-derived test case csv files. +test_case_set::csv_map test_case_set::std_csv_map; +const bool test_case_set::_map_init = __init(); + +bool +test_case_set::__init() +{ + std_csv_map.insert(test_case_set::csv_pair(0, test_case_set::CSV_TC_NUM)); + std_csv_map.insert(test_case_set::csv_pair(5, test_case_set::CSV_TC_NUM_MSGS)); + std_csv_map.insert(test_case_set::csv_pair(7, test_case_set::CSV_TC_MIN_DATA_SIZE)); + std_csv_map.insert(test_case_set::csv_pair(8, test_case_set::CSV_TC_MAX_DATA_SIZE)); + std_csv_map.insert(test_case_set::csv_pair(11, test_case_set::CSV_TC_AUTO_DEQ)); + std_csv_map.insert(test_case_set::csv_pair(9, test_case_set::CSV_TC_MIN_XID_SIZE)); + std_csv_map.insert(test_case_set::csv_pair(10, test_case_set::CSV_TC_MAX_XID_SIZE)); + std_csv_map.insert(test_case_set::csv_pair(12, test_case_set::CSV_TC_TRANSIENT)); + std_csv_map.insert(test_case_set::csv_pair(13, test_case_set::CSV_TC_EXTERNAL)); + std_csv_map.insert(test_case_set::csv_pair(20, test_case_set::CSV_TC_COMMENT)); + return true; +} + +} // namespace jtt +} // namespace mrg diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_set.h b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_set.h new file mode 100644 index 0000000000..94a1ee3172 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_set.h @@ -0,0 +1,99 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef mrg_jtt_test_case_set_hpp +#define mrg_jtt_test_case_set_hpp + +#include "test_case.h" + +#include <cstddef> +#include <boost/tokenizer.hpp> +#include <map> +#include <vector> + +namespace mrg +{ +namespace jtt +{ + + class test_case_set + { + public: + enum csv_col_enum { + CSV_TC_NUM = 0, + CSV_TC_NUM_MSGS, + CSV_TC_MIN_DATA_SIZE, + CSV_TC_MAX_DATA_SIZE, + CSV_TC_AUTO_DEQ, + CSV_TC_MIN_XID_SIZE, + CSV_TC_MAX_XID_SIZE, + CSV_TC_TRANSIENT, + CSV_TC_EXTERNAL, + CSV_TC_COMMENT }; + typedef std::pair<unsigned, csv_col_enum> csv_pair; + typedef std::map<unsigned, csv_col_enum> csv_map; + typedef csv_map::const_iterator csv_map_citr; + static csv_map std_csv_map; + + typedef std::vector<test_case::shared_ptr> tcl; + typedef tcl::iterator tcl_itr; + typedef tcl::const_iterator tcl_citr; + + typedef boost::tokenizer<boost::escaped_list_separator<char> > csv_tok; + typedef csv_tok::const_iterator csv_tok_citr; + + private: + tcl _tc_list; + static const bool _map_init; + unsigned _csv_ignored; + + public: + test_case_set(); + test_case_set(const std::string& csv_filename, const bool recover_mode, + const csv_map& cols = std_csv_map); + virtual ~test_case_set(); + + inline unsigned size() const { return _tc_list.size(); } + inline unsigned ignored() const { return _csv_ignored; } + inline bool empty() const { return _tc_list.empty(); } + + inline void append(const test_case::shared_ptr& tc) { _tc_list.push_back(tc); } + void append(const unsigned test_case_num, const u_int32_t num_msgs, + const std::size_t min_data_size, const std::size_t max_data_size, + const bool auto_deq, const std::size_t min_xid_size, + const std::size_t max_xid_size, const test_case::transient_t transient, + const test_case::external_t external, const std::string& comment); + void append_from_csv(const std::string& csv_filename, const bool recover_mode, + const csv_map& cols = std_csv_map); + inline tcl_itr begin() { return _tc_list.begin(); } + inline tcl_itr end() { return _tc_list.end(); } + inline const test_case::shared_ptr& operator[](unsigned i) { return _tc_list[i]; } + inline void clear() { _tc_list.clear(); } + + private: + test_case::shared_ptr get_tc_from_csv(const std::string& csv_line, const csv_map& cols); + static bool __init(); + }; + +} // namespace jtt +} // namespace mrg + +#endif // ifndef mrg_jtt_test_case_set_hpp diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_mgr.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_mgr.cpp new file mode 100644 index 0000000000..de0b5dbfb9 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_mgr.cpp @@ -0,0 +1,218 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "test_mgr.h" + +#include <cstdlib> +#include <iostream> +#include <sys/stat.h> +#include "test_case_set.h" + +namespace mrg +{ +namespace jtt +{ + +test_mgr::test_mgr(args& args): + _ji_list(), + _args(args), + _err_flag(false), + _random_fn_ptr(random_fn) +{ + if (_args.seed) + std::srand(_args.seed); +} + +test_mgr::~test_mgr() +{} + +void +test_mgr::run() +{ + // TODO: complete tidy-up of non-summary (verbose) results, then pull through + // a command-line summary to control this. + // Idea: --summary: prints short results afterwards + // --verbose: prints long version as test progresses + // defualt: none of these, similar to current summary = true version. + const bool summary = true; + + std::cout << "CSV file: \"" << _args.test_case_csv_file_name << "\""; + test_case_set tcs(_args.test_case_csv_file_name, _args.recover_mode); + + if (tcs.size()) + { + std::cout << " (found " << tcs.size() << " test case" << (tcs.size() != 1 ? "s" : "") << + ")" << std::endl; + if (tcs.ignored()) + std::cout << "WARNING: " << tcs.ignored() << " test cases were ignored. (All test " + "cases without auto-dequeue are ignored when recover-mode is selected.)" << + std::endl; + _args.print_args(); + } + else if(tcs.ignored()) + { + std::cout << " WARNING: All " << tcs.ignored() << " test case(s) were ignored. (All test " + "cases without auto-dequeue are ignored when recover-mode is selected.)" << + std::endl; + } + else + std::cout << " (WARNING: This CSV file is empty or does not exist.)" << std::endl; + + do + { + unsigned u = 0; + if (_args.randomize) + random_shuffle(tcs.begin(), tcs.end(), _random_fn_ptr); + for (test_case_set::tcl_itr tci = tcs.begin(); tci != tcs.end(); tci++, u++) + { + if (summary) + std::cout << "Test case " << (*tci)->test_case_num() << ": \"" << + (*tci)->comment() << "\"" << std::endl; + else + std::cout << (*tci)->str() << std::endl; + if (!_args.reuse_instance || _ji_list.empty()) + initialize_jrnls(); + for (ji_list_citr jii=_ji_list.begin(); jii!=_ji_list.end(); jii++) + (*jii)->init_tc(*tci, &_args); + for (ji_list_citr jii=_ji_list.begin(); jii!=_ji_list.end(); jii++) + (*jii)->run_tc(); + for (ji_list_citr jii=_ji_list.begin(); jii!=_ji_list.end(); jii++) + (*jii)->tc_wait_compl(); + + if (_args.format_chk) + { + for (ji_list_citr jii=_ji_list.begin(); jii!=_ji_list.end(); jii++) + { + jrnl_init_params::shared_ptr jpp = (*jii)->params(); + std::string ja = _args.jfile_analyzer; + if (ja.empty()) ja = "./jfile_chk.py"; + if (!exists(ja)) + { + std::ostringstream oss; + oss << "ERROR: Validation program \"" << ja << "\" does not exist" << std::endl; + throw std::runtime_error(oss.str()); + } + std::ostringstream oss; + oss << ja << " -b " << jpp->base_filename(); + // TODO: When jfile_check.py can handle previously recovered journals for + // specific tests, then remove this exclusion. + if (!_args.recover_mode) + { + oss << " -c " << _args.test_case_csv_file_name; + oss << " -t " << (*tci)->test_case_num(); + } + oss << " -q " << jpp->jdir(); + bool res = system(oss.str().c_str()) != 0; + (*tci)->set_fmt_chk_res(res, jpp->jid()); + if (res) _err_flag = true; + } + } + + if (!_args.recover_mode && !_args.keep_jrnls) + for (ji_list_citr jii=_ji_list.begin(); jii!=_ji_list.end(); jii++) + try { mrg::journal::jdir::delete_dir((*jii)->jrnl_dir()); } + catch (...) {} // TODO - work out exception strategy for failure here... + + print_results(*tci, summary); + if ((*tci)->average().exception()) + _err_flag = true; + if (_abort || (!_args.repeat_flag && _signal)) + break; + if (_args.pause_secs && tci != tcs.end()) + ::usleep(_args.pause_secs * 1000000); + } + } + while (_args.repeat_flag && !_signal); +} + +// static fn: +void +test_mgr::signal_handler(int sig) +{ + if (_signal) + _abort = true; + _signal = sig; + std::cout << std::endl; + std::cout << "********************************" << std::endl; + std::cout << "Caught signal " << sig << std::endl; + if (_abort) + std::cout << "Aborting..." << std::endl; + else + std::cout << "Completing current test cycle..." << std::endl; + std::cout << "********************************" << std::endl << std::endl; +} + +bool +test_mgr::exists(std::string fname) +{ + struct stat s; + if (::stat(fname.c_str(), &s)) + { + if (errno == ENOENT) // No such dir or file + return false; + // Throw for any other condition + std::ostringstream oss; + oss << "ERROR: test_mgr::exists(): file=\"" << fname << "\": " << FORMAT_SYSERR(errno); + throw std::runtime_error(oss.str()); + } + return true; +} + +void +test_mgr::initialize_jrnls() +{ + _ji_list.clear(); + for (unsigned i=0; i<_args.num_jrnls; i++) + { + std::ostringstream jid; + jid << std::hex << std::setfill('0'); + jid << "test_" << std::setw(4) << std::hex << i; + std::ostringstream jdir; + jdir << _args.journal_dir << "/" << jid.str(); + jrnl_init_params::shared_ptr jpp(new jrnl_init_params(jid.str(), jdir.str(), jid.str())); + jrnl_instance::shared_ptr jip(new jrnl_instance(jpp)); + _ji_list.push_back(jip); + } +} + +void +test_mgr::print_results(test_case::shared_ptr tcp, const bool summary) +{ + if (!summary) + std::cout << " === Results ===" << std::endl; + +// TODO - the reporting is broken when --repeat is used. The following commented-out +// section was an attempt to fix it, but there are too many side-effects. +// for (test_case::res_map_citr i=tcp->jmap_begin(); i!=tcp->jmap_end(); i++) +// std::cout << (*i).second->str(summary, summary); +// if (tcp->num_jrnls() > 1) + std::cout << tcp->average().str(false, summary); + + if (!summary) + std::cout << std::endl; +} + +// static instances +volatile sig_atomic_t test_mgr::_signal = 0; +volatile bool test_mgr::_abort = false; + +} // namespace jtt +} // namespace mrg diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_mgr.h b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_mgr.h new file mode 100644 index 0000000000..e608ac6280 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_mgr.h @@ -0,0 +1,68 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef mrg_jtt_test_mgr_hpp +#define mrg_jtt_test_mgr_hpp + +#include "args.h" +#include <csignal> +#include <cstdlib> +#include "jrnl_instance.h" + +namespace mrg +{ +namespace jtt +{ + class test_mgr + { + public: + typedef std::vector<jrnl_instance::shared_ptr> ji_list; + typedef ji_list::iterator ji_list_itr; + typedef ji_list::const_iterator ji_list_citr; + + private: + ji_list _ji_list; + args& _args; + bool _err_flag; + ptrdiff_t (*_random_fn_ptr)(const ptrdiff_t i); + static volatile std::sig_atomic_t _signal; + static volatile bool _abort; + + public: + test_mgr(args& args); + virtual ~test_mgr(); + void run(); + inline bool error() const { return _err_flag; } + + static void signal_handler(int signal); + + private: + static bool exists(std::string file_name); + void initialize_jrnls(); + void print_results(test_case::shared_ptr tcp, const bool summary); + inline static ptrdiff_t random_fn(const ptrdiff_t i) + { return static_cast<ptrdiff_t>(1.0 * i * std::rand() / RAND_MAX); } + }; + +} // namespace jtt +} // namespace mrg + +#endif // ifndef mrg_jtt_test_mgr_hpp diff --git a/qpid/cpp/src/tests/legacystore/jrnl/prof b/qpid/cpp/src/tests/legacystore/jrnl/prof new file mode 100755 index 0000000000..6e0e6da0eb --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/prof @@ -0,0 +1,32 @@ +#!/bin/bash + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +mkdir -p profile +opcontrol --setup --no-vmlinux --separate=library +opcontrol --start +# -- Do stuff here -- +./jtest wtests.csv 264 +# -- End of stuff -- +opcontrol --stop +opcontrol --dump +opcontrol --shutdown +opreport -l ./jtest +opannotate --source --output-dir=profile ./jtest diff --git a/qpid/cpp/src/tests/legacystore/jrnl/run-journal-tests b/qpid/cpp/src/tests/legacystore/jrnl/run-journal-tests new file mode 100755 index 0000000000..5e13967a01 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/run-journal-tests @@ -0,0 +1,47 @@ +#!/bin/bash + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +if test x${TMP_DATA_DIR} == x; then + export TMP_DATA_DIR=/tmp +fi +fail=0 +num_jrnls=3 + +# Run jtt using default test set +echo +echo "===== Mode 1: New journal instance, no recover =====" +jtt/jtt --analyzer ../../tools/store_chk --jrnl-dir ${TMP_DATA_DIR} --csv jtt/jtt.csv --format-chk --num-jrnls ${num_jrnls} || fail=1 +rm -rf ${TMP_DATA_DIR}/test_0* +echo +echo "===== Mode 2: Re-use journal instance, no recover =====" +jtt/jtt --analyzer ../../tools/store_chk --jrnl-dir ${TMP_DATA_DIR} --csv jtt/jtt.csv --reuse-instance --format-chk --num-jrnls ${num_jrnls} || fail=1 +rm -rf ${TMP_DATA_DIR}/test_0* +echo +echo "===== Mode 3: New journal instance, recover previous test journal =====" +jtt/jtt --analyzer ../../tools/store_chk --jrnl-dir ${TMP_DATA_DIR} --csv jtt/jtt.csv --recover-mode --format-chk --num-jrnls ${num_jrnls} || fail=1 +rm -rf ${TMP_DATA_DIR}/test_0* +echo +echo "===== Mode 4: Re-use journal instance, recover previous test journal =====" +jtt/jtt --analyzer ../../tools/store_chk --jrnl-dir ${TMP_DATA_DIR} --csv jtt/jtt.csv --reuse-instance --recover-mode --format-chk --num-jrnls ${num_jrnls} || fail=1 +rm -rf ${TMP_DATA_DIR}/test_0* +echo + +exit $fail diff --git a/qpid/cpp/src/tests/legacystore/jrnl/tests.ods b/qpid/cpp/src/tests/legacystore/jrnl/tests.ods Binary files differnew file mode 100644 index 0000000000..d900374321 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/tests.ods diff --git a/qpid/cpp/src/tests/legacystore/python_tests/__init__.py b/qpid/cpp/src/tests/legacystore/python_tests/__init__.py new file mode 100644 index 0000000000..ebb9da8670 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/python_tests/__init__.py @@ -0,0 +1,24 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Do not delete - marks this directory as a python package. + +from client_persistence import * +from resize import * + diff --git a/qpid/cpp/src/tests/legacystore/python_tests/client_persistence.py b/qpid/cpp/src/tests/legacystore/python_tests/client_persistence.py new file mode 100644 index 0000000000..3c62740a62 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/python_tests/client_persistence.py @@ -0,0 +1,234 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +from brokertest import EXPECT_EXIT_OK +from store_test import StoreTest, Qmf, store_args +from qpid.messaging import * + +class ExchangeQueueTests(StoreTest): + """ + Simple tests of the broker exchange and queue types + """ + + def test_direct_exchange(self): + """Test Direct exchange.""" + broker = self.broker(store_args(), name="test_direct_exchange", expect=EXPECT_EXIT_OK) + msg1 = Message("A_Message1", durable=True, correlation_id="Msg0001") + msg2 = Message("B_Message1", durable=True, correlation_id="Msg0002") + broker.send_message("a", msg1) + broker.send_message("b", msg2) + broker.terminate() + + broker = self.broker(store_args(), name="test_direct_exchange") + self.check_message(broker, "a", msg1, True) + self.check_message(broker, "b", msg2, True) + + def test_topic_exchange(self): + """Test Topic exchange.""" + broker = self.broker(store_args(), name="test_topic_exchange", expect=EXPECT_EXIT_OK) + ssn = broker.connect().session() + snd1 = ssn.sender("abc/key1; {create:always, node:{type:topic, durable:True}}") + snd2 = ssn.sender("abc/key2; {create:always, node:{type:topic, durable:True}}") + ssn.receiver("a; {create:always, link:{x-bindings:[{exchange:abc, key:key1}]}, node:{durable:True}}") + ssn.receiver("b; {create:always, link:{x-bindings:[{exchange:abc, key:key1}]}, node:{durable:True}}") + ssn.receiver("c; {create:always, link:{x-bindings:[{exchange:abc, key:key1}, " + "{exchange:abc, key: key2}]}, node:{durable:True}}") + ssn.receiver("d; {create:always, link:{x-bindings:[{exchange:abc, key:key2}]}, node:{durable:True}}") + ssn.receiver("e; {create:always, link:{x-bindings:[{exchange:abc, key:key2}]}, node:{durable:True}}") + msg1 = Message("Message1", durable=True, correlation_id="Msg0003") + snd1.send(msg1) + msg2 = Message("Message2", durable=True, correlation_id="Msg0004") + snd2.send(msg2) + broker.terminate() + + broker = self.broker(store_args(), name="test_topic_exchange") + self.check_message(broker, "a", msg1, True) + self.check_message(broker, "b", msg1, True) + self.check_messages(broker, "c", [msg1, msg2], True) + self.check_message(broker, "d", msg2, True) + self.check_message(broker, "e", msg2, True) + + + def test_legacy_lvq(self): + """Test legacy LVQ.""" + broker = self.broker(store_args(), name="test_lvq", expect=EXPECT_EXIT_OK) + ma1 = Message("A1", durable=True, correlation_id="Msg0005", properties={"qpid.LVQ_key":"A"}) + ma2 = Message("A2", durable=True, correlation_id="Msg0006", properties={"qpid.LVQ_key":"A"}) + mb1 = Message("B1", durable=True, correlation_id="Msg0007", properties={"qpid.LVQ_key":"B"}) + mb2 = Message("B2", durable=True, correlation_id="Msg0008", properties={"qpid.LVQ_key":"B"}) + mb3 = Message("B3", durable=True, correlation_id="Msg0009", properties={"qpid.LVQ_key":"B"}) + mc1 = Message("C1", durable=True, correlation_id="Msg0010", properties={"qpid.LVQ_key":"C"}) + broker.send_messages("lvq-test", [mb1, ma1, ma2, mb2, mb3, mc1], + xprops="arguments:{\"qpid.last_value_queue\":True}") + broker.terminate() + + broker = self.broker(store_args(), name="test_lvq", expect=EXPECT_EXIT_OK) + ssn = self.check_messages(broker, "lvq-test", [ma2, mb3, mc1], empty=True, ack=False) + # Add more messages while subscriber is active (no replacement): + ma3 = Message("A3", durable=True, correlation_id="Msg0011", properties={"qpid.LVQ_key":"A"}) + ma4 = Message("A4", durable=True, correlation_id="Msg0012", properties={"qpid.LVQ_key":"A"}) + mc2 = Message("C2", durable=True, correlation_id="Msg0013", properties={"qpid.LVQ_key":"C"}) + mc3 = Message("C3", durable=True, correlation_id="Msg0014", properties={"qpid.LVQ_key":"C"}) + mc4 = Message("C4", durable=True, correlation_id="Msg0015", properties={"qpid.LVQ_key":"C"}) + broker.send_messages("lvq-test", [mc2, mc3, ma3, ma4, mc4], session=ssn) + ssn.acknowledge() + broker.terminate() + + broker = self.broker(store_args(), name="test_lvq") + self.check_messages(broker, "lvq-test", [ma4, mc4], True) + + + def test_fanout_exchange(self): + """Test Fanout Exchange""" + broker = self.broker(store_args(), name="test_fanout_exchange", expect=EXPECT_EXIT_OK) + ssn = broker.connect().session() + snd = ssn.sender("TestFanoutExchange; {create: always, node: {type: topic, x-declare: {type: fanout}}}") + ssn.receiver("TestFanoutExchange; {link: {name: \"q1\", durable: True, reliability:at-least-once}}") + ssn.receiver("TestFanoutExchange; {link: {name: \"q2\", durable: True, reliability:at-least-once}}") + ssn.receiver("TestFanoutExchange; {link: {name: \"q3\", durable: True, reliability:at-least-once}}") + msg1 = Message("Msg1", durable=True, correlation_id="Msg0001") + snd.send(msg1) + msg2 = Message("Msg2", durable=True, correlation_id="Msg0002") + snd.send(msg2) + broker.terminate() + + broker = self.broker(store_args(), name="test_fanout_exchange") + self.check_messages(broker, "q1", [msg1, msg2], True) + self.check_messages(broker, "q2", [msg1, msg2], True) + self.check_messages(broker, "q3", [msg1, msg2], True) + + + def test_message_reject(self): + broker = self.broker(store_args(), name="test_message_reject", expect=EXPECT_EXIT_OK) + ssn = broker.connect().session() + snd = ssn.sender("tmr; {create:always, node:{type:queue, durable:True}}") + rcv = ssn.receiver("tmr; {create:always, node:{type:queue, durable:True}}") + m1 = Message("test_message_reject", durable=True, correlation_id="Msg0001") + snd.send(m1) + m2 = rcv.fetch() + ssn.acknowledge(message=m2, disposition=Disposition(REJECTED)) + broker.terminate() + + broker = self.broker(store_args(), name="test_message_reject") + qmf = Qmf(broker) + assert qmf.queue_message_count("tmr") == 0 + + + def test_route(self): + """ Test the recovery of a route (link and bridge objects.""" + broker = self.broker(store_args(), name="test_route", expect=EXPECT_EXIT_OK) + qmf = Qmf(broker) + qmf_broker_obj = qmf.get_objects("broker")[0] + + # create a "link" + link_args = {"host":"a.fake.host.com", "port":9999, "durable":True, + "authMechanism":"PLAIN", "username":"guest", "password":"guest", + "transport":"tcp"} + result = qmf_broker_obj.create("link", "test-link", link_args, False) + self.assertEqual(result.status, 0, result) + link = qmf.get_objects("link")[0] + + # create bridge + bridge_args = {"link":"test-link", "src":"amq.direct", "dest":"amq.fanout", + "key":"my-key", "durable":True} + result = qmf_broker_obj.create("bridge", "test-bridge", bridge_args, False); + self.assertEqual(result.status, 0, result) + bridge = qmf.get_objects("bridge")[0] + + broker.terminate() + + # recover the link and bridge + broker = self.broker(store_args(), name="test_route") + qmf = Qmf(broker) + qmf_broker_obj = qmf.get_objects("broker")[0] + self.assertEqual(len(qmf.get_objects("link")), 1) + self.assertEqual(len(qmf.get_objects("bridge")), 1) + + + +class AlternateExchangePropertyTests(StoreTest): + """ + Test the persistence of the Alternate Exchange property for exchanges and queues. + """ + + def test_exchange(self): + """Exchange alternate exchange property persistence test""" + broker = self.broker(store_args(), name="test_exchange", expect=EXPECT_EXIT_OK) + qmf = Qmf(broker) + qmf.add_exchange("altExch", "direct", durable=True) # Serves as alternate exchange instance + qmf.add_exchange("testExch", "direct", durable=True, alt_exchange_name="altExch") + qmf.close() + broker.terminate() + + broker = self.broker(store_args(), name="test_exchange") + qmf = Qmf(broker) + try: + qmf.add_exchange("altExch", "direct", passive=True) + except Exception, error: + self.fail("Alternate exchange (\"altExch\") instance not recovered: %s" % error) + try: + qmf.add_exchange("testExch", "direct", passive=True) + except Exception, error: + self.fail("Test exchange (\"testExch\") instance not recovered: %s" % error) + self.assertTrue(qmf.query_exchange("testExch", alt_exchange_name = "altExch"), + "Alternate exchange property not found or is incorrect on exchange \"testExch\".") + qmf.close() + + def test_queue(self): + """Queue alternate exchange property persistexchangeNamece test""" + broker = self.broker(store_args(), name="test_queue", expect=EXPECT_EXIT_OK) + qmf = Qmf(broker) + qmf.add_exchange("altExch", "direct", durable=True) # Serves as alternate exchange instance + qmf.add_queue("testQueue", durable=True, alt_exchange_name="altExch") + qmf.close() + broker.terminate() + + broker = self.broker(store_args(), name="test_queue") + qmf = Qmf(broker) + try: + qmf.add_exchange("altExch", "direct", passive=True) + except Exception, error: + self.fail("Alternate exchange (\"altExch\") instance not recovered: %s" % error) + try: + qmf.add_queue("testQueue", passive=True) + except Exception, error: + self.fail("Test queue (\"testQueue\") instance not recovered: %s" % error) + self.assertTrue(qmf.query_queue("testQueue", alt_exchange_name = "altExch"), + "Alternate exchange property not found or is incorrect on queue \"testQueue\".") + qmf.close() + + +class RedeliveredTests(StoreTest): + """ + Test the behavior of the redelivered flag in the context of persistence + """ + + def test_broker_recovery(self): + """Test that the redelivered flag is set on messages after recovery of broker""" + broker = self.broker(store_args(), name="test_broker_recovery", expect=EXPECT_EXIT_OK) + msg_content = "xyz"*100 + msg = Message(msg_content, durable=True) + broker.send_message("testQueue", msg) + broker.terminate() + + broker = self.broker(store_args(), name="test_broker_recovery") + rcv_msg = broker.get_message("testQueue") + self.assertEqual(msg_content, rcv_msg.content) + self.assertTrue(rcv_msg.redelivered) + diff --git a/qpid/cpp/src/tests/legacystore/python_tests/resize.py b/qpid/cpp/src/tests/legacystore/python_tests/resize.py new file mode 100644 index 0000000000..469e0f6730 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/python_tests/resize.py @@ -0,0 +1,167 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +import glob +import os +import subprocess + +from brokertest import EXPECT_EXIT_OK +from qpid.datatypes import uuid4 +from store_test import StoreTest, store_args +from qpid.messaging import Message + +class ResizeTest(StoreTest): + + resize_tool = os.getenv("QPID_STORE_RESIZE_TOOL", "qpid-store-resize") + print resize_tool + def _resize_store(self, store_dir, queue_name, resize_num_files, resize_file_size, exp_fail): + for f in glob.glob(os.path.join(store_dir, "*")): + final_store_dir = os.path.join(f, queue_name) + p = subprocess.Popen([self.resize_tool, final_store_dir, "--num-jfiles", str(resize_num_files), + "--jfile-size-pgs", str(resize_file_size), "--quiet"], stdout = subprocess.PIPE, + stderr = subprocess.STDOUT) + res = p.wait() + err_found = False + try: + for l in p.stdout: + if exp_fail: + err_found = True + print "[Expected error]:", + print l, + finally: + p.stdout.close() + return res + + def _resize_test(self, queue_name, num_msgs, msg_size, resize_num_files, resize_file_size, init_num_files = 8, + init_file_size = 24, exp_fail = False, wait_time = None): + # Using a sender will force the creation of an empty persistent queue which is needed for some tests + broker = self.broker(store_args(), name="broker", expect=EXPECT_EXIT_OK, wait=wait_time) + ssn = broker.connect().session() + snd = ssn.sender("%s; {create:always, node:{durable:True}}" % queue_name) + + msgs = [] + for index in range(0, num_msgs): + msg = Message(self.make_message(index, msg_size), durable=True, id=uuid4(), correlation_id="msg-%04d"%index) + msgs.append(msg) + snd.send(msg) + broker.terminate() + + res = self._resize_store(os.path.join(self.dir, "broker", "rhm", "jrnl"), queue_name, resize_num_files, + resize_file_size, exp_fail) + if res != 0: + if exp_fail: + return + self.fail("ERROR: Resize operation failed with return code %d" % res) + elif exp_fail: + self.fail("ERROR: Resize operation succeeded, but a failure was expected") + + broker = self.broker(store_args(), name="broker") + self.check_messages(broker, queue_name, msgs, True) + + # TODO: Check the physical files to check number and size are as expected. + + +class SimpleTest(ResizeTest): + """ + Simple tests of the resize utility for resizing a journal to larger and smaller sizes. + """ + + def test_empty_store_same(self): + self._resize_test(queue_name = "empty_store_same", + num_msgs = 0, msg_size = 0, + init_num_files = 8, init_file_size = 24, + resize_num_files = 8, resize_file_size = 24) + + def test_empty_store_up(self): + self._resize_test(queue_name = "empty_store_up", + num_msgs = 0, msg_size = 0, + init_num_files = 8, init_file_size = 24, + resize_num_files = 16, resize_file_size = 48) + + def test_empty_store_down(self): + self._resize_test(queue_name = "empty_store_down", + num_msgs = 0, msg_size = 0, + init_num_files = 8, init_file_size = 24, + resize_num_files = 6, resize_file_size = 12) + +# TODO: Put into long tests, make sure there is > 128GB free disk space +# def test_empty_store_max(self): +# self._resize_test(queue_name = "empty_store_max", +# num_msgs = 0, msg_size = 0, +# init_num_files = 8, init_file_size = 24, +# resize_num_files = 64, resize_file_size = 32768, +# wait_time = 120) + + def test_empty_store_min(self): + self._resize_test(queue_name = "empty_store_min", + num_msgs = 0, msg_size = 0, + init_num_files = 8, init_file_size = 24, + resize_num_files = 4, resize_file_size = 1) + + def test_basic_up(self): + self._resize_test(queue_name = "basic_up", + num_msgs = 100, msg_size = 10000, + init_num_files = 8, init_file_size = 24, + resize_num_files = 16, resize_file_size = 48) + + def test_basic_down(self): + self._resize_test(queue_name = "basic_down", + num_msgs = 100, msg_size = 10000, + init_num_files = 8, init_file_size = 24, + resize_num_files = 4, resize_file_size = 15) + + def test_basic_low(self): + self._resize_test(queue_name = "basic_low", + num_msgs = 100, msg_size = 10000, + init_num_files = 8, init_file_size = 24, + resize_num_files = 4, resize_file_size = 4, + exp_fail = True) + + def test_basic_under(self): + self._resize_test(queue_name = "basic_under", + num_msgs = 100, msg_size = 10000, + init_num_files = 8, init_file_size = 24, + resize_num_files = 4, resize_file_size = 3, + exp_fail = True) + + def test_very_large_msg_up(self): + self._resize_test(queue_name = "very_large_msg_up", + num_msgs = 4, msg_size = 2000000, + init_num_files = 8, init_file_size = 24, + resize_num_files = 16, resize_file_size = 48) + + def test_very_large_msg_down(self): + self._resize_test(queue_name = "very_large_msg_down", + num_msgs = 4, msg_size = 2000000, + init_num_files = 16, init_file_size = 64, + resize_num_files = 16, resize_file_size = 48) + + def test_very_large_msg_low(self): + self._resize_test(queue_name = "very_large_msg_low", + num_msgs = 4, msg_size = 2000000, + init_num_files = 8, init_file_size = 24, + resize_num_files = 7, resize_file_size = 20, + exp_fail = True) + + def test_very_large_msg_under(self): + self._resize_test(queue_name = "very_large_msg_under", + num_msgs = 4, msg_size = 2000000, + init_num_files = 8, init_file_size = 24, + resize_num_files = 6, resize_file_size = 8, + exp_fail = True) diff --git a/qpid/cpp/src/tests/legacystore/python_tests/store_test.py b/qpid/cpp/src/tests/legacystore/python_tests/store_test.py new file mode 100644 index 0000000000..2fcab4e38e --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/python_tests/store_test.py @@ -0,0 +1,416 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +import re +from brokertest import BrokerTest +from qpid.messaging import Empty +from qmf.console import Session + + +def store_args(store_dir = None): + """Return the broker args necessary to load the async store""" + assert BrokerTest.store_lib + if store_dir == None: + return [] + return ["--store-dir", store_dir] + +class Qmf: + """ + QMF functions not yet available in the new QMF API. Remove this and replace with new API when it becomes available. + """ + def __init__(self, broker): + self.__session = Session() + self.__broker = self.__session.addBroker("amqp://localhost:%d"%broker.port()) + + def add_exchange(self, exchange_name, exchange_type, alt_exchange_name=None, passive=False, durable=False, + arguments = None): + """Add a new exchange""" + amqp_session = self.__broker.getAmqpSession() + if arguments == None: + arguments = {} + if alt_exchange_name: + amqp_session.exchange_declare(exchange=exchange_name, type=exchange_type, + alternate_exchange=alt_exchange_name, passive=passive, durable=durable, + arguments=arguments) + else: + amqp_session.exchange_declare(exchange=exchange_name, type=exchange_type, passive=passive, durable=durable, + arguments=arguments) + + def add_queue(self, queue_name, alt_exchange_name=None, passive=False, durable=False, arguments = None): + """Add a new queue""" + amqp_session = self.__broker.getAmqpSession() + if arguments == None: + arguments = {} + if alt_exchange_name: + amqp_session.queue_declare(queue_name, alternate_exchange=alt_exchange_name, passive=passive, + durable=durable, arguments=arguments) + else: + amqp_session.queue_declare(queue_name, passive=passive, durable=durable, arguments=arguments) + + def delete_queue(self, queue_name): + """Delete an existing queue""" + amqp_session = self.__broker.getAmqpSession() + amqp_session.queue_delete(queue_name) + + def _query(self, name, _class, package, alt_exchange_name=None): + """Qmf query function which can optionally look for the presence of an alternate exchange name""" + try: + obj_list = self.__session.getObjects(_class=_class, _package=package) + found = False + for obj in obj_list: + if obj.name == name: + found = True + if alt_exchange_name != None: + alt_exch_list = self.__session.getObjects(_objectId=obj.altExchange) + if len(alt_exch_list) == 0 or alt_exch_list[0].name != alt_exchange_name: + return False + break + return found + except Exception: + return False + + + def query_exchange(self, exchange_name, alt_exchange_name=None): + """Test for the presence of an exchange, and optionally whether it has an alternate exchange set to a known + value.""" + return self._query(exchange_name, "exchange", "org.apache.qpid.broker", alt_exchange_name) + + def query_queue(self, queue_name, alt_exchange_name=None): + """Test for the presence of an exchange, and optionally whether it has an alternate exchange set to a known + value.""" + return self._query(queue_name, "queue", "org.apache.qpid.broker", alt_exchange_name) + + def queue_message_count(self, queue_name): + """Query the number of messages on a queue""" + queue_list = self.__session.getObjects(_class="queue", _name=queue_name) + if len(queue_list): + return queue_list[0].msgDepth + + def queue_empty(self, queue_name): + """Check if a queue is empty (has no messages waiting)""" + return self.queue_message_count(queue_name) == 0 + + def get_objects(self, target_class, target_package="org.apache.qpid.broker"): + return self.__session.getObjects(_class=target_class, _package=target_package) + + + def close(self): + self.__session.delBroker(self.__broker) + self.__session = None + + +class StoreTest(BrokerTest): + """ + This subclass of BrokerTest adds some convenience test/check functions + """ + + def _chk_empty(self, queue, receiver): + """Check if a queue is empty (has no more messages)""" + try: + msg = receiver.fetch(timeout=0) + self.assert_(False, "Queue \"%s\" not empty: found message: %s" % (queue, msg)) + except Empty: + pass + + @staticmethod + def make_message(msg_count, msg_size): + """Make message content. Format: 'abcdef....' followed by 'msg-NNNN', where NNNN is the message count""" + msg = "msg-%04d" % msg_count + msg_len = len(msg) + buff = "" + if msg_size != None and msg_size > msg_len: + for index in range(0, msg_size - msg_len): + if index == msg_size - msg_len - 1: + buff += "-" + else: + buff += chr(ord('a') + (index % 26)) + return buff + msg + + # Functions for formatting address strings + + @staticmethod + def _fmt_csv(string_list, list_braces = None): + """Format a list using comma-separation. Braces are optionally added.""" + if len(string_list) == 0: + return "" + first = True + str_ = "" + if list_braces != None: + str_ += list_braces[0] + for string in string_list: + if string != None: + if first: + first = False + else: + str_ += ", " + str_ += string + if list_braces != None: + str_ += list_braces[1] + return str_ + + def _fmt_map(self, string_list): + """Format a map {l1, l2, l3, ...} from a string list. Each item in the list must be a formatted map + element('key:val').""" + return self._fmt_csv(string_list, list_braces="{}") + + def _fmt_list(self, string_list): + """Format a list [l1, l2, l3, ...] from a string list.""" + return self._fmt_csv(string_list, list_braces="[]") + + def addr_fmt(self, node_name, **kwargs): + """Generic AMQP to new address formatter. Takes common (but not all) AMQP options and formats an address + string.""" + # Get keyword args + node_subject = kwargs.get("node_subject") + create_policy = kwargs.get("create_policy") + delete_policy = kwargs.get("delete_policy") + assert_policy = kwargs.get("assert_policy") + mode = kwargs.get("mode") + link = kwargs.get("link", False) + link_name = kwargs.get("link_name") + node_type = kwargs.get("node_type") + durable = kwargs.get("durable", False) + link_reliability = kwargs.get("link_reliability") + x_declare_list = kwargs.get("x_declare_list", []) + x_bindings_list = kwargs.get("x_bindings_list", []) + x_subscribe_list = kwargs.get("x_subscribe_list", []) + + node_flag = not link and (node_type != None or durable or len(x_declare_list) > 0 or len(x_bindings_list) > 0) + link_flag = link and (link_name != None or durable or link_reliability != None or len(x_declare_list) > 0 or + len(x_bindings_list) > 0 or len(x_subscribe_list) > 0) + assert not (node_flag and link_flag) + + opt_str_list = [] + if create_policy != None: + opt_str_list.append("create: %s" % create_policy) + if delete_policy != None: + opt_str_list.append("delete: %s" % delete_policy) + if assert_policy != None: + opt_str_list.append("assert: %s" % assert_policy) + if mode != None: + opt_str_list.append("mode: %s" % mode) + if node_flag or link_flag: + node_str_list = [] + if link_name != None: + node_str_list.append("name: \"%s\"" % link_name) + if node_type != None: + node_str_list.append("type: %s" % node_type) + if durable: + node_str_list.append("durable: True") + if link_reliability != None: + node_str_list.append("reliability: %s" % link_reliability) + if len(x_declare_list) > 0: + node_str_list.append("x-declare: %s" % self._fmt_map(x_declare_list)) + if len(x_bindings_list) > 0: + node_str_list.append("x-bindings: %s" % self._fmt_list(x_bindings_list)) + if len(x_subscribe_list) > 0: + node_str_list.append("x-subscribe: %s" % self._fmt_map(x_subscribe_list)) + if node_flag: + opt_str_list.append("node: %s" % self._fmt_map(node_str_list)) + else: + opt_str_list.append("link: %s" % self._fmt_map(node_str_list)) + addr_str = node_name + if node_subject != None: + addr_str += "/%s" % node_subject + if len(opt_str_list) > 0: + addr_str += "; %s" % self._fmt_map(opt_str_list) + return addr_str + + def snd_addr(self, node_name, **kwargs): + """ Create a send (node) address""" + # Get keyword args + topic = kwargs.get("topic") + topic_flag = kwargs.get("topic_flag", False) + auto_create = kwargs.get("auto_create", True) + auto_delete = kwargs.get("auto_delete", False) + durable = kwargs.get("durable", False) + exclusive = kwargs.get("exclusive", False) + ftd_count = kwargs.get("ftd_count") + ftd_size = kwargs.get("ftd_size") + policy = kwargs.get("policy", "flow-to-disk") + exchage_type = kwargs.get("exchage_type") + + create_policy = None + if auto_create: + create_policy = "always" + delete_policy = None + if auto_delete: + delete_policy = "always" + node_type = None + if topic != None or topic_flag: + node_type = "topic" + x_declare_list = ["\"exclusive\": %s" % exclusive] + if ftd_count != None or ftd_size != None: + queue_policy = ["\'qpid.policy_type\': %s" % policy] + if ftd_count: + queue_policy.append("\'qpid.max_count\': %d" % ftd_count) + if ftd_size: + queue_policy.append("\'qpid.max_size\': %d" % ftd_size) + x_declare_list.append("arguments: %s" % self._fmt_map(queue_policy)) + if exchage_type != None: + x_declare_list.append("type: %s" % exchage_type) + + return self.addr_fmt(node_name, topic=topic, create_policy=create_policy, delete_policy=delete_policy, + node_type=node_type, durable=durable, x_declare_list=x_declare_list) + + def rcv_addr(self, node_name, **kwargs): + """ Create a receive (link) address""" + # Get keyword args + auto_create = kwargs.get("auto_create", True) + auto_delete = kwargs.get("auto_delete", False) + link_name = kwargs.get("link_name") + durable = kwargs.get("durable", False) + browse = kwargs.get("browse", False) + exclusive = kwargs.get("exclusive", False) + binding_list = kwargs.get("binding_list", []) + ftd_count = kwargs.get("ftd_count") + ftd_size = kwargs.get("ftd_size") + policy = kwargs.get("policy", "flow-to-disk") + + create_policy = None + if auto_create: + create_policy = "always" + delete_policy = None + if auto_delete: + delete_policy = "always" + mode = None + if browse: + mode = "browse" + x_declare_list = ["\"exclusive\": %s" % exclusive] + if ftd_count != None or ftd_size != None: + queue_policy = ["\'qpid.policy_type\': %s" % policy] + if ftd_count: + queue_policy.append("\'qpid.max_count\': %d" % ftd_count) + if ftd_size: + queue_policy.append("\'qpid.max_size\': %d" % ftd_size) + x_declare_list.append("arguments: %s" % self._fmt_map(queue_policy)) + x_bindings_list = [] + for binding in binding_list: + x_bindings_list.append("{exchange: %s, key: %s}" % binding) + if durable: reliability = 'at-least-once' + else: reliability = None + return self.addr_fmt(node_name, create_policy=create_policy, delete_policy=delete_policy, mode=mode, link=True, + link_name=link_name, durable=durable, x_declare_list=x_declare_list, + x_bindings_list=x_bindings_list, link_reliability=reliability) + + def check_message(self, broker, queue, exp_msg, transactional=False, empty=False, ack=True, browse=False): + """Check that a message is on a queue by dequeuing it and comparing it to the expected message""" + return self.check_messages(broker, queue, [exp_msg], transactional, empty, ack, browse) + + def check_messages(self, broker, queue, exp_msg_list, transactional=False, empty=False, ack=True, browse=False, + emtpy_flag=False): + """Check that messages is on a queue by dequeuing them and comparing them to the expected messages""" + if emtpy_flag: + num_msgs = 0 + else: + num_msgs = len(exp_msg_list) + ssn = broker.connect().session(transactional=transactional) + rcvr = ssn.receiver(self.rcv_addr(queue, browse=browse), capacity=num_msgs) + if num_msgs > 0: + try: + recieved_msg_list = [rcvr.fetch(timeout=0) for i in range(num_msgs)] + except Empty: + self.assert_(False, "Queue \"%s\" is empty, unable to retrieve expected message %d." % (queue, i)) + for i in range(0, len(recieved_msg_list)): + self.assertEqual(recieved_msg_list[i].content, exp_msg_list[i].content) + self.assertEqual(recieved_msg_list[i].correlation_id, exp_msg_list[i].correlation_id) + if empty: + self._chk_empty(queue, rcvr) + if ack: + ssn.acknowledge() + if transactional: + ssn.commit() + ssn.connection.close() + else: + if transactional: + ssn.commit() + return ssn + + + # Functions for finding strings in the broker log file (or other files) + + @staticmethod + def _read_file(file_name): + """Returns the content of file named file_name as a string""" + file_handle = file(file_name) + try: + return file_handle.read() + finally: + file_handle.close() + + def _get_hits(self, broker, search): + """Find all occurrences of the search in the broker log (eliminating possible duplicates from msgs on multiple + queues)""" + # TODO: Use sets when RHEL-4 is no longer supported + hits = [] + for hit in search.findall(self._read_file(broker.log)): + if hit not in hits: + hits.append(hit) + return hits + + def _reconsile_hits(self, broker, ftd_msgs, release_hits): + """Remove entries from list release_hits if they match the message id in ftd_msgs. Check for remaining + release_hits.""" + for msg in ftd_msgs: + found = False + for hit in release_hits: + if str(msg.id) in hit: + release_hits.remove(hit) + #print "Found %s in %s" % (msg.id, broker.log) + found = True + break + if not found: + self.assert_(False, "Unable to locate released message %s in log %s" % (msg.id, broker.log)) + if len(release_hits) > 0: + err = "Messages were unexpectedly released in log %s:\n" % broker.log + for hit in release_hits: + err += " %s\n" % hit + self.assert_(False, err) + + def check_msg_release(self, broker, ftd_msgs): + """ Check for 'Content released' messages in broker log for messages in ftd_msgs""" + hits = self._get_hits(broker, re.compile("debug Message id=\"[0-9a-f-]{36}\"; pid=0x[0-9a-f]+: " + "Content released$", re.MULTILINE)) + self._reconsile_hits(broker, ftd_msgs, hits) + + def check_msg_release_on_commit(self, broker, ftd_msgs): + """ Check for 'Content released on commit' messages in broker log for messages in ftd_msgs""" + hits = self._get_hits(broker, re.compile("debug Message id=\"[0-9a-f-]{36}\"; pid=0x[0-9a-f]+: " + "Content released on commit$", re.MULTILINE)) + self._reconsile_hits(broker, ftd_msgs, hits) + + def check_msg_release_on_recover(self, broker, ftd_msgs): + """ Check for 'Content released after recovery' messages in broker log for messages in ftd_msgs""" + hits = self._get_hits(broker, re.compile("debug Message id=\"[0-9a-f-]{36}\"; pid=0x[0-9a-f]+: " + "Content released after recovery$", re.MULTILINE)) + self._reconsile_hits(broker, ftd_msgs, hits) + + def check_msg_block(self, broker, ftd_msgs): + """Check for 'Content release blocked' messages in broker log for messages in ftd_msgs""" + hits = self._get_hits(broker, re.compile("debug Message id=\"[0-9a-f-]{36}\"; pid=0x[0-9a-f]+: " + "Content release blocked$", re.MULTILINE)) + self._reconsile_hits(broker, ftd_msgs, hits) + + def check_msg_block_on_commit(self, broker, ftd_msgs): + """Check for 'Content release blocked' messages in broker log for messages in ftd_msgs""" + hits = self._get_hits(broker, re.compile("debug Message id=\"[0-9a-f-]{36}\"; pid=0x[0-9a-f]+: " + "Content release blocked on commit$", re.MULTILINE)) + self._reconsile_hits(broker, ftd_msgs, hits) + + diff --git a/qpid/cpp/src/tests/legacystore/run_python_tests b/qpid/cpp/src/tests/legacystore/run_python_tests index d9dec16963..130dee05f8 100644..100755 --- a/qpid/cpp/src/tests/legacystore/run_python_tests +++ b/qpid/cpp/src/tests/legacystore/run_python_tests @@ -18,47 +18,25 @@ # under the License. # -if test -z ${QPID_DIR} ; then - cat <<EOF +source ../test_env.sh - =========== WARNING: PYTHON TESTS DISABLED ============== - - QPID_DIR not set. - - =========================================================== - -EOF - exit -fi - -. `dirname $0`/tests_env.sh +#Add our directory to the python path +export PYTHONPATH=$srcdir/legacystore:$PYTHONPATH MODULENAME=python_tests echo "Running Python tests in module ${MODULENAME}..." -case x$1 in - xSHORT_TEST) - DEFAULT_PYTHON_TESTS="*.client_persistence.ExchangeQueueTests.* *.flow_to_disk.SimpleMaxSizeCountTest.test_browse_recover *.flow_to_disk.SimpleMaxSizeCountTest.test_durable_browse_recover *.flow_to_disk.MultiDurableQueueDurableMsgBrowseRecoverTxPTxCTest.test_mixed_limit_2" ;; - xLONG_TEST) - DEFAULT_PYTHON_TESTS= ;; - x) - DEFAULT_PYTHON_TESTS="*.client_persistence.* *.flow_to_disk.SimpleMaxSizeCountTest.* *.flow_to_disk.MultiDurableQueue*.test_mixed_limit_1 *.flow_to_disk.MultiQueue*.test_mixed_limit_1 *.resize.SimpleTest.* *.federation.*" ;; - *) - DEFAULT_PYTHON_TESTS=$1 -esac - -PYTHON_TESTS=${PYTHON_TESTS:-${DEFAULT_PYTHON_TESTS}} +test -d $PYTHON_DIR || { echo "Skipping python tests, no python dir."; exit 0; } +QPID_PORT=${QPID_PORT:-5672} +FAILING=${FAILING:-/dev/null} +PYTHON_TESTS=${PYTHON_TESTS:-$*} OUTDIR=${MODULENAME}.tmp rm -rf $OUTDIR # To debug a test, add the following options to the end of the following line: # -v DEBUG -c qpid.messaging.io.ops [*.testName] -${PYTHON_DIR}/qpid-python-test -m ${MODULENAME} -I ${FAILING_PYTHON_TESTS} ${PYTHON_TESTS} -DOUTDIR=$OUTDIR #-v DEBUG -RETCODE=$? +${QPID_PYTHON_TEST} -m ${MODULENAME} -I $FAILING -DOUTDIR=$OUTDIR \ + $PYTHON_TEST || exit 1 -if test x${RETCODE} != x0; then - exit 1; -fi -exit 0 diff --git a/qpid/cpp/src/tests/legacystore/run_test b/qpid/cpp/src/tests/legacystore/run_test deleted file mode 100644 index 1d5c2ae407..0000000000 --- a/qpid/cpp/src/tests/legacystore/run_test +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/bash - -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -# Set up environment and run a test executable or script. -# -# Output nothing if test passes, show the output if it fails and -# leave output in <test>.log for examination. -# -# If qpidd.port exists run test with QPID_PORT=`cat qpidd.port` -# -# If $VALGRIND if is set run under valgrind. If there are valgrind -# erros show valgrind output, also leave it in <test>.valgrind for -# examination. -# - -source `dirname $0`/vg_check - -# Export variables from makefile. -export VALGRIND srcdir - -# Export QPID_PORT if qpidd.port exists. -test -f qpidd.port && export QPID_PORT=`cat qpidd.port` - -# Avoid silly libtool error messages if these are not defined -test -z "$LC_ALL" && export LC_ALL= -test -z "$LC_CTYPE" && export LC_CTYPE= -test -z "$LC_COLLATE" && export LC_COLLATE= -test -z "$LC_MESSAGES" && export LC_MESSAGES= - -VG_LOG="$1.vglog" -rm -f $VG_LOG* - -if grep -l "^# Generated by .*libtool" "$1" >/dev/null 2>&1; then - # This is a libtool "executable". Valgrind it if VALGRIND specified. - test -n "$VALGRIND" && VALGRIND="$VALGRIND --log-file=$VG_LOG --" - # Hide output unless there's an error. - libtool --mode=execute $VALGRIND "$@" 2>&1 || ERROR=$? - test -n "$VALGRIND" && vg_check $VG_LOG* -else - # This is a non-libtool shell script, just execute it. - export VALGRIND srcdir - exec "$@" -fi - -if test -z "$ERROR"; then - # Clean up logs if there was no error. - rm -f $VG_LOG* - exit 0 -else - exit $ERROR -fi diff --git a/qpid/cpp/src/tests/ping_broker b/qpid/cpp/src/tests/ping_broker index be99a6ef46..bdf48f3358 100755 --- a/qpid/cpp/src/tests/ping_broker +++ b/qpid/cpp/src/tests/ping_broker @@ -57,7 +57,7 @@ def OptionsAndArguments(argv): parser.add_option("-t", "--timeout", action="store", type="int", default=10, metavar="<secs>", help="Maximum time to wait for broker connection (in seconds)") parser.add_option("--sasl-mechanism", action="store", type="string", metavar="<mech>", - help="SASL mechanism for authentication (e.g. EXTERNAL, ANONYMOUS, PLAIN, CRAM-MD, DIGEST-MD5, GSSAPI). SASL automatically picks the most secure available mechanism - use this option to override.") + help="SASL mechanism for authentication (e.g. EXTERNAL, ANONYMOUS, PLAIN, CRAM-MD5, DIGEST-MD5, GSSAPI). SASL automatically picks the most secure available mechanism - use this option to override.") parser.add_option("--ssl-certificate", action="store", type="string", metavar="<cert>", help="Client SSL certificate (PEM Format)") parser.add_option("--ssl-key", action="store", type="string", metavar="<key>", help="Client SSL private key (PEM Format)") parser.add_option("--ssl-trustfile", action="store", type="string", metavar="<CA>", help="List of trusted CAs (PEM Format)") diff --git a/qpid/cpp/src/tests/qpid-cluster-benchmark b/qpid/cpp/src/tests/qpid-cluster-benchmark index b72964c1a7..f20ac6ac30 100755 --- a/qpid/cpp/src/tests/qpid-cluster-benchmark +++ b/qpid/cpp/src/tests/qpid-cluster-benchmark @@ -1,5 +1,5 @@ #!/bin/sh -echo# +# # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information diff --git a/qpid/cpp/src/tests/reject_release.py b/qpid/cpp/src/tests/reject_release.py new file mode 100644 index 0000000000..d072b8aa78 --- /dev/null +++ b/qpid/cpp/src/tests/reject_release.py @@ -0,0 +1,65 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +from qpid.tests.messaging.implementation import * +from qpid.tests.messaging import VersionTest + +class RejectReleaseTests (VersionTest): + """ + Tests for reject and release with qpidd + """ + def test_reject(self): + name = str(uuid4()) + snd = self.ssn.sender("%s; {create:always, node:{properties:{alternate-exchange:amq.fanout}}}" % name) + rcv = self.ssn.receiver(name) + rcv2 = self.ssn.receiver("amq.fanout") + + msgs = [Message(content=s, subject = s) for s in ['a','b','c','d']] + + for m in msgs: snd.send(m) + + for expected in msgs: + msg = rcv.fetch(0) + assert msg.content == expected.content + self.ssn.reject(msg) + + for expected in msgs: + msg = rcv2.fetch(0) + assert msg.content == expected.content + + def test_release(self): + snd = self.ssn.sender("#") + rcv = self.ssn.receiver(snd.target) + + msgs = [Message(content=s, subject = s) for s in ['a','b','c','d']] + + for m in msgs: snd.send(m) + + msg = rcv.fetch(0) + assert msg.content == "a" + self.ssn.release(msg) + + msg = rcv.fetch(0) + assert msg.content == "a" + self.ssn.acknowledge(msg) + + msg = rcv.fetch(0) + assert msg.content == "b" + self.ssn.release(msg) + diff --git a/qpid/cpp/src/tests/swig_python_tests b/qpid/cpp/src/tests/swig_python_tests index 6f862ffa2d..cfe1c406d2 100755 --- a/qpid/cpp/src/tests/swig_python_tests +++ b/qpid/cpp/src/tests/swig_python_tests @@ -50,10 +50,10 @@ start_broker echo "Running swigged python tests using broker on port $QPID_PORT" export PYTHONPATH=$PYTHONPATH:$PYTHONPATH_SWIG +export QPID_USE_SWIG_CLIENT=1 $QPID_PYTHON_TEST -m qpid.tests.messaging.message -m qpid_tests.broker_0_10.priority -m qpid_tests.broker_0_10.lvq -m qpid_tests.broker_0_10.new_api -b localhost:$QPID_PORT -I $srcdir/failing-amqp0-10-python-tests || FAILED=1 -if [[ -a $AMQPC_LIB ]] ; then - export QPID_LOAD_MODULE=$AMQPC_LIB - $QPID_PYTHON_TEST --define="protocol_version=amqp1.0" -m qpid_tests.broker_1_0 -m qpid_tests.broker_0_10.new_api -b localhost:$QPID_PORT -I $srcdir/failing-amqp1.0-python-tests || FAILED=1 +if [[ -a $AMQP_LIB ]] ; then + $QPID_PYTHON_TEST --define="protocol_version=amqp1.0" -m qpid_tests.broker_1_0 -m qpid_tests.broker_0_10.new_api -m assertions -m reject_release -b localhost:$QPID_PORT -I $srcdir/failing-amqp1.0-python-tests || FAILED=1 fi stop_broker if [[ $FAILED -eq 1 ]]; then diff --git a/qpid/cpp/src/tests/test_env.sh.in b/qpid/cpp/src/tests/test_env.sh.in index 486034ca3b..f45d3708a2 100644 --- a/qpid/cpp/src/tests/test_env.sh.in +++ b/qpid/cpp/src/tests/test_env.sh.in @@ -67,7 +67,6 @@ exportmodule HA_LIB ha.so exportmodule XML_LIB xml.so exportmodule STORE_LIB legacystore.so exportmodule AMQP_LIB amqp.so -exportmodule AMQPC_LIB amqpc.so # Qpid options export QPID_NO_MODULE_DIR=1 # Don't accidentally load installed modules |