diff --git a/test/camera/buffer_import.cpp b/test/camera/buffer_import.cpp
index 6997ea78c9f608c9..3f392cdc0732941f 100644
--- a/test/camera/buffer_import.cpp
+++ b/test/camera/buffer_import.cpp
@@ -16,6 +16,7 @@
 #include "media_device.h"
 #include "v4l2_videodevice.h"
 
+#include "buffer_source.h"
 #include "camera_test.h"
 #include "test.h"
 
@@ -23,100 +24,6 @@ using namespace libcamera;
 
 namespace {
 
-/* A provider of external buffers, suitable for import by a Camera. */
-class BufferSource
-{
-public:
-	BufferSource()
-		: video_(nullptr)
-	{
-	}
-
-	~BufferSource()
-	{
-		if (video_) {
-			video_->releaseBuffers();
-			video_->close();
-		}
-
-		delete video_;
-		video_ = nullptr;
-
-		if (media_)
-			media_->release();
-	}
-
-	int allocate(const StreamConfiguration &config)
-	{
-		/* Locate and open the video device. */
-		std::string videoDeviceName = "vivid-000-vid-out";
-
-		std::unique_ptr<DeviceEnumerator> enumerator =
-			DeviceEnumerator::create();
-		if (!enumerator) {
-			std::cout << "Failed to create device enumerator" << std::endl;
-			return TestFail;
-		}
-
-		if (enumerator->enumerate()) {
-			std::cout << "Failed to enumerate media devices" << std::endl;
-			return TestFail;
-		}
-
-		DeviceMatch dm("vivid");
-		dm.add(videoDeviceName);
-
-		media_ = enumerator->search(dm);
-		if (!media_) {
-			std::cout << "No vivid output device available" << std::endl;
-			return TestSkip;
-		}
-
-		video_ = V4L2VideoDevice::fromEntityName(media_.get(), videoDeviceName);
-		if (!video_) {
-			std::cout << "Failed to get video device from entity "
-				  << videoDeviceName << std::endl;
-			return TestFail;
-		}
-
-		if (video_->open()) {
-			std::cout << "Unable to open " << videoDeviceName << std::endl;
-			return TestFail;
-		}
-
-		/* Configure the format. */
-		V4L2DeviceFormat format;
-		if (video_->getFormat(&format)) {
-			std::cout << "Failed to get format on output device" << std::endl;
-			return TestFail;
-		}
-
-		format.size = config.size;
-		format.fourcc = V4L2VideoDevice::toV4L2Fourcc(config.pixelFormat, false);
-		if (video_->setFormat(&format)) {
-			std::cout << "Failed to set format on output device" << std::endl;
-			return TestFail;
-		}
-
-		if (video_->exportBuffers(config.bufferCount, &buffers_) < 0) {
-			std::cout << "Failed to export buffers" << std::endl;
-			return TestFail;
-		}
-
-		return TestPass;
-	}
-
-	const std::vector<std::unique_ptr<FrameBuffer>> &buffers()
-	{
-		return buffers_;
-	}
-
-private:
-	std::shared_ptr<MediaDevice> media_;
-	V4L2VideoDevice *video_;
-	std::vector<std::unique_ptr<FrameBuffer>> buffers_;
-};
-
 class BufferImportTest : public CameraTest, public Test
 {
 public:
diff --git a/test/libtest/buffer_source.cpp b/test/libtest/buffer_source.cpp
new file mode 100644
index 0000000000000000..066049d342a491f0
--- /dev/null
+++ b/test/libtest/buffer_source.cpp
@@ -0,0 +1,98 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2020, Google Inc.
+ *
+ * A provider of external buffers, suitable for use in tests.
+ */
+
+#include "buffer_source.h"
+
+#include <iostream>
+
+#include "device_enumerator.h"
+
+#include "test.h"
+
+BufferSource::BufferSource()
+	: video_(nullptr)
+{
+}
+
+BufferSource::~BufferSource()
+{
+	if (video_) {
+		video_->releaseBuffers();
+		video_->close();
+	}
+
+	delete video_;
+	video_ = nullptr;
+
+	if (media_)
+		media_->release();
+}
+
+int BufferSource::allocate(const StreamConfiguration &config)
+{
+	/* Locate and open the video device. */
+	std::string videoDeviceName = "vivid-000-vid-out";
+
+	std::unique_ptr<DeviceEnumerator> enumerator =
+		DeviceEnumerator::create();
+	if (!enumerator) {
+		std::cout << "Failed to create device enumerator" << std::endl;
+		return TestFail;
+	}
+
+	if (enumerator->enumerate()) {
+		std::cout << "Failed to enumerate media devices" << std::endl;
+		return TestFail;
+	}
+
+	DeviceMatch dm("vivid");
+	dm.add(videoDeviceName);
+
+	media_ = enumerator->search(dm);
+	if (!media_) {
+		std::cout << "No vivid output device available" << std::endl;
+		return TestSkip;
+	}
+
+	video_ = V4L2VideoDevice::fromEntityName(media_.get(), videoDeviceName);
+	if (!video_) {
+		std::cout << "Failed to get video device from entity "
+			  << videoDeviceName << std::endl;
+		return TestFail;
+	}
+
+	if (video_->open()) {
+		std::cout << "Unable to open " << videoDeviceName << std::endl;
+		return TestFail;
+	}
+
+	/* Configure the format. */
+	V4L2DeviceFormat format;
+	if (video_->getFormat(&format)) {
+		std::cout << "Failed to get format on output device" << std::endl;
+		return TestFail;
+	}
+
+	format.size = config.size;
+	format.fourcc = V4L2VideoDevice::toV4L2Fourcc(config.pixelFormat, false);
+	if (video_->setFormat(&format)) {
+		std::cout << "Failed to set format on output device" << std::endl;
+		return TestFail;
+	}
+
+	if (video_->exportBuffers(config.bufferCount, &buffers_) < 0) {
+		std::cout << "Failed to export buffers" << std::endl;
+		return TestFail;
+	}
+
+	return TestPass;
+}
+
+const std::vector<std::unique_ptr<FrameBuffer>> &BufferSource::buffers()
+{
+	return buffers_;
+}
diff --git a/test/libtest/buffer_source.h b/test/libtest/buffer_source.h
new file mode 100644
index 0000000000000000..2d8fc5acf6d78771
--- /dev/null
+++ b/test/libtest/buffer_source.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2020, Google Inc.
+ *
+ * buffer_source.h - libcamera camera test helper to create FrameBuffers
+ */
+#ifndef __LIBCAMERA_BUFFER_SOURCE_TEST_H__
+#define __LIBCAMERA_BUFFER_SOURCE_TEST_H__
+
+#include <libcamera/libcamera.h>
+
+#include "media_device.h"
+#include "v4l2_videodevice.h"
+
+using namespace libcamera;
+
+class BufferSource
+{
+public:
+	BufferSource();
+	~BufferSource();
+
+	int allocate(const StreamConfiguration &config);
+	const std::vector<std::unique_ptr<FrameBuffer>> &buffers();
+
+private:
+	std::shared_ptr<MediaDevice> media_;
+	V4L2VideoDevice *video_;
+	std::vector<std::unique_ptr<FrameBuffer>> buffers_;
+};
+
+#endif /* __LIBCAMERA_BUFFER_SOURCE_TEST_H__ */
diff --git a/test/libtest/meson.build b/test/libtest/meson.build
index 3e798ef3810e9b0d..33565e0eb3b66d6a 100644
--- a/test/libtest/meson.build
+++ b/test/libtest/meson.build
@@ -1,14 +1,11 @@
 libtest_sources = files([
+    'buffer_source.cpp',
     'camera_test.cpp',
     'test.cpp',
 ])
 
-libtest = static_library('libtest', libtest_sources,
-                         dependencies : libcamera_dep)
-
 libtest_includes = include_directories('.')
 
-test_libraries = [libtest]
 
 test_includes_public = [
     libtest_includes,
@@ -18,3 +15,9 @@ test_includes_internal = [
     test_includes_public,
     libcamera_internal_includes,
 ]
+
+libtest = static_library('libtest', libtest_sources,
+                         dependencies : libcamera_dep,
+                         include_directories : test_includes_internal)
+
+test_libraries = [libtest]
