diff --git a/axfer/test/Makefile.am b/axfer/test/Makefile.am
index 76a93bfc..0e48b9f2 100644
--- a/axfer/test/Makefile.am
+++ b/axfer/test/Makefile.am
@@ -1,10 +1,12 @@
 TESTS = \
-	container-test  \
-	mapper-test
+	container-test \
+	mapper-test \
+	vumeter-test
 
 check_PROGRAMS = \
 	container-test \
-	mapper-test
+	mapper-test \
+	vumeter-test
 
 container_test_SOURCES = \
 	../container.h \
@@ -29,3 +31,9 @@ mapper_test_SOURCES = \
 	../mapper-multiple.c \
 	generator.c \
 	mapper-test.c
+
+vumeter_test_SOURCES = \
+	../vumeter.h \
+	../vumeter.c \
+	generator.c \
+	vumeter-test.c
diff --git a/axfer/test/vumeter-test.c b/axfer/test/vumeter-test.c
new file mode 100644
index 00000000..41a19b13
--- /dev/null
+++ b/axfer/test/vumeter-test.c
@@ -0,0 +1,128 @@
+/*
+ * test-vumeter.c - A unit test for Volume Unit meter.
+ *
+ * Copyright (c) 2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "../vumeter.h"
+#include "../misc.h"
+
+#include "generator.h"
+
+#include <stdlib.h>
+
+static int callback(struct test_generator *gen, snd_pcm_access_t access,
+		    snd_pcm_format_t sample_format,
+		    unsigned int samples_per_frame, void *frame_buffer,
+		    unsigned int frame_count)
+{
+	static const enum vumeter_mode modes[] = {
+		[0] = VUMETER_MODE_MONO,
+		[1] = VUMETER_MODE_STEREO,
+	};
+	struct vumeter_context *vu = gen->private_data;
+	int i;
+	int err;
+
+	for (i = 0; i < ARRAY_SIZE(modes); ++i) {
+		int j;
+
+		memset(vu, 0, sizeof(*vu));
+		err = vumeter_context_init(vu, modes[i], access, sample_format,
+					   samples_per_frame, 4000);
+		if (err < 0)
+			break;
+		assert(vu->significant_bits_per_sample >= 0);
+		assert(vu->bytes_per_sample >= 0);
+		assert(vu->samples_per_frame >= 0);
+		assert(vu->frames_per_second >= 0);
+		assert(vu->max > 0);
+		assert(vu->peaks != NULL);
+		assert(vu->ratios != NULL);
+		assert(vu->max_ratios_in_second != NULL);
+		assert(vu->printer != NULL);
+		assert(vu->calculator != NULL);
+
+		vumeter_context_prepare(vu);
+		for (j = 0; j < samples_per_frame; ++j) {
+			assert(vu->ratios[j] == 0);
+			assert(vu->max_ratios_in_second[j] == 0);
+		}
+
+		vumeter_context_calculate(vu, frame_buffer, frame_count);
+		for (j = 0; j < samples_per_frame; ++j) {
+			assert(vu->peaks[j] <= vu->max);
+			assert(vu->max_ratios_in_second[j] <= vu->max);
+			assert(vu->max_ratios_in_second[j] <= 100);
+		}
+
+		vumeter_context_destroy(vu);
+		assert(vu->ratios == NULL);
+		assert(vu->max_ratios_in_second == NULL);
+		assert(vu->peaks == NULL);
+	}
+
+	return err;
+}
+
+int main(int argc, const char *argv[])
+{
+	struct test_generator gen = {0};
+	uint64_t access_mask;
+	uint64_t sample_format_mask;
+	int err;
+
+	access_mask = (1ul << SND_PCM_ACCESS_MMAP_INTERLEAVED) |
+		      (1ul << SND_PCM_ACCESS_MMAP_NONINTERLEAVED) |
+		      (1ul << SND_PCM_ACCESS_RW_INTERLEAVED) |
+		      (1ul << SND_PCM_ACCESS_RW_NONINTERLEAVED);
+	sample_format_mask = (1ul << SND_PCM_FORMAT_S8) |
+			     (1ul << SND_PCM_FORMAT_U8) |
+			     (1ul << SND_PCM_FORMAT_S16_LE) |
+			     (1ul << SND_PCM_FORMAT_S16_BE) |
+			     (1ul << SND_PCM_FORMAT_U16_LE) |
+			     (1ul << SND_PCM_FORMAT_U16_BE) |
+			     (1ul << SND_PCM_FORMAT_S24_LE) |
+			     (1ul << SND_PCM_FORMAT_S24_BE) |
+			     (1ul << SND_PCM_FORMAT_U24_LE) |
+			     (1ul << SND_PCM_FORMAT_U24_BE) |
+			     (1ul << SND_PCM_FORMAT_S32_LE) |
+			     (1ul << SND_PCM_FORMAT_S32_BE) |
+			     (1ul << SND_PCM_FORMAT_U32_LE) |
+			     (1ul << SND_PCM_FORMAT_U32_BE) |
+			     (1ul << SND_PCM_FORMAT_S24_3LE) |
+			     (1ul << SND_PCM_FORMAT_S24_3BE) |
+			     (1ul << SND_PCM_FORMAT_U24_3LE) |
+			     (1ul << SND_PCM_FORMAT_U24_3BE) |
+			     (1ul << SND_PCM_FORMAT_S24_3LE) |
+			     (1ul << SND_PCM_FORMAT_S24_3BE) |
+			     (1ul << SND_PCM_FORMAT_U24_3LE) |
+			     (1ul << SND_PCM_FORMAT_U24_3BE) |
+			     (1ul << SND_PCM_FORMAT_S20_3LE) |
+			     (1ul << SND_PCM_FORMAT_S20_3BE) |
+			     (1ul << SND_PCM_FORMAT_U20_3LE) |
+			     (1ul << SND_PCM_FORMAT_U20_3BE) |
+			     (1ul << SND_PCM_FORMAT_S18_3LE) |
+			     (1ul << SND_PCM_FORMAT_S18_3BE) |
+			     (1ul << SND_PCM_FORMAT_U18_3LE) |
+			     (1ul << SND_PCM_FORMAT_U18_3BE);
+
+	err = generator_context_init(&gen, access_mask, sample_format_mask,
+				     1, 128, 23, 4500, 1024,
+				     sizeof(struct vumeter_context));
+	if (err < 0)
+		return EXIT_FAILURE;
+
+	err = generator_context_run(&gen, callback);
+
+	generator_context_destroy(&gen);
+
+	if (err < 0) {
+		printf("%s\n", strerror(-err));
+		return EXIT_FAILURE;
+	}
+
+	return EXIT_SUCCESS;
+}
