Skip to content

Commit b619242

Browse files
authored
Merged in feature/add-version-check-to-persistence (pull request #520) by rebazer
Feature/add version check to persistence
2 parents 2e79bbb + def67e6 commit b619242

File tree

8 files changed

+100
-27
lines changed

8 files changed

+100
-27
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ Table of Contents
2525

2626
### Breaking Changes
2727

28+
* Reports before version 1.7.0 cannot be loaded anymore. Simply re-run your tests with the new recheck version to create them again.
29+
2830
### Bug Fixes
2931

3032
### New Features

src/main/java/de/retest/recheck/ioerror/ReTestIOException.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ public ReTestIOException( final URI location, final Throwable throwable ) {
1212
this( location, throwable.getMessage(), throwable );
1313
}
1414

15+
public ReTestIOException( final URI location, final String details ) {
16+
super( "Error reading from '" + location + "': " + details );
17+
this.location = location;
18+
}
19+
1520
public ReTestIOException( final URI location, final String details, final Throwable throwable ) {
1621
super( "Error reading from '" + location + "': " + details, throwable );
1722
this.location = location;

src/main/java/de/retest/recheck/ioerror/ReTestLoadException.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ public ReTestLoadException( final URI location, final Throwable throwable ) {
1010
super( location, throwable );
1111
}
1212

13+
public ReTestLoadException( final URI location, final String details ) {
14+
super( location, details );
15+
}
16+
1317
public ReTestLoadException( final URI location, final String details, final Throwable throwable ) {
1418
super( location, details, throwable );
1519
}

src/main/java/de/retest/recheck/persistence/IncompatibleReportVersionException.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,24 @@
66

77
public class IncompatibleReportVersionException extends ReTestLoadException {
88

9+
private static final String MESSAGE =
10+
"Incompatible recheck versions: report was written with %s, but read with %s.";
11+
912
private static final long serialVersionUID = 1L;
1013

1114
private final String writerVersion;
1215
private final String readerVersion;
1316

17+
public IncompatibleReportVersionException( final String writerVersion, final String readerVersion,
18+
final URI location ) {
19+
super( location, String.format( MESSAGE, writerVersion, readerVersion ) );
20+
this.writerVersion = writerVersion;
21+
this.readerVersion = readerVersion;
22+
}
23+
1424
public IncompatibleReportVersionException( final String writerVersion, final String readerVersion,
1525
final URI location, final Throwable throwable ) {
16-
super( location, "Incompatible recheck versions: report was written with " + writerVersion + ", but read with "
17-
+ readerVersion + ".", throwable );
26+
super( location, String.format( MESSAGE, writerVersion, readerVersion ), throwable );
1827
this.writerVersion = writerVersion;
1928
this.readerVersion = readerVersion;
2029
}

src/main/java/de/retest/recheck/persistence/bin/KryoPersistence.java

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@
88
import java.net.URI;
99
import java.nio.file.Path;
1010
import java.nio.file.Paths;
11+
import java.util.HashMap;
12+
import java.util.Map;
1113
import java.util.regex.Matcher;
1214
import java.util.regex.Pattern;
1315

1416
import org.apache.commons.io.FileUtils;
1517
import org.objenesis.strategy.StdInstantiatorStrategy;
16-
import org.slf4j.Logger;
17-
import org.slf4j.LoggerFactory;
1818

1919
import com.esotericsoftware.kryo.Kryo;
2020
import com.esotericsoftware.kryo.Registration;
@@ -26,16 +26,28 @@
2626
import de.retest.recheck.persistence.IncompatibleReportVersionException;
2727
import de.retest.recheck.persistence.Persistable;
2828
import de.retest.recheck.persistence.Persistence;
29+
import de.retest.recheck.report.TestReport;
2930
import de.retest.recheck.util.FileUtil;
3031
import de.retest.recheck.util.VersionProvider;
32+
import lombok.extern.slf4j.Slf4j;
3133
import net.jpountz.lz4.LZ4FrameInputStream;
3234
import net.jpountz.lz4.LZ4FrameOutputStream;
3335

36+
@Slf4j
3437
public class KryoPersistence<T extends Persistable> implements Persistence<T> {
3538

3639
private static final String OLD_RECHECK_VERSION = "an old recheck version (pre 1.5.0)";
3740

38-
private static final Logger logger = LoggerFactory.getLogger( KryoPersistence.class );
41+
private static final Map<Class<?>, Integer> compatibleVersions = createCompatibleVersions();
42+
43+
private static Map<Class<?>, Integer> createCompatibleVersions() {
44+
final Map<Class<?>, Integer> map = new HashMap<>();
45+
46+
// Specify the class and lowest readable version here
47+
map.put( TestReport.class, TestReport.PERSISTENCE_VERSION );
48+
49+
return map;
50+
}
3951

4052
private final Kryo kryo;
4153
private final String version;
@@ -77,16 +89,15 @@ public void save( final URI identifier, final T element ) throws IOException {
7789
FileUtil.ensureFolder( path.toFile() );
7890
try ( final Output output = new Output( new LZ4FrameOutputStream( newOutputStream( path ) ) ) ) {
7991
output.writeString( version );
80-
logger.debug( "Writing {} to {}. Do not write to same identifier or interrupt until done.", element,
92+
log.debug( "Writing {} to {}. Do not write to same identifier or interrupt until done.", element,
8193
identifier );
8294
kryo.writeClassAndObject( output, element );
83-
logger.debug( "Done writing {} to {}", element, identifier );
84-
} catch ( Error | Exception anything ) {
85-
logger.error(
86-
"Error writing to file {}. Deleting what has been written to not leave corrupt file behind...",
87-
identifier, anything );
95+
log.debug( "Done writing {} to {}", element, identifier );
96+
} catch ( final Throwable t ) {
97+
log.error( "Error writing to file {}. Deleting what has been written to not leave corrupt file behind...",
98+
identifier, t );
8899
FileUtils.deleteQuietly( file );
89-
throw anything;
100+
throw t;
90101
}
91102
}
92103

@@ -97,23 +108,34 @@ public T load( final URI identifier ) throws IOException {
97108
String writerVersion = null;
98109
try ( final Input input = new Input( new LZ4FrameInputStream( newInputStream( path ) ) ) ) {
99110
writerVersion = input.readString();
100-
return (T) kryo.readClassAndObject( input );
111+
final T persistable = (T) kryo.readClassAndObject( input );
112+
if ( !isCompatible( persistable ) ) {
113+
throw new IncompatibleReportVersionException( writerVersion, version, identifier );
114+
}
115+
return persistable;
116+
} catch ( final IncompatibleReportVersionException e ) {
117+
throw e;
101118
} catch ( final Exception e ) {
102119
if ( version.equals( writerVersion ) ) {
103120
throw e;
104121
}
105122
if ( isUnknownFormat( writerVersion ) ) {
106123
writerVersion = OLD_RECHECK_VERSION;
107-
// TODO Remove after release of 1.6.0
108-
try ( final Input secondInput = new Input( newInputStream( path ) ) ) {
109-
return (T) kryo.readClassAndObject( secondInput );
110-
}
111-
// remove until here
112124
}
113125
throw new IncompatibleReportVersionException( writerVersion, version, identifier, e );
114126
}
115127
}
116128

129+
private boolean isCompatible( final T persistable ) {
130+
return isCompatible( persistable.getClass(), persistable.version() );
131+
}
132+
133+
// For testing only
134+
boolean isCompatible( final Class<? extends Persistable> clazz, final int version ) {
135+
final Integer minVersion = compatibleVersions.get( clazz );
136+
return minVersion == null || minVersion <= version;
137+
}
138+
117139
private static final Pattern VERSION_CHARS = Pattern.compile( "[\\w\\.\\{\\}\\$]+" );
118140

119141
private static boolean isUnknownFormat( final String writerVersion ) {

src/main/java/de/retest/recheck/report/TestReport.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
public class TestReport extends Persistable {
2525

2626
private static final long serialVersionUID = 1L;
27-
private static final int PERSISTENCE_VERSION = 20;
27+
public static final int PERSISTENCE_VERSION = 21;
2828

2929
@XmlElement( name = "suite" )
3030
private final List<SuiteReplayResult> suiteReplayResults = new ArrayList<>();

src/test/java/de/retest/recheck/persistence/bin/KryoPersistenceTest.java

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import com.esotericsoftware.kryo.KryoException;
2323

2424
import de.retest.recheck.persistence.IncompatibleReportVersionException;
25+
import de.retest.recheck.persistence.Persistable;
2526
import de.retest.recheck.report.TestReport;
2627
import de.retest.recheck.util.VersionProvider;
2728

@@ -67,21 +68,29 @@ void incompatible_version_should_give_persisting_version( @TempDir final Path te
6768
+ VersionProvider.RETEST_VERSION + ", but read with old Version." );
6869
}
6970

71+
@Test
72+
void load_should_not_be_able_to_load_1_6_0_report_version() throws Exception {
73+
final Path report = Paths.get( getClass().getResource( "1.6.0.report" ).toURI() );
74+
75+
final KryoPersistence<TestReport> cut = new KryoPersistence<>();
76+
77+
assertThatThrownBy( () -> cut.load( report.toUri() ) ) //
78+
.isInstanceOf( IncompatibleReportVersionException.class ) //
79+
.hasMessageContaining( "Incompatible recheck versions: report was written with 1.6.0" );
80+
}
81+
7082
@Test
7183
void unknown_version_should_give_correct_error() throws IOException {
7284
final Path file = Paths.get( "src/test/resources/de/retest/recheck/persistence/bin/old.report" );
7385
final URI identifier = file.toUri();
7486

7587
final KryoPersistence<TestReport> differentKryoPersistence = new KryoPersistence<>();
7688

77-
// TODO Reactivate that code after the TODO in KryoPersistence has been adressed (post 1.6.0)
78-
// assertThatThrownBy( () -> differentKryoPersistence.load( identifier ) )
79-
// .isInstanceOf( IncompatibleReportVersionException.class ).hasMessageContaining(
80-
// "Incompatible recheck versions: report was written with an old recheck version (pre 1.5.0), but read with "
81-
// + VersionProvider.RETEST_VERSION + "." );
82-
83-
final TestReport report = differentKryoPersistence.load( identifier );
84-
assertThat( report ).isNotNull();
89+
assertThatThrownBy( () -> differentKryoPersistence.load( identifier ) ) //
90+
.isInstanceOf( IncompatibleReportVersionException.class ) //
91+
.hasMessageContaining(
92+
"Incompatible recheck versions: report was written with an old recheck version (pre 1.5.0), but read with "
93+
+ VersionProvider.RETEST_VERSION + "." );
8594
}
8695

8796
@Test
@@ -95,4 +104,26 @@ void on_error_file_should_be_deleted() throws IOException {
95104
.isInstanceOf( KryoException.class );
96105
assertThat( nonexistent ).doesNotExist();
97106
}
107+
108+
@Test
109+
void isCompatible_should_be_compatible_with_equal_or_higher_value() throws Exception {
110+
final KryoPersistence<TestReport> cut = new KryoPersistence<>();
111+
112+
assertThat( cut.isCompatible( TestReport.class, TestReport.PERSISTENCE_VERSION ) ).isTrue();
113+
assertThat( cut.isCompatible( TestReport.class, TestReport.PERSISTENCE_VERSION + 1 ) ).isTrue();
114+
}
115+
116+
@Test
117+
void isCompatible_should_incompatible_with_lower_value() throws Exception {
118+
final KryoPersistence<TestReport> cut = new KryoPersistence<>();
119+
120+
assertThat( cut.isCompatible( TestReport.class, TestReport.PERSISTENCE_VERSION - 1 ) ).isFalse();
121+
}
122+
123+
@Test
124+
void isCompatible_should_compatible_with_not_present_value() throws Exception {
125+
final KryoPersistence<Persistable> cut = new KryoPersistence<>();
126+
127+
assertThat( cut.isCompatible( Persistable.class, 1 ) ).isTrue();
128+
}
98129
}
Binary file not shown.

0 commit comments

Comments
 (0)