2020-06-19 00:06:01 +03:00
# include <config.h>
# include "internal.h"
# include "testutils.h"
# include "node_device/node_device_driver.h"
# include "vircommand.h"
# define LIBVIRT_VIRCOMMANDPRIV_H_ALLOW
# include "vircommandpriv.h"
# define VIR_FROM_THIS VIR_FROM_NODEDEV
2021-04-09 23:30:01 +03:00
# define VIRT_TYPE "QEMU"
2021-07-16 21:46:52 +03:00
static virNodeDeviceDefParserCallbacks parser_callbacks = {
2021-07-17 00:33:43 +03:00
. postParse = nodeDeviceDefPostParse ,
2021-07-16 21:46:52 +03:00
. validate = nodeDeviceDefValidate
} ;
2021-03-31 16:40:36 +03:00
struct TestInfo {
2020-06-19 00:06:01 +03:00
const char * filename ;
2021-03-31 16:20:50 +03:00
virMdevctlCommand command ;
2020-06-19 00:06:01 +03:00
} ;
/* capture stdin passed to command */
static void
testCommandDryRunCallback ( const char * const * args G_GNUC_UNUSED ,
const char * const * env G_GNUC_UNUSED ,
const char * input ,
char * * output G_GNUC_UNUSED ,
char * * error G_GNUC_UNUSED ,
int * status G_GNUC_UNUSED ,
void * opaque G_GNUC_UNUSED )
{
char * * stdinbuf = opaque ;
* stdinbuf = g_strdup ( input ) ;
}
2021-03-31 16:40:36 +03:00
typedef virCommand * ( * MdevctlCmdFunc ) ( virNodeDeviceDef * , char * * , char * * ) ;
2021-01-27 19:55:07 +03:00
2020-06-19 00:06:01 +03:00
static int
2021-04-09 23:30:01 +03:00
testMdevctlCmd ( virMdevctlCommand cmd_type ,
2021-03-31 16:40:36 +03:00
const char * mdevxml ,
const char * cmdfile ,
const char * jsonfile )
2020-06-19 00:06:01 +03:00
{
g_autoptr ( virNodeDeviceDef ) def = NULL ;
2020-07-03 02:35:41 +03:00
g_auto ( virBuffer ) buf = VIR_BUFFER_INITIALIZER ;
2020-06-19 00:06:01 +03:00
const char * actualCmdline = NULL ;
2021-03-31 16:40:36 +03:00
g_autofree char * outbuf = NULL ;
g_autofree char * errbuf = NULL ;
2020-06-19 00:06:01 +03:00
g_autofree char * stdinbuf = NULL ;
g_autoptr ( virCommand ) cmd = NULL ;
2021-04-01 18:54:09 +03:00
g_autoptr ( virCommandDryRunToken ) dryRunToken = virCommandDryRunTokenNew ( ) ;
2021-04-09 23:30:01 +03:00
int create ;
switch ( cmd_type ) {
case MDEVCTL_CMD_CREATE :
case MDEVCTL_CMD_DEFINE :
create = CREATE_DEVICE ;
break ;
case MDEVCTL_CMD_START :
case MDEVCTL_CMD_STOP :
case MDEVCTL_CMD_UNDEFINE :
create = EXISTING_DEVICE ;
break ;
case MDEVCTL_CMD_LAST :
default :
2021-04-13 23:29:15 +03:00
return - 1 ;
2021-04-09 23:30:01 +03:00
}
2020-06-19 00:06:01 +03:00
2022-09-23 12:52:23 +03:00
if ( ! ( def = virNodeDeviceDefParse ( NULL , mdevxml , create , VIRT_TYPE ,
2022-10-18 13:34:07 +03:00
& parser_callbacks , NULL , false ) ) )
2021-04-13 23:29:15 +03:00
return - 1 ;
2020-06-19 00:06:01 +03:00
/* this function will set a stdin buffer containing the json configuration
* of the device . The json value is captured in the callback above */
2021-03-31 16:40:36 +03:00
cmd = nodeDeviceGetMdevctlCommand ( def , cmd_type , & outbuf , & errbuf ) ;
2020-06-19 00:06:01 +03:00
if ( ! cmd )
2021-04-13 23:29:15 +03:00
return - 1 ;
2020-06-19 00:06:01 +03:00
2021-03-31 16:40:36 +03:00
if ( create )
virCommandSetDryRun ( dryRunToken , & buf , true , true ,
testCommandDryRunCallback , & stdinbuf ) ;
else
virCommandSetDryRun ( dryRunToken , & buf , true , true , NULL , NULL ) ;
2020-06-19 00:06:01 +03:00
if ( virCommandRun ( cmd , NULL ) < 0 )
2021-04-13 23:29:15 +03:00
return - 1 ;
2020-06-19 00:06:01 +03:00
if ( ! ( actualCmdline = virBufferCurrentContent ( & buf ) ) )
2021-04-13 23:29:15 +03:00
return - 1 ;
2020-06-19 00:06:01 +03:00
2021-04-09 10:21:28 +03:00
if ( virTestCompareToFileFull ( actualCmdline , cmdfile , false ) < 0 )
2021-04-13 23:29:15 +03:00
return - 1 ;
2020-06-19 00:06:01 +03:00
2021-03-31 16:40:36 +03:00
if ( create & & virTestCompareToFile ( stdinbuf , jsonfile ) < 0 )
2021-04-13 23:29:15 +03:00
return - 1 ;
2020-06-19 00:06:01 +03:00
2021-04-13 23:29:15 +03:00
return 0 ;
2020-06-19 00:06:01 +03:00
}
2021-03-31 16:40:36 +03:00
2020-06-19 00:06:01 +03:00
static int
2021-03-31 16:40:36 +03:00
testMdevctlHelper ( const void * data )
2020-06-19 00:06:01 +03:00
{
2021-03-31 16:40:36 +03:00
const struct TestInfo * info = data ;
2021-04-01 01:03:08 +03:00
const char * cmd = virMdevctlCommandTypeToString ( info - > command ) ;
2021-01-27 19:55:07 +03:00
g_autofree char * mdevxml = NULL ;
g_autofree char * cmdlinefile = NULL ;
g_autofree char * jsonfile = NULL ;
mdevxml = g_strdup_printf ( " %s/nodedevschemadata/%s.xml " , abs_srcdir ,
info - > filename ) ;
cmdlinefile = g_strdup_printf ( " %s/nodedevmdevctldata/%s-%s.argv " ,
abs_srcdir , info - > filename , cmd ) ;
jsonfile = g_strdup_printf ( " %s/nodedevmdevctldata/%s-%s.json " , abs_srcdir ,
info - > filename , cmd ) ;
2020-06-19 00:06:01 +03:00
2021-04-09 23:30:01 +03:00
return testMdevctlCmd ( info - > command , mdevxml , cmdlinefile , jsonfile ) ;
2020-06-19 00:06:03 +03:00
}
2020-11-13 19:45:33 +03:00
2021-05-28 00:06:19 +03:00
static int
testMdevctlAutostart ( const void * data G_GNUC_UNUSED )
{
g_autoptr ( virNodeDeviceDef ) def = NULL ;
virBuffer buf = VIR_BUFFER_INITIALIZER ;
const char * actualCmdline = NULL ;
int ret = - 1 ;
g_autoptr ( virCommand ) enablecmd = NULL ;
g_autoptr ( virCommand ) disablecmd = NULL ;
g_autofree char * errmsg = NULL ;
/* just concatenate both calls into the same output file */
g_autofree char * cmdlinefile =
g_strdup_printf ( " %s/nodedevmdevctldata/mdevctl-autostart.argv " ,
abs_srcdir ) ;
g_autofree char * mdevxml =
g_strdup_printf ( " %s/nodedevschemadata/mdev_d069d019_36ea_4111_8f0a_8c9a70e21366.xml " ,
abs_srcdir ) ;
g_autoptr ( virCommandDryRunToken ) dryRunToken = virCommandDryRunTokenNew ( ) ;
2022-09-23 12:52:23 +03:00
if ( ! ( def = virNodeDeviceDefParse ( NULL , mdevxml , CREATE_DEVICE , VIRT_TYPE ,
2022-10-18 13:34:07 +03:00
& parser_callbacks , NULL , false ) ) )
2021-05-28 00:06:19 +03:00
return - 1 ;
virCommandSetDryRun ( dryRunToken , & buf , true , true , NULL , NULL ) ;
if ( ! ( enablecmd = nodeDeviceGetMdevctlSetAutostartCommand ( def , true , & errmsg ) ) )
goto cleanup ;
if ( virCommandRun ( enablecmd , NULL ) < 0 )
goto cleanup ;
if ( ! ( disablecmd = nodeDeviceGetMdevctlSetAutostartCommand ( def , false , & errmsg ) ) )
goto cleanup ;
if ( virCommandRun ( disablecmd , NULL ) < 0 )
goto cleanup ;
if ( ! ( actualCmdline = virBufferCurrentContent ( & buf ) ) )
goto cleanup ;
if ( virTestCompareToFileFull ( actualCmdline , cmdlinefile , false ) < 0 )
goto cleanup ;
ret = 0 ;
cleanup :
virBufferFreeAndReset ( & buf ) ;
return ret ;
}
2020-07-11 01:12:18 +03:00
static int
testMdevctlListDefined ( const void * data G_GNUC_UNUSED )
{
virBuffer buf = VIR_BUFFER_INITIALIZER ;
const char * actualCmdline = NULL ;
int ret = - 1 ;
g_autoptr ( virCommand ) cmd = NULL ;
g_autofree char * output = NULL ;
g_autofree char * errmsg = NULL ;
g_autofree char * cmdlinefile =
g_strdup_printf ( " %s/nodedevmdevctldata/mdevctl-list-defined.argv " ,
abs_srcdir ) ;
2021-04-01 18:54:09 +03:00
g_autoptr ( virCommandDryRunToken ) dryRunToken = virCommandDryRunTokenNew ( ) ;
2020-07-11 01:12:18 +03:00
cmd = nodeDeviceGetMdevctlListCommand ( true , & output , & errmsg ) ;
if ( ! cmd )
goto cleanup ;
2021-04-09 10:21:28 +03:00
virCommandSetDryRun ( dryRunToken , & buf , true , true , NULL , NULL ) ;
2020-07-11 01:12:18 +03:00
if ( virCommandRun ( cmd , NULL ) < 0 )
goto cleanup ;
if ( ! ( actualCmdline = virBufferCurrentContent ( & buf ) ) )
goto cleanup ;
2021-04-09 10:21:28 +03:00
if ( virTestCompareToFileFull ( actualCmdline , cmdlinefile , false ) < 0 )
2020-07-11 01:12:18 +03:00
goto cleanup ;
ret = 0 ;
cleanup :
virBufferFreeAndReset ( & buf ) ;
return ret ;
}
2020-07-11 01:12:18 +03:00
static int
testMdevctlParse ( const void * data )
{
g_autofree char * buf = NULL ;
const char * filename = data ;
g_autofree char * jsonfile = g_strdup_printf ( " %s/nodedevmdevctldata/%s.json " ,
abs_srcdir , filename ) ;
g_autofree char * xmloutfile = g_strdup_printf ( " %s/nodedevmdevctldata/%s.out.xml " ,
abs_srcdir , filename ) ;
int ret = - 1 ;
int nmdevs = 0 ;
virNodeDeviceDef * * mdevs = NULL ;
virBuffer xmloutbuf = VIR_BUFFER_INITIALIZER ;
size_t i ;
if ( virFileReadAll ( jsonfile , 1024 * 1024 , & buf ) < 0 ) {
VIR_TEST_DEBUG ( " Unable to read file %s " , jsonfile ) ;
return - 1 ;
}
2024-02-22 16:02:01 +03:00
if ( ( nmdevs = nodeDeviceParseMdevctlJSON ( buf , & mdevs , true ) ) < 0 ) {
2020-07-11 01:12:18 +03:00
VIR_TEST_DEBUG ( " Unable to parse json for %s " , filename ) ;
return - 1 ;
}
for ( i = 0 ; i < nmdevs ; i + + ) {
2024-02-22 16:02:01 +03:00
g_autofree char * devxml = virNodeDeviceDefFormat ( mdevs [ i ] , VIR_NODE_DEVICE_XML_INACTIVE ) ;
2020-07-11 01:12:18 +03:00
if ( ! devxml )
goto cleanup ;
virBufferAddStr ( & xmloutbuf , devxml ) ;
}
2021-04-09 10:21:28 +03:00
if ( virTestCompareToFileFull ( virBufferCurrentContent ( & xmloutbuf ) , xmloutfile , false ) < 0 )
2020-07-11 01:12:18 +03:00
goto cleanup ;
ret = 0 ;
cleanup :
virBufferFreeAndReset ( & xmloutbuf ) ;
for ( i = 0 ; i < nmdevs ; i + + )
virNodeDeviceDefFree ( mdevs [ i ] ) ;
g_free ( mdevs ) ;
return ret ;
}
2020-06-19 00:06:01 +03:00
static void
2021-03-11 10:16:13 +03:00
nodedevTestDriverFree ( virNodeDeviceDriverState * drv )
2020-06-19 00:06:01 +03:00
{
if ( ! drv )
return ;
virNodeDeviceObjListFree ( drv - > devs ) ;
virCondDestroy ( & drv - > initCond ) ;
virMutexDestroy ( & drv - > lock ) ;
2021-02-03 22:35:02 +03:00
g_free ( drv - > stateDir ) ;
g_free ( drv ) ;
2020-06-19 00:06:01 +03:00
}
/* Add a fake root 'computer' device */
2021-03-11 10:16:13 +03:00
static virNodeDeviceDef *
2020-06-19 00:06:01 +03:00
fakeRootDevice ( void )
{
2021-03-11 10:16:13 +03:00
virNodeDeviceDef * def = NULL ;
2020-06-19 00:06:01 +03:00
2020-09-23 01:42:45 +03:00
def = g_new0 ( virNodeDeviceDef , 1 ) ;
def - > caps = g_new0 ( virNodeDevCapsDef , 1 ) ;
2020-06-19 00:06:01 +03:00
def - > name = g_strdup ( " computer " ) ;
return def ;
}
/* Add a fake pci device that can be used as a parent device for mediated
* devices . For our purposes , it only needs to have a name that matches the
* parent of the mdev , and it needs a PCI address
*/
2021-03-11 10:16:13 +03:00
static virNodeDeviceDef *
2021-07-07 23:01:20 +03:00
fakePCIDevice ( void )
2020-06-19 00:06:01 +03:00
{
2021-03-11 10:16:13 +03:00
virNodeDeviceDef * def = NULL ;
virNodeDevCapPCIDev * pci_dev ;
2020-06-19 00:06:01 +03:00
2020-09-23 01:42:45 +03:00
def = g_new0 ( virNodeDeviceDef , 1 ) ;
def - > caps = g_new0 ( virNodeDevCapsDef , 1 ) ;
2020-06-19 00:06:01 +03:00
def - > name = g_strdup ( " pci_0000_00_02_0 " ) ;
def - > parent = g_strdup ( " computer " ) ;
def - > caps - > data . type = VIR_NODE_DEV_CAP_PCI_DEV ;
pci_dev = & def - > caps - > data . pci_dev ;
pci_dev - > domain = 0 ;
pci_dev - > bus = 0 ;
pci_dev - > slot = 2 ;
pci_dev - > function = 0 ;
return def ;
}
2021-07-07 23:01:20 +03:00
/* Add a fake matrix device that can be used as a parent device for mediated
* devices . For our purposes , it only needs to have a name that matches the
* parent of the mdev , and it needs the proper name
*/
static virNodeDeviceDef *
fakeMatrixDevice ( void )
{
virNodeDeviceDef * def = NULL ;
virNodeDevCapAPMatrix * cap ;
def = g_new0 ( virNodeDeviceDef , 1 ) ;
def - > caps = g_new0 ( virNodeDevCapsDef , 1 ) ;
def - > name = g_strdup ( " ap_matrix " ) ;
def - > parent = g_strdup ( " computer " ) ;
def - > caps - > data . type = VIR_NODE_DEV_CAP_AP_MATRIX ;
cap = & def - > caps - > data . ap_matrix ;
cap - > addr = g_strdup ( " matrix " ) ;
return def ;
}
2022-02-04 18:32:17 +03:00
/* Add a fake css device that can be used as a parent device for mediated
* devices . For our purposes , it only needs to have a name that matches the
* parent of the mdev , and it needs the proper name
*/
static virNodeDeviceDef *
fakeCSSDevice ( void )
{
virNodeDeviceDef * def = NULL ;
virNodeDevCapCCW * css_dev ;
def = g_new0 ( virNodeDeviceDef , 1 ) ;
def - > caps = g_new0 ( virNodeDevCapsDef , 1 ) ;
def - > name = g_strdup ( " css_0_0_0052 " ) ;
def - > parent = g_strdup ( " computer " ) ;
def - > caps - > data . type = VIR_NODE_DEV_CAP_CSS_DEV ;
css_dev = & def - > caps - > data . ccw_dev ;
css_dev - > cssid = 0 ;
css_dev - > ssid = 0 ;
css_dev - > devno = 82 ;
return def ;
}
2020-06-19 00:06:01 +03:00
static int
2021-03-11 10:16:13 +03:00
addDevice ( virNodeDeviceDef * def )
2020-06-19 00:06:01 +03:00
{
2021-03-11 10:16:13 +03:00
virNodeDeviceObj * obj ;
2020-06-19 00:06:01 +03:00
if ( ! def )
return - 1 ;
2020-07-28 20:50:28 +03:00
obj = virNodeDeviceObjListAssignDef ( driver - > devs , def ) ;
2020-06-19 00:06:01 +03:00
if ( ! obj ) {
virNodeDeviceDefFree ( def ) ;
return - 1 ;
}
virNodeDeviceObjEndAPI ( & obj ) ;
return 0 ;
}
static int
nodedevTestDriverAddTestDevices ( void )
{
if ( addDevice ( fakeRootDevice ( ) ) < 0 | |
2021-07-07 23:01:20 +03:00
addDevice ( fakePCIDevice ( ) ) < 0 | |
2022-02-04 18:32:17 +03:00
addDevice ( fakeMatrixDevice ( ) ) < 0 | |
addDevice ( fakeCSSDevice ( ) ) < 0 )
2020-06-19 00:06:01 +03:00
return - 1 ;
return 0 ;
}
/* Bare minimum driver init to be able to test nodedev functionality */
static int
nodedevTestDriverInit ( void )
{
int ret = - 1 ;
2020-09-23 01:42:45 +03:00
driver = g_new0 ( virNodeDeviceDriverState , 1 ) ;
2020-06-19 00:06:01 +03:00
driver - > lockFD = - 1 ;
if ( virMutexInit ( & driver - > lock ) < 0 | |
virCondInit ( & driver - > initCond ) < 0 ) {
VIR_TEST_DEBUG ( " Unable to initialize test nodedev driver " ) ;
goto error ;
}
if ( ! ( driver - > devs = virNodeDeviceObjListNew ( ) ) )
goto error ;
return 0 ;
error :
nodedevTestDriverFree ( driver ) ;
return ret ;
}
static int
mymain ( void )
{
int ret = 0 ;
if ( nodedevTestDriverInit ( ) < 0 )
return EXIT_FAILURE ;
/* add a mock device to the device list so it can be used as a parent
* reference */
if ( nodedevTestDriverAddTestDevices ( ) < 0 ) {
ret = EXIT_FAILURE ;
goto done ;
}
# define DO_TEST_FULL(desc, func, info) \
2021-03-30 19:05:59 +03:00
if ( virTestRun ( desc , func , info ) < 0 ) \
2020-06-19 00:06:01 +03:00
ret = - 1 ;
2021-04-09 23:30:01 +03:00
# define DO_TEST_CMD(desc, filename, command) \
2020-06-19 00:06:01 +03:00
do { \
2021-04-09 23:30:01 +03:00
struct TestInfo info = { filename , command } ; \
2021-03-31 16:40:36 +03:00
DO_TEST_FULL ( desc , testMdevctlHelper , & info ) ; \
2020-06-19 00:06:01 +03:00
} \
2020-07-01 19:08:34 +03:00
while ( 0 )
2020-06-19 00:06:01 +03:00
2021-03-31 16:32:44 +03:00
# define DO_TEST_CREATE(filename) \
2021-04-09 23:30:01 +03:00
DO_TEST_CMD ( " create mdev " filename , filename , MDEVCTL_CMD_CREATE )
2021-01-27 19:55:07 +03:00
# define DO_TEST_DEFINE(filename) \
2021-04-09 23:30:01 +03:00
DO_TEST_CMD ( " define mdev " filename , filename , MDEVCTL_CMD_DEFINE )
2020-11-13 19:45:33 +03:00
2021-04-08 18:25:48 +03:00
# define DO_TEST_STOP(filename) \
2021-04-09 23:30:01 +03:00
DO_TEST_CMD ( " stop mdev " filename , filename , MDEVCTL_CMD_STOP )
2020-06-19 00:06:03 +03:00
2021-04-08 18:25:48 +03:00
# define DO_TEST_UNDEFINE(filename) \
2021-04-09 23:30:01 +03:00
DO_TEST_CMD ( " undefine mdev " filename , filename , MDEVCTL_CMD_UNDEFINE )
2020-07-02 23:14:30 +03:00
2021-03-31 16:32:44 +03:00
# define DO_TEST_START(filename) \
2021-04-09 23:30:01 +03:00
DO_TEST_CMD ( " start mdev " filename , filename , MDEVCTL_CMD_START )
2020-06-18 18:21:13 +03:00
2020-07-11 01:12:18 +03:00
# define DO_TEST_LIST_DEFINED() \
2021-03-31 16:40:36 +03:00
DO_TEST_FULL ( " list defined mdevs " , testMdevctlListDefined , NULL )
2020-07-11 01:12:18 +03:00
2021-05-28 00:06:19 +03:00
# define DO_TEST_AUTOSTART() \
DO_TEST_FULL ( " autostart mdevs " , testMdevctlAutostart , NULL )
2020-07-11 01:12:18 +03:00
# define DO_TEST_PARSE_JSON(filename) \
DO_TEST_FULL ( " parse mdevctl json " filename , testMdevctlParse , filename )
2021-03-31 16:32:44 +03:00
DO_TEST_CREATE ( " mdev_d069d019_36ea_4111_8f0a_8c9a70e21366 " ) ;
DO_TEST_CREATE ( " mdev_fedc4916_1ca8_49ac_b176_871d16c13076 " ) ;
DO_TEST_CREATE ( " mdev_d2441d39_495e_4243_ad9f_beb3f14c23d9 " ) ;
2022-02-04 18:32:17 +03:00
DO_TEST_CREATE ( " mdev_cc000052_9b13_9b13_9b13_cc23009b1326 " ) ;
2020-06-19 00:06:01 +03:00
2020-06-19 00:06:03 +03:00
/* Test mdevctl stop command, pass an arbitrary uuid */
2021-04-08 18:25:48 +03:00
DO_TEST_STOP ( " mdev_d069d019_36ea_4111_8f0a_8c9a70e21366 " ) ;
2020-06-19 00:06:03 +03:00
2020-07-11 01:12:18 +03:00
DO_TEST_LIST_DEFINED ( ) ;
2023-08-24 10:58:33 +03:00
DO_TEST_PARSE_JSON ( " mdevctl-list-empty " ) ;
2023-08-24 10:57:39 +03:00
DO_TEST_PARSE_JSON ( " mdevctl-list-empty-array " ) ;
2020-07-11 01:12:18 +03:00
DO_TEST_PARSE_JSON ( " mdevctl-list-multiple " ) ;
2021-01-27 19:55:07 +03:00
DO_TEST_DEFINE ( " mdev_d069d019_36ea_4111_8f0a_8c9a70e21366 " ) ;
DO_TEST_DEFINE ( " mdev_fedc4916_1ca8_49ac_b176_871d16c13076 " ) ;
DO_TEST_DEFINE ( " mdev_d2441d39_495e_4243_ad9f_beb3f14c23d9 " ) ;
2022-02-04 18:32:17 +03:00
DO_TEST_DEFINE ( " mdev_cc000052_9b13_9b13_9b13_cc23009b1326 " ) ;
2021-01-27 19:55:07 +03:00
2021-04-08 18:25:48 +03:00
DO_TEST_UNDEFINE ( " mdev_d069d019_36ea_4111_8f0a_8c9a70e21366 " ) ;
2020-07-02 23:14:30 +03:00
2021-03-31 16:32:44 +03:00
DO_TEST_START ( " mdev_d069d019_36ea_4111_8f0a_8c9a70e21366 " ) ;
2020-06-18 18:21:13 +03:00
2021-05-28 00:06:19 +03:00
DO_TEST_AUTOSTART ( ) ;
2020-06-19 00:06:01 +03:00
done :
nodedevTestDriverFree ( driver ) ;
return ret = = 0 ? EXIT_SUCCESS : EXIT_FAILURE ;
}
VIR_TEST_MAIN ( mymain )