blob: 24cb862fd2372d0588a02c6e0e7500cc54dbcf7f [file] [log] [blame]
/******************************************************************************
*
* Copyright (C) 2014 Google, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
#include <cutils/properties.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include "base.h"
#include "btcore/include/bdaddr.h"
#include "cases/cases.h"
#include "osi/include/config.h"
#include "support/callbacks.h"
#include "support/hal.h"
#include "support/gatt.h"
#include "support/pan.h"
#include "support/rfcomm.h"
// How long the watchdog thread should wait before checking if a test has completed.
// Any individual test will have at least WATCHDOG_PERIOD_SEC and at most
// 2 * WATCHDOG_PERIOD_SEC seconds to complete.
static const int WATCHDOG_PERIOD_SEC = 1 * 60;
static const char *CONFIG_FILE_PATH = "/data/misc/bluedroid/bt_config.conf";
const bt_interface_t *bt_interface;
bt_bdaddr_t bt_remote_bdaddr;
static pthread_t watchdog_thread;
static int watchdog_id;
static bool watchdog_running;
static void *watchdog_fn(void *arg) {
int current_id = 0;
for (;;) {
// Check every second whether this thread should exit and check
// every WATCHDOG_PERIOD_SEC whether we should terminate the process.
for (int i = 0; watchdog_running && i < WATCHDOG_PERIOD_SEC; ++i) {
sleep(1);
}
if (!watchdog_running)
break;
if (current_id == watchdog_id) {
printf("Watchdog detected hanging test suite, aborting...\n");
exit(-1);
}
current_id = watchdog_id;
}
return NULL;
}
static bool is_shell_running(void) {
char property_str[100];
property_get("init.svc.zygote", property_str, NULL);
if (!strcmp("running", property_str)) {
return true;
}
return false;
}
static void print_usage(const char *program_name) {
printf("Usage: %s [options] [test name]\n", program_name);
printf("\n");
printf("Options:\n");
printf(" %-20sdisplay this help text.\n", "--help");
printf(" %-20sdo not run sanity suite.\n", "--insanity");
printf("\n");
printf("Valid test names are:\n");
for (size_t i = 0; i < sanity_suite_size; ++i) {
printf(" %s\n", sanity_suite[i].function_name);
}
for (size_t i = 0; i < test_suite_size; ++i) {
printf(" %s\n", test_suite[i].function_name);
}
}
static bool is_valid(const char *test_name) {
for (size_t i = 0; i < sanity_suite_size; ++i) {
if (!strcmp(test_name, sanity_suite[i].function_name)) {
return true;
}
}
for (size_t i = 0; i < test_suite_size; ++i) {
if (!strcmp(test_name, test_suite[i].function_name)) {
return true;
}
}
return false;
}
int main(int argc, char **argv) {
const char *test_name = NULL;
bool skip_sanity_suite = false;
for (int i = 1; i < argc; ++i) {
if (!strcmp("--help", argv[i])) {
print_usage(argv[0]);
return 0;
}
if (!strcmp("--insanity", argv[i])) {
skip_sanity_suite = true;
continue;
}
if (!is_valid(argv[i])) {
printf("Error: invalid test name.\n");
print_usage(argv[0]);
return -1;
}
if (test_name != NULL) {
printf("Error: invalid arguments.\n");
print_usage(argv[0]);
return -1;
}
test_name = argv[i];
}
if (is_shell_running()) {
printf("Run 'adb shell stop' before running %s.\n", argv[0]);
return -1;
}
config_t *config = config_new(CONFIG_FILE_PATH);
if (!config) {
printf("Error: unable to open stack config file.\n");
print_usage(argv[0]);
return -1;
}
for (const config_section_node_t *node = config_section_begin(config); node != config_section_end(config); node = config_section_next(node)) {
const char *name = config_section_name(node);
if (config_has_key(config, name, "LinkKey") && string_to_bdaddr(name, &bt_remote_bdaddr)) {
break;
}
}
config_free(config);
if (bdaddr_is_empty(&bt_remote_bdaddr)) {
printf("Error: unable to find paired device in config file.\n");
print_usage(argv[0]);
return -1;
}
if (!hal_open(callbacks_get_adapter_struct())) {
printf("Unable to open Bluetooth HAL.\n");
return 1;
}
if (!btsocket_init()) {
printf("Unable to initialize Bluetooth sockets.\n");
return 2;
}
if (!pan_init()) {
printf("Unable to initialize PAN.\n");
return 3;
}
if (!gatt_init()) {
printf("Unable to initialize GATT.\n");
return 4;
}
watchdog_running = true;
pthread_create(&watchdog_thread, NULL, watchdog_fn, NULL);
static const char *DEFAULT = "\x1b[0m";
static const char *GREEN = "\x1b[0;32m";
static const char *RED = "\x1b[0;31m";
// If the output is not a TTY device, don't colorize output.
if (!isatty(fileno(stdout))) {
DEFAULT = GREEN = RED = "";
}
int pass = 0;
int fail = 0;
int case_num = 0;
// If test name is specified, run that specific test.
// Otherwise run through the sanity suite.
if (!skip_sanity_suite) {
for (size_t i = 0; i < sanity_suite_size; ++i) {
if (!test_name || !strcmp(test_name, sanity_suite[i].function_name)) {
callbacks_init();
if (sanity_suite[i].function()) {
printf("[%4d] %-64s [%sPASS%s]\n", ++case_num, sanity_suite[i].function_name, GREEN, DEFAULT);
++pass;
} else {
printf("[%4d] %-64s [%sFAIL%s]\n", ++case_num, sanity_suite[i].function_name, RED, DEFAULT);
++fail;
}
callbacks_cleanup();
++watchdog_id;
}
}
}
// If there was a failure in the sanity suite, don't bother running the rest of the tests.
if (fail) {
printf("\n%sSanity suite failed with %d errors.%s\n", RED, fail, DEFAULT);
hal_close();
return 4;
}
// If test name is specified, run that specific test.
// Otherwise run through the full test suite.
for (size_t i = 0; i < test_suite_size; ++i) {
if (!test_name || !strcmp(test_name, test_suite[i].function_name)) {
callbacks_init();
CALL_AND_WAIT(bt_interface->enable(), adapter_state_changed);
if (test_suite[i].function()) {
printf("[%4d] %-64s [%sPASS%s]\n", ++case_num, test_suite[i].function_name, GREEN, DEFAULT);
++pass;
} else {
printf("[%4d] %-64s [%sFAIL%s]\n", ++case_num, test_suite[i].function_name, RED, DEFAULT);
++fail;
}
CALL_AND_WAIT(bt_interface->disable(), adapter_state_changed);
callbacks_cleanup();
++watchdog_id;
}
}
printf("\n");
if (fail) {
printf("%d/%d tests failed. See above for failed test cases.\n", fail, sanity_suite_size + test_suite_size);
} else {
printf("All tests passed!\n");
}
watchdog_running = false;
pthread_join(watchdog_thread, NULL);
hal_close();
return 0;
}