| /****************************************************************************** |
| * |
| * 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(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; |
| } |