From 3e9fa75023cc4c918e4547997e6579e01a6e551d Mon Sep 17 00:00:00 2001 From: Moncef STITI <moncef.stiti@etu.u-pec.fr> Date: Fri, 7 Mar 2025 22:13:57 +0100 Subject: [PATCH] =?UTF-8?q?Am=C3=A9lioration=20de=20la=20javadoc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bakefile.jar | Bin 12101 -> 0 bytes src/fr/monlouyan/bakefile/BakeCLI.java | 4 +- src/fr/monlouyan/bakefile/BakeEngine.java | 49 +- src/fr/monlouyan/bakefile/BakefileParser.java | 42 + .../monlouyan/bakefile/CommandExecutor.java | 38 +- .../bakefile/DependencyResolver.java | 44 +- src/fr/monlouyan/bakefile/Main.java | 9 +- src/fr/monlouyan/bakefile/Rule.java | 52 +- .../monlouyan/bakefile/TimestampManager.java | 11 +- .../bakefile/tests/BakeTestRunner.java | 884 ------------------ tests/bakefile.jar | Bin 12101 -> 0 bytes 11 files changed, 234 insertions(+), 899 deletions(-) delete mode 100644 bakefile.jar delete mode 100644 src/fr/monlouyan/bakefile/tests/BakeTestRunner.java delete mode 100644 tests/bakefile.jar diff --git a/bakefile.jar b/bakefile.jar deleted file mode 100644 index 875b1b3bdfb568a74d7ba081bfe6268d33fcb5b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12101 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 N*1tB+z<>Qe{|`rCu-*Uw diff --git a/src/fr/monlouyan/bakefile/BakeCLI.java b/src/fr/monlouyan/bakefile/BakeCLI.java index b909324..3fba9cc 100644 --- a/src/fr/monlouyan/bakefile/BakeCLI.java +++ b/src/fr/monlouyan/bakefile/BakeCLI.java @@ -8,7 +8,6 @@ import java.util.ArrayList; * * @author Moncef STITI * @version 1.0 - * @date 03/02/2025 * @see Main */ public class BakeCLI { @@ -26,7 +25,6 @@ public class BakeCLI { * Constructeur de la classe BakeCLI * * @param args Les arguments passés en ligne de commande - * @return void * @see Main */ public BakeCLI(String[] args){ @@ -58,7 +56,7 @@ public class BakeCLI { /** * Permet de récupérer les arguments autres que "-d" passés en ligne de commande - * @return + * @return La liste des arguments */ public static List<String> getTargets(){ return targets; } } diff --git a/src/fr/monlouyan/bakefile/BakeEngine.java b/src/fr/monlouyan/bakefile/BakeEngine.java index a536064..22b5a7d 100644 --- a/src/fr/monlouyan/bakefile/BakeEngine.java +++ b/src/fr/monlouyan/bakefile/BakeEngine.java @@ -4,36 +4,83 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +/** + * Moteur principal de l'application Bake. + * Cette classe est responsable de l'orchestration du processus de build + * en utilisant les règles définies dans le fichier Bakefile. + * + * @author Moncef STITI, Yanis HAMOUDI, Louay DARDOURI + * @version 1.0 + */ public class BakeEngine { + + /** Analyseur syntaxique du fichier Bakefile */ private BakefileParser parser; + + /** Responsable de la résolution des dépendances entre règles */ private DependencyResolver resolver; + + /** Exécuteur des commandes définies dans les règles */ private CommandExecutor executor; + + /** Map qui stocke toutes les règles indexées par leur nom */ private static Map<String, Rule> ruleMap; + /** + * Constructeur qui initialise le moteur Bake. + * Crée un parser pour le fichier "Bakefile" par défaut et initialise le résolveur de dépendances. + */ public BakeEngine() { this.parser = new BakefileParser("Bakefile"); this.resolver = new DependencyResolver(BakeCLI.isDebug()); ruleMap = new HashMap<>(); } + /** + * Vérifie si une règle avec le nom spécifié existe. + * + * @param target Nom de la règle à vérifier + * @return true si la règle existe, false sinon + */ public static boolean hasRule(String target) { return ruleMap.containsKey(target); } + + /** + * Récupère une règle par son nom. + * + * @param target Nom de la règle à récupérer + * @return La règle correspondante ou null si elle n'existe pas + */ public static Rule getRule(String target) { return ruleMap.get(target); } + /** + * Exécute le processus de build complet. + * Cette méthode réalise les étapes suivantes : + * <ol> + * <li>Parse le fichier Bakefile</li> + * <li>Construit la carte des règles</li> + * <li>Résout les dépendances entre les règles</li> + * <li>Exécute les règles dans l'ordre déterminé</li> + * </ol> + */ public void run() { + // Analyse le fichier Bakefile pour extraire les règles List<Rule> rules = parser.parse(); + // Ajoute toutes les règles à la map pour référence rapide for (Rule rule : rules) { ruleMap.put(rule.getName(), rule); } + // Résout les dépendances pour déterminer l'ordre d'exécution List<Rule> rulesToBuild = resolver.resolve(rules, BakeCLI.getTargets()); this.executor = new CommandExecutor(BakeCLI.isDebug(), resolver.isCircular()); - + + // Exécute les règles dans l'ordre déterminé for (Rule rule : rulesToBuild) { executor.execute(rule); } diff --git a/src/fr/monlouyan/bakefile/BakefileParser.java b/src/fr/monlouyan/bakefile/BakefileParser.java index 711312a..6f6b039 100644 --- a/src/fr/monlouyan/bakefile/BakefileParser.java +++ b/src/fr/monlouyan/bakefile/BakefileParser.java @@ -8,6 +8,14 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; +/** + * Parseur de fichier Bakefile. + * Cette classe est responsable de l'analyse syntaxique du fichier Bakefile + * pour extraire les règles de build, les dépendances et les commandes associées. + * + * @author Moncef STITI, Yanis HAMOUDI, Louay DARDOURI + * @version 1.0 + */ public class BakefileParser { /** @@ -62,11 +70,21 @@ public class BakefileParser { */ private Map<String, String> variables = new HashMap<>(); + /** + * Constructeur de la classe BakefileParser. + * @param filename Nom du fichier Bakefile à parser + */ public BakefileParser(String filename) { this.filename = filename; firstTarget = null; } + /** + * Gérer les lignes de continuation. + * @param lines Liste des lignes du fichier Bakefile + * @param startIndex Index de la première ligne de continuation + * @return La ligne combinée + */ private String handleContinuationLines(List<String> lines, int startIndex) { StringBuilder combinedLine = new StringBuilder(); int i = startIndex; @@ -89,6 +107,11 @@ public class BakefileParser { return combinedLine.toString(); } + /** + * Remplacer les variables dans une chaîne. + * @param input Chaîne à traiter + * @return Chaîne avec les variables remplacées + */ private String replaceVariables(String input) { if (input == null) return null; @@ -126,12 +149,22 @@ public class BakefileParser { return result.trim(); } + /** + * Remplacer les variables dans une liste de chaînes. + * @param items Liste de chaînes à traiter + * @return Liste de chaînes avec les variables remplacées + */ private List<String> replaceVariablesInList(List<String> items) { return items.stream() .map(this::replaceVariables) .collect(Collectors.toList()); } + /** + * Découper les dépendances en une liste de chaînes. + * @param depStr Chaîne de dépendances + * @return Liste de dépendances + */ private List<String> splitDependencies(String depStr) { if (depStr == null || depStr.trim().isEmpty()) { return new ArrayList<>(); @@ -144,6 +177,11 @@ public class BakefileParser { .collect(Collectors.toList()); } + /** + * Découper les cibles en une liste de chaînes. + * @param targetStr Chaîne de cibles + * @return Liste de cibles + */ private List<String> splitTargets(String targetStr) { if (targetStr == null || targetStr.trim().isEmpty()) { return new ArrayList<>(); @@ -156,6 +194,10 @@ public class BakefileParser { .collect(Collectors.toList()); } + /** + * Analyser le fichier Bakefile pour extraire les règles de build. + * @return Liste des règles extraites + */ public List<Rule> parse() { List<Rule> rules = new ArrayList<>(); Set<String> phonyTargets = new HashSet<>(); diff --git a/src/fr/monlouyan/bakefile/CommandExecutor.java b/src/fr/monlouyan/bakefile/CommandExecutor.java index 4d1287e..5aeaa02 100644 --- a/src/fr/monlouyan/bakefile/CommandExecutor.java +++ b/src/fr/monlouyan/bakefile/CommandExecutor.java @@ -3,17 +3,49 @@ package fr.monlouyan.bakefile; import java.io.File; import java.io.IOException; +/** + * Exécuteur des commandes définies dans les règles. + * Cette classe est responsable de l'exécution des commandes définies dans les règles + * du fichier Bakefile. + * + * @author Moncef STITI, Yanis HAMOUDI, Louay DARDOURI + * @version 1.0 + */ public class CommandExecutor { + /** + * true si le mode debug est activé, false sinon + */ private boolean debug; - private boolean needsUpdate = false; // Pour tracker si quelque chose doit être mis à jour - private boolean isCircular = false; // Pour tracker si un cycle a été détecté - private boolean futureTimestampDetected = false; // Pour détecter les timestamps dans le futur + /** + * Pour tracker si quelque chose doit être mis à jour + */ + private boolean needsUpdate = false; + + /** + * Pour tracker si un cycle a été détecté + */ + private boolean isCircular = false; + + /** + * Pour détecter les timestamps dans le futur + */ + private boolean futureTimestampDetected = false; + + /** + * Constructeur de la classe CommandExecutor. + * @param debug true si le mode debug est activé, false sinon + * @param isCircular true si on est en mode circulaire, false sinon + */ public CommandExecutor(boolean debug, boolean isCircular) { this.debug = debug; this.isCircular = isCircular; } + /** + * Exécute les commandes d'une règle. + * @param rule La règle à exécuter + */ public void execute(Rule rule) { // On vérifie d'abord si cette règle a besoin d'être mise à jour boolean ruleNeedsUpdate = rule.needsUpdate(); diff --git a/src/fr/monlouyan/bakefile/DependencyResolver.java b/src/fr/monlouyan/bakefile/DependencyResolver.java index 26c5378..b2574b4 100644 --- a/src/fr/monlouyan/bakefile/DependencyResolver.java +++ b/src/fr/monlouyan/bakefile/DependencyResolver.java @@ -2,16 +2,40 @@ package fr.monlouyan.bakefile; import java.util.*; +/** + * Résolveur de dépendances entre les règles du fichier Bakefile. + * Cette classe est responsable de la résolution des dépendances entre les règles + * pour déterminer l'ordre dans lequel les règles doivent être + * + * @author Moncef STITI, Yanis HAMOUDI, Louay DARDOURI + * @version 1.0 + */ public class DependencyResolver { + + /*** true si le mode debug est activé, false sinon */ private boolean debug; + + /*** Map des règles indexées par leur nom */ private Map<String, Rule> ruleMap; + + /*** true si une dépendance circulaire est détectée, false sinon */ private boolean isCircular; - + + /** + * Constructeur de la classe DependencyResolver. + * @param debug true si le mode debug est activé, false sinon + */ public DependencyResolver(boolean debug) { this.debug = debug; this.isCircular = false; } + /** + * Résout les dépendances entre les règles. + * @param allRules Liste de toutes les règles + * @param requestedRules Liste des règles demandées + * @return Liste + */ public List<Rule> resolve(List<Rule> allRules, List<String> requestedRules) { List<Rule> rulesToBuild = new ArrayList<>(); ruleMap = new HashMap<>(); @@ -64,6 +88,12 @@ public class DependencyResolver { return rulesToBuild; } + /** + * Méthode de tri topologique pour résoudre les dépendances. + * @param ruleName Nom de la règle + * @param processed Set des règles déjà traitées + * @param buildOrder Liste des règles dans l'ordre + */ private void topologicalSort(String ruleName, Set<String> processed, List<String> buildOrder) { if (!ruleMap.containsKey(ruleName) || processed.contains(ruleName)) { return; @@ -85,7 +115,13 @@ public class DependencyResolver { buildOrder.add(ruleName); } - // La méthode detectCycle avec une modification pour les chemins home + /** + * Méthode de détection des cycles dans les dépendances. + * @param ruleName Nom de la règle + * @param visited Set des règles déjà visitées + * @param stack Set des règles en cours de traitement + * @param parent Nom de la règle parente + */ private void detectCycle(String ruleName, Set<String> visited, Set<String> stack, String parent) { if (stack.contains(ruleName)) { if (parent != null) { @@ -121,6 +157,10 @@ public class DependencyResolver { } } + /** + * Vérifie si une dépendance circulaire a été détectée. + * @return true si une dépendance circulaire a été détectée, false sinon + */ public boolean isCircular() { return isCircular; } diff --git a/src/fr/monlouyan/bakefile/Main.java b/src/fr/monlouyan/bakefile/Main.java index 3f455f1..ee4e909 100755 --- a/src/fr/monlouyan/bakefile/Main.java +++ b/src/fr/monlouyan/bakefile/Main.java @@ -5,15 +5,20 @@ package fr.monlouyan.bakefile; * * @version 1.0 * @author Moncef STITI - * @date 03/02/2025 */ public class Main{ + /** + * Constructeur de la classe Main (privé pour empêcher l'instanciation) + */ + private Main() { + // Constructeur inutile + } + /** * Méthode principale du programme * * @param args Les arguments passés en ligne de commande - * @return void */ public static void main(String[] args){ @SuppressWarnings("unused") diff --git a/src/fr/monlouyan/bakefile/Rule.java b/src/fr/monlouyan/bakefile/Rule.java index 2a1d074..f2f3648 100644 --- a/src/fr/monlouyan/bakefile/Rule.java +++ b/src/fr/monlouyan/bakefile/Rule.java @@ -2,14 +2,36 @@ package fr.monlouyan.bakefile; import java.io.File; import java.util.List; -import java.util.Date; +/** + * Représente une règle du fichier Bakefile. + * Chaque règle est composée d'un nom, d'une liste de dépendances et d'une liste de commandes. + * Une règle peut être phony, c'est-à-dire qu'elle n'a pas de fichier cible associé. + * + * @author Moncef STITI, Yanis HAMOUDI, Louay DARDOURI + * @version 1.0 + */ public class Rule { + + /*** Nom de la règle */ private String name; + + /*** Liste des dépendances de la règle */ private List<String> dependencies; + + /*** Liste des commandes */ private List<String> commands; + + /*** true si la règle est phony, false sinon */ private boolean isPhony; + /** + * Constructeur de la classe Rule. + * @param name Nom de la règle + * @param dependencies Liste des dépendances + * @param commands Liste des commandes + * @param isPhony true si la règle est phony, false sinon + */ public Rule(String name, List<String> dependencies, List<String> commands, boolean isPhony) { this.name = name; this.dependencies = dependencies; @@ -17,12 +39,40 @@ public class Rule { this.isPhony = isPhony; } + /** + * Récupère le nom de la règle. + * @return Le nom de la règle + */ public String getName() { return name; } + + /** + * Récupère la liste des dépendances de la règle. + * @return La liste des dépendances + */ public List<String> getDependencies() { return dependencies; } + + /** + * Récupère la liste des commandes de la règle. + * @return La liste des commandes + */ public List<String> getCommands() { return commands; } + + /** + * Vérifie si la règle est phony. + * @return true si la règle est phony, false sinon. + */ public boolean isPhony() { return isPhony; } + + /** + * Vérifie si la règle est vide (sans dépendances ni commandes). + * @return true si la règle est vide, false sinon + */ public boolean isEmpty() { return dependencies.isEmpty() && commands.isEmpty(); } + /** + * Vérifie si la règle doit être mise à jour. + * @return true si la règle doit être mise à jour, false sinon + */ public boolean needsUpdate() { if (BakeCLI.isDebug()){ System.out.println("Debug : Checking if rule " + name + " needs update"); diff --git a/src/fr/monlouyan/bakefile/TimestampManager.java b/src/fr/monlouyan/bakefile/TimestampManager.java index 6ffac34..76f8cfc 100644 --- a/src/fr/monlouyan/bakefile/TimestampManager.java +++ b/src/fr/monlouyan/bakefile/TimestampManager.java @@ -6,18 +6,23 @@ import java.util.Date; /** * Classe utilitaire pour la gestion des timestamps des fichiers. - * Dernière modification : 04/02/2025 * * @author Moncef STITI, Yanis HAMOUDI * @version 1.0 - * @date 04/02/2025 */ public class TimestampManager { + /** + * Constructeur de TimestampManager. + */ + public TimestampManager() { + // Constructeur inutile + } + /** * Récupère le timestamp d'un fichier. - * @param filePath Le chemin du fichier. * @return Le timestamp du fichier, ou 0 si le fichier n'existe pas. + * @param file Le fichier */ public static long getTimestamp(File file) { if (file.exists()) { diff --git a/src/fr/monlouyan/bakefile/tests/BakeTestRunner.java b/src/fr/monlouyan/bakefile/tests/BakeTestRunner.java deleted file mode 100644 index 2f2cfae..0000000 --- a/src/fr/monlouyan/bakefile/tests/BakeTestRunner.java +++ /dev/null @@ -1,884 +0,0 @@ -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.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)); - - // Panel du haut avec contrôles modernisés - JPanel controlPanel = new JPanel(); - controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.Y_AXIS)); - - // Première ligne de contrôles - JPanel buttonPanel = 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"); - - // Styliser les boutons - runSelectedButton.setBackground(new Color(255, 255, 255)); // Steel Blue - runSelectedButton.setForeground(Color.BLACK); - runSelectedButton.setFont(new Font("Dialog", Font.BOLD, 12)); - - runAllButton.setBackground(new Color(255, 255, 255)); // Medium Sea Green - runAllButton.setForeground(Color.BLACK); - runAllButton.setFont(new Font("Dialog", Font.BOLD, 12)); - - compareSelectedButton.setBackground(new Color(255, 255, 255)); // Dodger Blue - compareSelectedButton.setForeground(Color.BLACK); - compareSelectedButton.setFont(new Font("Dialog", Font.BOLD, 12)); - - buttonPanel.add(runSelectedButton); - buttonPanel.add(runAllButton); - buttonPanel.add(compareSelectedButton); - - // Deuxième ligne de contrôles - JPanel filterPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 5)); - filterPanel.add(new JLabel("Language:")); - languageComboBox = new JComboBox<>(new String[]{"All", "C", "Java"}); - languageComboBox.setPreferredSize(new Dimension(100, 25)); - - openLogsButton = new JButton("Open Logs Directory"); - openLogsButton.setBackground(new Color(211, 211, 211)); // Light Gray - openLogsButton.setFont(new Font("Dialog", Font.PLAIN, 12)); - - filterPanel.add(languageComboBox); - filterPanel.add(Box.createHorizontalStrut(10)); - filterPanel.add(openLogsButton); - - controlPanel.add(buttonPanel); - controlPanel.add(Box.createVerticalStrut(5)); - controlPanel.add(filterPanel); - - // Tableau stylisé pour la liste des tests - String[] columnNames = {"#", "Language", "Test Name", "Status", "Last Run"}; - tableModel = new DefaultTableModel(columnNames, 0) { - @Override - public boolean isCellEditable(int row, int column) { - return false; - } - - @Override - public Class<?> getColumnClass(int column) { - if (column == 0) return Integer.class; - return String.class; - } - }; - - testTable = new JTable(tableModel) { - @Override - public Component prepareRenderer(javax.swing.table.TableCellRenderer renderer, int row, int column) { - Component comp = super.prepareRenderer(renderer, row, column); - - // Alterner les couleurs des lignes - if (!comp.getBackground().equals(getSelectionBackground())) { - String status = (String) getValueAt(row, 3); - if ("PASSED".equals(status)) { - comp.setBackground(PASSED_COLOR); - } else if ("FAILED".equals(status)) { - comp.setBackground(FAILED_COLOR); - } else if ("RUNNING".equals(status)) { - comp.setBackground(RUNNING_COLOR); - } else { - comp.setBackground(row % 2 == 0 ? new Color(240, 240, 250) : Color.WHITE); - } - } - return comp; - } - }; - - testTable.getColumnModel().getColumn(0).setPreferredWidth(30); - testTable.getColumnModel().getColumn(0).setMaxWidth(50); - testTable.getColumnModel().getColumn(1).setPreferredWidth(70); - testTable.getColumnModel().getColumn(1).setMaxWidth(100); - testTable.getColumnModel().getColumn(2).setPreferredWidth(300); - testTable.getColumnModel().getColumn(3).setPreferredWidth(80); - testTable.getColumnModel().getColumn(3).setMaxWidth(120); - testTable.getColumnModel().getColumn(4).setPreferredWidth(150); - - testTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); - testTable.setRowHeight(28); - testTable.setShowGrid(false); - testTable.setIntercellSpacing(new Dimension(0, 0)); - testTable.getTableHeader().setFont(new Font("Dialog", Font.BOLD, 12)); - testTable.getTableHeader().setOpaque(false); - testTable.getTableHeader().setBackground(new Color(240, 240, 240)); - testTable.setFont(new Font("Dialog", Font.PLAIN, 12)); - - // Zone de logs améliorée - logArea = new JTextArea(); - logArea.setEditable(false); - logArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12)); - logArea.setBackground(new Color(250, 250, 250)); - logArea.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); - DefaultCaret caret = (DefaultCaret) logArea.getCaret(); - caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE); - - JPanel logPanel = new JPanel(new BorderLayout()); - logPanel.add(new JLabel(" Test Output:"), BorderLayout.NORTH); - logPanel.add(new JScrollPane(logArea), BorderLayout.CENTER); - - // Barre de progression stylisée - progressBar = new JProgressBar(0, 100); - progressBar.setStringPainted(true); - progressBar.setString("Ready"); - progressBar.setFont(new Font("Dialog", Font.BOLD, 11)); - progressBar.setForeground(new Color(46, 139, 87)); // Sea Green - - // Layout - JSplitPane splitPane = new JSplitPane( - JSplitPane.VERTICAL_SPLIT, - new JScrollPane(testTable), - logPanel - ); - splitPane.setDividerLocation(300); - splitPane.setContinuousLayout(true); - splitPane.setDividerSize(5); - - mainPanel.add(controlPanel, BorderLayout.NORTH); - mainPanel.add(splitPane, BorderLayout.CENTER); - - JPanel statusPanel = new JPanel(new BorderLayout()); - statusPanel.setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 0)); - statusPanel.add(progressBar, BorderLayout.CENTER); - mainPanel.add(statusPanel, 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 = ""; - String comparisonResults = ""; - - // Better parsing - enhanced for clearer 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(); - comparisonResults = content.substring(comparisonIndex).trim(); - } else { - bakeOutput = content.substring(bakeIndex).trim(); - } - } - - JTextPane makeArea = createStyledTextPane(makeOutput); - JTextPane bakeArea = createStyledTextPane(bakeOutput); - JTextPane comparisonArea = createStyledTextPane( - comparisonIndex != -1 ? comparisonResults : "No comparison results available." - ); - - JTabbedPane tabbedPane = new JTabbedPane(); - tabbedPane.addTab("Make Output", new JScrollPane(makeArea)); - tabbedPane.addTab("Bake Output", new JScrollPane(bakeArea)); - tabbedPane.addTab("Comparison", new JScrollPane(comparisonArea)); - tabbedPane.setFont(new Font("Dialog", Font.PLAIN, 12)); - - // Détection et mise en évidence des différences - highlightDifferences(makeArea, bakeArea); - - dialog.add(tabbedPane, BorderLayout.CENTER); - - JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); - JButton closeButton = new JButton("Close"); - closeButton.setFont(new Font("Dialog", Font.BOLD, 12)); - closeButton.addActionListener(e -> dialog.dispose()); - 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); - } - } - - // Méthode pour créer un JTextPane stylisé - private JTextPane createStyledTextPane(String content) { - JTextPane textPane = new JTextPane(); - textPane.setEditable(false); - textPane.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12)); - textPane.setText(content); - textPane.setMargin(new Insets(10, 10, 10, 10)); - textPane.setBackground(new Color(252, 252, 252)); - - // Appliquer une coloration syntaxique basique - highlightKeywords(textPane); - - return textPane; - } - -// Méthode pour mettre en évidence les mots clés -private void highlightKeywords(JTextPane textPane) { - // Obtenez le document et le kit de style - javax.swing.text.StyledDocument doc = textPane.getStyledDocument(); - javax.swing.text.Style defaultStyle = javax.swing.text.StyleContext.getDefaultStyleContext() - .getStyle(javax.swing.text.StyleContext.DEFAULT_STYLE); - - // Créez différents styles pour différents types de texte - javax.swing.text.Style errorStyle = doc.addStyle("errorStyle", defaultStyle); - javax.swing.text.StyleConstants.setForeground(errorStyle, new Color(220, 0, 0)); - javax.swing.text.StyleConstants.setBold(errorStyle, true); - - javax.swing.text.Style successStyle = doc.addStyle("successStyle", defaultStyle); - javax.swing.text.StyleConstants.setForeground(successStyle, new Color(0, 128, 0)); - javax.swing.text.StyleConstants.setBold(successStyle, true); - - // Appliquez les styles aux mots-clés - String text = textPane.getText(); - applyStyle(doc, text, "Error", errorStyle); - applyStyle(doc, text, "error", errorStyle); - applyStyle(doc, text, "ERROR", errorStyle); - applyStyle(doc, text, "failed", errorStyle); - applyStyle(doc, text, "FAILED", errorStyle); - - applyStyle(doc, text, "PASSED", successStyle); - applyStyle(doc, text, "passed", successStyle); - applyStyle(doc, text, "successful", successStyle); -} - -// Méthode auxiliaire pour appliquer un style à un mot-clé spécifique -private void applyStyle(javax.swing.text.StyledDocument doc, String text, String pattern, javax.swing.text.Style style) { - int pos = 0; - while ((pos = text.indexOf(pattern, pos)) >= 0) { - doc.setCharacterAttributes(pos, pattern.length(), style, true); - pos += pattern.length(); - } -} - -// Méthode pour mettre en évidence les différences entre les sorties make et bake -private void highlightDifferences(JTextPane makeArea, JTextPane bakeArea) { - // Cette méthode devrait idéalement comparer les lignes et mettre en évidence les différences - // Pour une première implémentation simple, nous allons juste colorer les lignes contenant des erreurs - - highlightKeywords(makeArea); - highlightKeywords(bakeArea); -} - - 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 diff --git a/tests/bakefile.jar b/tests/bakefile.jar deleted file mode 100644 index 875b1b3bdfb568a74d7ba081bfe6268d33fcb5b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12101 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 N*1tB+z<>Qe{|`rCu-*Uw