Added unit test for shard io.

This commit is contained in:
Andrew Lalis 2021-05-29 11:18:43 +02:00
parent 4d40431bcd
commit b92aedf872
14 changed files with 379 additions and 80 deletions

113
.gitignore vendored Normal file
View File

@ -0,0 +1,113 @@
### Java template
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### Maven template
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
# https://github.com/takari/maven-wrapper#usage-without-binary-jar
.mvn/wrapper/maven-wrapper.jar
.idea/
clusters/

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333333 4.2333333"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="cluster_node_icon.svg"
inkscape:export-filename="A:\Programming\GitHub-andrewlalis\CrystalKeep\src\main\resources\ui\images\cluster_node_icon.png"
inkscape:export-xdpi="192"
inkscape:export-ydpi="192">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="31.678384"
inkscape:cx="9.6523068"
inkscape:cy="7.5940447"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
units="px" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76667)">
<path
sodipodi:type="star"
style="fill:#f072ff;fill-opacity:1;stroke:none;stroke-width:0.07803907;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke"
id="path817"
sodipodi:sides="9"
sodipodi:cx="2.1166666"
sodipodi:cy="294.93815"
sodipodi:r1="1.8178079"
sodipodi:r2="1.2179312"
sodipodi:arg1="0.52359878"
sodipodi:arg2="0.87266463"
inkscape:flatsided="false"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 3.6909344,295.84704 -0.7913968,0.0241 -0.1611441,0.77519 -0.6217269,-0.49025 -0.621727,0.49025 -0.1611441,-0.77519 -0.79139678,-0.0241 0.37483979,-0.69741 -0.59076328,-0.52715 0.73543197,-0.2933 -0.11370504,-0.78356 0.75190744,0.24804 0.416557,-0.67333 0.4165569,0.67333 0.7519075,-0.24804 -0.1137051,0.78356 0.735432,0.2933 -0.5907633,0.52715 z"
inkscape:transform-center-x="0.06028767"
inkscape:transform-center-y="0.030212626" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333333 4.2333333"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="shard_node_icon.svg"
inkscape:export-filename="A:\Programming\GitHub-andrewlalis\CrystalKeep\src\main\resources\ui\images\shard_node_icon.png"
inkscape:export-xdpi="192"
inkscape:export-ydpi="192">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="31.678384"
inkscape:cx="4.3963569"
inkscape:cy="7.5940447"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
units="px" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76667)">
<path
sodipodi:type="star"
style="fill:#72deff;fill-opacity:1;stroke:none;stroke-width:0.07803907;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke"
id="path817"
sodipodi:sides="9"
sodipodi:cx="2.1166666"
sodipodi:cy="294.93815"
sodipodi:r1="1.8178079"
sodipodi:r2="1.2179312"
sodipodi:arg1="0.52359878"
sodipodi:arg2="0.87266463"
inkscape:flatsided="false"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 3.6909344,295.84704 -0.7913968,0.0241 -0.1611441,0.77519 -0.6217269,-0.49025 -0.621727,0.49025 -0.1611441,-0.77519 -0.79139678,-0.0241 0.37483979,-0.69741 -0.59076328,-0.52715 0.73543197,-0.2933 -0.11370504,-0.78356 0.75190744,0.24804 0.416557,-0.67333 0.4165569,0.67333 0.7519075,-0.24804 -0.1137051,0.78356 0.735432,0.2933 -0.5907633,0.52715 z"
inkscape:transform-center-x="0.06028767"
inkscape:transform-center-y="0.030212626" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

13
pom.xml
View File

