sane-project-backends/testsuite/sanei/sanei_usb_test.c

950 wiersze
22 KiB
C

#include "../../include/sane/config.h"
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <math.h>
#include <stddef.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_MKDIR
#include <sys/stat.h>
#include <sys/types.h>
#endif
#include <assert.h>
#define BACKEND_NAME sanei_usb
#include "../../include/sane/sane.h"
#include "../../include/sane/sanei.h"
#include "../../include/sane/saneopts.h"
#include "../../include/sane/sanei_backend.h"
#include "../../include/sane/sanei_usb.h"
#include "../../include/_stdint.h"
/*
* In order to avoid modifying sanei_usb.c to allow for unit tests
* we include it so we can use its private variables and structures
* and still test the code.
*/
#include "../../sanei/sanei_usb.c"
/** test sanei_usb_init()
* calls sanei_usb_init
* @param expected expected use count
* @return 1 on success, else 0
*/
static int
test_init (int expected)
{
/* initialize USB */
printf ("%s starting ...\n", __func__);
sanei_usb_init ();
if (initialized == 0)
{
printf ("ERROR: sanei_usb not initialized!\n");
return 0;
}
if (initialized != expected)
{
printf ("ERROR: incorrect use count, expected %d, got %d!\n", expected,
initialized);
return 0;
}
printf ("sanei_usb initialized, use count is %d ...\n", initialized);
printf ("%s success\n\n", __func__);
return 1;
}
/** test sanei_usb_exit()
* calls sanei_usb_exit
* @param expected use count after exit call
* @return 1 on success, else 0
*/
static int
test_exit (int expected)
{
printf ("%s starting ...\n", __func__);
/* end of USB use test */
sanei_usb_exit ();
if (initialized != expected)
{
printf ("ERROR: incorrect use count, expected %d, got %d!\n", expected,
initialized);
return 0;
}
printf ("%s success\n\n", __func__);
return 1;
}
/** count detected devices
* count all detected devices and check it against expected value
* @param expected detected count
* @return 1 on success, else 0
*/
static int
count_detected (int expected)
{
int num = 0;
int i;
for (i = 0; i < device_number; i++)
{
if (devices[i].missing == 0 && devices[i].devname != NULL)
{
num++;
}
}
if (num != expected)
{
printf ("ERROR: %d detected devices, expected %d!\n", num, expected);
return 0;
}
printf ("%d devices still detected.\n", num);
return 1;
}
/** create mock device
* create a mock device entry
* @param device device pointer to fill with mock data
* @return nothing
*/
static void
create_mock_device (char *devname, device_list_type * device)
{
memset (device, 0, sizeof (device_list_type));
device->devname = strdup (devname);
device->vendor = 0xdead;
device->product = 0xbeef;
#if defined(HAVE_LIBUSB_LEGACY) || defined(HAVE_LIBUSB)
device->method = sanei_usb_method_libusb;
#endif
#ifdef HAVE_USBCALLS
device->method = sanei_usb_method_usbcalls;
#endif
#if !defined(HAVE_LIBUSB_LEGACY) && !defined(HAVE_LIBUSB) && !defined(HAVE_USBCALLS)
device->method == sanei_usb_method_scanner_driver;
#endif
}
/** test store_device
* test store_device for corner cases not covered by the
* other regular use by sanei_usb_scan_devices
* the startiing situation is that the mock device has never
* put into device list.
* @return 1 on success, else 0
*/
static int
test_store_device (void)
{
int current_number;
int expected;
int i;
int found;
device_list_type mock;
create_mock_device ("mock", &mock);
/* first test store when there is no more room
* to store device */
current_number = device_number;
device_number = MAX_DEVICES;
/* give unused devices a name so strcmp() won't crash. */
for (i = current_number; i < MAX_DEVICES; i++)
devices[i].devname = "";
store_device (mock);
/* there should be no more devices */
if (device_number > MAX_DEVICES)
{
printf ("ERROR: store past end of device list!\n");
return 0;
}
/* walk device list to be sure mock device hasn't been stored */
for (i = 0; i < MAX_DEVICES; i++)
{
if (devices[i].devname && !strcmp (devices[i].devname, mock.devname))
{
printf
("ERROR: device stored although there were no place for it!\n");
return 0;
}
}
/* restore device_number */
device_number = current_number;
/* reset unused devnames to NULL */
for (i = current_number; i < MAX_DEVICES; i++)
devices[i].devname = NULL;
expected = device_number + 1;
/* store mock device */
store_device (mock);
found = 0;
for (i = 0; i < MAX_DEVICES && !found; i++)
{
if (devices[i].devname && !strcmp (devices[i].devname, mock.devname))
{
found = 1;
}
}
if (device_number != expected || !found)
{
printf ("ERROR: mock device not stored !\n");
return 0;
}
/* scan devices should mark it as missing, and device_number should decrease */
sanei_usb_scan_devices ();
found = 0;
for (i = 0; i < MAX_DEVICES && !found; i++)
{
if (devices[i].devname
&& devices[i].missing == 1
&& !strcmp (devices[i].devname, mock.devname))
{
found = 1;
}
}
if (device_number != expected || !found)
{
printf ("ERROR: mock device still present !\n");
return 0;
}
/* second scan devices should mark missing to 2 */
sanei_usb_scan_devices ();
found = 0;
for (i = 0; i < MAX_DEVICES && !found; i++)
{
if (devices[i].devname
&& devices[i].missing == 2
&& !strcmp (devices[i].devname, mock.devname))
{
found = 1;
}
}
if (device_number != expected || !found)
{
printf ("ERROR: mock device slot not reusable !\n");
return 0;
}
/* store mock device again, slot in devices should be reused
* and device_number shouldn't change */
create_mock_device ("mock2", &mock);
store_device (mock);
found = 0;
for (i = 0; i < MAX_DEVICES && !found; i++)
{
if (devices[i].devname && !strcmp (devices[i].devname, mock.devname))
{
found = 1;
}
}
if (device_number != expected || !found)
{
printf ("ERROR: mock device not stored !\n");
return 0;
}
/* last rescan to wipe mock device out */
sanei_usb_scan_devices ();
return 1;
}
/** return count of opened devices
* @return count of opened devices
*/
static int
get_opened (void)
{
int num = 0;
int i;
for (i = 0; i < device_number; i++)
{
if (devices[i].missing == 0 && devices[i].devname != NULL
&& devices[i].open == SANE_TRUE)
{
num++;
}
}
return num;
}
/** count opened devices
* count all opended devices and check it against expected value
* @param expected use opened count
* @return 1 on success, else 0
*/
static int
count_opened (int expected)
{
int num = get_opened();
if (num != expected)
{
printf ("ERROR: %d opened devices, expected %d!\n", num, expected);
return 0;
}
printf ("%d devices still opened.\n", num);
return 1;
}
/** open all devices
* loop on all existing devices and open them
* @param dn array to store opened device number
* @param expected number of devices to be opened
* @return 1 on success, else 0
*/
static int
test_open_all (SANE_Int * dn, int expected)
{
int opened = 0;
int i;
int last;
SANE_Status status;
/* loop on detected devices and open them */
last = -1;
for (i = 0; i < device_number; i++)
{
if (devices[i].missing == 0 && devices[i].devname != NULL)
{
/* open device */
status = sanei_usb_open (devices[i].devname, dn + opened);
if (status == SANE_STATUS_GOOD)
{
opened++;
last = i;
}
else
{
if (status == SANE_STATUS_ACCESS_DENIED ||
status == SANE_STATUS_DEVICE_BUSY)
{
expected--;
}
else
{
printf ("ERROR: couldn't open device %s!\n",
devices[i].devname);
return 0;
}
}
}
}
printf ("opened %d devices\n", opened);
/* try to reopen an opened device when there is one */
if (last >= 0)
{
status = sanei_usb_open (devices[last].devname, dn + opened);
if (status == SANE_STATUS_GOOD)
{
printf ("ERROR: unexpected success when opening %s twice!\n",
devices[last].devname);
return 0;
}
}
/* there should be as many opened devices than detected devices */
return count_opened (expected);
}
/** test opening invalid device
* try to open an non existing device
* @return 1 on success, else 0
*/
static int
test_open_invalid (void)
{
SANE_Status status;
SANE_Int dn;
status = sanei_usb_open ("invalid device", &dn);
if (status == SANE_STATUS_GOOD)
{
printf ("ERROR: unexpected success opening invalid device!\n");
return 0;
}
return 1;
}
/** close all devices
* loop on all opened devices and close them
* @param dn array of opened device number
* @param expected number of devices to be closed
* @return 1 on success, else 0
*/
static int
test_close_all (SANE_Int * dn, int expected)
{
int closed = 0;
int i;
/* loop on detected devices and open them */
for (i = 0; i < expected; i++)
{
/* close device */
sanei_usb_close (dn[i]);
closed++;
}
printf ("closed %d devices\n", closed);
/* there should be any more opened devices */
return count_opened (0);
}
/** claim all open devices
* loop on all opened devices and claim interface 0
* @param dn array of opened device number
* @param expected number of devices to be claimed
* @return 1 on success, else 0
*/
static int
test_claim_all (SANE_Int * dn, int expected)
{
int claimed = 0;
int i;
SANE_Status status;
device_list_type mock;
claimed = 0;
for (i = 0; i < expected; i++)
{
status = sanei_usb_claim_interface (dn[i], devices[dn[i]].interface_nr);
if (status != SANE_STATUS_GOOD)
{
printf ("ERROR: couldn't claim interface 0 on device %d!\n", dn[i]);
}
else
{
claimed++;
}
}
if (claimed != expected)
{
printf ("ERROR: expected %d claimed interfaces, got %d!\n", expected,
claimed);
return 0;
}
printf ("%d devices claimed...\n\n", claimed);
/* try to claim invalid device entry */
status = sanei_usb_claim_interface (device_number, 0);
if (status == SANE_STATUS_GOOD)
{
printf ("ERROR: could claim interface 0 on invalid device!\n");
return 0;
}
/* create a mock device and make it missing by rescanning */
create_mock_device ("mock", &mock);
store_device (mock);
sanei_usb_scan_devices ();
/* try to claim interface on missing device */
status = sanei_usb_claim_interface (device_number - 1, 0);
if (status == SANE_STATUS_GOOD)
{
printf ("ERROR: could claim interface 0 on invalid device!\n");
return 0;
}
/* remove mock device */
device_number--;
free (devices[device_number].devname);
devices[device_number].devname = NULL;
return 1;
}
/** release all claimed devices
* loop on all opened devices and claim interface 0
* @param dn array of opened device number
* @param expected number of devices to be claimed
* @return 1 on success, else 0
*/
static int
test_release_all (SANE_Int * dn, int expected)
{
int released = 0;
int i;
SANE_Status status;
device_list_type mock;
released = 0;
for (i = 0; i < expected; i++)
{
status =
sanei_usb_release_interface (dn[i], devices[dn[i]].interface_nr);
if (status != SANE_STATUS_GOOD)
{
printf ("ERROR: couldn't release interface 0 on device %d!\n",
dn[i]);
}
else
{
released++;
}
}
if (released != expected)
{
printf ("ERROR: expected %d released interfaces, got %d!\n", expected,
released);
return 0;
}
printf ("%d devices released...\n\n", released);
/* try to release invalid device entry */
status = sanei_usb_release_interface (device_number, 0);
if (status == SANE_STATUS_GOOD)
{
printf ("ERROR: could release interface 0 on invalid device!\n");
return 0;
}
/* create a mock device and make it missing by rescanning */
create_mock_device ("mock", &mock);
store_device (mock);
sanei_usb_scan_devices ();
/* try to claim interface on missing device */
status = sanei_usb_release_interface (device_number - 1, 0);
if (status == SANE_STATUS_GOOD)
{
printf ("ERROR: could release interface 0 on invalid device!\n");
return 0;
}
/* remove mock device */
device_number--;
free (devices[device_number].devname);
devices[device_number].devname = NULL;
return 1;
}
/** get id for all devices names
* loop on all existing devices and get vendor
* and product id by name.
* @param expected count
* @return 1 on success, else 0
*/
static int
test_vendor_by_devname (void)
{
int i;
SANE_Status status;
SANE_Word vendor, product;
device_list_type mock;
/* loop on detected devices and open them */
for (i = 0; i < device_number; i++)
{
if (devices[i].missing == 0 && devices[i].devname != NULL)
{
/* get device id */
status = sanei_usb_get_vendor_product_byname (devices[i].devname,
&vendor, &product);
if (status != SANE_STATUS_GOOD)
{
printf ("ERROR: couldn't query device %s!\n",
devices[i].devname);
return 0;
}
if (vendor == 0 || product == 0)
{
printf ("ERROR: incomplete device id for %s!\n",
devices[i].devname);
return 0;
}
printf ("%s is %04x:%04x\n", devices[i].devname, vendor, product);
}
}
/* add mock device */
create_mock_device ("mock", &mock);
store_device (mock);
status = sanei_usb_get_vendor_product_byname ("mock", &vendor, &product);
if (status != SANE_STATUS_GOOD)
{
printf ("ERROR: getting vendor for mock devname!\n");
return 0;
}
if (vendor != mock.vendor || product != mock.product)
{
printf ("ERROR: wrong vendor/product for mock devname!\n");
return 0;
}
/* remove mock device */
device_number--;
free (devices[device_number].devname);
devices[device_number].devname = NULL;
/* try go get id for an invalid devname */
status = sanei_usb_get_vendor_product_byname ("invalid devname",
&vendor, &product);
if (status == SANE_STATUS_GOOD)
{
printf ("ERROR: unexpected success getting id for invalid devname!\n");
return 0;
}
printf ("\n");
return 1;
}
/** get vendor for all devices id
* loop on all existing devices and get vendor
* and product id.
* @param expected count
* @return 1 on success, else 0
*/
static int
test_vendor_by_id (void)
{
int i;
SANE_Status status;
SANE_Word vendor, product;
device_list_type mock;
/* loop on detected devices and open them */
for (i = 0; i < device_number; i++)
{
if (devices[i].missing == 0 && devices[i].devname != NULL)
{
/* get device id */
status = sanei_usb_get_vendor_product (i, &vendor, &product);
if (status != SANE_STATUS_GOOD)
{
printf ("ERROR: couldn't query device %d!\n", i);
return 0;
}
if (vendor == 0 || product == 0)
{
printf ("ERROR: incomplete device id for %d!\n", i);
return 0;
}
printf ("%d is %04x:%04x\n", i, vendor, product);
}
}
/* add mock device */
create_mock_device ("mock", &mock);
store_device (mock);
status =
sanei_usb_get_vendor_product (device_number - 1, &vendor, &product);
if (status != SANE_STATUS_GOOD)
{
printf ("ERROR: getting vendor for mock devname!\n");
return 0;
}
if (vendor != mock.vendor || product != mock.product)
{
printf ("ERROR: wrong vendor/product for mock devname!\n");
return 0;
}
/* remove mock device */
device_number--;
free (devices[device_number].devname);
devices[device_number].devname = NULL;
/* try go get id for an invalid id */
status =
sanei_usb_get_vendor_product (device_number + 1, &vendor, &product);
if (status == SANE_STATUS_GOOD)
{
printf
("ERROR: unexpected success getting vendor for invalid devname!\n");
return 0;
}
printf ("\n");
return 1;
}
/** test timeout functions : libusb only
* @return 1 on success, else 0
*/
static int
test_timeout (void)
{
#if defined(HAVE_LIBUSB_LEGACY) || defined(HAVE_LIBUSB)
int timeout = libusb_timeout;
sanei_usb_set_timeout (5000);
if (libusb_timeout != 5000)
{
printf ("ERROR: failed to set timeout\n");
return 1;
}
sanei_usb_set_timeout (timeout);
#endif
return 1;
}
/** test device scanning
* call sanei_usb_scan_devices, since it has no return code, no real
* assert can be done, but at least we can test it doesn't break
* other functions or don't leak memory
* @return always 1
*/
static int
test_scan_devices (int detected, int opened)
{
int rc;
printf ("rescanning for devices ...\n");
sanei_usb_scan_devices ();
rc = count_detected (detected);
if (!rc)
{
printf ("ERROR: scanning devices change detected count!\n");
return 0;
}
rc = count_opened (opened);
if (!rc)
{
printf ("ERROR: scanning devices change opened count!\n");
return 0;
}
printf ("\n");
return 1;
}
/**
* flag for dummy attach
*/
static int dummy_flag;
/**
* expected device name during attach
*/
static char *expected_device;
/** dummy attach function
* dummy attach function
* @return return SANE_STATUS_GOOD
*/
static SANE_Status
dummy_attach (const char *dev)
{
dummy_flag = (strcmp (expected_device, dev) == 0);
if (dummy_flag)
{
printf ("success attaching to %s...\n", dev);
}
else
{
printf ("failed attaching to %s...\n", dev);
}
return SANE_STATUS_GOOD;
}
/** test attaching usb device
* create a mock device and attach to it, checking
* if it is ok
* @return 1 on success, else 0
*/
static int
test_attach (void)
{
device_list_type mock;
/* add mock device and try to attach to it */
dummy_flag = 0;
create_mock_device ("mock", &mock);
expected_device = mock.devname;
store_device (mock);
sanei_usb_attach_matching_devices ("usb 0xdead 0xbeef", dummy_attach);
/* flag must be set */
if (dummy_flag != 1)
{
printf ("ERROR: couldn't attach to 'usb xdead 0xbeef' device!\n");
return 0;
}
/* attach by devname */
dummy_flag = 0;
sanei_usb_attach_matching_devices (mock.devname, dummy_attach);
/* flag must be set */
if (dummy_flag != 1)
{
printf ("ERROR: couldn't attach to 'mock' device!\n");
return 0;
}
/* attach to bogus device */
dummy_flag = 0;
sanei_usb_attach_matching_devices ("usb 0x0001 0x0001", dummy_attach);
/* flag must not be set */
if (dummy_flag != 0)
{
printf ("ERROR: shouldn't be attached to bogus device!\n");
return 0;
}
/* attach by bogus devname */
sanei_usb_attach_matching_devices ("bogus", dummy_attach);
/* flag must not be set */
if (dummy_flag != 0)
{
printf ("ERROR: shouldn't be attached to bogus device!\n");
return 0;
}
/* remove mock device */
device_number--;
free (devices[device_number].devname);
devices[device_number].devname = NULL;
dummy_flag = 0;
return 1;
}
int
main (int __sane_unused__ argc, char **argv)
{
int detected, opened, i;
SANE_Int dn[MAX_DEVICES];
#ifdef HAVE_LIBUSB_LEGACY
printf ("\n%s built with old libusb\n\n", argv[0]);
#endif
#ifdef HAVE_LIBUSB
printf ("\n%s built with libusb-1.0\n\n", argv[0]);
#endif
#ifdef HAVE_USBCALLS
printf ("\n%s built with usbcalls\n\n", argv[0]);
#endif
#if !defined(HAVE_LIBUSB_LEGACY) && !defined(HAVE_LIBUSB) && !defined(HAVE_USBCALLS)
printf ("\n%s relying on deprecated scanner kernel module\n", argv[0]);
#endif
/* start sanei_usb */
assert (test_init (1));
/* test timeout function */
assert (test_timeout ());
/* count available devices */
detected = 0;
for (i = 0; i < device_number; i++)
{
if (devices[i].missing == 0 && devices[i].devname != NULL)
{
detected++;
}
}
printf ("%d devices found.\n", detected);
/* rescan devices : detected count shouldn't change */
assert (test_scan_devices (detected, 0));
/* test corner cases with mock device */
assert (test_store_device ());
/* get vendor/product id for all available devices devname */
assert (test_vendor_by_devname ());
/* get vendor/product id for all available devices id */
assert (test_vendor_by_id ());
/* open all available devices */
assert (test_open_all (dn, detected));
opened = get_opened();
/* rescan devices : detected and opened count shouldn't change */
assert (test_scan_devices (detected, opened));
/* try to open an inexisting device */
assert (test_open_invalid ());
/* increase sanei _sub use count */
assert (test_init (2));
/* there should be still as many detected devices */
assert (count_detected (detected));
/* there should be still as many opened devices */
assert (count_opened (opened));
assert (test_exit (1));
/* there should be still as many opened devices */
assert (count_opened (opened));
/* count devices again , sanei_usb_exit() shouldn't have
* change the count */
assert (count_detected (detected));
/* claim all available devices */
assert (test_claim_all (dn, opened));
/* then release them all */
assert (test_release_all (dn, opened));
/* close all opened devices */
assert (test_close_all (dn, opened));
/* check there is no opened device */
assert (count_opened (0));
/* finally free resources */
assert (test_exit (0));
/* check there is no more devices */
assert (count_detected (0));
/* test attach matching device with a mock */
assert (test_attach ());
/* try to call sanei_usb_exit() when it not initialized */
assert (test_exit (0));
/* scan devices when sanei usb is not initialized */
assert (test_scan_devices (0, 0));
/* we re start use of sanei usb so we check we have left it
* it he correct state after "closing" it. */
printf ("\n============================================================\n");
printf ("restart use of sanei usb after having freed all resources...\n\n");
assert (test_init (1));
/* we should have the same initial count of detected devices */
assert (count_detected (detected));
/* finally free resources */
assert (test_exit (0));
/* all the tests are OK ! */
return 0;
}
/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */