shithub: aubio

Download patch

ref: 8968ea7d492dfc56d83b953ec68df093569f3ead
parent: c0ce78fc1b46449b7e03a0d77a2a4b453400d87d
parent: 2cf905ffab87a4fc1b85b1d6b301a5142a471531
author: Paul Brossier <piem@piem.org>
date: Thu Nov 29 14:19:52 EST 2018

Merge branch 'feature/c_tests'

--- /dev/null
+++ b/tests/create_tests_source.py
@@ -1,0 +1,42 @@
+#! /usr/bin/env python
+
+""" Create a simple stereo file containing a sine tone at 441 Hz, using only
+python's built-in modules. """
+
+import wave
+import math
+import struct
+
+
+def create_sine_wave(freq, samplerate, nframes, nchannels):
+    """ create a pure tone (without numpy) """
+    _x = [0.7 * math.sin(2. * math.pi * freq * t / float(samplerate))
+          for t in range(nframes)]
+    _x = [int(a * 32767) for a in _x]
+    _x = b''.join([b''.join([struct.pack('h', v)
+                             for _ in range(nchannels)])
+                   for v in _x])
+    return _x
+
+
+def create_test_sound(pathname, freq=441, duration=None,
+                      framerate=44100, nchannels=2):
+    """ create a sound file at pathname, overwriting exiting file """
+    sampwidth = 2
+    nframes = duration or framerate  # defaults to 1 second duration
+    fid = wave.open(pathname, 'w')
+    fid.setnchannels(nchannels)
+    fid.setsampwidth(sampwidth)
+    fid.setframerate(framerate)
+    fid.setnframes(nframes)
+    frames = create_sine_wave(freq, framerate, nframes, nchannels)
+    fid.writeframes(frames)
+    fid.close()
+    return 0
+
+
+if __name__ == '__main__':
+    import sys
+    if len(sys.argv) < 2:
+        sys.exit(2)
+    sys.exit(create_test_sound(sys.argv[1]))
--- a/tests/src/io/test-sink-multi.c
+++ b/tests/src/io/test-sink-multi.c
@@ -9,8 +9,8 @@
   sint_t err = 0;
 
   if (argc < 3) {
-    err = 2;
-    PRINT_ERR("not enough arguments\n");
+    PRINT_ERR("not enough arguments, running tests\n");
+    err = run_on_default_source_and_sink(main);
     PRINT_MSG("usage: %s <input_path> <output_path> [samplerate] [channels] [hop_size]\n", argv[0]);
     return err;
   }
--- a/tests/src/io/test-sink.c
+++ b/tests/src/io/test-sink.c
@@ -6,8 +6,8 @@
   sint_t err = 0;
 
   if (argc < 3) {
-    err = 2;
-    PRINT_ERR("not enough arguments\n");
+    PRINT_ERR("not enough arguments, running tests\n");
+    err = run_on_default_source_and_sink(main);
     PRINT_MSG("usage: %s <input_path> <output_path> [samplerate] [hop_size]\n", argv[0]);
     return err;
   }
--- a/tests/src/io/test-sink_apple_audio-multi.c
+++ b/tests/src/io/test-sink_apple_audio-multi.c
@@ -10,8 +10,8 @@
   sint_t err = 0;
 
   if (argc < 3) {
-    err = 2;
-    PRINT_ERR("not enough arguments\n");
+    PRINT_ERR("not enough arguments, running tests\n");
+    err = run_on_default_source_and_sink(main);
     PRINT_MSG("usage: %s <input_path> <output_path> [samplerate] [channels] [hop_size]\n", argv[0]);
     return err;
   }
@@ -71,7 +71,7 @@
   del_aubio_source(i);
 beach_source:
 #else /* HAVE_SINK_APPLE_AUDIO */
-  err = 3;
+  err = 0;
   PRINT_ERR("aubio was not compiled with aubio_sink_apple_audio\n");
 #endif /* HAVE_SINK_APPLE_AUDIO */
   return err;
