summaryrefslogtreecommitdiff
path: root/test/timer.cpp
blob: fc90b11040d8f41b9249c60368eba96d9607211e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <libcamera/camera_manager.h>

#include "v4l2_camera_proxy.h"

class V4L2CompatManager
{
public:
	struct FileOperations {
		using openat_func_t = int (*)(int dirfd, const char *path,
					      int oflag, ...);
		using dup_func_t = int (*)(int oldfd);
		using close_func_t = int (*)(int fd);
		using ioctl_func_t = int (*)(int fd, unsigned long request, ...);
		using mmap_func_t = void *(*)(void *addr, size_t length, int prot,
					      int flags, int fd, off64_t offset);
		using munmap_func_t = int (*)(void *addr, size_t length);

		openat_func_t openat;
		dup_func_t dup;
		close_func_t close;
		ioctl_func_t ioctl;
		mmap_func_t mmap;
		munmap_func_t munmap;
	};

	static V4L2CompatManager *instance();

	const FileOperations &fops() const { return fops_; }

	int openat(int dirfd, const char *path, int oflag, mode_t mode);

	int dup(int oldfd);
	int close(int fd);
	void *mmap(void *addr, size_t length, int prot, int flags,
		   int fd, off64_t offset);
	int munmap(void *addr, size_t length);
	int ioctl(int fd, unsigned long request, void *arg);

private:
	V4L2CompatManager();
	~V4L2CompatManager();

	int start();
	int getCameraIndex(int fd);
	std::shared_ptr<V4L2CameraFile> cameraFile(/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Copyright (C) 2019, Google Inc.
 *
 * timer.cpp - Timer test
 */

#include <chrono>
#include <iostream>

#include "libcamera/internal/event_dispatcher.h"
#include "libcamera/internal/thread.h"
#include "libcamera/internal/timer.h"

#include "test.h"

using namespace std;
using namespace libcamera;

class ManagedTimer : public Timer
{
public:
	ManagedTimer()
		: Timer(), count_(0)
	{
		timeout.connect(this, &ManagedTimer::timeoutHandler);
	}

	void start(int msec)
	{
		count_ = 0;
		start_ = std::chrono::steady_clock::now();
		expiration_ = std::chrono::steady_clock::time_point();

		Timer::start(msec);
	}

	void start(std::chrono::steady_clock::time_point deadline)
	{
		count_ = 0;
		start_ = std::chrono::steady_clock::now();
		expiration_ = std::chrono::steady_clock::time_point();

		Timer::start(deadline);
	}

	int jitter()
	{
		std::chrono::steady_clock::duration duration = expiration_ - deadline();
		return abs(std::chrono::duration_cast<std::chrono::milliseconds>(duration).count());
	}

	bool hasFailed()
	{
		return isRunning() || count_ != 1 || jitter() > 50;
	}

private:
	void timeoutHandler([[maybe_unused]] Timer *timer)
	{
		expiration_ = std::chrono::steady_clock::now();
		count_++;
	}

	unsigned int count_;
	std::chrono::steady_clock::time_point start_;
	std::chrono::steady_clock::time_point expiration_;
};

class TimerTest : public Test
{
protected:
	int init()
	{
		return 0;
	}

	int run()
	{
		EventDispatcher *dispatcher = Thread::current()->eventDispatcher();
		ManagedTimer timer;
		ManagedTimer timer2;

		/* Timer expiration. */
		timer.start(1000);

		if (!timer.isRunning()) {
			cout << "Timer expiration test failed" << endl;
			return TestFail;
		}

		dispatcher->processEvents();

		if (timer.hasFailed()) {
			cout << "Timer expiration test failed" << endl;
			return TestFail;
		}

		/*
		 * 32 bit wrap test
		 * Nanosecond resolution in a 32 bit value wraps at 4.294967
		 * seconds (0xFFFFFFFF / 1000000)
		 */
		timer.start(4295);
		dispatcher->processEvents();

		if (timer.hasFailed()) {
			cout << "Timer expiration test failed" << endl;
			return TestFail;
		}

		/* Timer restart. */
		timer.start(500);

		if (!timer.isRunning()) {
			cout << "Timer restart test failed" << endl;
			return TestFail;
		}

		dispatcher->processEvents();

		if (timer.hasFailed()) {
			cout << "Timer restart test failed" << endl;
			return TestFail;
		}

		/* Timer restart before expiration. */
		timer.start(50);
		timer.start(100);
		timer.start(150);

		dispatcher->processEvents();

		if (timer.hasFailed()) {
			cout << "Timer restart before expiration test failed" << endl;
			return TestFail;
		}

		/* Timer with absolute deadline. */
		timer.start(std::chrono::steady_clock::now() + std::chrono::milliseconds(200));

		dispatcher->processEvents();

		if (timer.hasFailed()) {
			cout << "Absolute deadline test failed" << endl;
			return TestFail;
		}

		/* Two timers. */
		timer.start(1000);
		timer2.start(300);

		dispatcher->processEvents();

		if (!timer.isRunning()) {
			cout << "Two timers test failed" << endl;
			return TestFail;
		}

		if (timer2.jitter() > 50) {
			cout << "Two timers test failed" << endl;
			return TestFail;
		}

		dispatcher->processEvents();

		if (timer.jitter() > 50) {
			cout << "Two timers test failed" << endl;
			return TestFail;
		}

		/* Restart timer before expiration. */
		timer.start(1000);
		timer2.start(300);

		dispatcher->processEvents();

		if (timer2.jitter() > 50) {
			cout << "Two timers test failed" << endl;
			return TestFail;
		}

		timer.start(1000);

		dispatcher->processEvents();

		if (timer.jitter() > 50) {
			cout << "Two timers test failed" << endl;
			return TestFail;
		}

		/*
		 * Test that dynamically allocated timers are stopped when
		 * deleted. This will result in a crash on failure.
		 */
		ManagedTimer *dyntimer = new ManagedTimer();
		dyntimer->start(100);
		delete dyntimer;

		timer.start(200);
		dispatcher->processEvents();

		return TestPass;
	}

	void cleanup()
	{
	}
};

TEST_REGISTER(TimerTest)