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