|  | /* | 
|  | * Copyright (C) 2007 The Android Open Source Project | 
|  | * | 
|  | * 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 <assert.h> | 
|  | #include <stdlib.h> | 
|  | #include <stdio.h> | 
|  | #include <sys/types.h> | 
|  | #include <sys/wait.h> | 
|  | #include <unistd.h> | 
|  | #include <ctest/ctest.h> | 
|  |  | 
|  | #define MAX_TESTS 255 | 
|  |  | 
|  | /** Semi-random number used to identify assertion errors. */ | 
|  | #define ASSERTION_ERROR 42 | 
|  |  | 
|  | typedef void TestCase(); | 
|  |  | 
|  | /** A suite of tests. */ | 
|  | typedef struct { | 
|  | int size; | 
|  | const char* testNames[MAX_TESTS]; | 
|  | TestCase* tests[MAX_TESTS]; | 
|  | int currentTest; | 
|  | FILE* out; | 
|  | } TestSuite; | 
|  |  | 
|  | /** Gets the test suite. Creates it if necessary. */ | 
|  | static TestSuite* getTestSuite() { | 
|  | static TestSuite* suite = NULL; | 
|  |  | 
|  | if (suite != NULL) { | 
|  | return suite; | 
|  | } | 
|  |  | 
|  | suite = calloc(1, sizeof(TestSuite)); | 
|  | assert(suite != NULL); | 
|  |  | 
|  | suite->out = tmpfile(); | 
|  | assert(suite->out != NULL); | 
|  |  | 
|  | return suite; | 
|  | } | 
|  |  | 
|  | void addNamedTest(const char* name, TestCase* test) { | 
|  | TestSuite* testSuite = getTestSuite(); | 
|  | assert(testSuite->size <= MAX_TESTS); | 
|  |  | 
|  | int index = testSuite->size; | 
|  | testSuite->testNames[index] = name; | 
|  | testSuite->tests[index] = test; | 
|  |  | 
|  | testSuite->size++; | 
|  | } | 
|  |  | 
|  | /** Prints failures to stderr. */ | 
|  | static void printFailures(int failures) { | 
|  | TestSuite* suite = getTestSuite(); | 
|  |  | 
|  | fprintf(stderr, "FAILURE! %d of %d tests failed. Failures:\n", | 
|  | failures, suite->size); | 
|  |  | 
|  | // Copy test output to stdout. | 
|  | rewind(suite->out); | 
|  | char buffer[512]; | 
|  | size_t read; | 
|  | while ((read = fread(buffer, sizeof(char), 512, suite->out)) > 0) { | 
|  | // TODO: Make sure we actually wrote 'read' bytes. | 
|  | fwrite(buffer, sizeof(char), read, stderr); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** Runs a single test case. */ | 
|  | static int runCurrentTest() { | 
|  | TestSuite* suite = getTestSuite(); | 
|  |  | 
|  | pid_t pid = fork(); | 
|  | if (pid == 0) { | 
|  | // Child process. Runs test case. | 
|  | suite->tests[suite->currentTest](); | 
|  |  | 
|  | // Exit successfully. | 
|  | exit(0); | 
|  | } else if (pid < 0) { | 
|  | fprintf(stderr, "Fork failed."); | 
|  | exit(1); | 
|  | } else { | 
|  | // Parent process. Wait for child. | 
|  | int status; | 
|  | waitpid(pid, &status, 0); | 
|  |  | 
|  | if (!WIFEXITED(status)) { | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | return WEXITSTATUS(status); | 
|  | } | 
|  | } | 
|  |  | 
|  | void runTests() { | 
|  | TestSuite* suite = getTestSuite(); | 
|  |  | 
|  | int failures = 0; | 
|  | for (suite->currentTest = 0; suite->currentTest < suite->size; | 
|  | suite->currentTest++) { | 
|  | // Flush stdout before forking. | 
|  | fflush(stdout); | 
|  |  | 
|  | int result = runCurrentTest(); | 
|  |  | 
|  | if (result != 0) { | 
|  | printf("X"); | 
|  |  | 
|  | failures++; | 
|  |  | 
|  | // Handle errors other than assertions. | 
|  | if (result != ASSERTION_ERROR) { | 
|  | // TODO: Report file name. | 
|  | fprintf(suite->out, "Process failed: [%s] status: %d\n", | 
|  | suite->testNames[suite->currentTest], result); | 
|  | fflush(suite->out); | 
|  | } | 
|  | } else { | 
|  | printf("."); | 
|  | } | 
|  | } | 
|  |  | 
|  | printf("\n"); | 
|  |  | 
|  | if (failures > 0) { | 
|  | printFailures(failures); | 
|  | } else { | 
|  | printf("SUCCESS! %d tests ran successfully.\n", suite->size); | 
|  | } | 
|  | } | 
|  |  | 
|  | void assertTrueWithSource(int value, const char* file, int line, char* message) { | 
|  | if (!value) { | 
|  | TestSuite* suite = getTestSuite(); | 
|  |  | 
|  | fprintf(suite->out, "Assertion failed: [%s:%d] %s: %s\n", file, line, | 
|  | suite->testNames[suite->currentTest], message); | 
|  | fflush(suite->out); | 
|  |  | 
|  | // Exit the process for this test case. | 
|  | exit(ASSERTION_ERROR); | 
|  | } | 
|  | } |