From 1a0865da7a25e84a5689306e4a2d94667acdd9b2 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Thu, 14 Mar 2019 14:13:53 +0100
Subject: [PATCH] test/run_filter: ensure that partial frames will not get
 passed to the filter

---
 test/run_filter.cxx | 54 +++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 50 insertions(+), 4 deletions(-)

diff --git a/test/run_filter.cxx b/test/run_filter.cxx
index b5f16be01..b148028cf 100644
--- a/test/run_filter.cxx
+++ b/test/run_filter.cxx
@@ -26,6 +26,7 @@
 #include "filter/Prepared.hxx"
 #include "pcm/Volume.hxx"
 #include "mixer/MixerControl.hxx"
+#include "system/Error.hxx"
 #include "util/ConstBuffer.hxx"
 #include "util/StringBuffer.hxx"
 #include "util/RuntimeError.hxx"
@@ -59,6 +60,50 @@ LoadFilter(const ConfigData &config, const char *name)
 	return filter_configured_new(*param);
 }
 
+static size_t
+ReadOrThrow(int fd, void *buffer, size_t size)
+{
+	auto nbytes = read(fd, buffer, size);
+	if (nbytes < 0)
+		throw MakeErrno("Read failed");
+
+	return nbytes;
+}
+
+static void
+FullRead(int fd, void *_buffer, size_t size)
+{
+	auto buffer = (uint8_t *)_buffer;
+
+	while (size > 0) {
+		size_t nbytes = ReadOrThrow(fd, buffer, size);
+		if (nbytes == 0)
+			throw std::runtime_error("Premature end of input");
+
+		buffer += nbytes;
+		size -= nbytes;
+	}
+}
+
+static size_t
+ReadFrames(int fd, void *_buffer, size_t size, size_t frame_size)
+{
+	auto buffer = (uint8_t *)_buffer;
+
+	size = (size / frame_size) * frame_size;
+
+	size_t nbytes = ReadOrThrow(fd, buffer, size);
+
+	const size_t modulo = nbytes % frame_size;
+	if (modulo > 0) {
+		size_t rest = frame_size - modulo;
+		FullRead(fd, buffer + nbytes, rest);
+		nbytes += rest;
+	}
+
+	return nbytes;
+}
+
 int main(int argc, char **argv)
 try {
 	if (argc < 3 || argc > 4) {
@@ -79,6 +124,8 @@ try {
 	if (argc > 3)
 		audio_format = ParseAudioFormat(argv[3], false);
 
+	const size_t in_frame_size = audio_format.GetFrameSize();
+
 	/* initialize the filter */
 
 	auto prepared_filter = LoadFilter(config, argv[2]);
@@ -97,10 +144,9 @@ try {
 	while (true) {
 		char buffer[4096];
 
-		ssize_t nbytes;
-
-		nbytes = read(0, buffer, sizeof(buffer));
-		if (nbytes <= 0)
+		ssize_t nbytes = ReadFrames(0, buffer, sizeof(buffer),
+					    in_frame_size);
+		if (nbytes == 0)
 			break;
 
 		auto dest = filter->FilterPCM({(const void *)buffer, (size_t)nbytes});