bool CallOnceOperationsTestCase::run_() const
{
#if DISTORTOS_CALLONCE_SUPPORTED == 1 || DOXYGEN == 1

	const auto contextSwitchCount = statistics::getContextSwitchCount();

	SequenceAsserter sequenceAsserter;
	OnceFlag onceFlag;

	std::array<DynamicThread, totalThreads> threads
	{{
			makeTestThread(testCasePriority_ - 1, sequenceAsserter, SequencePoints{0, 12}, onceFlag),
			makeTestThread(testCasePriority_ - 1, sequenceAsserter, SequencePoints{2, 13}, onceFlag),
			makeTestThread(testCasePriority_ - 1, sequenceAsserter, SequencePoints{3, 14}, onceFlag),
			makeTestThread(testCasePriority_ - 1, sequenceAsserter, SequencePoints{4, 15}, onceFlag),
			makeTestThread(testCasePriority_ - 1, sequenceAsserter, SequencePoints{5, 16}, onceFlag),
			makeTestThread(testCasePriority_ - 1, sequenceAsserter, SequencePoints{6, 17}, onceFlag),
			makeTestThread(testCasePriority_ - 1, sequenceAsserter, SequencePoints{7, 18}, onceFlag),
			makeTestThread(testCasePriority_ - 1, sequenceAsserter, SequencePoints{8, 19}, onceFlag),
			makeTestThread(testCasePriority_ - 1, sequenceAsserter, SequencePoints{9, 20}, onceFlag),
			makeTestThread(testCasePriority_ - 1, sequenceAsserter, SequencePoints{10, 21}, onceFlag),
	}};

	waitForNextTick();
	const auto start = TickClock::now();

	for (auto& thread : threads)
		thread.start();

	ThisThread::setPriority(testCasePriority_ - 2);

	bool invalidState {};
	for (size_t i = 1; i < threads.size(); ++i)
		if (threads[i].getState() != ThreadState::BlockedOnOnceFlag)
			invalidState = true;

	for (auto& thread : threads)
		thread.join();

	if (invalidState != false)
		return false;

	if (TickClock::now() - start != sleepForDuration + decltype(sleepForDuration){1})
		return false;

	if (sequenceAsserter.assertSequence(totalThreads * 2 + 2) == false)
		return false;

	constexpr auto totalContextSwitches = 2 * totalThreads + 3 + waitForNextTickContextSwitchCount;
	if (statistics::getContextSwitchCount() - contextSwitchCount != totalContextSwitches)
		return false;

#endif	// DISTORTOS_CALLONCE_SUPPORTED == 1 || DOXYGEN == 1

	return true;
}
bool ThreadSleepForTestCase::run_() const
{
	for (const auto& phase : priorityTestPhases)
	{
		SequenceAsserter sequenceAsserter;
		std::array<TickClock::duration, totalThreads> durationDeviations {{}};

		std::array<TestThread, totalThreads> threads
		{{
				makeTestThread(phase.first[phase.second[0]], sequenceAsserter, durationDeviations[0]),
				makeTestThread(phase.first[phase.second[1]], sequenceAsserter, durationDeviations[1]),
				makeTestThread(phase.first[phase.second[2]], sequenceAsserter, durationDeviations[2]),
				makeTestThread(phase.first[phase.second[3]], sequenceAsserter, durationDeviations[3]),
				makeTestThread(phase.first[phase.second[4]], sequenceAsserter, durationDeviations[4]),
				makeTestThread(phase.first[phase.second[5]], sequenceAsserter, durationDeviations[5]),
				makeTestThread(phase.first[phase.second[6]], sequenceAsserter, durationDeviations[6]),
				makeTestThread(phase.first[phase.second[7]], sequenceAsserter, durationDeviations[7]),
				makeTestThread(phase.first[phase.second[8]], sequenceAsserter, durationDeviations[8]),
				makeTestThread(phase.first[phase.second[9]], sequenceAsserter, durationDeviations[9]),
		}};

		{
			architecture::InterruptMaskingLock interruptMaskingLock;

			// wait for beginning of next tick - test threads should be started in the same tick
			ThisThread::sleepFor({});

			for (auto& thread : threads)
				thread.start();
		}

		for (auto& thread : threads)
			thread.join();

		if (sequenceAsserter.assertSequence(totalThreads) == false)
			return false;

		// sleepFor() always sleeps one tick longer
		for (const auto& durationDeviation : durationDeviations)
			if (durationDeviation != TickClock::duration{1})
				return false;
	}

	return true;
}
bool ThreadPriorityTestCase::run_() const
{
	for (const auto& phase : priorityTestPhases)
	{
		SequenceAsserter sequenceAsserter;

		std::array<TestThread, totalThreads> threads
		{{
				makeTestThread(phase.first[phase.second[0]], sequenceAsserter),
				makeTestThread(phase.first[phase.second[1]], sequenceAsserter),
				makeTestThread(phase.first[phase.second[2]], sequenceAsserter),
				makeTestThread(phase.first[phase.second[3]], sequenceAsserter),
				makeTestThread(phase.first[phase.second[4]], sequenceAsserter),
				makeTestThread(phase.first[phase.second[5]], sequenceAsserter),
				makeTestThread(phase.first[phase.second[6]], sequenceAsserter),
				makeTestThread(phase.first[phase.second[7]], sequenceAsserter),
				makeTestThread(phase.first[phase.second[8]], sequenceAsserter),
				makeTestThread(phase.first[phase.second[9]], sequenceAsserter),
		}};

		{
			architecture::InterruptMaskingLock interruptMaskingLock;

			for (auto& thread : threads)
				thread.start();
		}

		for (auto& thread : threads)
			thread.join();

		if (sequenceAsserter.assertSequence(totalThreads) == false)
			return false;
	}

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

	for (const auto& stage : stages)
		for (const auto& phase : priorityTestPhases)
		{
			StaticFifoQueueWrapper<totalThreads> fifoQueueWrapper;
			StaticRawFifoQueueWrapper<totalThreads> rawFifoQueueWrapper;
			using QueueWrapperHolder = estd::ReferenceHolder<const QueueWrapper>;
			const QueueWrapperHolder queueWrappers[fifoQueueTypes]
			{
					QueueWrapperHolder{fifoQueueWrapper},
					QueueWrapperHolder{rawFifoQueueWrapper},
			};

			for (auto& queueWrapperHolder : queueWrappers)
			{
				auto& queueWrapper = queueWrapperHolder.get();
				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),
						makeTestThread(threadFunction, 3, phase.first[phase.second[3]], sequenceAsserter, queueWrapper),
						makeTestThread(threadFunction, 4, phase.first[phase.second[4]], sequenceAsserter, queueWrapper),
						makeTestThread(threadFunction, 5, phase.first[phase.second[5]], sequenceAsserter, queueWrapper),
						makeTestThread(threadFunction, 6, phase.first[phase.second[6]], sequenceAsserter, queueWrapper),
						makeTestThread(threadFunction, 7, phase.first[phase.second[7]], sequenceAsserter, queueWrapper),
						makeTestThread(threadFunction, 8, phase.first[phase.second[8]], sequenceAsserter, queueWrapper),
						makeTestThread(threadFunction, 9, phase.first[phase.second[9]], sequenceAsserter, queueWrapper),
				}};

				// execute Prepare
				if (std::get<1>(stage) != nullptr)
					std::get<1>(stage)(queueWrapper);

				bool result {true};

				for (auto& thread : threads)
				{
					thread.start();
					// 2 context switches: "into" the thread and "back" to main thread when test thread blocks on FIFO
					// queue
					expectedContextSwitchCount += 2;
					if (statistics::getContextSwitchCount() - contextSwitchCount != expectedContextSwitchCount)
						result = false;
				}

				if (sequenceAsserter.assertSequence(totalThreads) == false)
					result = false;

				for (size_t i = 0; i < threads.size(); ++i)
				{
					const auto triggerResult = std::get<2>(stage)(queueWrapper, i);
					if (triggerResult == false)
						result = false;
					// 2 context switches: into" the unblocked thread and "back" to main thread when test thread
					// terminates
					expectedContextSwitchCount += 2;
					if (statistics::getContextSwitchCount() - contextSwitchCount != expectedContextSwitchCount)
						result = false;
				}

				for (auto& thread : threads)
					thread.join();

				// execute FinalCheck
				if (std::get<3>(stage) != nullptr && std::get<3>(stage)(queueWrapper) == false)
					result = false;

				if (result == false || sequenceAsserter.assertSequence(totalThreads * 2) == false)
					return false;
			}
		}

	if (statistics::getContextSwitchCount() - contextSwitchCount != 2 * 4 * totalThreads * priorityTestPhases.size() *
			fifoQueueTypes)
		return false;

	return true;
}
bool ThreadSleepUntilTestCase::run_() const
{
	const auto allocatedMemory = mallinfo().uordblks;

	for (const auto& phase : priorityTestPhases)
	{
		{
			SequenceAsserter sequenceAsserter;
			std::array<TickClock::duration, totalThreads> timePointDeviations {{}};
			std::array<int, totalThreads> sharedRets {{}};
			const auto now = TickClock::now();

			std::array<DynamicThread, totalThreads> threads
			{{
					makeTestThread(now, phase.first[phase.second[0]], sequenceAsserter, timePointDeviations[0],
							sharedRets[0]),
					makeTestThread(now, phase.first[phase.second[1]], sequenceAsserter, timePointDeviations[1],
							sharedRets[1]),
					makeTestThread(now, phase.first[phase.second[2]], sequenceAsserter, timePointDeviations[2],
							sharedRets[2]),
					makeTestThread(now, phase.first[phase.second[3]], sequenceAsserter, timePointDeviations[3],
							sharedRets[3]),
					makeTestThread(now, phase.first[phase.second[4]], sequenceAsserter, timePointDeviations[4],
							sharedRets[4]),
					makeTestThread(now, phase.first[phase.second[5]], sequenceAsserter, timePointDeviations[5],
							sharedRets[5]),
					makeTestThread(now, phase.first[phase.second[6]], sequenceAsserter, timePointDeviations[6],
							sharedRets[6]),
					makeTestThread(now, phase.first[phase.second[7]], sequenceAsserter, timePointDeviations[7],
							sharedRets[7]),
					makeTestThread(now, phase.first[phase.second[8]], sequenceAsserter, timePointDeviations[8],
							sharedRets[8]),
					makeTestThread(now, phase.first[phase.second[9]], sequenceAsserter, timePointDeviations[9],
							sharedRets[9]),
			}};

			{
				architecture::InterruptMaskingLock interruptMaskingLock;

				for (auto& thread : threads)
					thread.start();
			}

			bool invalidState {};
			for (const auto& thread : threads)
				if (thread.getState() != ThreadState::sleeping)
					invalidState = true;

			for (auto& thread : threads)
				thread.join();

			if (invalidState != false)
				return false;

			if (sequenceAsserter.assertSequence(totalThreads) == false)
				return false;

			for (const auto& timePointDeviation : timePointDeviations)
				if (timePointDeviation != TickClock::duration{})
					return false;

			for (const auto& sharedRet : sharedRets)
				if (sharedRet != 0)
					return false;
		}

		if (mallinfo().uordblks != allocatedMemory)	// dynamic memory must be deallocated after each test phase
			return false;
	}

	return true;
}
bool MutexPriorityTestCase::run_() const
{
	using Parameters = std::tuple<Mutex::Type, Mutex::Protocol, uint8_t>;
	static const Parameters parametersArray[]
	{
			Parameters{Mutex::Type::normal, Mutex::Protocol::none, {}},
			Parameters{Mutex::Type::normal, Mutex::Protocol::priorityProtect, UINT8_MAX},
			Parameters{Mutex::Type::normal, Mutex::Protocol::priorityInheritance, {}},
			Parameters{Mutex::Type::errorChecking, Mutex::Protocol::none, {}},
			Parameters{Mutex::Type::errorChecking, Mutex::Protocol::priorityProtect, UINT8_MAX},
			Parameters{Mutex::Type::errorChecking, Mutex::Protocol::priorityInheritance, {}},
			Parameters{Mutex::Type::recursive, Mutex::Protocol::none, {}},
			Parameters{Mutex::Type::recursive, Mutex::Protocol::priorityProtect, UINT8_MAX},
			Parameters{Mutex::Type::recursive, Mutex::Protocol::priorityInheritance, {}},
	};

	for (const auto& parameters : parametersArray)
		for (const auto& phase : priorityTestPhases)
		{
			SequenceAsserter sequenceAsserter;
			Mutex mutex {std::get<0>(parameters), std::get<1>(parameters), std::get<2>(parameters)};

			std::array<DynamicThread, totalThreads> threads
			{{
					makeTestThread(phase.first[phase.second[0]], sequenceAsserter, mutex),
					makeTestThread(phase.first[phase.second[1]], sequenceAsserter, mutex),
					makeTestThread(phase.first[phase.second[2]], sequenceAsserter, mutex),
					makeTestThread(phase.first[phase.second[3]], sequenceAsserter, mutex),
					makeTestThread(phase.first[phase.second[4]], sequenceAsserter, mutex),
					makeTestThread(phase.first[phase.second[5]], sequenceAsserter, mutex),
					makeTestThread(phase.first[phase.second[6]], sequenceAsserter, mutex),
					makeTestThread(phase.first[phase.second[7]], sequenceAsserter, mutex),
					makeTestThread(phase.first[phase.second[8]], sequenceAsserter, mutex),
					makeTestThread(phase.first[phase.second[9]], sequenceAsserter, mutex),
			}};

			mutex.lock();

			{
				architecture::InterruptMaskingLock interruptMaskingLock;

				for (auto& thread : threads)
				{
					thread.start();
					// make sure each test threads blocks on mutex in the order of starting, even if current thread
					// inherits test thread's high priority
					ThisThread::sleepFor(TickClock::duration{1});
				}
			}

			bool invalidState {};
			for (const auto& thread : threads)
				if (thread.getState() != ThreadState::blockedOnMutex)
					invalidState = true;

			mutex.unlock();

			for (auto& thread : threads)
				thread.join();

			if (invalidState != false)
				return false;

			if (sequenceAsserter.assertSequence(totalThreads) == false)
				return false;
		}

	return true;
}