SaveFileFormat.java

1
package com.rox.save;
2
3
import com.rox.ByteUtil;
4
5
import java.io.IOException;
6
import java.nio.file.Files;
7
import java.nio.file.Path;
8
import java.util.Optional;
9
10
/**
11
 * Public save-file codec. A missing file, bad magic/version, or a CRC mismatch all read back
12
 * identically as {@link Optional#empty()} - callers treat that exactly like "no save file exists",
13
 * never a crash.
14
 */
15
public final class SaveFileFormat {
16
    /** The board's PRG-RAM size on every currently-supported mapper - see {@code Mapper.prgRam()}. */
17
    private static final int PRG_RAM_SIZE = 0x2000;
18
19
    private SaveFileFormat(){
20
    }
21
22
    public static void writeBatterySave(final Path path, final SaveMetadata metadata, final int[] prgRam) throws IOException {
23 1 1. writeBatterySave : negated conditional → KILLED
        if (prgRam.length != PRG_RAM_SIZE){
24
            throw new IllegalArgumentException("Expected " + PRG_RAM_SIZE + " bytes of PRG-RAM, got " + prgRam.length);
25
        }
26 1 1. writeBatterySave : removed call to com/rox/save/SaveCodec::writeAtomically → KILLED
        SaveCodec.writeAtomically(path, SaveCodec.encode(SaveType.BATTERY_PRG_RAM, metadata, ByteUtil.toBytes(prgRam)));
27
    }
28
29
    public static Optional<BatterySaveFile> readBatterySave(final Path path){
30 1 1. readBatterySave : replaced return value with Optional.empty for com/rox/save/SaveFileFormat::readBatterySave → KILLED
        return readFile(path)
31
                .flatMap(SaveCodec::decode)
32 2 1. lambda$readBatterySave$0 : negated conditional → KILLED
2. lambda$readBatterySave$0 : replaced boolean return with true for com/rox/save/SaveFileFormat::lambda$readBatterySave$0 → KILLED
                .filter(decoded -> decoded.type() == SaveType.BATTERY_PRG_RAM)
33
                //reject here or leave to crash deep inside Mapper.restorePrgRam()'s own length check
34 2 1. lambda$readBatterySave$1 : replaced boolean return with true for com/rox/save/SaveFileFormat::lambda$readBatterySave$1 → KILLED
2. lambda$readBatterySave$1 : negated conditional → KILLED
                .filter(decoded -> decoded.payload().length == PRG_RAM_SIZE)
35 1 1. lambda$readBatterySave$2 : replaced return value with null for com/rox/save/SaveFileFormat::lambda$readBatterySave$2 → KILLED
                .map(decoded -> new BatterySaveFile(decoded.metadata(), ByteUtil.toUnsigned(decoded.payload())));
36
    }
37
38
    public static void writeLiveSnapshot(final Path path, final SaveMetadata metadata, final SystemSnapshot snapshot) throws IOException {
39 1 1. writeLiveSnapshot : removed call to com/rox/save/SaveCodec::writeAtomically → KILLED
        SaveCodec.writeAtomically(path, SaveCodec.encode(SaveType.LIVE_SNAPSHOT, metadata, SnapshotCodec.encode(snapshot)));
40
    }
41
42
    public static Optional<LiveSaveFile> readLiveSnapshot(final Path path){
43 1 1. readLiveSnapshot : replaced return value with Optional.empty for com/rox/save/SaveFileFormat::readLiveSnapshot → KILLED
        return readFile(path)
44
                .flatMap(SaveCodec::decode)
45 2 1. lambda$readLiveSnapshot$0 : replaced boolean return with true for com/rox/save/SaveFileFormat::lambda$readLiveSnapshot$0 → KILLED
2. lambda$readLiveSnapshot$0 : negated conditional → KILLED
                .filter(decoded -> decoded.type() == SaveType.LIVE_SNAPSHOT)
46 1 1. lambda$readLiveSnapshot$1 : replaced return value with Optional.empty for com/rox/save/SaveFileFormat::lambda$readLiveSnapshot$1 → KILLED
                .flatMap(decoded -> SnapshotCodec.decode(decoded.payload(), SystemSnapshot.class)
47 1 1. lambda$readLiveSnapshot$2 : replaced return value with null for com/rox/save/SaveFileFormat::lambda$readLiveSnapshot$2 → KILLED
                        .map(snapshot -> new LiveSaveFile(decoded.metadata(), snapshot)));
48
    }
49
50
    /** A missing or unreadable file is just "no save", never an error. */
51
    private static Optional<byte[]> readFile(final Path path){
52 1 1. readFile : negated conditional → KILLED
        if (!Files.isRegularFile(path)){
53
            return Optional.empty();
54
        }
55
        try {
56 1 1. readFile : replaced return value with Optional.empty for com/rox/save/SaveFileFormat::readFile → KILLED
            return Optional.of(Files.readAllBytes(path));
57
        } catch (IOException e){
58
            //Something changed between isRegularFile and here (TOCTOU) or permissions check fail -> No usable save
59
            return Optional.empty();
60
        }
61
    }
62
}

Mutations

23

1.1
Location : writeBatterySave
Killed by : com.rox.save.SaveFileFormatTest.[engine:junit-jupiter]/[class:com.rox.save.SaveFileFormatTest]/[method:writeBatterySaveRejectsTheWrongPrgRamSize(java.nio.file.Path)]
negated conditional → KILLED

26

1.1
Location : writeBatterySave
Killed by : com.rox.save.SaveFileFormatTest.[engine:junit-jupiter]/[class:com.rox.save.SaveFileFormatTest]/[method:writeCreatesParentDirectoriesThatDoNotYetExist(java.nio.file.Path)]
removed call to com/rox/save/SaveCodec::writeAtomically → KILLED

