diff --git a/Makefile b/Makefile
index f6254fc..b468cea 100644
--- a/Makefile
+++ b/Makefile
@@ -42,4 +42,9 @@ jar:
 deploy-tests:
 	@echo "Deploying JAR to 'bake' directories..."
 	@find $(TESTDIR) -type d -name 'bake' -exec cp $(JARNAME) {} \;
+	@echo "Done."
+
+run_test:
+	@echo "Running tests..."
+	@java -cp build fr.monlouyan.bakefile.tests.BakeTestRunner
 	@echo "Done."
\ No newline at end of file
diff --git a/src/fr/monlouyan/bakefile/tests/BakeTestRunner.java b/src/fr/monlouyan/bakefile/tests/BakeTestRunner.java
new file mode 100644
index 0000000..e3a49c9
--- /dev/null
+++ b/src/fr/monlouyan/bakefile/tests/BakeTestRunner.java
@@ -0,0 +1,748 @@
+package fr.monlouyan.bakefile.tests;
+
+import javax.swing.*;
+import javax.swing.border.EmptyBorder;
+import javax.swing.table.DefaultTableModel;
+import javax.swing.text.DefaultCaret;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.*;
+import java.nio.file.*;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+public class BakeTestRunner extends JFrame {
+    private JPanel mainPanel;
+    private JTable testTable;
+    private DefaultTableModel tableModel;
+    private JTextArea logArea;
+    private JButton runSelectedButton;
+    private JButton runAllButton;
+    private JComboBox<String> languageComboBox;
+    private JProgressBar progressBar;
+    private JButton openLogsButton;
+    private JButton compareSelectedButton;
+    
+    private final ExecutorService executor = Executors.newSingleThreadExecutor();
+    private final String baseDir = System.getProperty("user.dir");
+    private final String logsDir = baseDir + File.separator + "logs";
+    private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+    
+    private final Color PASSED_COLOR = new Color(220, 255, 220);
+    private final Color FAILED_COLOR = new Color(255, 220, 220);
+    private final Color RUNNING_COLOR = new Color(220, 220, 255);
+    
+    enum TestStatus {
+        NOT_RUN, RUNNING, PASSED, FAILED
+    }
+    
+    public BakeTestRunner() {
+        setTitle("Bake Test Runner");
+        setSize(1000, 700);
+        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+        setLocationRelativeTo(null);
+        
+        setupUI();
+        loadTests();
+        createLogsDirectory();
+    }
+    
+    private void setupUI() {
+        mainPanel = new JPanel(new BorderLayout(10, 10));
+        mainPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
+        
+        // Top panel with controls
+        JPanel controlPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 5));
+        runSelectedButton = new JButton("Run Selected Tests");
+        runAllButton = new JButton("Run All Tests");
+        compareSelectedButton = new JButton("Compare Selected Test");
+        languageComboBox = new JComboBox<>(new String[]{"All", "C", "Java"});
+        openLogsButton = new JButton("Open Logs Directory");
+        
+        controlPanel.add(runSelectedButton);
+        controlPanel.add(runAllButton);
+        controlPanel.add(compareSelectedButton);
+        controlPanel.add(new JLabel("Language:"));
+        controlPanel.add(languageComboBox);
+        controlPanel.add(openLogsButton);
+        
+        // Table for test list
+        String[] columnNames = {"#", "Language", "Test Name", "Status", "Last Run"};
+        tableModel = new DefaultTableModel(columnNames, 0) {
+            @Override
+            public boolean isCellEditable(int row, int column) {
+                return false;
+            }
+        };
+        testTable = new JTable(tableModel);
+        testTable.getColumnModel().getColumn(0).setPreferredWidth(30);
+        testTable.getColumnModel().getColumn(1).setPreferredWidth(70);
+        testTable.getColumnModel().getColumn(2).setPreferredWidth(300);
+        testTable.getColumnModel().getColumn(3).setPreferredWidth(80);
+        testTable.getColumnModel().getColumn(4).setPreferredWidth(150);
+        testTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+        testTable.setRowHeight(25);
+        
+        // Log area
+        logArea = new JTextArea();
+        logArea.setEditable(false);
+        logArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
+        DefaultCaret caret = (DefaultCaret) logArea.getCaret();
+        caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
+        
+        // Progress bar
+        progressBar = new JProgressBar(0, 100);
+        progressBar.setStringPainted(true);
+        progressBar.setString("Ready");
+        
+        // Layout
+        JSplitPane splitPane = new JSplitPane(
+            JSplitPane.VERTICAL_SPLIT,
+            new JScrollPane(testTable),
+            new JScrollPane(logArea)
+        );
+        splitPane.setDividerLocation(300);
+        
+        mainPanel.add(controlPanel, BorderLayout.NORTH);
+        mainPanel.add(splitPane, BorderLayout.CENTER);
+        mainPanel.add(progressBar, BorderLayout.SOUTH);
+        
+        setContentPane(mainPanel);
+        
+        // Add action listeners
+        runSelectedButton.addActionListener(e -> runSelectedTests());
+        runAllButton.addActionListener(e -> runAllTests());
+        compareSelectedButton.addActionListener(e -> compareSelectedTest());
+        openLogsButton.addActionListener(e -> openLogsDirectory());
+        languageComboBox.addActionListener(e -> filterTestsByLanguage());
+    }
+    
+    private void createLogsDirectory() {
+        try {
+            Files.createDirectories(Paths.get(logsDir));
+        } catch (IOException e) {
+            logMessage("Error creating logs directory: " + e.getMessage());
+        }
+    }
+    
+    private void openLogsDirectory() {
+        try {
+            Desktop.getDesktop().open(new File(logsDir));
+        } catch (IOException e) {
+            logMessage("Error opening logs directory: " + e.getMessage());
+            JOptionPane.showMessageDialog(this, 
+                "Could not open logs directory: " + e.getMessage(), 
+                "Error", JOptionPane.ERROR_MESSAGE);
+        }
+    }
+    
+    private void compareSelectedTest() {
+        int selectedRow = testTable.getSelectedRow();
+        if (selectedRow == -1) {
+            JOptionPane.showMessageDialog(this, 
+                "Please select a test to compare", 
+                "No Test Selected", JOptionPane.WARNING_MESSAGE);
+            return;
+        }
+        
+        String language = (String) tableModel.getValueAt(selectedRow, 1);
+        String testName = (String) tableModel.getValueAt(selectedRow, 2);
+        
+        String logFilePath = logsDir + File.separator + language + "_" + testName + ".log";
+        File logFile = new File(logFilePath);
+        
+        if (!logFile.exists()) {
+            JOptionPane.showMessageDialog(this, 
+                "No log file found for this test. Please run the test first.", 
+                "Log Not Found", JOptionPane.WARNING_MESSAGE);
+            return;
+        }
+        
+        showComparisonDialog(logFile, language, testName);
+    }
+    
+    private void showComparisonDialog(File logFile, String language, String testName) {
+        JDialog dialog = new JDialog(this, "Comparison: " + language + " - " + testName, true);
+        dialog.setLayout(new BorderLayout(10, 10));
+        dialog.setSize(1000, 600);
+        dialog.setLocationRelativeTo(this);
+        
+        try {
+            List<String> lines = Files.readAllLines(logFile.toPath());
+            String content = String.join("\n", lines);
+            
+            // Split content to make and bake sections if possible
+            String makeOutput = "";
+            String bakeOutput = "";
+            
+            // Basic parsing - can be enhanced for better splitting
+            int makeIndex = content.indexOf("=== Make Output ===");
+            int bakeIndex = content.indexOf("=== Bake Output ===");
+            int comparisonIndex = content.indexOf("=== Comparison Results ===");
+            
+            if (makeIndex != -1 && bakeIndex != -1) {
+                makeOutput = content.substring(makeIndex, bakeIndex).trim();
+                if (comparisonIndex != -1) {
+                    bakeOutput = content.substring(bakeIndex, comparisonIndex).trim();
+                } else {
+                    bakeOutput = content.substring(bakeIndex).trim();
+                }
+            }
+            
+            JTextArea makeArea = new JTextArea(makeOutput);
+            JTextArea bakeArea = new JTextArea(bakeOutput);
+            JTextArea comparisonArea = new JTextArea();
+            
+            if (comparisonIndex != -1) {
+                comparisonArea.setText(content.substring(comparisonIndex).trim());
+            } else {
+                comparisonArea.setText("No comparison results available.");
+            }
+            
+            makeArea.setEditable(false);
+            bakeArea.setEditable(false);
+            comparisonArea.setEditable(false);
+            
+            makeArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
+            bakeArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
+            comparisonArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
+            
+            JTabbedPane tabbedPane = new JTabbedPane();
+            tabbedPane.addTab("Make Output", new JScrollPane(makeArea));
+            tabbedPane.addTab("Bake Output", new JScrollPane(bakeArea));
+            tabbedPane.addTab("Comparison", new JScrollPane(comparisonArea));
+            
+            dialog.add(tabbedPane, BorderLayout.CENTER);
+            
+            JButton closeButton = new JButton("Close");
+            closeButton.addActionListener(e -> dialog.dispose());
+            
+            JPanel buttonPanel = new JPanel();
+            buttonPanel.add(closeButton);
+            dialog.add(buttonPanel, BorderLayout.SOUTH);
+            
+            dialog.setVisible(true);
+            
+        } catch (IOException e) {
+            JOptionPane.showMessageDialog(dialog, 
+                "Error reading log file: " + e.getMessage(), 
+                "Error", JOptionPane.ERROR_MESSAGE);
+        }
+    }
+    
+    private void loadTests() {
+        tableModel.setRowCount(0);
+        
+        // Load C tests
+        loadTestsForLanguage("C");
+        
+        // Load Java tests
+        loadTestsForLanguage("Java");
+    }
+    
+    private void loadTestsForLanguage(String language) {
+        File languageDir = new File(baseDir + File.separator + "tests" + File.separator + language);
+        if (!languageDir.exists() || !languageDir.isDirectory()) {
+            logMessage("Warning: Directory not found: " + languageDir.getPath());
+            return;
+        }
+        
+        File[] testDirs = languageDir.listFiles(File::isDirectory);
+        if (testDirs == null) {
+            logMessage("Warning: No test directories found in " + languageDir.getPath());
+            return;
+        }
+        
+        Arrays.sort(testDirs, (a, b) -> {
+            // Extract test number for sorting
+            Pattern pattern = Pattern.compile("test-(\\d+)");
+            Matcher matcherA = pattern.matcher(a.getName());
+            Matcher matcherB = pattern.matcher(b.getName());
+            
+            if (matcherA.find() && matcherB.find()) {
+                try {
+                    int numA = Integer.parseInt(matcherA.group(1));
+                    int numB = Integer.parseInt(matcherB.group(1));
+                    return Integer.compare(numA, numB);
+                } catch (NumberFormatException e) {
+                    return a.getName().compareTo(b.getName());
+                }
+            }
+            return a.getName().compareTo(b.getName());
+        });
+        
+        for (File testDir : testDirs) {
+            String testName = testDir.getName();
+            if (testName.startsWith("test-")) {
+                Object[] row = {tableModel.getRowCount() + 1, language, testName, "Not Run", ""};
+                tableModel.addRow(row);
+            }
+        }
+    }
+    
+    private void filterTestsByLanguage() {
+        String selectedLanguage = (String) languageComboBox.getSelectedItem();
+        if (selectedLanguage == null || selectedLanguage.equals("All")) {
+            loadTests();
+            return;
+        }
+        
+        tableModel.setRowCount(0);
+        loadTestsForLanguage(selectedLanguage);
+    }
+    
+    private void runSelectedTests() {
+        int[] selectedRows = testTable.getSelectedRows();
+        if (selectedRows.length == 0) {
+            JOptionPane.showMessageDialog(this, 
+                "Please select at least one test to run", 
+                "No Test Selected", JOptionPane.WARNING_MESSAGE);
+            return;
+        }
+        
+        List<TestInfo> testsToRun = new ArrayList<>();
+        for (int row : selectedRows) {
+            String language = (String) tableModel.getValueAt(row, 1);
+            String testName = (String) tableModel.getValueAt(row, 2);
+            testsToRun.add(new TestInfo(row, language, testName));
+        }
+        
+        disableButtons();
+        runTests(testsToRun);
+    }
+    
+    private void runAllTests() {
+        List<TestInfo> testsToRun = new ArrayList<>();
+        for (int row = 0; row < tableModel.getRowCount(); row++) {
+            String language = (String) tableModel.getValueAt(row, 1);
+            String testName = (String) tableModel.getValueAt(row, 2);
+            testsToRun.add(new TestInfo(row, language, testName));
+        }
+        
+        disableButtons();
+        runTests(testsToRun);
+    }
+    
+    private void disableButtons() {
+        runSelectedButton.setEnabled(false);
+        runAllButton.setEnabled(false);
+        compareSelectedButton.setEnabled(false);
+        languageComboBox.setEnabled(false);
+    }
+    
+    private void enableButtons() {
+        runSelectedButton.setEnabled(true);
+        runAllButton.setEnabled(true);
+        compareSelectedButton.setEnabled(true);
+        languageComboBox.setEnabled(true);
+    }
+    
+    private void runTests(List<TestInfo> testsToRun) {
+        progressBar.setValue(0);
+        progressBar.setString("Running tests (0/" + testsToRun.size() + ")");
+        logArea.setText("");
+        
+        executor.submit(() -> {
+            try {
+                int total = testsToRun.size();
+                int current = 0;
+                
+                for (int i = 0; i < testsToRun.size(); i++) {
+                    TestInfo test = testsToRun.get(i);
+                    final int currentTest = i + 1;
+                    
+                    // Update UI to show we're running this test
+                    SwingUtilities.invokeLater(() -> {
+                        tableModel.setValueAt(TestStatus.RUNNING.name(), test.row, 3);
+                        testTable.setValueAt(TestStatus.RUNNING.name(), test.row, 3);
+                        testTable.setValueAt(dateFormat.format(new Date()), test.row, 4);
+                        
+                        // Highlight the row
+                        testTable.setRowSelectionInterval(test.row, test.row);
+                        
+                        // Update the progress bar
+                        progressBar.setValue((int)((double)currentTest / total * 100));
+                        progressBar.setString("Running tests (" + currentTest + "/" + total + ")");
+                    });
+                    
+                    // Run the test
+                    logMessage("\n========================================================");
+                    logMessage("Running Test: " + test.language + " - " + test.testName);
+                    logMessage("========================================================");
+                    
+                    boolean success = runTest(test);
+                    
+                    // Update UI with the result
+                    SwingUtilities.invokeLater(() -> {
+                        testTable.setValueAt(success ? TestStatus.PASSED.name() : TestStatus.FAILED.name(), 
+                                         test.row, 3);
+                    });
+                }
+                
+                // Test run complete
+                SwingUtilities.invokeLater(() -> {
+                    progressBar.setValue(100);
+                    progressBar.setString("All tests completed");
+                    enableButtons();
+                    logMessage("\n========================================================");
+                    logMessage("Test run completed at " + dateFormat.format(new Date()));
+                    logMessage("========================================================");
+                });
+            } catch (Exception e) {
+                SwingUtilities.invokeLater(() -> {
+                    logMessage("Error running tests: " + e.getMessage());
+                    for (StackTraceElement element : e.getStackTrace()) {
+                        logMessage("    " + element.toString());
+                    }
+                    progressBar.setString("Error running tests");
+                    enableButtons();
+                });
+            }
+        });
+    }
+    
+    private boolean runTest(TestInfo test) {
+        String testDir = baseDir + File.separator + "tests" + File.separator + 
+                         test.language + File.separator + test.testName;
+        
+        File makeDir = new File(testDir + File.separator + "make");
+        File bakeDir = new File(testDir + File.separator + "bake");
+        
+        if (!makeDir.exists() || !bakeDir.exists()) {
+            logMessage("Error: Make or Bake directory not found for test " + test.testName);
+            return false;
+        }
+        
+        String logFilePath = logsDir + File.separator + test.language + "_" + test.testName + ".log";
+        
+        try (PrintWriter writer = new PrintWriter(new FileWriter(logFilePath))) {
+            // Header information
+            writer.println("Test: " + test.language + " - " + test.testName);
+            writer.println("Date: " + dateFormat.format(new Date()));
+            writer.println("========================================================");
+            
+            // Compare initial file state
+            writer.println("\n=== Initial File Comparison ===");
+            logMessage("Comparing initial files...");
+            
+            Map<String, FileInfo> makeFiles = scanDirectory(makeDir);
+            Map<String, FileInfo> bakeFiles = scanDirectory(bakeDir);
+            
+            compareAndLogFiles(makeFiles, bakeFiles, writer);
+            
+            // Run make
+            writer.println("\n=== Make Output ===");
+            logMessage("Running make...");
+            
+            ProcessResult makeResult = runProcess("make", makeDir);
+            writer.println(makeResult.output);
+            logMessage(makeResult.output);
+            
+            // Run bake
+            writer.println("\n=== Bake Output ===");
+            logMessage("Running bake...");
+            
+            ProcessResult bakeResult = runProcess("java -cp bakefile.jar fr.monlouyan.bakefile.Main", bakeDir);
+            writer.println(bakeResult.output);
+            logMessage(bakeResult.output);
+            
+            // Compare results
+            logMessage("Comparing results...");
+            writer.println("\n=== Comparison Results ===");
+            
+            // Compare exit codes
+            boolean exitCodesMatch = makeResult.exitCode == bakeResult.exitCode;
+            writer.println("Exit codes match: " + exitCodesMatch);
+            writer.println("Make exit code: " + makeResult.exitCode);
+            writer.println("Bake exit code: " + bakeResult.exitCode);
+            
+            // Compare output patterns (ignoring the tool name differences)
+            boolean outputPatternsMatch = compareOutputPatterns(makeResult.output, bakeResult.output);
+            writer.println("Output patterns match: " + outputPatternsMatch);
+            
+            // Compare final file state
+            writer.println("\n=== Final File State Comparison ===");
+            
+            Map<String, FileInfo> makeFinalFiles = scanDirectory(makeDir);
+            Map<String, FileInfo> bakeFinalFiles = scanDirectory(bakeDir);
+            
+            compareAndLogFiles(makeFinalFiles, bakeFinalFiles, writer);
+            
+            // Check if files were created or modified as expected
+            boolean fileChangesMatch = compareFileChanges(makeFiles, makeFinalFiles, bakeFiles, bakeFinalFiles);
+            writer.println("File changes match: " + fileChangesMatch);
+            
+            // Test summary
+            boolean testPassed = exitCodesMatch && outputPatternsMatch && fileChangesMatch;
+            writer.println("\n=== Test Result ===");
+            writer.println(testPassed ? "PASSED" : "FAILED");
+            
+            logMessage(testPassed ? "Test PASSED" : "Test FAILED");
+            
+            return testPassed;
+            
+        } catch (IOException e) {
+            logMessage("Error running test: " + e.getMessage());
+            return false;
+        }
+    }
+    
+    private boolean compareFileChanges(
+            Map<String, FileInfo> makeInitial, 
+            Map<String, FileInfo> makeFinal,
+            Map<String, FileInfo> bakeInitial, 
+            Map<String, FileInfo> bakeFinal) {
+        
+        // Check if the same files were created in both directories
+        Set<String> makeCreated = new HashSet<>(makeFinal.keySet());
+        makeCreated.removeAll(makeInitial.keySet());
+        
+        Set<String> bakeCreated = new HashSet<>(bakeFinal.keySet());
+        bakeCreated.removeAll(bakeInitial.keySet());
+        
+        if (!makeCreated.equals(bakeCreated)) {
+            logMessage("Different files created:\nMake: " + makeCreated + "\nBake: " + bakeCreated);
+            return false;
+        }
+        
+        // Check if the same files were modified
+        boolean filesMatch = true;
+        for (String file : makeInitial.keySet()) {
+            if (makeFinal.containsKey(file) && bakeInitial.containsKey(file) && bakeFinal.containsKey(file)) {
+                boolean makeModified = !makeInitial.get(file).equals(makeFinal.get(file));
+                boolean bakeModified = !bakeInitial.get(file).equals(bakeFinal.get(file));
+                
+                if (makeModified != bakeModified) {
+                    logMessage("File modification mismatch for " + file + 
+                              "\nMake modified: " + makeModified + 
+                              "\nBake modified: " + bakeModified);
+                    filesMatch = false;
+                }
+            }
+        }
+        
+        return filesMatch;
+    }
+    
+    private ProcessResult runProcess(String command, File directory) throws IOException {
+        ProcessBuilder processBuilder = new ProcessBuilder("/bin/sh", "-c", command);
+        processBuilder.directory(directory);
+        Process process = processBuilder.start();
+        
+        // Capture stdout and stderr
+        StringBuilder output = new StringBuilder();
+        try (BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getInputStream()));
+             BufferedReader stdError = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
+            
+            String line;
+            while ((line = stdInput.readLine()) != null) {
+                output.append(line).append("\n");
+            }
+            
+            while ((line = stdError.readLine()) != null) {
+                output.append("ERROR: ").append(line).append("\n");
+            }
+        }
+        
+        try {
+            process.waitFor();
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+        }
+        
+        return new ProcessResult(process.exitValue(), output.toString());
+    }
+    
+    private Map<String, FileInfo> scanDirectory(File directory) throws IOException {
+        Map<String, FileInfo> files = new HashMap<>();
+        
+        if (!directory.exists() || !directory.isDirectory()) {
+            return files;
+        }
+        
+        Files.walkFileTree(directory.toPath(), new SimpleFileVisitor<Path>() {
+            @Override
+            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
+                String relativePath = directory.toPath().relativize(file).toString();
+                
+                // Skip bakefile.jar to avoid differences
+                if (relativePath.equals("bakefile.jar")) {
+                    return FileVisitResult.CONTINUE;
+                }
+                
+                FileInfo info = new FileInfo(
+                    Files.isRegularFile(file),
+                    attrs.size(),
+                    attrs.lastModifiedTime().toMillis()
+                );
+                
+                files.put(relativePath, info);
+                return FileVisitResult.CONTINUE;
+            }
+        });
+        
+        return files;
+    }
+    
+    private void compareAndLogFiles(Map<String, FileInfo> makeFiles, 
+                                   Map<String, FileInfo> bakeFiles, 
+                                   PrintWriter writer) {
+        Set<String> allFiles = new HashSet<>();
+        allFiles.addAll(makeFiles.keySet());
+        allFiles.addAll(bakeFiles.keySet());
+        
+        List<String> sortedFiles = new ArrayList<>(allFiles);
+        Collections.sort(sortedFiles);
+        
+        writer.println("File comparison:");
+        for (String file : sortedFiles) {
+            FileInfo makeInfo = makeFiles.get(file);
+            FileInfo bakeInfo = bakeFiles.get(file);
+            
+            writer.print(file + ": ");
+            if (makeInfo == null) {
+                writer.println("Only in Bake");
+            } else if (bakeInfo == null) {
+                writer.println("Only in Make");
+            } else if (makeInfo.equals(bakeInfo)) {
+                writer.println("Identical");
+            } else {
+                writer.println("Different");
+                writer.println("  Make: " + makeInfo);
+                writer.println("  Bake: " + bakeInfo);
+            }
+        }
+    }
+    
+    private boolean compareOutputPatterns(String makeOutput, String bakeOutput) {
+        // Normalize output by replacing tool-specific words
+        String normalizedMake = makeOutput.replaceAll("\\bmake\\b", "TOOL")
+                                         .replaceAll("\\bMake\\b", "TOOL");
+        String normalizedBake = bakeOutput.replaceAll("\\bbake\\b", "TOOL")
+                                         .replaceAll("\\bBake\\b", "TOOL");
+        
+        // Compare line by line, ignoring exact timestamps or specific paths
+        String[] makeLines = normalizedMake.split("\n");
+        String[] bakeLines = normalizedBake.split("\n");
+        
+        // If line counts are very different, they're probably not matching
+        if (Math.abs(makeLines.length - bakeLines.length) > 2) {
+            logMessage("Output line count mismatch: Make=" + makeLines.length + 
+                      ", Bake=" + bakeLines.length);
+            return false;
+        }
+        
+        // Compare key patterns like error messages, file operations, etc.
+        Pattern errorPattern = Pattern.compile(".*Error.*|.*\\*\\*\\*.*|.*failed.*", 
+                                             Pattern.CASE_INSENSITIVE);
+        Pattern commandPattern = Pattern.compile("^[a-z0-9_\\-]+ .*|^\\$.*");
+        
+        List<String> makeErrors = extractMatches(makeLines, errorPattern);
+        List<String> bakeErrors = extractMatches(bakeLines, errorPattern);
+        
+        List<String> makeCommands = extractMatches(makeLines, commandPattern);
+        List<String> bakeCommands = extractMatches(bakeLines, commandPattern);
+        
+        // If error counts are different, that's a significant difference
+        if (makeErrors.size() != bakeErrors.size()) {
+            logMessage("Error count mismatch: Make=" + makeErrors.size() + 
+                      ", Bake=" + bakeErrors.size());
+            return false;
+        }
+        
+        // If command counts are different, that's a significant difference
+        if (makeCommands.size() != bakeCommands.size()) {
+            logMessage("Command count mismatch: Make=" + makeCommands.size() + 
+                      ", Bake=" + bakeCommands.size());
+            return false;
+        }
+        
+        return true;
+    }
+    
+    private List<String> extractMatches(String[] lines, Pattern pattern) {
+        return Arrays.stream(lines)
+                    .filter(line -> pattern.matcher(line).matches())
+                    .collect(Collectors.toList());
+    }
+    
+    private void logMessage(String message) {
+        SwingUtilities.invokeLater(() -> {
+            logArea.append(message + "\n");
+            // Scroll to bottom
+            logArea.setCaretPosition(logArea.getDocument().getLength());
+        });
+    }
+    
+    private static class TestInfo {
+        int row;
+        String language;
+        String testName;
+        
+        TestInfo(int row, String language, String testName) {
+            this.row = row;
+            this.language = language;
+            this.testName = testName;
+        }
+    }
+    
+    private static class FileInfo {
+        boolean isFile;
+        long size;
+        long lastModified;
+        
+        FileInfo(boolean isFile, long size, long lastModified) {
+            this.isFile = isFile;
+            this.size = size;
+            this.lastModified = lastModified;
+        }
+        
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof FileInfo)) {
+                return false;
+            }
+            FileInfo other = (FileInfo) obj;
+            return isFile == other.isFile && size == other.size;
+            // We don't compare lastModified times directly
+        }
+        
+        @Override
+        public String toString() {
+            return "isFile=" + isFile + ", size=" + size + ", lastModified=" + 
+                   new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(lastModified));
+        }
+    }
+    
+    private static class ProcessResult {
+        int exitCode;
+        String output;
+        
+        ProcessResult(int exitCode, String output) {
+            this.exitCode = exitCode;
+            this.output = output;
+        }
+    }
+    
+    public static void main(String[] args) {
+        SwingUtilities.invokeLater(() -> {
+            try {
+                // Set native look and feel
+                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+            
+            BakeTestRunner runner = new BakeTestRunner();
+            runner.setVisible(true);
+        });
+    }
+}
\ No newline at end of file