--- a/tests/src/io/test-sink_apple_audio.c
+++ b/tests/src/io/test-sink_apple_audio.c
@@ -10,8 +10,8 @@
   sint_t err = 0;
 
   if (argc < 3) {
-    err = 2;
-    PRINT_ERR("not enough arguments\n");
+    PRINT_ERR("not enough arguments, running tests\n");
+    err = run_on_default_source_and_sink(main);
     PRINT_MSG("usage: %s <input_path> <output_path> [samplerate] [hop_size]\n", argv[0]);
     return err;
   }
@@ -60,7 +60,7 @@
   del_fvec(vec);
 beach_fvec:
 #else /* HAVE_SINK_APPLE_AUDIO */
-  err = 3;
+  err = 0;
   PRINT_ERR("aubio was not compiled with aubio_source_apple_audio\n");
 #endif /* HAVE_SINK_APPLE_AUDIO */
   return err;
--- a/tests/src/io/test-sink_sndfile-multi.c
+++ b/tests/src/io/test-sink_sndfile-multi.c
@@ -10,8 +10,8 @@
   sint_t err = 0;
 
   if (argc < 3) {
-    err = 2;
-    PRINT_ERR("not enough arguments\n");
+    PRINT_ERR("not enough arguments, running tests\n");
+    err = run_on_default_source_and_sink(main);
     PRINT_MSG("usage: %s <input_path> <output_path> [samplerate] [channels] [hop_size]\n", argv[0]);
     return err;
   }
@@ -71,7 +71,7 @@
   del_aubio_source(i);
 beach_source:
 #else
-  err = 3;
+  err = 0;
   PRINT_ERR("aubio was not compiled with aubio_sink_sndfile\n");
 #endif /* HAVE_SNDFILE */
   return err;
--- a/tests/src/io/test-sink_sndfile.c
+++ b/tests/src/io/test-sink_sndfile.c
@@ -10,8 +10,8 @@
   sint_t err = 0;
 
   if (argc < 3) {
-    err = 2;
-    PRINT_ERR("not enough arguments\n");
+    PRINT_ERR("not enough arguments, running tests\n");
+    err = run_on_default_source_and_sink(main);
     PRINT_MSG("usage: %s <input_path> <output_path> [samplerate] [hop_size]\n", argv[0]);
     return err;
   }
@@ -60,7 +60,7 @@
   del_fvec(vec);
 beach_fvec:
 #else
-  err = 3;
+  err = 0;
   PRINT_ERR("aubio was not compiled with aubio_source_sndfile\n");
 #endif /* HAVE_SNDFILE */
   return err;
--- a/tests/src/io/test-sink_wavwrite-multi.c
+++ b/tests/src/io/test-sink_wavwrite-multi.c
@@ -10,8 +10,8 @@
   sint_t err = 0;
 
   if (argc < 3) {
-    err = 2;
-    PRINT_ERR("not enough arguments\n");
+    PRINT_ERR("not enough arguments, running tests\n");
+    err = run_on_default_source_and_sink(main);
     PRINT_MSG("usage: %s <input_path> <output_path> [samplerate] [channels] [hop_size]\n", argv[0]);
     return err;
   }
@@ -71,7 +71,7 @@
   del_aubio_source(i);
 beach_source:
 #else
-  err = 3;
+  err = 0;
   PRINT_ERR("aubio was not compiled with aubio_sink_wavwrite\n");
 #endif /* HAVE_WAVWRITE */
   return err;
--- a/tests/src/io/test-sink_wavwrite.c
+++ b/tests/src/io/test-sink_wavwrite.c
@@ -10,8 +10,8 @@
   sint_t err = 0;
 
   if (argc < 3) {
-    err = 2;
-    PRINT_ERR("not enough arguments\n");
+    PRINT_ERR("not enough arguments, running tests\n");
+    err = run_on_default_source_and_sink(main);
     PRINT_MSG("usage: %s <input_path> <output_path> [samplerate] [hop_size]\n", argv[0]);
     return err;
   }