30

1.1
Location : readBatterySave
Killed by : com.rox.save.SaveFileFormatTest.[engine:junit-jupiter]/[class:com.rox.save.SaveFileFormatTest]/[method:prgRamBytesAreMaskedTo0To255(java.nio.file.Path)]
replaced return value with Optional.empty for com/rox/save/SaveFileFormat::readBatterySave → KILLED

32

1.1
Location : lambda$readBatterySave$0
Killed by : com.rox.save.SaveFileFormatTest.[engine:junit-jupiter]/[class:com.rox.save.SaveFileFormatTest]/[method:readBatterySaveIsEmptyForAFileOfTheWrongSaveType(java.nio.file.Path)]
negated conditional → KILLED

2.2
Location : lambda$readBatterySave$0
Killed by : com.rox.save.SaveFileFormatTest.[engine:junit-jupiter]/[class:com.rox.save.SaveFileFormatTest]/[method:readBatterySaveIsEmptyForAFileOfTheWrongSaveType(java.nio.file.Path)]
replaced boolean return with true for com/rox/save/SaveFileFormat::lambda$readBatterySave$0 → KILLED

34

1.1
Location : lambda$readBatterySave$1
Killed by : com.rox.save.SaveFileFormatTest.[engine:junit-jupiter]/[class:com.rox.save.SaveFileFormatTest]/[method:readBatterySaveIsEmptyForAPayloadOfTheWrongSize(java.nio.file.Path)]
replaced boolean return with true for com/rox/save/SaveFileFormat::lambda$readBatterySave$1 → KILLED

2.2
Location : lambda$readBatterySave$1
Killed by : com.rox.save.SaveFileFormatTest.[engine:junit-jupiter]/[class:com.rox.save.SaveFileFormatTest]/[method:readBatterySaveIsEmptyForAPayloadOfTheWrongSize(java.nio.file.Path)]
negated conditional → KILLED

35

1.1
Location : lambda$readBatterySave$2
Killed by : com.rox.save.SaveFileFormatTest.[engine:junit-jupiter]/[class:com.rox.save.SaveFileFormatTest]/[method:prgRamBytesAreMaskedTo0To255(java.nio.file.Path)]
replaced return value with null for com/rox/save/SaveFileFormat::lambda$readBatterySave$2 → KILLED

39

1.1
Location : writeLiveSnapshot
Killed by : com.rox.save.SaveFileFormatTest.[engine:junit-jupiter]/[class:com.rox.save.SaveFileFormatTest]/[method:writeThenReadRoundTripsALiveSnapshotAndMetadata(java.nio.file.Path)]
removed call to com/rox/save/SaveCodec::writeAtomically → KILLED

43

1.1
Location : readLiveSnapshot
Killed by : com.rox.save.SaveFileFormatTest.[engine:junit-jupiter]/[class:com.rox.save.SaveFileFormatTest]/[method:writeThenReadRoundTripsALiveSnapshotAndMetadata(java.nio.file.Path)]
replaced return value with Optional.empty for com/rox/save/SaveFileFormat::readLiveSnapshot → KILLED

45

1.1
Location : lambda$readLiveSnapshot$0
Killed by : com.rox.save.SaveFileFormatTest.[engine:junit-jupiter]/[class:com.rox.save.SaveFileFormatTest]/[method:readLiveSnapshotIsEmptyForASnapshotPayloadInAFileTaggedAsAnotherSaveType(java.nio.file.Path)]
replaced boolean return with true for com/rox/save/SaveFileFormat::lambda$readLiveSnapshot$0 → KILLED

2.2
Location : lambda$readLiveSnapshot$0
Killed by : com.rox.save.SaveFileFormatTest.[engine:junit-jupiter]/[class:com.rox.save.SaveFileFormatTest]/[method:readLiveSnapshotIsEmptyForASnapshotPayloadInAFileTaggedAsAnotherSaveType(java.nio.file.Path)]
negated conditional → KILLED

46

1.1
Location : lambda$readLiveSnapshot$1
Killed by : com.rox.save.SaveFileFormatTest.[engine:junit-jupiter]/[class:com.rox.save.SaveFileFormatTest]/[method:writeThenReadRoundTripsALiveSnapshotAndMetadata(java.nio.file.Path)]
replaced return value with Optional.empty for com/rox/save/SaveFileFormat::lambda$readLiveSnapshot$1 → KILLED

47

1.1
Location : lambda$readLiveSnapshot$2
Killed by : com.rox.save.SaveFileFormatTest.[engine:junit-jupiter]/[class:com.rox.save.SaveFileFormatTest]/[method:writeThenReadRoundTripsALiveSnapshotAndMetadata(java.nio.file.Path)]
replaced return value with null for com/rox/save/SaveFileFormat::lambda$readLiveSnapshot$2 → KILLED

52

1.1
Location : readFile
Killed by : com.rox.save.SaveFileFormatTest.[engine:junit-jupiter]/[class:com.rox.save.SaveFileFormatTest]/[method:prgRamBytesAreMaskedTo0To255(java.nio.file.Path)]
negated conditional → KILLED

56

1.1
Location : readFile
Killed by : com.rox.save.SaveFileFormatTest.[engine:junit-jupiter]/[class:com.rox.save.SaveFileFormatTest]/[method:prgRamBytesAreMaskedTo0To255(java.nio.file.Path)]
replaced return value with Optional.empty for com/rox/save/SaveFileFormat::readFile → KILLED

Active mutators

Tests examined


Report generated by PIT 1.22.1