diff --git a/Makefile.am b/Makefile.am
index f714ccc83..6f7b1ac8a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1240,6 +1240,7 @@ test_dump_playlist_LDADD = \
 	libpcm.a \
 	$(GLIB_LIBS)
 test_dump_playlist_SOURCES = test/dump_playlist.cxx \
+	test/FakeDecoderAPI.cxx \
 	$(DECODER_SRC) \
 	src/Log.cxx \
 	src/IOThread.cxx \
@@ -1293,6 +1294,7 @@ test_read_tags_LDADD = \
 	libutil.a \
 	$(GLIB_LIBS)
 test_read_tags_SOURCES = test/read_tags.cxx \
+	test/FakeDecoderAPI.cxx \
 	src/Log.cxx \
 	src/IOThread.cxx \
 	src/ReplayGainInfo.cxx \
diff --git a/test/FakeDecoderAPI.cxx b/test/FakeDecoderAPI.cxx
new file mode 100644
index 000000000..e2040b40d
--- /dev/null
+++ b/test/FakeDecoderAPI.cxx
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "DecoderAPI.hxx"
+#include "InputStream.hxx"
+#include "util/Error.hxx"
+#include "Compiler.h"
+
+#include <glib.h>
+
+#include <unistd.h>
+
+void
+decoder_initialized(gcc_unused Decoder &decoder,
+		    gcc_unused const AudioFormat audio_format,
+		    gcc_unused bool seekable,
+		    gcc_unused float total_time)
+{
+}
+
+DecoderCommand
+decoder_get_command(gcc_unused Decoder &decoder)
+{
+	return DecoderCommand::NONE;
+}
+
+void
+decoder_command_finished(gcc_unused Decoder &decoder)
+{
+}
+
+double
+decoder_seek_where(gcc_unused Decoder &decoder)
+{
+	return 1.0;
+}
+
+void
+decoder_seek_error(gcc_unused Decoder &decoder)
+{
+}
+
+size_t
+decoder_read(gcc_unused Decoder *decoder,
+	     InputStream &is,
+	     void *buffer, size_t length)
+{
+	return is.LockRead(buffer, length, IgnoreError());
+}
+
+void
+decoder_timestamp(gcc_unused Decoder &decoder,
+		  gcc_unused double t)
+{
+}
+
+DecoderCommand
+decoder_data(gcc_unused Decoder &decoder,
+	     gcc_unused InputStream *is,
+	     const void *data, size_t datalen,
+	     gcc_unused uint16_t kbit_rate)
+{
+	gcc_unused ssize_t nbytes = write(1, data, datalen);
+	return DecoderCommand::NONE;
+}
+
+DecoderCommand
+decoder_tag(gcc_unused Decoder &decoder,
+	    gcc_unused InputStream *is,
+	    gcc_unused Tag &&tag)
+{
+	return DecoderCommand::NONE;
+}
+
+void
+decoder_replay_gain(gcc_unused Decoder &decoder,
+		    const ReplayGainInfo *rgi)
+{
+	const ReplayGainTuple *tuple = &rgi->tuples[REPLAY_GAIN_ALBUM];
+	if (tuple->IsDefined())
+		g_printerr("replay_gain[album]: gain=%f peak=%f\n",
+			   tuple->gain, tuple->peak);
+
+	tuple = &rgi->tuples[REPLAY_GAIN_TRACK];
+	if (tuple->IsDefined())
+		g_printerr("replay_gain[track]: gain=%f peak=%f\n",
+			   tuple->gain, tuple->peak);
+}
+
+void
+decoder_mixramp(gcc_unused Decoder &decoder, gcc_unused MixRampInfo &&mix_ramp)
+{
+}
diff --git a/test/dump_playlist.cxx b/test/dump_playlist.cxx
index d11562930..4ac02985a 100644
--- a/test/dump_playlist.cxx
+++ b/test/dump_playlist.cxx
@@ -24,7 +24,6 @@
 #include "Directory.hxx"
 #include "InputStream.hxx"
 #include "ConfigGlobal.hxx"
-#include "DecoderAPI.hxx"
 #include "DecoderList.hxx"
 #include "InputInit.hxx"
 #include "IOThread.hxx"
@@ -53,88 +52,6 @@ my_log_func(const gchar *log_domain, gcc_unused GLogLevelFlags log_level,
 		g_printerr("%s\n", message);
 }
 