@@ -60,7 +60,7 @@
   del_fvec(vec);
 beach_fvec:
 #else
-  err = 3;
+  err = 0;
   PRINT_ERR("aubio was not compiled with aubio_sink_wavwrite\n");
 #endif /* HAVE_WAVWRITE */
   return err;
--- a/tests/src/io/test-source.c
+++ b/tests/src/io/test-source.c
@@ -5,8 +5,8 @@
 {
   uint_t err = 0;
   if (argc < 2) {
-    err = 2;
-    PRINT_ERR("not enough arguments\n");
+    PRINT_ERR("not enough arguments, running tests\n");
+    err = run_on_default_source(main);
     PRINT_MSG("read a wave file as a mono vector\n");
     PRINT_MSG("usage: %s <source_path> [samplerate] [hop_size]\n", argv[0]);
     PRINT_MSG("examples:\n");
@@ -22,8 +22,8 @@
   uint_t samplerate = 0;
   uint_t hop_size = 256;
   uint_t n_frames = 0, read = 0;
-  if ( argc == 3 ) samplerate = atoi(argv[2]);
-  if ( argc == 4 ) hop_size = atoi(argv[3]);
+  if ( argc >= 3 ) samplerate = atoi(argv[2]);
+  if ( argc >= 4 ) hop_size = atoi(argv[3]);
 
   char_t *source_path = argv[1];
 
--- a/tests/src/io/test-source_apple_audio.c
+++ b/tests/src/io/test-source_apple_audio.c
@@ -9,8 +9,8 @@
 {
   uint_t err = 0;
   if (argc < 2) {
-    err = 2;
-    PRINT_ERR("not enough arguments\n");
+    PRINT_ERR("not enough arguments, running tests\n");
+    err = run_on_default_source(main);
     PRINT_MSG("read a wave file as a mono vector\n");
     PRINT_MSG("usage: %s <source_path> [samplerate] [hop_size]\n", argv[0]);
     PRINT_MSG("examples:\n");
@@ -27,8 +27,8 @@
   uint_t samplerate = 0;
   uint_t hop_size = 256;
   uint_t n_frames = 0, read = 0;
-  if ( argc == 3 ) samplerate = atoi(argv[2]);
-  if ( argc == 4 ) hop_size = atoi(argv[3]);
+  if ( argc >= 3 ) samplerate = atoi(argv[2]);
+  if ( argc >= 4 ) hop_size = atoi(argv[3]);
 
   char_t *source_path = argv[1];
 
@@ -56,7 +56,7 @@
   del_aubio_source_apple_audio (s);
 beach:
 #else /* HAVE_SOURCE_APPLE_AUDIO */
-  err = 3;
+  err = 0;
   PRINT_ERR("aubio was not compiled with aubio_source_apple_audio\n");
 #endif /* HAVE_SOURCE_APPLE_AUDIO */
   return err;
--- a/tests/src/io/test-source_avcodec.c
+++ b/tests/src/io/test-source_avcodec.c
@@ -9,8 +9,8 @@
 {
   uint_t err = 0;
   if (argc < 2) {
-    err = 2;
-    PRINT_ERR("not enough arguments\n");
+    PRINT_ERR("not enough arguments, running tests\n");
+    err = run_on_default_source(main);
     PRINT_MSG("read a wave file as a mono vector\n");
     PRINT_MSG("usage: %s <source_path> [samplerate] [hop_size]\n", argv[0]);
     PRINT_MSG("examples:\n");
@@ -27,8 +27,8 @@
   uint_t samplerate = 0;
   uint_t hop_size = 256;
   uint_t n_frames = 0, read = 0;
-  if ( argc == 3 ) samplerate = atoi(argv[2]);
-  if ( argc == 4 ) hop_size = atoi(argv[3]);
+  if ( argc >= 3 ) samplerate = atoi(argv[2]);
+  if ( argc >= 4 ) hop_size = atoi(argv[3]);
 
   char_t *source_path = argv[1];
 
@@ -56,7 +56,7 @@
   del_aubio_source_avcodec (s);
 beach:
 #else /* HAVE_LIBAV */
-  err = 3;
+  err = 0;
   PRINT_ERR("aubio was not compiled with aubio_source_avcodec\n");
 #endif /* HAVE_LIBAV */
   return err;
--- a/tests/src/io/test-source_multi.c
+++ b/tests/src/io/test-source_multi.c
@@ -5,8 +5,8 @@
 {
   sint_t err = 0;
   if (argc < 2) {
-    err = -2;
-    PRINT_ERR("not enough arguments\n");
+    PRINT_ERR("not enough arguments, running tests\n");
+    err = run_on_default_source(main);
     PRINT_MSG("read a wave file as a mono vector\n");
     PRINT_MSG("usage: %s <source_path> [samplerate] [hop_size]\n", argv[0]);
     PRINT_MSG("examples:\n");
--- a/tests/src/io/test-source_seek.c
+++ b/tests/src/io/test-source_seek.c
@@ -5,8 +5,8 @@
 {
   uint_t err = 0;
   if (argc < 2) {
-    err = 2;
-    PRINT_ERR("not enough arguments\n");
+    PRINT_ERR("not enough arguments, running tests\n");
+    err = run_on_default_source(main);
     PRINT_MSG("read a wave file as a mono vector\n");
     PRINT_MSG("usage: %s <source_path> [samplerate] [hop_size]\n", argv[0]);
     PRINT_MSG("examples:\n");
@@ -23,8 +23,8 @@
   uint_t hop_size = 256;
   uint_t n_frames = 0, read = 0;
   uint_t old_n_frames_1 = 0, old_n_frames_2 = 0, old_n_frames_3 = 0;
-  if ( argc == 3 ) samplerate = atoi(argv[2]);
-  if ( argc == 4 ) hop_size = atoi(argv[3]);
+  if ( argc >= 3 ) samplerate = atoi(argv[2]);
+  if ( argc >= 4 ) hop_size = atoi(argv[3]);
 
   char_t *source_path = argv[1];
 
--- a/tests/src/io/test-source_sndfile.c
+++ b/tests/src/io/test-source_sndfile.c
@@ -9,8 +9,8 @@
 {
   uint_t err = 0;
   if (argc < 2) {
-    err = 2;
-    PRINT_ERR("not enough arguments\n");
+    PRINT_ERR("not enough arguments, running tests\n");
+    err = run_on_default_source(main);
     PRINT_MSG("read a wave file as a mono vector\n");
     PRINT_MSG("usage: %s <source_path> [samplerate] [hop_size]\n", argv[0]);
     PRINT_MSG("examples:\n");
@@ -27,8 +27,8 @@
   uint_t samplerate = 0;
   uint_t hop_size = 256;
   uint_t n_frames = 0, read = 0;
-  if ( argc == 3 ) samplerate = atoi(argv[2]);
-  if ( argc == 4 ) hop_size = atoi(argv[3]);
+  if ( argc >= 3 ) samplerate = atoi(argv[2]);
+  if ( argc >= 4 ) hop_size = atoi(argv[3]);
 
   char_t *source_path = argv[1];
 
@@ -56,7 +56,7 @@
   del_aubio_source_sndfile (s);
 beach:
 #else
-  err = 3;
+  err = 0;
   PRINT_ERR("aubio was not compiled with aubio_source_sndfile\n");
 #endif /* HAVE_SNDFILE */
   return err;
--- a/tests/src/io/test-source_wavread.c
+++ b/tests/src/io/test-source_wavread.c
@@ -9,8 +9,8 @@
 {
   uint_t err = 0;
   if (argc < 2) {
-    err = 2;
-    PRINT_ERR("not enough arguments\n");
+    PRINT_ERR("not enough arguments, running tests\n");
+    err = run_on_default_source(main);
     PRINT_MSG("read a wave file as a mono vector\n");
     PRINT_MSG("usage: %s <source_path> [samplerate] [hop_size]\n", argv[0]);
     PRINT_MSG("examples:\n");
@@ -27,8 +27,8 @@
   uint_t samplerate = 0;
   uint_t hop_size = 256;
   uint_t n_frames = 0, read = 0;
-  if ( argc == 3 ) samplerate = atoi(argv[2]);
-  if ( argc == 4 ) hop_size = atoi(argv[3]);
+  if ( argc >= 3 ) samplerate = atoi(argv[2]);
+  if ( argc >= 4 ) hop_size = atoi(argv[3]);
 
   char_t *source_path = argv[1];
 
@@ -35,7 +35,6 @@
 
   aubio_source_wavread_t * s =
     new_aubio_source_wavread(source_path, samplerate, hop_size);
-
   if (!s) { err = 1; goto beach; }
   fvec_t *vec = new_fvec(hop_size);
 
@@ -57,7 +56,7 @@
   del_aubio_source_wavread (s);
 beach:
 #else
-  err = 3;
+  err = 0;
   PRINT_ERR("aubio was not compiled with aubio_source_wavread\n");
 #endif /* HAVE_WAVREAD */
   return err;
--- a/tests/src/onset/test-onset.c
+++ b/tests/src/onset/test-onset.c
@@ -9,12 +9,7 @@
   if (argc < 2) {
     err = 2;
     PRINT_WRN("no arguments, running tests\n");
-    if (test_wrong_params() != 0) {
-      PRINT_ERR("tests failed!\n");
-      err = 1;
-    } else {
-      err = 0;
-    }
+    err = test_wrong_params();
     PRINT_MSG("usage: %s <source_path> [samplerate] [hop_size]\n", argv[0]);
     return err;
   }
@@ -22,8 +17,8 @@
   uint_t win_s = 1024; // window size
   uint_t hop_size = win_s / 4;
   uint_t n_frames = 0, read = 0;
-  if ( argc == 3 ) samplerate = atoi(argv[2]);
-  if ( argc == 4 ) hop_size = atoi(argv[3]);
+  if ( argc >= 3 ) samplerate = atoi(argv[2]);
+  if ( argc >= 4 ) hop_size = atoi(argv[3]);
 
   char_t *source_path = argv[1];
   aubio_source_t * source = new_aubio_source(source_path, samplerate, hop_size);
@@ -89,15 +84,17 @@
   // specdesc creation failed
   if (new_aubio_onset("abcd", win_size, win_size/2, samplerate))
     return 1;
-  // pv creation failed
-  if (new_aubio_onset("default", 5, 2, samplerate))
-    return 1;
 
   aubio_onset_t *o;
+
+  // pv creation might fail
+  o = new_aubio_onset("default", 5, 2, samplerate);
+  if (o) del_aubio_onset(o);
+
   o = new_aubio_onset("default", win_size, hop_size, samplerate);
   if (!aubio_onset_set_default_parameters(o, "wrong_type"))
     return 1;
   del_aubio_onset(o);
 
-  return 0;
+  return run_on_default_source(main);
 }
--- a/tests/src/spectral/test-awhitening.c
+++ b/tests/src/spectral/test-awhitening.c
@@ -10,12 +10,7 @@
   if (argc < 3) {
     err = 2;
     PRINT_WRN("no arguments, running tests\n");
-    if (test_wrong_params() != 0) {
-      PRINT_ERR("tests failed!\n");
-      err = 1;
-    } else {
-      err = 0;
-    }
+    err = test_wrong_params();
     PRINT_MSG("usage: %s <input_path> <output_path> [samplerate] [hop_size]\n", argv[0]);
     return err;
   }
@@ -30,11 +25,6 @@
 
   if ( argc >= 4 ) samplerate = atoi(argv[3]);
   if ( argc >= 5 ) hop_size = atoi(argv[4]);
-  if ( argc >= 6 ) {
-    err = 2;
-    PRINT_ERR("too many arguments\n");
-    return err;
-  }
 
   fvec_t *vec = new_fvec(hop_size);
   fvec_t *out = new_fvec(hop_size); // output buffer
@@ -108,5 +98,5 @@
 
   del_aubio_spectral_whitening(o);
 
-  return 0;
+  return run_on_default_source_and_sink(main);
 }
--- a/tests/src/spectral/test-dct.c
+++ b/tests/src/spectral/test-dct.c
@@ -32,9 +32,7 @@
     aubio_dct_do (dct, in, dctout);
     aubio_dct_rdo (dct, dctout, out);
     for (j = 0; j < in->length; j++) {
-      if (fabsf(in->data[j] - out->data[j]) > 10.e-4) {
-        fprintf(stderr, "dct reconstruction failed\n");
-      }
+      return_code += (fabsf(in->data[j] - out->data[j]) > 10.e-4);
     }
   }
 
--- a/tests/src/synth/test-sampler.c
+++ b/tests/src/synth/test-sampler.c
@@ -1,3 +1,5 @@
+#include <string.h> // strncpy
+#include <limits.h> // PATH_MAX
 #include <aubio.h>
 #include "utils_tests.h"
 
@@ -5,9 +7,9 @@
 {
   sint_t err = 0;
 
-  if (argc < 4) {
-    err = 2;
-    PRINT_ERR("not enough arguments\n");
+  if (argc < 3) {
+    PRINT_ERR("not enough arguments, running tests\n");
+    err = run_on_default_source_and_sink(main);
     PRINT_MSG("usage: %s <input_path> <output_path> <sample_path> [samplerate]\n", argv[0]);
     return err;
   }
@@ -18,8 +20,15 @@
 
   char_t *source_path = argv[1];
   char_t *sink_path = argv[2];
-  char_t *sample_path = argv[3];
-  if ( argc == 5 ) samplerate = atoi(argv[4]);
+  char_t sample_path[PATH_MAX];
+  if ( argc >= 4 ) {
+    strncpy(sample_path, argv[3], PATH_MAX - 1);
+  } else {
+    // use input_path as sample
+    strncpy(sample_path, source_path, PATH_MAX - 1);
+  }
+  sample_path[PATH_MAX - 1] = '\0';
+  if ( argc >= 5 ) samplerate = atoi(argv[4]);
 
   fvec_t *vec = new_fvec(hop_size);
   aubio_source_t *source = new_aubio_source(source_path, samplerate, hop_size);
--- a/tests/src/synth/test-wavetable.c
+++ b/tests/src/synth/test-wavetable.c
@@ -6,8 +6,8 @@
   sint_t err = 0;
 
   if (argc < 2) {
-    err = 2;
-    PRINT_ERR("not enough arguments\n");
+    PRINT_ERR("not enough arguments, running tests\n");
+    err = run_on_default_sink(main);
     PRINT_MSG("usage: %s <output_path> [freq] [samplerate]\n", argv[0]);
     return err;
   }
@@ -17,8 +17,8 @@
   smpl_t freq = 440.;
 
   char_t *sink_path = argv[1];
-  if ( argc == 4 ) samplerate = atoi(argv[3]);
-  if ( argc == 3 ) freq = atof(argv[2]);
+  if ( argc >= 4 ) samplerate = atoi(argv[3]);
+  if ( argc >= 3 ) freq = atof(argv[2]);
 
   fvec_t *vec = new_fvec(hop_size);
   aubio_sink_t *sink = new_aubio_sink(sink_path, samplerate);
--- a/tests/src/tempo/test-tempo.c
+++ b/tests/src/tempo/test-tempo.c
@@ -7,14 +7,8 @@
 {
   uint_t err = 0;
   if (argc < 2) {
-    err = 2;
     PRINT_WRN("no arguments, running tests\n");
-    if (test_wrong_params() != 0) {
-      PRINT_ERR("tests failed!\n");
-      err = 1;
-    } else {
-      err = 0;
-    }
+    err = test_wrong_params();
     PRINT_MSG("usage: %s <source_path> [samplerate] [win_size] [hop_size]\n",
         argv[0]);
     return err;
@@ -135,5 +129,5 @@
   del_fvec(in);
   del_fvec(out);
 
-  return 0;
+  return run_on_default_source(main);
 }
--- a/tests/src/test-mathutils.c
+++ b/tests/src/test-mathutils.c
@@ -102,7 +102,7 @@
   fvec_print(window);
 
   window_size /= 2.;
-  window = new_aubio_window("triangle", window_size);
+  window = new_aubio_window("parzen", window_size);
   fvec_print(window);
   del_fvec(window);
 
@@ -116,5 +116,6 @@
   test_next_power_of_two();
   test_miditofreq();
   test_freqtomidi();
+  test_aubio_window();
   return 0;
 }
--- a/tests/utils_tests.h
+++ b/tests/utils_tests.h
@@ -5,6 +5,36 @@
 #include <assert.h>
 #include "config.h"
 
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> // unlink, close
+#endif
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h> // PATH_MAX
+#endif /* HAVE_LIMITS_H */
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+#if defined(HAVE_WIN_HACKS) && !defined(__GNUC__)
+#include <io.h> // _access
+#endif
+
+// This macro is used to pass a string to msvc compiler: since msvc's -D flag
+// strips the quotes, we define the string without quotes and re-add them with
+// this macro.
+
+#define REDEFINESTRING(x) #x
+#define DEFINEDSTRING(x) REDEFINESTRING(x)
+
+#ifndef AUBIO_TESTS_SOURCE
+#error "AUBIO_TESTS_SOURCE is not defined"
+#endif
+
 #ifdef HAVE_C99_VARARGS_MACROS
 #define PRINT_ERR(...)   fprintf(stderr, "AUBIO-TESTS ERROR: " __VA_ARGS__)
 #define PRINT_MSG(...)   fprintf(stdout, __VA_ARGS__)
@@ -47,7 +77,113 @@
 void utils_init_random (void) {
   time_t now = time(0);
   struct tm *tm_struct = localtime(&now);
-  int seed = tm_struct->tm_sec;
+  size_t **tm_address = (void*)&tm_struct;
+  int seed = tm_struct->tm_sec + (size_t)tm_address;
   //PRINT_WRN("current seed: %d\n", seed);
-  srandom (seed);
+  srandom ((unsigned int)seed);
+}
+
+// create_temp_sink / close_temp_sink
+#if defined(__GNUC__) // mkstemp
+
+int check_source(char *source_path)
+{
+  return access(source_path, R_OK);
+}
+
+int create_temp_sink(char *sink_path)
+{
+  return mkstemp(sink_path);
+}
+
+int close_temp_sink(char *sink_path, int sink_fildes)
+{
+  int err;
+  if ((err = close(sink_fildes)) != 0) return err;
+  if ((err = unlink(sink_path)) != 0) return err;
+  return err;
+}
+
+#elif defined(HAVE_WIN_HACKS) //&& !defined(__GNUC__)
+// windows workaround, where mkstemp does not exist...
+
+int check_source(char *source_path)
+{
+  return _access(source_path, 04);
+}
+
+int create_temp_sink(char *templ)
+{
+  int i = 0;
+  static const char letters[] = "abcdefg0123456789";
+  int letters_len = strlen(letters);
+  int templ_len = strlen(templ);
+  if (templ_len == 0) return 0;
+  utils_init_random();
+  for (i = 0; i < 6; i++)
+  {
+    templ[templ_len - i] = letters[rand() % letters_len];
+  }
+  return 1;
+}
+
+int close_temp_sink(char* sink_path, int sink_fildes) {
+  // the file should be closed when not using mkstemp, no need to open it
+  if (sink_fildes == 0) return 1;
+  return _unlink(sink_path);
+}
+
+#else // windows workaround
+// otherwise, we don't really know what to do yet
+#error "mkstemp undefined, but not on windows. additional workaround required."
+#endif
+
+// pass progname / default
+int run_on_default_source( int main(int, char**) )
+{
+  const int argc = 2;
+  int err = 0;
+  char** argv = (char**)calloc(argc, sizeof(char*));
+  argv[0] = __FILE__;
+  argv[1] = DEFINEDSTRING(AUBIO_TESTS_SOURCE);
+  // check if the file can be read
+  if ( check_source(argv[1]) ) return 1;
+  err = main(argc, argv);
+  if (argv) free(argv);
+  return err;
+}
+
+int run_on_default_sink( int main(int, char**) )
+{
+  const int argc = 2;
+  int err = 0;
+  char** argv = (char**)calloc(argc, sizeof(char*));
+  char sink_path[PATH_MAX] = "tmp_aubio_XXXXXX";
+  int fd = create_temp_sink(sink_path);
+  if (!fd) return 1;
+  argv[0] = __FILE__;
+  argv[1] = sink_path;
+  err = main(argc, argv);
+  close_temp_sink(sink_path, fd);
+  if (argv) free(argv);
+  return err;
+}
+
+int run_on_default_source_and_sink( int main(int, char**) )
+{
+  const int argc = 3;
+  int err = 0;
+  char** argv = (char**)calloc(argc, sizeof(char*));
+  char sink_path[PATH_MAX] = "tmp_aubio_XXXXXX";
+  int fd = create_temp_sink(sink_path);
+  if (!fd) return 1;
+  argv[0] = __FILE__;
+  argv[1] = DEFINEDSTRING(AUBIO_TESTS_SOURCE);
+  argv[2] = sink_path;
+  // check if the file can be read
+  if ( check_source(argv[1]) ) return 1;
+  err = main(argc, argv);
+  close_temp_sink(sink_path, fd);
+  if (argv) free(argv);
+  return err;
 }
--- a/tests/wscript_build
+++ b/tests/wscript_build
@@ -7,13 +7,30 @@
 includes = ['../src', '.']
 programs_sources = ctx.path.ant_glob('src/**/*.c')
 
+test_sound_target = '44100Hz_44100f_sine441_stereo.wav'
+test_sound_abspath = bld.path.get_bld().make_node(test_sound_target)
+# workaround to double escape backslash characters on windows
+test_sound_abspath = str(test_sound_abspath).replace('\\', '\\\\')
+
+b = bld(name='create_tests_source',
+    rule='python ${SRC} ${TGT}',
+    source='create_tests_source.py',
+    target=test_sound_target)
+# use post() to create the task, keep a reference to it
+b.post()
+create_tests_source = b.tasks[0]
+
 for source_file in programs_sources:
     target = os.path.basename(os.path.splitext(str(source_file))[0])
-    bld(features = 'c cprogram test',
+    a = bld(features = 'c cprogram test',
             source = source_file,
             target = target,
             includes = includes,
             use = uselib,
             install_path = None,
-            defines = 'AUBIO_UNSTABLE_API=1',
+            defines = ['AUBIO_UNSTABLE_API=1',
+                        'AUBIO_TESTS_SOURCE={}'.format(test_sound_abspath)]
        )
+    a.post()
+    # make sure the unit_test task runs *after* the source is created
+    a.tasks[-1].set_run_after(create_tests_source)
--- a/wscript
+++ b/wscript
@@ -517,6 +517,10 @@
     doxygen(bld)
     sphinx(bld)
 
+    from waflib.Tools import waf_unit_test
+    bld.add_post_fun(waf_unit_test.summary)
+    bld.add_post_fun(waf_unit_test.set_exit_code)
+
 def txt2man(bld):
     # build manpages from txt files using txt2man
     if bld.env['TXT2MAN']: