Skip to content

Commit bef5f67

Browse files
committed
refactor(test): Unify reader tests and use new API
1 parent 4d572f0 commit bef5f67

File tree

4 files changed

+323
-568
lines changed

4 files changed

+323
-568
lines changed
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
package edu.kit.datamanager.ro_crate.reader;
2+
3+
import edu.kit.datamanager.ro_crate.Crate;
4+
import edu.kit.datamanager.ro_crate.HelpFunctions;
5+
import edu.kit.datamanager.ro_crate.RoCrate;
6+
import edu.kit.datamanager.ro_crate.entities.data.DataEntity;
7+
import edu.kit.datamanager.ro_crate.entities.data.FileEntity;
8+
import edu.kit.datamanager.ro_crate.writer.CrateWriter;
9+
import edu.kit.datamanager.ro_crate.writer.Writers;
10+
import org.apache.commons.io.FileUtils;
11+
import org.junit.jupiter.api.Test;
12+
import org.junit.jupiter.api.io.TempDir;
13+
14+
import java.io.*;
15+
import java.nio.charset.Charset;
16+
import java.nio.file.Path;
17+
18+
import static org.junit.jupiter.api.Assertions.*;
19+
20+
/**
21+
* Abstract class for testing crate readers.
22+
*
23+
* @param <SOURCE_T> the source type of the reader strategy. Even though each implementation knows this T,
24+
* we can't use it everywhere we'd like to as the code here needs to be generic.
25+
* We therefore rely on methods to take a path (as we always assume local testing).
26+
* Streams, for example, will therefore need to stream from/to a file.
27+
* This parameter is only required to satisfy the generic reader strategy.
28+
* @param <READER_STRATEGY> the type of the reader strategy
29+
*/
30+
abstract class CrateReaderTest<SOURCE_T, READER_STRATEGY extends GenericReaderStrategy<SOURCE_T>> {
31+
32+
protected static RoCrate.RoCrateBuilder newBaseCrate() {
33+
return new RoCrate.RoCrateBuilder(
34+
"minimal",
35+
"minimal RO_crate",
36+
"2024",
37+
"https://creativecommons.org/licenses/by-nc-sa/3.0/au/"
38+
);
39+
}
40+
41+
protected static FileEntity newDataEntity(Path filePath) throws IllegalArgumentException {
42+
return new FileEntity.FileEntityBuilder()
43+
.setLocationWithExceptions(filePath)
44+
.setId(filePath.toFile().getName())
45+
.addProperty("name", "Survey responses")
46+
.addProperty("contentSize", "26452")
47+
.addProperty("encodingFormat", "text/csv")
48+
.build();
49+
}
50+
51+
/**
52+
* Saves the crate with the writer fitting to the reader of {@link #readCrate(Path)}.
53+
*
54+
* @param crate the crate to save
55+
* @param target the target path to the save location
56+
* @throws IOException if an error occurs while saving the crate
57+
*/
58+
abstract protected void saveCrate(Crate crate, Path target) throws IOException;
59+
60+
/**
61+
* Reads the crate with the reader fitting to the writer of {@link #saveCrate(Crate, Path)}.
62+
* @param source the source path to the crate
63+
* @return the read crate
64+
* @throws IOException if an error occurs while reading the crate
65+
*/
66+
abstract protected Crate readCrate(Path source) throws IOException;
67+
68+
/**
69+
* Creates a new reader strategy with a non-default temporary directory (if supported, default otherwise).
70+
*
71+
* @param tmpDirectory the temporary directory to use
72+
* @param useUuidSubfolder whether to create a UUID subfolder under the temporary directory
73+
* @return a new reader strategy
74+
*/
75+
abstract protected READER_STRATEGY newReaderStrategyWithTmp(Path tmpDirectory, boolean useUuidSubfolder);
76+
77+
/**
78+
* Reads the crate using the provided reader strategy.
79+
*
80+
* @param strategy the reader strategy to use
81+
* @param source the source path to the crate
82+
* @return the read crate
83+
* @throws IOException if an error occurs while reading the crate
84+
*/
85+
abstract protected Crate readCrate(READER_STRATEGY strategy, Path source) throws IOException;
86+
87+
@Test
88+
void testReadingBasicCrate(@TempDir Path temp) throws IOException {
89+
90+
RoCrate roCrate = newBaseCrate().build();
91+
Path zipPath = temp.resolve("result.zip");
92+
this.saveCrate(roCrate, zipPath);
93+
Crate importedCrate = this.readCrate(zipPath);
94+
HelpFunctions.compareTwoCrateJson(roCrate, importedCrate);
95+
}
96+
97+
@Test
98+
void testWithFile(@TempDir Path temp) throws IOException {
99+
Path csvPath = temp.resolve("survey-responses-2019.csv");
100+
FileUtils.touch(csvPath.toFile());
101+
FileUtils.writeStringToFile(csvPath.toFile(), "Dummy content", Charset.defaultCharset());
102+
RoCrate rawCrate = newBaseCrate()
103+
.addDataEntity(newDataEntity(csvPath))
104+
.build();
105+
106+
assertEquals(1, rawCrate.getAllDataEntities().size());
107+
108+
Path zipPath = temp.resolve("result.zip");
109+
this.saveCrate(rawCrate, zipPath);
110+
Crate importedCrate = this.readCrate(zipPath);
111+
112+
HelpFunctions.compareTwoCrateJson(rawCrate, importedCrate);
113+
}
114+
115+
@Test
116+
void testWithFileUrlEncoded(@TempDir Path temp) throws IOException {
117+
// This URL will be encoded because of whitespaces
118+
Path csvPath = temp.resolve("survey responses 2019.csv");
119+
FileUtils.touch(csvPath.toFile());
120+
FileUtils.writeStringToFile(csvPath.toFile(), "Dummy content", Charset.defaultCharset());
121+
RoCrate rawCrate = newBaseCrate()
122+
.addDataEntity(newDataEntity(csvPath))
123+
.build();
124+
125+
DataEntity rawEntity = rawCrate.getAllDataEntities().iterator().next();
126+
assertTrue(rawEntity.getId().contains("survey"));
127+
assertFalse(rawEntity.getId().contains(" "));
128+
assertEquals(1, rawCrate.getAllDataEntities().size());
129+
130+
Path zipPath = temp.resolve("result.zip");
131+
this.saveCrate(rawCrate, zipPath);
132+
Crate importedCrate = this.readCrate(zipPath);
133+
134+
DataEntity importedEntity = importedCrate.getAllDataEntities().iterator().next();
135+
assertTrue(importedEntity.getId().contains("survey"));
136+
assertFalse(importedEntity.getId().contains(" "));
137+
assertEquals(1, importedCrate.getAllDataEntities().size());
138+
139+
HelpFunctions.compareTwoCrateJson(rawCrate, importedCrate);
140+
}
141+
142+
@Test
143+
void TestWithFileWithLocation(@TempDir Path temp) throws IOException {
144+
Path csvPath = temp.resolve("survey-responses-2019.csv");
145+
FileUtils.writeStringToFile(csvPath.toFile(), "Dummy content", Charset.defaultCharset());
146+
RoCrate rawCrate = newBaseCrate()
147+
.addDataEntity(newDataEntity(csvPath))
148+
.setPreview(null)//disable preview to allow to compare folders before and after
149+
.build();
150+
151+
// write to zip file and read via zip stream
152+
Path zipPath = temp.resolve("result.zip");
153+
this.saveCrate(rawCrate, zipPath);
154+
Crate importedCrate = this.readCrate(zipPath);
155+
156+
// write raw crate and imported crate to folders and compare the results
157+
Path rawCrateTarget = temp.resolve("rawCrateSaved");
158+
Path importedCrateTarget = temp.resolve("importedCrateSaved");
159+
{
160+
// write raw crate and imported crate to two different directories
161+
CrateWriter<String> writer = Writers.newFolderWriter();
162+
writer.save(rawCrate, rawCrateTarget.toString());
163+
writer.save(importedCrate, importedCrateTarget.toString());
164+
}
165+
166+
assertTrue(HelpFunctions.compareTwoDir(rawCrateTarget.toFile(), importedCrateTarget.toFile()));
167+
HelpFunctions.compareTwoCrateJson(rawCrate, importedCrate);
168+
}
169+
170+
@Test
171+
void TestWithFileWithLocationAddEntity(@TempDir Path temp) throws IOException {
172+
Path csvPath = temp.resolve("file.csv");
173+
FileUtils.writeStringToFile(csvPath.toFile(), "fakecsv.1", Charset.defaultCharset());
174+
RoCrate rawCrate = newBaseCrate()
175+
.addDataEntity(newDataEntity(csvPath))
176+
.build();
177+
178+
// write to zip file and import via zip stream
179+
Path zipPath = temp.resolve("result.zip");
180+
this.saveCrate(rawCrate, zipPath);
181+
Crate importedCrate = this.readCrate(zipPath);
182+
{
183+
// modify the imported crate
184+
Path newFile = temp.resolve("new_file");
185+
FileUtils.writeStringToFile(newFile.toFile(), "Some file content", Charset.defaultCharset());
186+
importedCrate.addDataEntity(new FileEntity.FileEntityBuilder()
187+
.setEncodingFormat("setnew")
188+
.setLocationWithExceptions(newFile)
189+
.setId("new_file")
190+
.build());
191+
}
192+
// write raw crate to a folder
193+
Path rawCrateTarget = temp.resolve("rawCrateSaved");
194+
Path importedCrateTarget = temp.resolve("importedCrateSaved");
195+
{
196+
// write raw crate and imported crate to two different directories
197+
CrateWriter<String> writer = Writers.newFolderWriter();
198+
writer.save(rawCrate, rawCrateTarget.toString());
199+
writer.save(importedCrate, importedCrateTarget.toFile().toString());
200+
}
201+
// assert the folders are different
202+
assertFalse(HelpFunctions.compareTwoDir(rawCrateTarget.toFile(), importedCrateTarget.toFile()));
203+
HelpFunctions.compareTwoMetadataJsonNotEqual(rawCrate, importedCrate);
204+
// assert the importedCrateTarget folder contains newFile
205+
assertTrue(importedCrateTarget.resolve("new_file").toFile().isFile());
206+
}
207+
208+
@Test
209+
void testReadingBasicCrateWithCustomPath(@TempDir Path temp) throws IOException {
210+
RoCrate rawCrate = newBaseCrate().build();
211+
212+
// Write to zip file
213+
Path zipPath = temp.resolve("result.zip");
214+
this.saveCrate(rawCrate, zipPath);
215+
216+
// read again and compare using custom path for temporary extraction folder
217+
// (if available, otherwise uses default)
218+
Path differentFolder = temp.resolve("differentFolder");
219+
READER_STRATEGY strategy = this.newReaderStrategyWithTmp(differentFolder, true);
220+
Crate importedCrate = this.readCrate(strategy, zipPath);
221+
HelpFunctions.compareTwoCrateJson(rawCrate, importedCrate);
222+
223+
{
224+
// try it again without the UUID subfolder and test if the directory is being cleaned up (for coverage).
225+
READER_STRATEGY strategyWithoutSubfolder = this.newReaderStrategyWithTmp(differentFolder, false);
226+
Crate crate = this.readCrate(strategyWithoutSubfolder, zipPath);
227+
HelpFunctions.compareTwoCrateJson(rawCrate, crate);
228+
}
229+
}
230+
}

0 commit comments

Comments
 (0)