Browse Source

REXM: ADDED: Example automated-testing -WIP-

Ray 1 month ago
parent
commit
b5caef1ffb
1 changed files with 119 additions and 0 deletions
  1. 119 0
      tools/rexm/rexm.c

+ 119 - 0
tools/rexm/rexm.c

@@ -121,6 +121,7 @@ typedef enum {
     OP_VALIDATE = 5,        // Validate examples, using [examples_list.txt] as main source by default
     OP_UPDATE   = 6,        // Validate and update required examples (as far as possible)
     OP_BUILD    = 7,        // Build example for desktop and web, copy web output
+    OP_TEST     = 8,        // Test example: check output LOG WARNINGS
 } rlExampleOperation;
 
 static const char *exCategories[REXM_MAX_EXAMPLE_CATEGORIES] = { "core", "shapes", "textures", "text", "models", "shaders", "audio", "others" };
@@ -413,6 +414,36 @@ int main(int argc, char *argv[])
                 }
             }
         }
+        else if (strcmp(argv[1], "test") == 0)
+        {
+            // Build and test example for PLATFORM_DESKTOP
+            // NOTE: Build outputs to default directory, usually where the .c file is located,
+            // to avoid issues with copying resources (at least on Desktop)
+            if (argc == 2) LOG("WARNING: No example name provided to test\n");
+            else if (argc > 3) LOG("WARNING: Too many arguments provided\n");
+            else
+            {
+                // Support building not only individual examples but categories and "ALL"
+                if ((strcmp(argv[2], "ALL") == 0) || TextInList(argv[2], exCategories, REXM_MAX_EXAMPLE_CATEGORIES))
+                {
+                    // Category/ALL rebuilt requested
+                    strcpy(exRebuildRequested, argv[2]);
+                }
+                else
+                {
+                    // Verify example exists in collection to be removed
+                    char *exColInfo = LoadFileText(exCollectionFilePath);
+                    if (TextFindIndex(exColInfo, argv[2]) != -1) // Example in the collection
+                    {
+                        strcpy(exName, argv[2]); // Register example name
+                        strncpy(exCategory, exName, TextFindIndex(exName, "_"));
+                        opCode = OP_TEST;
+                    }
+                    else LOG("WARNING: TEST: Example requested not available in the collection\n");
+                    UnloadFileText(exColInfo);
+                }
+            }
+        }
 
         // Process command line options arguments
         for (int i = 1; i < argc; i++)
@@ -1487,6 +1518,94 @@ int main(int argc, char *argv[])
             UnloadExamplesData(exCollection);
             //------------------------------------------------------------------------------------------------
 
+        } break;
+        case OP_TEST:
+        {
+            LOG("INFO: Command requested: TEST\n");
+            LOG("INFO: Example to be built and tested: %s\n", exName);
+
+            // Steps to follow
+            // STEP 1: Load example.c and replace required code to inject basic testing code: frames to run
+            //    OPTION 1: Code injection required multiple changes for testing but it does not require raylib changes!
+            //    OPTION 2: Support testing on raylib side: Args processing and events injection: SUPPORT_AUTOMATD_TESTING_SYSTEM, EVENTS_TESTING_MODE
+            // STEP 2: Build example (PLATFORM_DESKTOP)
+            // STEP 3: Run example with arguments: --frames 2 > <example>.out.log
+            // STEP 4: Load <example>.out.log and check "WARNING:" messages -> Some could maybe be ignored
+            // STEP 5: Generate report with results
+
+            // STEP 1: Load example and inject required code
+            //    PROBLEM: As we need to modify the example source code for building, we need to keep a copy or something
+            //      WARNING: If we make a copy and something fails, it could not be restored at the end
+            //    PROBLEM: Trying to build a copy won't work because Makefile is setup to look for specific example on specific path -> No output dir config
+            //    IDEA: Create directory for testing data -> It implies moving files and set working dir...
+            //    SOLUTION: Make a copy of original file -> Modify original -> Build -> Rename to <example>.test.exe
+            FileCopy(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), 
+                TextFormat("%s/%s/%s.original.c", exBasePath, exCategory, exName));
+            char *srcText = LoadFileText(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName));
+
+            static const char *mainReplaceText =
+                "#include <string.h>\n"
+                "#include <stdlib.h>\n"
+                "int main(int argc, char *argv[])\n{\n"
+                "    int requestedTestFrames = 0;\n"
+                "    int testFramesCount = 0;\n"
+                "    if ((argc > 1) && (argc == 3) && (strcmp(argv[1], \"--frames\") != 0)) requestedTestFrames = atoi(argv[2]);\n";
+
+            char *srcTextUpdated[3] = { 0 };
+            srcTextUpdated[0] = TextReplace(srcText, "int main(void)\n{", mainReplaceText);
+            srcTextUpdated[1] = TextReplace(srcTextUpdated[0], "WindowShouldClose()", "WindowShouldClose() && (testFramesCount < requestedTestFrames)");
+            srcTextUpdated[2] = TextReplace(srcTextUpdated[1], "EndDrawing();", "EndDrawing(); testFramesCount++;");
+            UnloadFileText(srcText);
+
+            SaveFileText(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), srcTextUpdated[2]);
+            for (int i = 0; i < 3; i++) { MemFree(srcTextUpdated[i]); srcTextUpdated[i] = NULL; }
+
+            // STEP 2: Build example for DESKTOP platform
+#if defined(_WIN32)
+            // Set required environment variables
+            //putenv(TextFormat("RAYLIB_DIR=%s\\..", exBasePath));
+            _putenv("PATH=%PATH%;C:\\raylib\\w64devkit\\bin");
+            //putenv("MAKE=mingw32-make");
+            //ChangeDirectory(exBasePath);
+#endif
+            // Build example for PLATFORM_DESKTOP
+#if defined(_WIN32)
+            LOG("INFO: [%s] Building example for PLATFORM_DESKTOP (Host: Win32)\n", exName);
+            system(TextFormat("mingw32-make -C %s %s/%s PLATFORM=PLATFORM_DESKTOP -B", exBasePath, exCategory, exName));
+#else
+            LOG("INFO: [%s] Building example for PLATFORM_DESKTOP (Host: POSIX)\n", exName);
+            system(TextFormat("make -C %s %s/%s PLATFORM=PLATFORM_DESKTOP -B", exBasePath, exCategory, exName));
+#endif
+            // Restore original source code before continue
+            FileCopy(TextFormat("%s/%s/%s.original.c", exBasePath, exCategory, exName), 
+                TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName));
+            FileRemove(TextFormat("%s/%s/%s.original.c", exBasePath, exCategory, exName));
+
+            // STEP 3: Run example with required arguments
+            ChangeDirectory(TextFormat("%s/%s", exBasePath, exCategory));
+            system(TextFormat("%s --frames 2 > %s.log", exName, exName));
+
+            // STEP 4: Load and validate log -> WARNINGS
+            char *exTestLog = LoadFileText(TextFormat("%s/%s/%s.log", exBasePath, exCategory, exName));
+            int exTestLogLinesCount = 0;
+            char **exTestLogLines = LoadTextLines(exTestLog, &exTestLogLinesCount);
+            UnloadFileText(exTestLog);
+
+            int issueCounter = false;
+            for (int i = 0; i < exTestLogLinesCount; i++)
+            {
+                if (TextFindIndex(exTestLogLines[i], "WARNING") >= 0)
+                {
+                    LOG("TEST: [%s] %s\n", exName, exTestLogLines[i]);
+                    issueCounter++;
+                }
+            }
+
+            UnloadTextLines(exTestLogLines, exTestLogLinesCount);
+
+            // STEP 5: Generate auto-test report
+            //if (issueCounter > 0)
+
         } break;
         default:    // Help
         {