/****************************************************************************** * * 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 #include #include #include #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(false), 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; }