<commit_before>/**
 * \file
 * \brief FifoQueuePriorityTestCase class implementation
 *
 * \author Copyright (C) 2014-2015 <NAME> http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * \date 2015-11-05
 */

#include "FifoQueuePriorityTestCase.hpp"

#include "QueueWrappers.hpp"

#include "priorityTestPhases.hpp"
#include "SequenceAsserter.hpp"

#include "distortos/StaticThread.hpp"
#include "distortos/statistics.hpp"

#include <malloc.h>

namespace distortos
{

namespace test
{

namespace
{

/*---------------------------------------------------------------------------------------------------------------------+
| local constants
+---------------------------------------------------------------------------------------------------------------------*/

/// array with "false" and "true" bool values
constexpr bool falseTrue[] {false, true};

/// size of stack for test thread, bytes
constexpr size_t testThreadStackSize {384};

/*---------------------------------------------------------------------------------------------------------------------+
| local types
+---------------------------------------------------------------------------------------------------------------------*/

/// pair of sequence points
using SequencePoints = std::pair<unsigned int, unsigned int>;

/// type of test thread function
using TestThreadFunction = void(SequenceAsserter&, SequencePoints, const QueueWrapper&);

/// type of test thread
using TestThread = decltype(makeStaticThread<testThreadStackSize>({}, std::declval<TestThreadFunction>(),
		std::ref(std::declval<SequenceAsserter&>()), std::declval<SequencePoints>(),
		std::cref(std::declval<const QueueWrapper&>())));

/// function executed to prepare queue for test
using Prepare = void(const QueueWrapper&);

/// function executed on queue to trigger unblocking of test thread
using Trigger = bool(const QueueWrapper&, size_t);

/// function with final check of queue's contents after all test threads are terminated
using FinalCheck = bool(const QueueWrapper&);

/// tuple with functions for one stage, Prepare and FinalCheck may be nullptr
using Stage = std::tuple<const TestThreadFunction&, const Prepare* const, const Trigger* const,
		const FinalCheck* const>;

/*---------------------------------------------------------------------------------------------------------------------+
| local functions
+---------------------------------------------------------------------------------------------------------------------*/

/**
 * \brief Final check for "pop" stage.
 *
 * The queue should contain "second" sequence points of test threads in the same order as expected by SequenceAsserter.
 *
 * \param [in] queueWrapper is a reference to shared QueueWrapper object
 *
 * \return true if final check succeeded, false otherwise
 */

bool popFinalCheck(const QueueWrapper& queueWrapper)
{
	for (size_t i = 0; i < totalThreads; ++i)
	{
		uint8_t priority {};
		OperationCountingType testValue {};
		const auto ret = queueWrapper.tryPop(priority, testValue);
		if (ret != 0 || testValue.getValue() != i + totalThreads)
			return false;
	}

	return true;
}

/**
 * \brief Prepares queue for "pop" stage - just fills it completely with increasing values.
 *
 * \param [in] queueWrapper is a reference to shared QueueWrapper object
 */

void popPrepare(const QueueWrapper& queueWrapper)
{
	for (size_t i = 0; i < totalThreads; ++i)
		queueWrapper.tryPush(uint8_t{}, OperationCountingType{i});
}

/**
 * \brief QueueWrapper::pop() test thread
 *
 * Marks the first sequence point in SequenceAsserter, waits for the last sequence point from FIFO queue and marks it in
 * SequenceAsserter.
 *
 * \param [in] sequenceAsserter is a reference to SequenceAsserter shared object
 * \param [in] sequencePoints is a pair of sequence points for this instance (second one is ignored)
 * \param [in] queueWrapper is a reference to shared QueueWrapper object
 */

void popThread(SequenceAsserter& sequenceAsserter, const SequencePoints sequencePoints,
		const QueueWrapper& queueWrapper)
{
	sequenceAsserter.sequencePoint(sequencePoints.first);
	uint8_t priority {};
	OperationCountingType lastSequencePoint {};
	queueWrapper.pop(priority, lastSequencePoint);
	sequenceAsserter.sequencePoint(lastSequencePoint.getValue());
}

/**
 * \brief Trigger action with QueueWrapper::pop().
 *
 * \param [in] queueWrapper is a reference to shared QueueWrapper object
 * \param [in] i is the iteration counter
 *
 * \return true if trigger check succeeded, false otherwise
 */

bool popTrigger(const QueueWrapper& queueWrapper, const size_t i)
{
	uint8_t priority {};
	OperationCountingType testValue {};
	queueWrapper.pop(priority, testValue);
	return testValue.getValue() == i;
}

/**
 * \brief QueueWrapper::push() test thread
 *
 * Marks the first sequence point in SequenceAsserter, waits for free space in FIFO queue and marks last sequence point
 * in SequenceAsserter.
 *
 * \param [in] sequenceAsserter is a reference to SequenceAsserter shared object
 * \param [in] sequencePoints is a pair of sequence points for this instance
 * \param [in] queueWrapper is a reference to shared QueueWrapper object
 */

void pushThread(SequenceAsserter& sequenceAsserter, const SequencePoints sequencePoints,
		const QueueWrapper& queueWrapper)
{
	sequenceAsserter.sequencePoint(sequencePoints.first);
	queueWrapper.push(uint8_t{}, OperationCountingType{sequencePoints.second});
	sequenceAsserter.sequencePoint(sequencePoints.second);
}

/**
 * \brief Trigger action with QueueWrapper::push().
 *
 * \param [in] queueWrapper is a reference to shared QueueWrapper object
 * \param [in] i is the iteration counter
 *
 * \return true if trigger check succeeded, false otherwise
 */

bool pushTrigger(const QueueWrapper& queueWrapper, const size_t i)
{
	queueWrapper.push(uint8_t{}, OperationCountingType{i + totalThreads});
	return true;
}

/**
 * \brief Builder of TestThread objects.
 *
 * \param [in] testThreadFunction is a reference to test thread function that will be used in TestThread
 * \param [in] firstSequencePoint is the first sequence point for this instance - equal to the order in which this
 * thread will be started
 * \param [in] threadParameters is a reference to ThreadParameters object
 * \param [in] sequenceAsserter is a reference to SequenceAsserter shared object
 * \param [in] queueWrapper is a reference to shared QueueWrapper object
 *
 * \return constructed TestThread object
 */

TestThread makeTestThread(const TestThreadFunction& testThreadFunction, const unsigned int firstSequencePoint,
		const ThreadParameters& threadParameters, SequenceAsserter& sequenceAsserter, const QueueWrapper& queueWrapper)
{
	return makeStaticThread<testThreadStackSize>(threadParameters.first, testThreadFunction, std::ref(sequenceAsserter),
			SequencePoints{firstSequencePoint, threadParameters.second + totalThreads}, std::cref(queueWrapper));
}

/*---------------------------------------------------------------------------------------------------------------------+
| local constants
+---------------------------------------------------------------------------------------------------------------------*/

/// test stages
const std::array<Stage, 2> stages
{{
		Stage{popThread, nullptr, pushTrigger, nullptr},
		Stage{pushThread, popPrepare, popTrigger, popFinalCheck},
}};

}	// namespace

/*---------------------------------------------------------------------------------------------------------------------+
| private functions
+---------------------------------------------------------------------------------------------------------------------*/

bool FifoQueuePriorityTestCase::run_() const
{
	const auto contextSwitchCount = statistics::getContextSwitchCount();
	std::remove_const<decltype(contextSwitchCount)>::type expectedContextSwitchCount {};
	constexpr size_t fifoQueueTypes {4};

	for (const auto& stage : stages)
		for (const auto& phase : priorityTestPhases)
			for (const auto dynamic : falseTrue)
				for (const auto raw : falseTrue)
				{
					{
						const auto queueWrapper = makeQueueWrapper<totalThreads>(dynamic, raw, true);
						SequenceAsserter sequenceAsserter;

						const auto& threadFunction = std::get<0>(stage);
						std::array<TestThread, totalThreads> threads
						{{
								makeTestThread(threadFunction, 0, phase.first[phase.second[0]], sequenceAsserter,
										*queueWrapper),
								makeTestThread(threadFunction, 1, phase.first[phase.second[1]], sequenceAsserter,
										*queueWrapper),
								makeTestThread(threadFunction, 2, phase.first[phase.second[2]], sequenceAsserter,
										*queueWrapper),<commit_msg>test: convert FifoQueuePriorityTestCase to use dynamic threads<commit_after>/**
 * \file
 * \brief FifoQueuePriorityTestCase class implementation
 *
 * \author Copyright (C) 2014-2016 <NAME> http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * \date 2016-01-04
 */

#include "FifoQueuePriorityTestCase.hpp"

#include "QueueWrappers.hpp"

#include "priorityTestPhases.hpp"
#include "SequenceAsserter.hpp"

#include "distortos/DynamicThread.hpp"
#include "distortos/statistics.hpp"

#include <malloc.h>

namespace distortos
{

namespace test
{

namespace
{

/*---------------------------------------------------------------------------------------------------------------------+
| local constants
+---------------------------------------------------------------------------------------------------------------------*/

/// array with "false" and "true" bool values
constexpr bool falseTrue[] {false, true};

/// size of stack for test thread, bytes
constexpr size_t testThreadStackSize {384};

/*---------------------------------------------------------------------------------------------------------------------+
| local types
+---------------------------------------------------------------------------------------------------------------------*/

/// pair of sequence points
using SequencePoints = std::pair<unsigned int, unsigned int>;

/// type of test thread function
using TestThreadFunction = void(SequenceAsserter&, SequencePoints, const QueueWrapper&);

/// function executed to prepare queue for test
using Prepare = void(const QueueWrapper&);

/// function executed on queue to trigger unblocking of test thread
using Trigger = bool(const QueueWrapper&, size_t);

/// function with final check of queue's contents after all test threads are terminated
using FinalCheck = bool(const QueueWrapper&);

/// tuple with functions for one stage, Prepare and FinalCheck may be nullptr
using Stage = std::tuple<const TestThreadFunction&, const Prepare* const, const Trigger* const,
		const FinalCheck* const>;

/*---------------------------------------------------------------------------------------------------------------------+
| local functions
+---------------------------------------------------------------------------------------------------------------------*/

/**
 * \brief Final check for "pop" stage.
 *
 * The queue should contain "second" sequence points of test threads in the same order as expected by SequenceAsserter.
 *
 * \param [in] queueWrapper is a reference to shared QueueWrapper object
 *
 * \return true if final check succeeded, false otherwise
 */

bool popFinalCheck(const QueueWrapper& queueWrapper)
{
	for (size_t i = 0; i < totalThreads; ++i)
	{
		uint8_t priority {};
		OperationCountingType testValue {};
		const auto ret = queueWrapper.tryPop(priority, testValue);
		if (ret != 0 || testValue.getValue() != i + totalThreads)
			return false;
	}

	return true;
}

/**
 * \brief Prepares queue for "pop" stage - just fills it completely with increasing values.
 *
 * \param [in] queueWrapper is a reference to shared QueueWrapper object
 */

void popPrepare(const QueueWrapper& queueWrapper)
{
	for (size_t i = 0; i < totalThreads; ++i)
		queueWrapper.tryPush(uint8_t{}, OperationCountingType{i});
}

/**
 * \brief QueueWrapper::pop() test thread
 *
 * Marks the first sequence point in SequenceAsserter, waits for the last sequence point from FIFO queue and marks it in
 * SequenceAsserter.
 *
 * \param [in] sequenceAsserter is a reference to SequenceAsserter shared object
 * \param [in] sequencePoints is a pair of sequence points for this instance (second one is ignored)
 * \param [in] queueWrapper is a reference to shared QueueWrapper object
 */

void popThread(SequenceAsserter& sequenceAsserter, const SequencePoints sequencePoints,
		const QueueWrapper& queueWrapper)
{
	sequenceAsserter.sequencePoint(sequencePoints.first);
	uint8_t priority {};
	OperationCountingType lastSequencePoint {};
	queueWrapper.pop(priority, lastSequencePoint);
	sequenceAsserter.sequencePoint(lastSequencePoint.getValue());
}

/**
 * \brief Trigger action with QueueWrapper::pop().
 *
 * \param [in] queueWrapper is a reference to shared QueueWrapper object
 * \param [in] i is the iteration counter
 *
 * \return true if trigger check succeeded, false otherwise
 */

bool popTrigger(const QueueWrapper& queueWrapper, const size_t i)
{
	uint8_t priority {};
	OperationCountingType testValue {};
	queueWrapper.pop(priority, testValue);
	return testValue.getValue() == i;
}

/**
 * \brief QueueWrapper::push() test thread
 *
 * Marks the first sequence point in SequenceAsserter, waits for free space in FIFO queue and marks last sequence point
 * in SequenceAsserter.
 *
 * \param [in] sequenceAsserter is a reference to SequenceAsserter shared object
 * \param [in] sequencePoints is a pair of sequence points for this instance
 * \param [in] queueWrapper is a reference to shared QueueWrapper object
 */

void pushThread(SequenceAsserter& sequenceAsserter, const SequencePoints sequencePoints,
		const QueueWrapper& queueWrapper)
{
	sequenceAsserter.sequencePoint(sequencePoints.first);
	queueWrapper.push(uint8_t{}, OperationCountingType{sequencePoints.second});
	sequenceAsserter.sequencePoint(sequencePoints.second);
}

/**
 * \brief Trigger action with QueueWrapper::push().
 *
 * \param [in] queueWrapper is a reference to shared QueueWrapper object
 * \param [in] i is the iteration counter
 *
 * \return true if trigger check succeeded, false otherwise
 */

bool pushTrigger(const QueueWrapper& queueWrapper, const size_t i)
{
	queueWrapper.push(uint8_t{}, OperationCountingType{i + totalThreads});
	return true;
}

/**
 * \brief Builder of test threads
 *
 * \param [in] testThreadFunction is a reference to function that will be used in test thread
 * \param [in] firstSequencePoint is the first sequence point for this instance - equal to the order in which this
 * thread will be started
 * \param [in] threadParameters is a reference to ThreadParameters object
 * \param [in] sequenceAsserter is a reference to SequenceAsserter shared object
 * \param [in] queueWrapper is a reference to shared QueueWrapper object
 *
 * \return constructed DynamicThread object
 */

DynamicThread makeTestThread(const TestThreadFunction& testThreadFunction, const unsigned int firstSequencePoint,
		const ThreadParameters& threadParameters, SequenceAsserter& sequenceAsserter, const QueueWrapper& queueWrapper)
{
	return makeDynamicThread({testThreadStackSize, threadParameters.first}, testThreadFunction,
			std::ref(sequenceAsserter), SequencePoints{firstSequencePoint, threadParameters.second + totalThreads},
			std::cref(queueWrapper));
}

/*---------------------------------------------------------------------------------------------------------------------+
| local constants
+---------------------------------------------------------------------------------------------------------------------*/

/// test stages
const std::array<Stage, 2> stages
{{
		Stage{popThread, nullptr, pushTrigger, nullptr},
		Stage{pushThread, popPrepare, popTrigger, popFinalCheck},
}};

}	// namespace

/*---------------------------------------------------------------------------------------------------------------------+
| private functions
+---------------------------------------------------------------------------------------------------------------------*/

bool FifoQueuePriorityTestCase::run_() const
{
	const auto contextSwitchCount = statistics::getContextSwitchCount();
	std::remove_const<decltype(contextSwitchCount)>::type expectedContextSwitchCount {};
	constexpr size_t fifoQueueTypes {4};

	for (const auto& stage : stages)
		for (const auto& phase : priorityTestPhases)
			for (const auto dynamic : falseTrue)
				for (const auto raw : falseTrue)
				{
					{
						const auto queueWrapper = makeQueueWrapper<totalThreads>(dynamic, raw, true);
						SequenceAsserter sequenceAsserter;

						const auto& threadFunction = std::get<0>(stage);
						std::array<DynamicThread, totalThreads> threads
						{{
								makeTestThread(threadFunction, 0, phase.first[phase.second[0]], sequenceAsserter,
										*queueWrapper),
								makeTestThread(threadFunction, 1, phase.first[phase.second[1]], sequenceAsserter,
										*queueWrapper),
								makeTestThread(threadFunction, 2, phase.first[phase.second[2]], sequenceAsserter,
										*queueWrapper),
{'fraction_non_alphanumeric': 0.2201571731773074, 'fraction_numerical': 0.004703722824528194, 'mean_word_length': 2.527007889945377, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4029305', 'n_tokens_mistral': 4563, 'n_tokens_neox': 4251, 'n_words': 1445}