-void
-decoder_initialized(gcc_unused Decoder &decoder,
-		    gcc_unused const AudioFormat audio_format,
-		    gcc_unused bool seekable,
-		    gcc_unused float total_time)
-{
-}
-
-DecoderCommand
-decoder_get_command(gcc_unused Decoder &decoder)
-{
-	return DecoderCommand::NONE;
-}
-
-void
-decoder_command_finished(gcc_unused Decoder &decoder)
-{
-}
-
-double
-decoder_seek_where(gcc_unused Decoder &decoder)
-{
-	return 1.0;
-}
-
-void
-decoder_seek_error(gcc_unused Decoder &decoder)
-{
-}
-
-size_t
-decoder_read(gcc_unused Decoder *decoder,
-	     InputStream &is,
-	     void *buffer, size_t length)
-{
-	return is.LockRead(buffer, length, IgnoreError());
-}
-
-void
-decoder_timestamp(gcc_unused Decoder &decoder,
-		  gcc_unused double t)
-{
-}
-
-DecoderCommand
-decoder_data(gcc_unused Decoder &decoder,
-	     gcc_unused InputStream *is,
-	     const void *data, size_t datalen,
-	     gcc_unused uint16_t kbit_rate)
-{
-	gcc_unused ssize_t nbytes = write(1, data, datalen);
-	return DecoderCommand::NONE;
-}
-
-DecoderCommand
-decoder_tag(gcc_unused Decoder &decoder,
-	    gcc_unused InputStream *is,
-	    gcc_unused Tag &&tag)
-{
-	return DecoderCommand::NONE;
-}
-
-void
-decoder_replay_gain(gcc_unused Decoder &decoder,
-		    const ReplayGainInfo *rgi)
-{
-	const ReplayGainTuple *tuple = &rgi->tuples[REPLAY_GAIN_ALBUM];
-	if (tuple->IsDefined())
-		g_printerr("replay_gain[album]: gain=%f peak=%f\n",
-			   tuple->gain, tuple->peak);
-
-	tuple = &rgi->tuples[REPLAY_GAIN_TRACK];
-	if (tuple->IsDefined())
-		g_printerr("replay_gain[track]: gain=%f peak=%f\n",
-			   tuple->gain, tuple->peak);
-}
-
-void
-decoder_mixramp(gcc_unused Decoder &decoder, gcc_unused MixRampInfo &&mix_ramp)
-{
-}
-
 int main(int argc, char **argv)
 {
 	const char *uri;
diff --git a/test/read_tags.cxx b/test/read_tags.cxx
index 90f1424d9..52b2561b8 100644
--- a/test/read_tags.cxx
+++ b/test/read_tags.cxx
@@ -20,7 +20,7 @@
 #include "config.h"
 #include "IOThread.hxx"
 #include "DecoderList.hxx"
-#include "DecoderAPI.hxx"
+#include "DecoderPlugin.hxx"
 #include "InputInit.hxx"
 #include "InputStream.hxx"
 #include "AudioFormat.hxx"
@@ -42,79 +42,6 @@
 #include <locale.h>
 #endif
 
-void
-decoder_initialized(gcc_unused Decoder &decoder,
-		    gcc_unused const AudioFormat audio_format,
-		    gcc_unused bool seekable,
-		    gcc_unused float total_time)
-{
-}
-
-DecoderCommand
-decoder_get_command(gcc_unused Decoder &decoder)
-{
-	return DecoderCommand::NONE;
-}
-
-void
-decoder_command_finished(gcc_unused Decoder &decoder)
-{
-}
-
-double
-decoder_seek_where(gcc_unused Decoder &decoder)
-{
-	return 1.0;
-}
-
-void
-decoder_seek_error(gcc_unused Decoder &decoder)
-{
-}
-
-size_t
-decoder_read(gcc_unused Decoder *decoder,
-	     InputStream &is,
-	     void *buffer, size_t length)
-{
-	return is.LockRead(buffer, length, IgnoreError());
-}
-
-void
-decoder_timestamp(gcc_unused Decoder &decoder,
-		  gcc_unused double t)
-{
-}
-
-DecoderCommand
-decoder_data(gcc_unused Decoder &decoder,
-	     gcc_unused InputStream *is,
-	     const void *data, size_t datalen,
-	     gcc_unused uint16_t kbit_rate)
-{
-	gcc_unused ssize_t nbytes = write(1, data, datalen);
-	return DecoderCommand::NONE;
-}
-
-DecoderCommand
-decoder_tag(gcc_unused Decoder &decoder,
-	    gcc_unused InputStream *is,
-	    gcc_unused Tag &&tag)
-{
-	return DecoderCommand::NONE;
-}
-
-void
-decoder_replay_gain(gcc_unused Decoder &decoder,
-		    gcc_unused const ReplayGainInfo *replay_gain_info)
-{
-}
-
-void
-decoder_mixramp(gcc_unused Decoder &decoder, gcc_unused MixRampInfo &&mix_ramp)
-{
-}
-
 static bool empty = true;
 
 static void