diff --git a/megameklab/src/megameklab/util/UnitUtil.java b/megameklab/src/megameklab/util/UnitUtil.java index 61e2502d7..9cbc9f891 100644 --- a/megameklab/src/megameklab/util/UnitUtil.java +++ b/megameklab/src/megameklab/util/UnitUtil.java @@ -19,7 +19,10 @@ package megameklab.util; import java.awt.Font; +import java.io.BufferedWriter; import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; @@ -32,12 +35,16 @@ import javax.swing.JFrame; import javax.swing.JOptionPane; +import megamek.client.ui.enums.ValidationState; import megamek.common.*; import megamek.common.annotations.Nullable; import megamek.common.equipment.AmmoMounted; import megamek.common.equipment.ArmorType; import megamek.common.equipment.MiscMounted; import megamek.common.equipment.WeaponMounted; +import megamek.common.loaders.BLKFile; +import megamek.common.loaders.EntityLoadingException; +import megamek.common.loaders.EntitySavingException; import megamek.common.verifier.*; import megamek.common.verifier.TestEntity.Ceil; import megamek.common.weapons.AmmoWeapon; @@ -71,6 +78,17 @@ public class UnitUtil { private static Font rsFont = null; private static Font rsBoldFont = null; + public enum UnitValidation { + VALID, + INVALID; + + public static UnitValidation of(boolean valid) { + return valid ? VALID : INVALID; + } + } + + public record Validation(UnitValidation state, String report) { } + /** * tells is EquipementType is an equipment that uses crits/mounted and is * spread across multiple locations @@ -1689,9 +1707,11 @@ public static TestEntity getEntityVerifier(Entity unit) { /** * check that the unit is vaild * + * @deprecated prefer to use the new verify method which uses the validation record * @param unit The entity - * @return + * @return report on the errors */ + @Deprecated public static String validateUnit(Entity unit) { StringBuffer sb = new StringBuffer(); TestEntity testEntity = getEntityVerifier(unit); @@ -1703,6 +1723,28 @@ public static String validateUnit(Entity unit) { return sb.toString(); } + + /** + * verify + *

+ * Checks if the unit is valid, returns a record with a state enum saying if it is valid or not, and a report if it failed. + *

+ * + * @param unit Entity + * @return returns a Validation record with the results of the verification, being an enum saying if it succeeded or failed and the + * report if needed + */ + public static Validation verify(Entity unit) { + StringBuffer sb = new StringBuffer(); + TestEntity testEntity = getEntityVerifier(unit); + + var succeeded = testEntity.correctEntity(sb, unit.getTechLevel()); + + var validation = new Validation(UnitValidation.of(succeeded), sb.toString()); + + return validation; + } + public static void removeAllMiscMounteds(Entity unit, BigInteger flag) { for (int pos = unit.getEquipment().size() - 1; pos >= 0; pos--) { Mounted mount = unit.getEquipment().get(pos); @@ -2140,4 +2182,19 @@ static boolean isNonMekOrTankWeapon(Entity unit, WeaponType weapon) { } return false; } + + public static boolean persistUnit(File outFile, Entity entity) throws EntitySavingException { + if (entity instanceof Mek) { + try (BufferedWriter out = new BufferedWriter(new FileWriter(outFile))) { + out.write(((Mek) entity).getMtf()); + } catch (Exception e) { + System.out.println(e.getMessage()); + return false; + } + return true; + } + + BLKFile.encode(outFile, entity); + return true; + } } diff --git a/megameklab/unittests/megameklab/loaders/BLKFileTest.java b/megameklab/unittests/megameklab/loaders/BLKFileTest.java new file mode 100644 index 000000000..18f58cf14 --- /dev/null +++ b/megameklab/unittests/megameklab/loaders/BLKFileTest.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2024 - The MegaMek Team. All Rights Reserved. + * + * This file is part of MegaMek. + * + * MegaMek is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MegaMek is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MegaMek. If not, see . + */ +package megameklab.loaders; + +import megamek.common.*; +import megamek.common.equipment.ArmorType; +import megamek.common.loaders.EntitySavingException; +import megameklab.util.UnitUtil; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.UUID; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; + +class BLKFileTest { + + @ParameterizedTest(name = "{0}") + @MethodSource("allMekfiles") + void loadVerifyUnit(File unitFIle) { + Entity entity = loadUnit(unitFIle); + var validation = UnitUtil.verify(entity); + assertEquals(validation.state(), UnitUtil.UnitValidation.VALID, + "The unit is invalid:\n\t" + entity.getDisplayName() + "\n" + validation.report()); + } + + // Total of 169 failed entities + // from 4990 entities + @ParameterizedTest(name = "{0}") + @MethodSource("allMekfiles") + void loadVerifySaveVerify(File file) throws EntitySavingException, IOException { + Entity entity = loadUnit(file); + var validation = UnitUtil.verify(entity); + + assertEquals(UnitUtil.UnitValidation.VALID, validation.state(), + "The unit is invalid:\n\t" + entity.getDisplayName() + "\n" + validation.report()); + File tmpFile; + if (entity instanceof Mek) { + tmpFile = File.createTempFile("tmp_mekfiles/" + UUID.randomUUID() + "/" + file.getName(), ".mtf"); + } else { + tmpFile = File.createTempFile("tmp_mekfiles/" + UUID.randomUUID() + "/" + file.getName(), ".blk"); + } + + if (UnitUtil.persistUnit(tmpFile, entity)) { + Entity repersistedEntity = loadUnit(tmpFile); + var reValidation = UnitUtil.verify(repersistedEntity); + assertEquals(UnitUtil.UnitValidation.VALID, reValidation.state(), + "The unit is invalid after repersisting:\n\t" + tmpFile + "\n\t" + entity.getDisplayName() + "\n" + reValidation.report()); + assertEquals(reValidation.state(), validation.state()); + } + } + + public static List allMekfiles() { + initializeStuff(); + try (Stream paths = Files.walk(Paths.get("data/mekfiles"))) { + return paths + .filter(Files::isRegularFile) + .filter(path -> path.toString().endsWith(".blk")) + .map(Path::toFile) + .toList(); + } catch (IOException e) { + // do nothing + } + return List.of(); + } + + private static void initializeStuff() { + EquipmentType.initializeTypes(); + AmmoType.initializeTypes(); + ArmorType.initializeTypes(); + WeaponType.initializeTypes(); + MiscType.initializeTypes(); + BombType.initializeTypes(); + } + + private Entity loadUnit(File file) { + try { + MekFileParser mfp = new MekFileParser(file); + return mfp.getEntity(); + } catch (Exception ex) { + fail(ex.getMessage()); + } + throw new IllegalStateException("Should not reach here"); + } + +}