bool SoftwareTimerOrderingTestCase::run_() const
{
	constexpr auto totalSoftwareTimers = totalThreads;

	for (const auto& phase : priorityTestPhases)
	{
		SequenceAsserter sequenceAsserter;

		using TestSoftwareTimer = decltype(makeStaticSoftwareTimer(&SequenceAsserter::sequencePoint,
				std::ref(std::declval<SequenceAsserter&>()), std::declval<unsigned int>()));
		std::array<TestSoftwareTimer, totalSoftwareTimers> softwareTimers
		{{
				makeStaticSoftwareTimer(&SequenceAsserter::sequencePoint, std::ref(sequenceAsserter),
						static_cast<unsigned int>(phase.first[phase.second[0]].second)),
				makeStaticSoftwareTimer(&SequenceAsserter::sequencePoint, std::ref(sequenceAsserter),
						static_cast<unsigned int>(phase.first[phase.second[1]].second)),
				makeStaticSoftwareTimer(&SequenceAsserter::sequencePoint, std::ref(sequenceAsserter),
						static_cast<unsigned int>(phase.first[phase.second[2]].second)),
				makeStaticSoftwareTimer(&SequenceAsserter::sequencePoint, std::ref(sequenceAsserter),
						static_cast<unsigned int>(phase.first[phase.second[3]].second)),
				makeStaticSoftwareTimer(&SequenceAsserter::sequencePoint, std::ref(sequenceAsserter),
						static_cast<unsigned int>(phase.first[phase.second[4]].second)),
				makeStaticSoftwareTimer(&SequenceAsserter::sequencePoint, std::ref(sequenceAsserter),
						static_cast<unsigned int>(phase.first[phase.second[5]].second)),
				makeStaticSoftwareTimer(&SequenceAsserter::sequencePoint, std::ref(sequenceAsserter),
						static_cast<unsigned int>(phase.first[phase.second[6]].second)),
				makeStaticSoftwareTimer(&SequenceAsserter::sequencePoint, std::ref(sequenceAsserter),
						static_cast<unsigned int>(phase.first[phase.second[7]].second)),
				makeStaticSoftwareTimer(&SequenceAsserter::sequencePoint, std::ref(sequenceAsserter),
						static_cast<unsigned int>(phase.first[phase.second[8]].second)),
				makeStaticSoftwareTimer(&SequenceAsserter::sequencePoint, std::ref(sequenceAsserter),
						static_cast<unsigned int>(phase.first[phase.second[9]].second)),
		}};

		waitForNextTick();

		for (size_t i = 0; i < softwareTimers.size(); ++i)
			softwareTimers[i].start(TickClock::duration{maxPhasePriority + 1 - phase.first[phase.second[i]].first});

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

		for (const auto& softwareTimer : softwareTimers)
			while(softwareTimer.isRunning())
			{

			}

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

	return true;
}
bool CallOnceOperationsTestCase::run_() const
{
#if DISTORTOS_CALLONCE_SUPPORTED == 1 || DOXYGEN == 1

	const auto contextSwitchCount = statistics::getContextSwitchCount();

	SequenceAsserter sequenceAsserter;
	OnceFlag onceFlag;

	using TestThread = decltype(makeTestThread(uint8_t{}, sequenceAsserter, SequencePoints{}, onceFlag));
	std::array<TestThread, 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);

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

	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 MutexRecursiveOperationsTestCase::run_() const
{
	using Parameters = std::pair<Mutex::Protocol, uint8_t>;
	static const Parameters parametersArray[]
	{
			Parameters{Mutex::Protocol::none, {}},
			Parameters{Mutex::Protocol::priorityProtect, UINT8_MAX},
			Parameters{Mutex::Protocol::priorityProtect, testCasePriority_},
			Parameters{Mutex::Protocol::priorityInheritance, {}},
	};

	for (const auto& parameters : parametersArray)
	{
		Mutex mutex {Mutex::Type::recursive, parameters.first, parameters.second};

		size_t lockCount {};

		{
			// simple lock - must succeed immediately
			++lockCount;
			waitForNextTick();
			const auto start = TickClock::now();
			const auto ret = mutex.lock();
			if (ret != 0 || start != TickClock::now())
				return false;
		}

		const auto maxRecursiveLocks = mutex.getMaxRecursiveLocks();
		if (maxRecursiveLocks < 4)
			return false;

		{
			// recursive lock - must succeed immediately
			++lockCount;
			waitForNextTick();
			const auto start = TickClock::now();
			const auto ret = mutex.lock();
			if (ret != 0 || start != TickClock::now())
				return false;
		}

		{
			// recursive lock - must succeed immediately
			++lockCount;
			waitForNextTick();
			const auto start = TickClock::now();
			const auto ret = mutex.tryLock();
			if (ret != 0 || start != TickClock::now())
				return false;
		}

		{
			// recursive lock - must succeed immediately
			++lockCount;
			waitForNextTick();
			const auto start = TickClock::now();
			const auto ret = mutex.tryLockFor(singleDuration);
			if (ret != 0 || start != TickClock::now())
				return false;
		}

		{
			// recursive lock - must succeed immediately
			++lockCount;
			waitForNextTick();
			const auto start = TickClock::now();
			const auto ret = mutex.tryLockUntil(start + singleDuration);
			if (ret != 0 || start != TickClock::now())
				return false;
		}

		while (lockCount < maxRecursiveLocks + 1)
		{
			// recursive lock - must succeed
			++lockCount;
			const auto ret = mutex.tryLock();
			if (ret != 0)
				return false;
		}

		{
			// excessive recursive lock - must fail with EAGAIN immediately
			waitForNextTick();
			const auto start = TickClock::now();
			const auto ret = mutex.lock();
			if (ret != EAGAIN || start != TickClock::now())
				return false;
		}

		{
			// excessive recursive lock - must fail with EAGAIN immediately
			waitForNextTick();
			const auto start = TickClock::now();
			const auto ret = mutex.tryLock();
			if (ret != EAGAIN || start != TickClock::now())
				return false;
		}

		{
			// excessive recursive lock - must fail with EAGAIN immediately
			waitForNextTick();
			const auto start = TickClock::now();
			const auto ret = mutex.tryLockFor(singleDuration);
			if (ret != EAGAIN || start != TickClock::now())
				return false;
		}

		{
			// excessive recursive lock - must fail with EAGAIN immediately
			waitForNextTick();
			const auto start = TickClock::now();
			const auto ret = mutex.tryLockUntil(start + singleDuration);
			if (ret != EAGAIN || start != TickClock::now())
				return false;
		}

		{
			const auto ret = mutexTestTryLockWhenLocked(mutex, testCasePriority_);
			if (ret != true)
				return ret;
		}

		{
			const auto ret = mutexTestUnlockFromWrongThread(mutex);
			if (ret != true)
				return ret;
		}

		{
			// simple unlock - must succeed immediately
			--lockCount;
			waitForNextTick();
			const auto start = TickClock::now();
			const auto ret = mutex.unlock();
			if (ret != 0 || start != TickClock::now())
				return false;
		}

		{
			const auto ret = mutexTestTryLockWhenLocked(mutex, testCasePriority_);
			if (ret != true)
				return ret;
		}

		while (lockCount > 0)
		{
			// simple unlock - must succeed
			--lockCount;
			const auto ret = mutex.unlock();
			if (ret != 0)
				return false;
		}

		{
			// excessive unlock - must fail with EPERM immediately
			waitForNextTick();
			const auto start = TickClock::now();
			const auto ret = mutex.unlock();
			if (ret != EPERM || start != TickClock::now())
				return false;
		}
	}

	return true;
}