@ -48,5 +48,18 @@
<scope>provided</scope> <scope>provided</scope>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.7.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -7,17 +7,13 @@ import javafx.stage.Stage;
import nl.andrewlalis.crystalkeep.control.MainViewController; import nl.andrewlalis.crystalkeep.control.MainViewController;
import nl.andrewlalis.crystalkeep.model.Cluster; import nl.andrewlalis.crystalkeep.model.Cluster;
import nl.andrewlalis.crystalkeep.model.Model; import nl.andrewlalis.crystalkeep.model.Model;
import nl.andrewlalis.crystalkeep.model.Shard;
import nl.andrewlalis.crystalkeep.model.serialization.ClusterLoader; import nl.andrewlalis.crystalkeep.model.serialization.ClusterLoader;
import nl.andrewlalis.crystalkeep.model.serialization.ClusterSerializer;
import nl.andrewlalis.crystalkeep.model.shards.LoginCredentialsShard; import nl.andrewlalis.crystalkeep.model.shards.LoginCredentialsShard;
import nl.andrewlalis.crystalkeep.model.shards.TextShard; import nl.andrewlalis.crystalkeep.model.shards.TextShard;
import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Arrays;
public class CrystalKeep extends Application { public class CrystalKeep extends Application {
public static void main(String[] args) { public static void main(String[] args) {
@ -41,10 +37,10 @@ public class CrystalKeep extends Application {
System.out.println("Loaded existing root cluster."); System.out.println("Loaded existing root cluster.");
} catch (IOException e) { } catch (IOException e) {
rootCluster = new Cluster("Root"); rootCluster = new Cluster("Root");
rootCluster.addShard(new TextShard(rootCluster, "Example Shard", LocalDateTime.now(), "Hello world!")); rootCluster.addShard(new TextShard("Example Shard", LocalDateTime.now(), "Hello world!"));
rootCluster.addShard(new LoginCredentialsShard(rootCluster, "Netflix", LocalDateTime.now(), "user", "secret password")); rootCluster.addShard(new LoginCredentialsShard("Netflix", LocalDateTime.now(), "user", "secret password"));
for (int i = 0; i < 100; i++) { for (int i = 0; i < 100; i++) {
rootCluster.addShard(new TextShard(rootCluster, "test " + i, LocalDateTime.now(), "value: " + i)); rootCluster.addShard(new TextShard("test " + i, LocalDateTime.now(), "value: " + i));
} }
clusterLoader.saveDefault(rootCluster); clusterLoader.saveDefault(rootCluster);
System.out.println("Saved root cluster on first load."); System.out.println("Saved root cluster on first load.");
@ -57,30 +53,4 @@ public class CrystalKeep extends Application {
controller.init(model); controller.init(model);
System.out.println(rootCluster); System.out.println(rootCluster);
} }
public static void test() throws IOException {
Cluster c = new Cluster("Test");
Shard s = new TextShard(c, "sample", LocalDateTime.now(), "Hello world!");
Shard s2 = new LoginCredentialsShard(c, "logs", LocalDateTime.now().plusHours(3), "andrew", "testing");
c.addShard(s);
c.addShard(s2);
Cluster c2 = new Cluster("Test2");
Shard s3 = new TextShard(c2, "another sample", LocalDateTime.now().plusMinutes(3), "Testing this stuff....");
c2.addShard(s3);
Cluster parent = new Cluster("Parent");
parent.addCluster(c);
parent.addCluster(c2);
System.out.println(parent);
long start = System.currentTimeMillis();
byte[] cBytes = ClusterSerializer.toBytes(parent);
long dur = System.currentTimeMillis() - start;
System.out.println("Duration: " + dur + " milliseconds");
System.out.println(Arrays.toString(cBytes));
Cluster cLoaded = ClusterSerializer.clusterFromBytes(new ByteArrayInputStream(cBytes), null);
System.out.println(cLoaded);
System.out.println(Arrays.toString(ClusterSerializer.toBytes(cLoaded)));
System.out.println(Arrays.equals(cBytes, ClusterSerializer.toBytes(cLoaded)));
}
} }

View File

@ -27,7 +27,6 @@ public class Cluster implements Comparable<Cluster>, CrystalItem {
public void addShard(Shard shard) { public void addShard(Shard shard) {
this.shards.add(shard); this.shards.add(shard);
shard.setCluster(this);
} }
public void addCluster(Cluster cluster) { public void addCluster(Cluster cluster) {

View File

@ -19,15 +19,12 @@ import java.util.Objects;
*/ */
@Getter @Getter
public abstract class Shard implements Comparable<Shard>, CrystalItem { public abstract class Shard implements Comparable<Shard>, CrystalItem {
@Setter
private Cluster cluster;
@Setter @Setter
private String name; private String name;
private final LocalDateTime createdAt; private final LocalDateTime createdAt;
private final ShardType type; private final ShardType type;
public Shard(Cluster cluster, String name, LocalDateTime createdAt, ShardType type) { public Shard(String name, LocalDateTime createdAt, ShardType type) {
this.cluster = cluster;
this.name = name; this.name = name;
this.createdAt = createdAt; this.createdAt = createdAt;
this.type = type; this.type = type;
@ -45,12 +42,12 @@ public abstract class Shard implements Comparable<Shard>, CrystalItem {
if (this == o) return true; if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; if (o == null || getClass() != o.getClass()) return false;
Shard shard = (Shard) o; Shard shard = (Shard) o;
return getCluster().equals(shard.getCluster()) && getName().equals(shard.getName()) && getType() == shard.getType(); return getName().equals(shard.getName()) && getType() == shard.getType();
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(getCluster(), getName(), getType()); return Objects.hash(getName(), getType());
} }
@Override @Override

View File

@ -5,6 +5,7 @@ import nl.andrewlalis.crystalkeep.model.Cluster;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
@ -14,12 +15,13 @@ public class ClusterLoader {
public Cluster loadDefault() throws IOException { public Cluster loadDefault() throws IOException {
InputStream is = new FileInputStream(DEFAULT_CLUSTER.toFile()); InputStream is = new FileInputStream(DEFAULT_CLUSTER.toFile());
return ClusterSerializer.clusterFromBytes(is, null); return ClusterSerializer.readCluster(is, null);
} }
public void saveDefault(Cluster cluster) throws IOException { public void saveDefault(Cluster cluster) throws IOException {
Files.createDirectories(CLUSTER_PATH); Files.createDirectories(CLUSTER_PATH);
byte[] bytes = ClusterSerializer.toBytes(cluster); OutputStream os = Files.newOutputStream(DEFAULT_CLUSTER);
Files.write(DEFAULT_CLUSTER, bytes); ClusterSerializer.writeCluster(cluster, os);
os.close();
} }
} }

View File

@ -6,9 +6,9 @@ import nl.andrewlalis.crystalkeep.model.shards.LoginCredentialsShard;
import nl.andrewlalis.crystalkeep.model.shards.ShardType; import nl.andrewlalis.crystalkeep.model.shards.ShardType;
import nl.andrewlalis.crystalkeep.model.shards.TextShard; import nl.andrewlalis.crystalkeep.model.shards.TextShard;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
@ -33,31 +33,28 @@ public class ClusterSerializer {
/** /**
* Serializes a cluster to a byte array, including all shards and nested * Serializes a cluster to a byte array, including all shards and nested
* clusters. * clusters.
* TODO: Use output stream instead of byte array.
* @param cluster The cluster to serialize. * @param cluster The cluster to serialize.
* @return The byte array representing the cluster. * @param os The output stream to write to.
* @throws IOException If an error occurs while writing the cluster. * @throws IOException If an error occurs while writing the cluster.
*/ */
public static byte[] toBytes(Cluster cluster) throws IOException { public static void writeCluster(Cluster cluster, OutputStream os) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream(); ByteUtils.writeLengthPrefixed(cluster.getName(), os);
ByteUtils.writeLengthPrefixed(cluster.getName(), bos);
bos.write(ByteUtils.toBytes(cluster.getClusters().size())); os.write(ByteUtils.toBytes(cluster.getClusters().size()));
Cluster[] children = new Cluster[cluster.getClusters().size()]; Cluster[] children = new Cluster[cluster.getClusters().size()];
cluster.getClusters().toArray(children); cluster.getClusters().toArray(children);
Arrays.sort(children); Arrays.sort(children);
for (Cluster child : children) { for (Cluster child : children) {
bos.write(toBytes(child)); writeCluster(child, os);
} }
bos.write(ByteUtils.toBytes(cluster.getShards().size())); os.write(ByteUtils.toBytes(cluster.getShards().size()));
Shard[] shards = new Shard[cluster.getShards().size()]; Shard[] shards = new Shard[cluster.getShards().size()];
cluster.getShards().toArray(shards); cluster.getShards().toArray(shards);
Arrays.sort(shards); Arrays.sort(shards);
for (Shard shard : shards) { for (Shard shard : shards) {
bos.write(toBytes(shard)); writeShard(shard, os);
} }
return bos.toByteArray();
} }
/** /**
@ -67,16 +64,16 @@ public class ClusterSerializer {
* @return The cluster that was read. * @return The cluster that was read.
* @throws IOException If data could not be read from the stream. * @throws IOException If data could not be read from the stream.
*/ */
public static Cluster clusterFromBytes(InputStream is, Cluster parent) throws IOException { public static Cluster readCluster(InputStream is, Cluster parent) throws IOException {
String name = ByteUtils.readLengthPrefixedString(is); String name = ByteUtils.readLengthPrefixedString(is);
Cluster cluster = new Cluster(name, new HashSet<>(), new HashSet<>(), parent); Cluster cluster = new Cluster(name, new HashSet<>(), new HashSet<>(), parent);
int childCount = toInt(is.readNBytes(4)); int childCount = toInt(is.readNBytes(4));
for (int i = 0; i < childCount; i++) { for (int i = 0; i < childCount; i++) {
cluster.addCluster(clusterFromBytes(is, cluster)); cluster.addCluster(readCluster(is, cluster));
} }
int shardCount = toInt(is.readNBytes(4)); int shardCount = toInt(is.readNBytes(4));
for (int i = 0; i < shardCount; i++) { for (int i = 0; i < shardCount; i++) {
cluster.addShard(shardFromBytes(is, cluster)); cluster.addShard(readShard(is));
} }
return cluster; return cluster;
} }
@ -86,29 +83,26 @@ public class ClusterSerializer {
* standard shard information, then using a specific {@link ShardSerializer} * standard shard information, then using a specific {@link ShardSerializer}
* to get the bytes that represent the body of the shard. * to get the bytes that represent the body of the shard.
* @param shard The shard to serialize. * @param shard The shard to serialize.
* @return A byte array representing the shard. * @param os The output stream to write to.
* @throws IOException If byte array stream could not be written to. * @throws IOException If byte array stream could not be written to.
*/ */
@SuppressWarnings({"unchecked", "rawtypes"}) @SuppressWarnings({"unchecked", "rawtypes"})
public static byte[] toBytes(Shard shard) throws IOException { public static void writeShard(Shard shard, OutputStream os) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream(); ByteUtils.writeLengthPrefixed(shard.getName().getBytes(StandardCharsets.UTF_8), os);
ByteUtils.writeLengthPrefixed(shard.getName().getBytes(StandardCharsets.UTF_8), bos); ByteUtils.writeLengthPrefixed(shard.getCreatedAt().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME).getBytes(StandardCharsets.UTF_8), os);
ByteUtils.writeLengthPrefixed(shard.getCreatedAt().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME).getBytes(StandardCharsets.UTF_8), bos); os.write(ByteUtils.toBytes(shard.getType().getValue()));
bos.write(ByteUtils.toBytes(shard.getType().getValue()));
ShardSerializer serializer = serializers.get(shard.getType()); ShardSerializer serializer = serializers.get(shard.getType());
bos.write(serializer.serialize(shard)); os.write(serializer.serialize(shard));
return bos.toByteArray();
} }
/** /**
* Deserializes a shard from a byte array that's being read by the given * Deserializes a shard from a byte array that's being read by the given
* input stream. * input stream.
* @param is The input stream to read from. * @param is The input stream to read from.
* @param cluster The cluster that the shard should belong to.
* @return The shard that was deserialized. * @return The shard that was deserialized.
* @throws IOException If an error occurs while reading bytes. * @throws IOException If an error occurs while reading bytes.
*/ */
public static Shard shardFromBytes(InputStream is, Cluster cluster) throws IOException { public static Shard readShard(InputStream is) throws IOException {
String name = ByteUtils.readLengthPrefixedString(is); String name = ByteUtils.readLengthPrefixedString(is);
LocalDateTime createdAt = LocalDateTime.parse(ByteUtils.readLengthPrefixedString(is), DateTimeFormatter.ISO_LOCAL_DATE_TIME); LocalDateTime createdAt = LocalDateTime.parse(ByteUtils.readLengthPrefixedString(is), DateTimeFormatter.ISO_LOCAL_DATE_TIME);
ShardType type = ShardType.valueOf(ByteUtils.toInt(is.readNBytes(4))); ShardType type = ShardType.valueOf(ByteUtils.toInt(is.readNBytes(4)));
@ -116,6 +110,6 @@ public class ClusterSerializer {
if (serializer == null) { if (serializer == null) {
throw new IOException("Unsupported shard type."); throw new IOException("Unsupported shard type.");
} }
return serializer.deserialize(is, cluster, name, createdAt); return serializer.deserialize(is, name, createdAt);
} }
} }

View File

@ -1,9 +1,7 @@
package nl.andrewlalis.crystalkeep.model.serialization; package nl.andrewlalis.crystalkeep.model.serialization;
import nl.andrewlalis.crystalkeep.model.Cluster;
import nl.andrewlalis.crystalkeep.model.Shard; import nl.andrewlalis.crystalkeep.model.Shard;
import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@ -11,5 +9,5 @@ import java.time.LocalDateTime;
public interface ShardSerializer<T extends Shard> { public interface ShardSerializer<T extends Shard> {
byte[] serialize(T shard) throws IOException; byte[] serialize(T shard) throws IOException;
T deserialize(InputStream bis, Cluster cluster, String name, LocalDateTime createdAt) throws IOException; T deserialize(InputStream is, String name, LocalDateTime createdAt) throws IOException;
} }

View File

@ -2,7 +2,6 @@ package nl.andrewlalis.crystalkeep.model.shards;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import nl.andrewlalis.crystalkeep.model.Cluster;
import nl.andrewlalis.crystalkeep.model.Shard; import nl.andrewlalis.crystalkeep.model.Shard;
import nl.andrewlalis.crystalkeep.model.serialization.ByteUtils; import nl.andrewlalis.crystalkeep.model.serialization.ByteUtils;
import nl.andrewlalis.crystalkeep.model.serialization.ShardSerializer; import nl.andrewlalis.crystalkeep.model.serialization.ShardSerializer;
@ -17,8 +16,8 @@ public class LoginCredentialsShard extends Shard {
private String username; private String username;
private String password; private String password;
public LoginCredentialsShard(Cluster cluster, String name, LocalDateTime createdAt, String username, String password) { public LoginCredentialsShard(String name, LocalDateTime createdAt, String username, String password) {
super(cluster, name, createdAt, ShardType.LOGIN_CREDENTIALS); super(name, createdAt, ShardType.LOGIN_CREDENTIALS);
this.username = username; this.username = username;
this.password = password; this.password = password;
} }
@ -36,10 +35,10 @@ public class LoginCredentialsShard extends Shard {
} }
@Override @Override
public LoginCredentialsShard deserialize(InputStream is, Cluster cluster, String name, LocalDateTime createdAt) throws IOException { public LoginCredentialsShard deserialize(InputStream is, String name, LocalDateTime createdAt) throws IOException {
String username = ByteUtils.readLengthPrefixedString(is); String username = ByteUtils.readLengthPrefixedString(is);
String password = ByteUtils.readLengthPrefixedString(is); String password = ByteUtils.readLengthPrefixedString(is);
return new LoginCredentialsShard(cluster, name, createdAt, username, password); return new LoginCredentialsShard(name, createdAt, username, password);
} }
} }
} }

View File

@ -14,8 +14,8 @@ import java.time.LocalDateTime;
public class TextShard extends Shard { public class TextShard extends Shard {
private String text; private String text;
public TextShard(Cluster cluster, String name, LocalDateTime createdAt, String text) { public TextShard(String name, LocalDateTime createdAt, String text) {
super(cluster, name, createdAt, ShardType.TEXT); super(name, createdAt, ShardType.TEXT);
this.text = text; this.text = text;
} }
@ -31,9 +31,9 @@ public class TextShard extends Shard {
} }
@Override @Override
public TextShard deserialize(InputStream is, Cluster cluster, String name, LocalDateTime createdAt) throws IOException { public TextShard deserialize(InputStream is, String name, LocalDateTime createdAt) throws IOException {
String text = ByteUtils.readLengthPrefixedString(is); String text = ByteUtils.readLengthPrefixedString(is);
return new TextShard(cluster, name, createdAt, text); return new TextShard(name, createdAt, text);
} }
} }
} }

View File

@ -0,0 +1,54 @@
package nl.andrewlalis.crystalkeep.model.serialization;
import nl.andrewlalis.crystalkeep.model.Shard;
import nl.andrewlalis.crystalkeep.model.shards.LoginCredentialsShard;
import nl.andrewlalis.crystalkeep.model.shards.TextShard;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
public class ClusterSerializerTest {
private static List<Shard> testShardIOData() {
return List.of(
new TextShard("a", LocalDateTime.now(), "Hello world!"),
new TextShard("Another", LocalDateTime.now(), "Testing"),
new TextShard("", LocalDateTime.now(), ""),
new LoginCredentialsShard("login", LocalDateTime.now(), "andrew", "password"),
new LoginCredentialsShard("test", LocalDateTime.now(), "", "")
);
}
@ParameterizedTest
@MethodSource("testShardIOData")
public void testShardIO(Shard s1) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ClusterSerializer.writeShard(s1, bos);
byte[] data = bos.toByteArray();
assertNotEquals(0, data.length, "Serialized shard should never result in empty bytes.");
assertEquals(
s1.getName().length(),
ByteUtils.toInt(Arrays.copyOfRange(data, 0, 4)),
"Serialized shard name length does not match expected."
);
assertEquals(
s1.getName(),
new String(Arrays.copyOfRange(data, 4, 4 + s1.getName().length())),
"First letter of shard name does not match expected."
);
Shard loadedS1 = ClusterSerializer.readShard(new ByteArrayInputStream(data));
assertEquals(s1, loadedS1, "Loaded shard should equal original shard.");
bos.reset();
ClusterSerializer.writeShard(loadedS1, bos);
byte[] data2 = bos.toByteArray();
assertArrayEquals(data, data2, "Serialized data from a shard should not change.");
}
}

View File

@ -0,0 +1,4 @@
/**
* This package contains all tests for CrystalKeep.
*/
package nl.andrewlalis.crystalkeep;