2017-08-05 21:02:09 +02:00
/** Unit tests for IO formats.
*
* @ author Steffen Vogel < stvogel @ eonerc . rwth - aachen . de >
* @ copyright 2017 , Institute for Automation of Complex Power Systems , EONERC
* @ license GNU General Public License ( version 3 )
*
* VILLASnode
*
* 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 3 of the License , or
* 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 , see < http : //www.gnu.org/licenses/>.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include <stdio.h>
2017-08-14 14:42:07 +02:00
# include <float.h>
2017-08-05 21:02:09 +02:00
# include <criterion/criterion.h>
# include <criterion/parameterized.h>
# include <criterion/logging.h>
2018-03-26 12:50:15 +02:00
# include <villas/utils.h>
# include <villas/timing.h>
# include <villas/sample.h>
2018-08-20 18:32:10 +02:00
# include <villas/signal.h>
2018-03-26 12:50:15 +02:00
# include <villas/plugin.h>
# include <villas/pool.h>
# include <villas/io.h>
2018-05-12 13:56:12 +02:00
# include <villas/formats/raw.h>
2017-08-05 21:02:09 +02:00
2018-08-20 18:32:10 +02:00
extern void init_memory ( ) ;
2017-08-05 21:02:09 +02:00
# define NUM_VALUES 10
2018-08-20 18:32:10 +02:00
struct param {
const char * fmt ;
int cnt ;
int bits ;
} ;
static struct param params [ ] = {
{ " gtnet " , 1 , 32 } ,
{ " gtnet.fake " , 1 , 32 } ,
{ " raw.8 " , 1 , 8 } ,
{ " raw.16.be " , 1 , 16 } ,
{ " raw.16.le " , 1 , 16 } ,
{ " raw.32.be " , 1 , 32 } ,
{ " raw.32.le " , 1 , 32 } ,
{ " raw.64.be " , 1 , 64 } ,
{ " raw.64.le " , 1 , 64 } ,
{ " villas.human " , 10 , 0 } ,
{ " villas.binary " , 10 , 0 } ,
{ " csv " , 10 , 0 } ,
{ " json " , 10 , 0 } ,
2018-06-25 06:00:34 +02:00
# ifdef LIBPROTOBUF_FOUND
2018-08-20 18:32:10 +02:00
{ " protobuf " , 10 , 0 }
2017-08-23 15:45:28 +02:00
# endif
} ;
2018-08-20 18:32:10 +02:00
void fill_sample_data ( struct list * signals , struct sample * smps [ ] , unsigned cnt )
2017-08-05 21:02:09 +02:00
{
2018-03-26 12:50:30 +02:00
struct timespec delta , now ;
2017-08-23 15:45:28 +02:00
2018-03-26 12:50:30 +02:00
now = time_now ( ) ;
delta = time_from_double ( 50e-6 ) ;
2017-08-23 15:45:28 +02:00
for ( int i = 0 ; i < cnt ; i + + ) {
2018-08-20 18:32:10 +02:00
smps [ i ] - > flags = SAMPLE_HAS_SEQUENCE | SAMPLE_HAS_DATA | SAMPLE_HAS_TS_ORIGIN ;
smps [ i ] - > length = list_length ( signals ) ;
2017-08-23 15:45:28 +02:00
smps [ i ] - > sequence = 235 + i ;
2018-03-26 12:50:30 +02:00
smps [ i ] - > ts . origin = now ;
2018-08-20 18:32:10 +02:00
smps [ i ] - > signals = signals ;
2017-08-23 15:45:28 +02:00
2018-08-20 18:32:10 +02:00
for ( int j = 0 ; j < list_length ( signals ) ; j + + ) {
struct signal * sig = ( struct signal * ) list_at ( signals , j ) ;
switch ( sig - > type ) {
case SIGNAL_TYPE_BOOLEAN :
smps [ i ] - > data [ j ] . b = j * 0.1 + i * 100 ;
break ;
case SIGNAL_TYPE_COMPLEX :
smps [ i ] - > data [ j ] . z = CMPLXF ( j * 0.1 , i * 100 ) ;
break ;
case SIGNAL_TYPE_FLOAT :
smps [ i ] - > data [ j ] . f = j * 0.1 + i * 100 ;
break ;
case SIGNAL_TYPE_INTEGER :
smps [ i ] - > data [ j ] . i = j + i * 1000 ;
break ;
default : { }
}
2018-03-26 12:50:30 +02:00
}
now = time_add ( & now , & delta ) ;
}
}
2018-08-20 18:32:10 +02:00
void cr_assert_eq_sample ( struct sample * a , struct sample * b , int flags )
2018-03-26 12:50:30 +02:00
{
cr_assert_eq ( a - > length , b - > length ) ;
2018-08-20 18:32:10 +02:00
if ( flags & SAMPLE_HAS_SEQUENCE )
cr_assert_eq ( a - > sequence , b - > sequence ) ;
2018-03-26 12:50:30 +02:00
2018-08-20 18:32:10 +02:00
if ( flags & SAMPLE_HAS_TS_ORIGIN ) {
cr_assert_eq ( a - > ts . origin . tv_sec , b - > ts . origin . tv_sec ) ;
cr_assert_eq ( a - > ts . origin . tv_nsec , b - > ts . origin . tv_nsec ) ;
}
if ( flags & SAMPLE_HAS_DATA ) {
for ( int j = 0 ; j < MIN ( a - > length , b - > length ) ; j + + ) {
cr_assert_eq ( sample_format ( a , j ) , sample_format ( b , j ) ) ;
switch ( sample_format ( b , j ) ) {
case SIGNAL_TYPE_FLOAT :
cr_assert_float_eq ( a - > data [ j ] . f , b - > data [ j ] . f , 1e-3 , " Sample data mismatch at index %d: %f != %f " , j , a - > data [ j ] . f , b - > data [ j ] . f ) ;
break ;
case SIGNAL_TYPE_INTEGER :
cr_assert_eq ( a - > data [ j ] . i , b - > data [ j ] . i , " Sample data mismatch at index %d: %lld != %lld " , j , a - > data [ j ] . i , b - > data [ j ] . i ) ;
break ;
2018-03-26 12:50:30 +02:00
2018-08-20 18:32:10 +02:00
case SIGNAL_TYPE_BOOLEAN :
cr_assert_eq ( a - > data [ j ] . b , b - > data [ j ] . b , " Sample data mismatch at index %d: %s != %s " , j , a - > data [ j ] . b ? " true " : " false " , b - > data [ j ] . b ? " true " : " false " ) ;
break ;
case SIGNAL_TYPE_COMPLEX :
cr_assert_float_eq ( cabs ( a - > data [ j ] . z - b - > data [ j ] . z ) , 0 , 1e-6 , " Sample data mismatch at index %d: %f+%fi != %f+%fi " , j , creal ( a - > data [ j ] . z ) , cimag ( a - > data [ j ] . z ) , creal ( b - > data [ j ] . z ) , cimag ( b - > data [ j ] . z ) ) ;
break ;
default : { }
}
2018-03-26 12:50:30 +02:00
}
2017-08-23 15:45:28 +02:00
}
}
2018-08-20 18:32:10 +02:00
void cr_assert_eq_sample_raw ( struct sample * a , struct sample * b , int flags , int bits )
2017-08-23 15:45:28 +02:00
{
2018-08-20 18:32:10 +02:00
cr_assert_eq ( a - > length , b - > length ) ;
2017-08-23 15:45:28 +02:00
2018-08-20 18:32:10 +02:00
if ( flags & SAMPLE_HAS_SEQUENCE )
cr_assert_eq ( a - > sequence , b - > sequence ) ;
2017-08-23 15:45:28 +02:00
2018-08-20 18:32:10 +02:00
if ( flags & SAMPLE_HAS_TS_ORIGIN ) {
cr_assert_eq ( a - > ts . origin . tv_sec , b - > ts . origin . tv_sec ) ;
cr_assert_eq ( a - > ts . origin . tv_nsec , b - > ts . origin . tv_nsec ) ;
}
if ( flags & SAMPLE_HAS_DATA ) {
for ( int j = 0 ; j < MIN ( a - > length , b - > length ) ; j + + ) {
cr_assert_eq ( sample_format ( a , j ) , sample_format ( b , j ) ) ;
switch ( sample_format ( b , j ) ) {
case SIGNAL_TYPE_FLOAT :
if ( bits ! = 8 & & bits ! = 16 )
cr_assert_float_eq ( a - > data [ j ] . f , b - > data [ j ] . f , 1e-3 , " Sample data mismatch at index %d: %f != %f " , j , a - > data [ j ] . f , b - > data [ j ] . f ) ;
break ;
case SIGNAL_TYPE_INTEGER :
cr_assert_eq ( a - > data [ j ] . i , b - > data [ j ] . i , " Sample data mismatch at index %d: %lld != %lld " , j , a - > data [ j ] . i , b - > data [ j ] . i ) ;
break ;
case SIGNAL_TYPE_BOOLEAN :
cr_assert_eq ( a - > data [ j ] . b , b - > data [ j ] . b , " Sample data mismatch at index %d: %s != %s " , j , a - > data [ j ] . b ? " true " : " false " , b - > data [ j ] . b ? " true " : " false " ) ;
break ;
case SIGNAL_TYPE_COMPLEX :
if ( bits ! = 8 & & bits ! = 16 )
cr_assert_float_eq ( cabs ( a - > data [ j ] . z - b - > data [ j ] . z ) , 0 , 1e-6 , " Sample data mismatch at index %d: %f+%fi != %f+%fi " , j , creal ( a - > data [ j ] . z ) , cimag ( a - > data [ j ] . z ) , creal ( b - > data [ j ] . z ) , cimag ( b - > data [ j ] . z ) ) ;
break ;
default : { }
2017-08-23 15:45:28 +02:00
}
}
}
2017-08-05 21:02:09 +02:00
}
2017-08-23 15:45:28 +02:00
ParameterizedTestParameters ( io , lowlevel )
2017-08-05 21:02:09 +02:00
{
2018-08-20 18:32:10 +02:00
return cr_make_param_array ( struct param , params , ARRAY_LEN ( params ) ) ;
2017-08-23 15:45:28 +02:00
}
2017-08-05 21:02:09 +02:00
2018-08-20 18:32:10 +02:00
ParameterizedTest ( struct param * p , io , lowlevel , . init = init_memory )
2017-08-23 15:45:28 +02:00
{
2018-08-20 18:32:10 +02:00
int ret , cnt ;
2017-08-23 15:45:28 +02:00
char buf [ 8192 ] ;
size_t wbytes , rbytes ;
2017-09-04 14:30:07 +02:00
2018-05-12 13:56:12 +02:00
struct format_type * f ;
2017-08-23 15:45:28 +02:00
2018-08-20 18:32:10 +02:00
struct pool pool = { . state = STATE_DESTROYED } ;
2018-05-13 12:55:31 +02:00
struct io io = { . state = STATE_DESTROYED } ;
2018-08-20 18:32:10 +02:00
struct list signals = { . state = STATE_DESTROYED } ;
struct sample * smps [ p - > cnt ] ;
struct sample * smpt [ p - > cnt ] ;
2017-08-23 15:45:28 +02:00
2018-08-20 18:32:10 +02:00
ret = pool_init ( & pool , 2 * p - > cnt , SAMPLE_LENGTH ( NUM_VALUES ) , & memory_hugepage ) ;
2017-08-23 15:45:28 +02:00
cr_assert_eq ( ret , 0 ) ;
2018-08-20 18:32:10 +02:00
info ( " Running test for format=%s, cnt=%u " , p - > fmt , p - > cnt ) ;
list_init ( & signals ) ;
signal_list_generate ( & signals , NUM_VALUES , SIGNAL_TYPE_FLOAT ) ;
ret = sample_alloc_many ( & pool , smps , p - > cnt ) ;
cr_assert_eq ( ret , p - > cnt ) ;
2018-03-26 12:50:30 +02:00
2018-08-20 18:32:10 +02:00
ret = sample_alloc_many ( & pool , smpt , p - > cnt ) ;
cr_assert_eq ( ret , p - > cnt ) ;
2017-09-04 14:30:07 +02:00
2018-08-20 18:32:10 +02:00
fill_sample_data ( & signals , smps , p - > cnt ) ;
2017-09-04 14:30:07 +02:00
2018-08-20 18:32:10 +02:00
f = format_type_lookup ( p - > fmt ) ;
cr_assert_not_null ( f , " Format '%s' does not exist " , p - > fmt ) ;
ret = io_init ( & io , f , & signals , SAMPLE_HAS_ALL ) ;
2018-05-12 15:25:47 +02:00
cr_assert_eq ( ret , 0 ) ;
2018-08-20 18:32:10 +02:00
ret = io_check ( & io ) ;
cr_assert_eq ( ret , 0 ) ;
2017-09-04 14:30:07 +02:00
2018-08-20 18:32:10 +02:00
cnt = io_sprint ( & io , buf , sizeof ( buf ) , & wbytes , smps , p - > cnt ) ;
cr_assert_eq ( cnt , p - > cnt , " Written only %d of %d samples for format %s " , cnt , p - > cnt , format_type_name ( f ) ) ;
2017-09-04 14:30:07 +02:00
2018-08-20 18:32:10 +02:00
cnt = io_sscan ( & io , buf , wbytes , & rbytes , smpt , p - > cnt ) ;
cr_assert_eq ( cnt , p - > cnt , " Read only %d of %d samples back for format %s " , cnt , p - > cnt , format_type_name ( f ) ) ;
2017-09-04 14:30:07 +02:00
2018-08-20 18:32:10 +02:00
cr_assert_eq ( rbytes , wbytes , " rbytes != wbytes: %#zx != %#zx " , rbytes , wbytes ) ;
2017-08-23 15:45:28 +02:00
2018-08-20 18:32:10 +02:00
for ( int i = 0 ; i < cnt ; i + + ) {
if ( p - > bits )
cr_assert_eq_sample_raw ( smps [ i ] , smpt [ i ] , f - > flags , p - > bits ) ;
else
cr_assert_eq_sample ( smps [ i ] , smpt [ i ] , f - > flags ) ;
}
sample_free_many ( smps , p - > cnt ) ;
sample_free_many ( smpt , p - > cnt ) ;
ret = pool_destroy ( & pool ) ;
2017-08-23 15:45:28 +02:00
cr_assert_eq ( ret , 0 ) ;
}
ParameterizedTestParameters ( io , highlevel )
{
2018-08-20 18:32:10 +02:00
return cr_make_param_array ( struct param , params , ARRAY_LEN ( params ) ) ;
2017-08-05 21:02:09 +02:00
}
2018-08-20 18:32:10 +02:00
ParameterizedTest ( struct param * p , io , highlevel , . init = init_memory )
2017-08-05 21:02:09 +02:00
{
2017-08-14 14:42:07 +02:00
int ret , cnt ;
2017-10-20 11:37:25 +02:00
char * retp ;
2017-08-05 21:02:09 +02:00
2018-05-12 13:56:12 +02:00
struct format_type * f ;
2017-08-14 14:42:07 +02:00
2018-05-13 12:55:31 +02:00
struct io io = { . state = STATE_DESTROYED } ;
2018-08-20 18:32:10 +02:00
struct pool pool = { . state = STATE_DESTROYED } ;
struct list signals = { . state = STATE_DESTROYED } ;
2017-08-05 21:02:09 +02:00
2018-08-20 18:32:10 +02:00
struct sample * smps [ p - > cnt ] ;
struct sample * smpt [ p - > cnt ] ;
2018-05-12 13:56:12 +02:00
2018-08-20 18:32:10 +02:00
info ( " Running test for format=%s, cnt=%u " , p - > fmt , p - > cnt ) ;
ret = pool_init ( & pool , 2 * p - > cnt , SAMPLE_LENGTH ( NUM_VALUES ) , & memory_hugepage ) ;
2017-08-05 21:02:09 +02:00
cr_assert_eq ( ret , 0 ) ;
2018-08-20 18:32:10 +02:00
ret = sample_alloc_many ( & pool , smps , p - > cnt ) ;
cr_assert_eq ( ret , p - > cnt ) ;
ret = sample_alloc_many ( & pool , smpt , p - > cnt ) ;
cr_assert_eq ( ret , p - > cnt ) ;
list_init ( & signals ) ;
signal_list_generate ( & signals , NUM_VALUES , SIGNAL_TYPE_FLOAT ) ;
fill_sample_data ( & signals , smps , p - > cnt ) ;
2017-08-05 21:02:09 +02:00
/* Open a file for IO */
2017-08-14 14:42:07 +02:00
char * fn , dir [ 64 ] ;
strncpy ( dir , " /tmp/villas.XXXXXX " , sizeof ( dir ) ) ;
2017-09-04 14:30:07 +02:00
2017-10-20 11:37:25 +02:00
retp = mkdtemp ( dir ) ;
cr_assert_not_null ( retp ) ;
2017-08-20 10:48:44 +02:00
// ret = asprintf(&fn, "file://%s/file", dir);
2017-08-14 14:42:07 +02:00
ret = asprintf ( & fn , " %s/file " , dir ) ;
cr_assert_gt ( ret , 0 ) ;
2017-08-05 21:02:09 +02:00
2018-08-20 18:32:10 +02:00
f = format_type_lookup ( p - > fmt ) ;
cr_assert_not_null ( f , " Format '%s' does not exist " , p - > fmt ) ;
ret = io_init ( & io , f , & signals , SAMPLE_HAS_ALL ) ;
cr_assert_eq ( ret , 0 ) ;
2017-08-05 21:02:09 +02:00
2018-08-20 18:32:10 +02:00
ret = io_check ( & io ) ;
2017-08-05 21:02:09 +02:00
cr_assert_eq ( ret , 0 ) ;
2017-08-20 10:48:44 +02:00
ret = io_open ( & io , fn ) ;
2017-08-05 21:02:09 +02:00
cr_assert_eq ( ret , 0 ) ;
2018-08-20 18:32:10 +02:00
cnt = io_print ( & io , smps , p - > cnt ) ;
cr_assert_eq ( cnt , p - > cnt , " Written only %d of %d samples for format %s " , cnt , p - > cnt , format_type_name ( f ) ) ;
2017-09-04 14:30:07 +02:00
2017-08-20 10:48:44 +02:00
ret = io_flush ( & io ) ;
2017-09-04 14:30:07 +02:00
cr_assert_eq ( ret , 0 ) ;
2017-08-05 21:02:09 +02:00
2017-08-20 10:48:44 +02:00
#if 0 /* Show the file contents */
2017-08-05 21:02:09 +02:00
char cmd [ 128 ] ;
2018-08-20 18:32:10 +02:00
if ( ! strcmp ( p - > fmt , " csv " ) | | ! strcmp ( p - > fmt , " json " ) | | ! strcmp ( p - > fmt , " villas.human " ) )
2017-08-14 14:42:07 +02:00
snprintf ( cmd , sizeof ( cmd ) , " cat %s " , fn ) ;
else
snprintf ( cmd , sizeof ( cmd ) , " hexdump -C %s " , fn ) ;
2017-08-05 21:02:09 +02:00
system ( cmd ) ;
2017-08-14 14:42:07 +02:00
# endif
2017-09-04 14:30:07 +02:00
2018-05-12 13:56:12 +02:00
io_rewind ( & io ) ;
2017-08-20 10:48:44 +02:00
if ( io . mode = = IO_MODE_ADVIO )
2018-08-09 08:39:27 +02:00
adownload ( io . in . stream . adv , 0 ) ;
2017-08-20 10:48:44 +02:00
2018-08-20 18:32:10 +02:00
cnt = io_scan ( & io , smpt , p - > cnt ) ;
2018-05-12 13:56:12 +02:00
cr_assert_gt ( cnt , 0 , " Failed to read samples back: cnt=%d " , cnt ) ;
2017-08-14 14:42:07 +02:00
2018-08-20 18:32:10 +02:00
cr_assert_eq ( cnt , p - > cnt , " Read only %d of %d samples back " , cnt , p - > cnt ) ;
for ( int i = 0 ; i < cnt ; i + + ) {
if ( p - > bits )
cr_assert_eq_sample_raw ( smps [ i ] , smpt [ i ] , f - > flags , p - > bits ) ;
else
cr_assert_eq_sample ( smps [ i ] , smpt [ i ] , f - > flags ) ;
}
2017-08-05 21:02:09 +02:00
ret = io_close ( & io ) ;
cr_assert_eq ( ret , 0 ) ;
ret = io_destroy ( & io ) ;
cr_assert_eq ( ret , 0 ) ;
2017-08-14 14:42:07 +02:00
ret = unlink ( fn ) ;
cr_assert_eq ( ret , 0 ) ;
2017-09-04 14:30:07 +02:00
2017-08-14 14:42:07 +02:00
ret = rmdir ( dir ) ;
2017-08-05 21:02:09 +02:00
cr_assert_eq ( ret , 0 ) ;
2017-09-04 14:30:07 +02:00
2017-08-14 14:42:07 +02:00
free ( fn ) ;
2017-08-05 21:02:09 +02:00
2018-08-20 18:32:10 +02:00
sample_free_many ( smps , p - > cnt ) ;
sample_free_many ( smpt , p - > cnt ) ;
2017-08-05 21:02:09 +02:00
2018-08-20 18:32:10 +02:00
ret = pool_destroy ( & pool ) ;
2017-08-05 21:02:09 +02:00
cr_assert_eq ( ret , 0 ) ;
}