diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index f7a95f5..96fb4d3 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -33,8 +33,15 @@ jobs:
java-version: ${{ matrix.java }}
cache: 'maven'
- - name: Build with Maven
- run: ./mvnw -B package
+ - name: Build and test with Maven
+ run: ./mvnw -B verify
+
+ - name: Upload coverage report
+ if: matrix.os == 'ubuntu-latest' && matrix.java == 21
+ uses: actions/upload-artifact@v4
+ with:
+ name: jacoco-report
+ path: target/site/jacoco
- name: Start Tool
run: java -jar target/JavaCrypt.jar
diff --git a/pom.xml b/pom.xml
index f0883b0..01f8712 100644
--- a/pom.xml
+++ b/pom.xml
@@ -15,9 +15,42 @@
11
+
+
+ org.junit.jupiter
+ junit-jupiter
+ 5.14.4
+ test
+
+
+
JavaCrypt
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.6.0
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ 0.8.15
+
+
+
+ prepare-agent
+
+
+
+ report
+ test
+
+ report
+
+
+
+
org.apache.maven.plugins
maven-jar-plugin
diff --git a/src/main/java/javacrypt/RunEncrypt.java b/src/main/java/javacrypt/RunEncrypt.java
index 413e552..dcfc851 100644
--- a/src/main/java/javacrypt/RunEncrypt.java
+++ b/src/main/java/javacrypt/RunEncrypt.java
@@ -17,7 +17,6 @@
import java.util.List;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
-import java.nio.file.Files;
/**
* This class represents the RunEncrypt command, which performs RSA encryption using the public key.
@@ -142,26 +141,4 @@ public void run(List args) {
System.out.println("Number of bytes transferred: " + size);
}
-
- /**
- * Encrypts the input file using the public key and saves the result to the output file.
- *
- * @param publicKey The public key.
- * @param inputFile The input file.
- * @param outputFile The output file.
- * @return The number of bytes transferred.
- * @throws Exception If an error occurs during encryption.
- */
- private int encryptDecryptFile(PublicKey publicKey, File inputFile, File outputFile) throws Exception {
- try {
- Cipher cipher = getCipher(publicKey);
- byte[] inputBytes = Files.readAllBytes(inputFile.toPath());
- byte[] encryptedBytes = crypt(inputBytes, publicKey, cipher);
- Files.write(outputFile.toPath(), encryptedBytes);
- return encryptedBytes.length;
- } catch (Exception e) {
- System.err.println("Exception: " + e.getMessage());
- throw e;
- }
- }
}
diff --git a/src/main/java/javacrypt/TestJava.java b/src/main/java/javacrypt/TestJava.java
deleted file mode 100644
index 016a9bd..0000000
--- a/src/main/java/javacrypt/TestJava.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package javacrypt;
-
-// Test Class
-public class TestJava {
- public static void main(String[] args) {
- System.out.println("Java compilation and execution successful!");
- }
-}
diff --git a/src/test/java/javacrypt/CopyTest.java b/src/test/java/javacrypt/CopyTest.java
new file mode 100644
index 0000000..c5197f8
--- /dev/null
+++ b/src/test/java/javacrypt/CopyTest.java
@@ -0,0 +1,29 @@
+package javacrypt;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Random;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+class CopyTest {
+
+ @TempDir
+ Path dir;
+
+ @Test
+ void copiesFileLargerThanOneBuffer() throws Exception {
+ byte[] data = new byte[RunCopy.RSA_COPY_BUFSIZE * 2 + 17];
+ new Random(1).nextBytes(data);
+ Path in = dir.resolve("in.dat");
+ Path out = dir.resolve("out.dat");
+ Files.write(in, data);
+
+ new RunCopy().run(Arrays.asList(in.toString(), out.toString()));
+
+ assertArrayEquals(data, Files.readAllBytes(out));
+ }
+}
diff --git a/src/test/java/javacrypt/FactoryTest.java b/src/test/java/javacrypt/FactoryTest.java
new file mode 100644
index 0000000..c5eb021
--- /dev/null
+++ b/src/test/java/javacrypt/FactoryTest.java
@@ -0,0 +1,41 @@
+package javacrypt;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+class FactoryTest {
+
+ private final RunnableKeyValueFactory factory = new RunnableKeyValueFactory(new String[][]{
+ {"-encrypt", "javacrypt.RunEncrypt"},
+ {"-copy", "javacrypt.RunCopy"}
+ });
+
+ @Test
+ void knowsRegisteredKeywords() {
+ assertTrue(factory.containsKey("-encrypt"));
+ assertFalse(factory.containsKey("-unknown"));
+ assertFalse(factory.containsKey(null));
+ }
+
+ @Test
+ void resolvesClassName() {
+ assertEquals("javacrypt.RunCopy", factory.getClassNameOfKey("-copy"));
+ assertNull(factory.getClassNameOfKey("-unknown"));
+ }
+
+ @Test
+ void instantiatesRunnable() throws Exception {
+ assertInstanceOf(RunEncrypt.class, factory.getInstanceFromKey("-encrypt"));
+ assertNull(factory.getInstanceFromKey("-unknown"));
+ }
+
+ @Test
+ void acceptsNullMapping() {
+ assertFalse(new RunnableKeyValueFactory(null).containsKey("-copy"));
+ }
+}
diff --git a/src/test/java/javacrypt/RoundtripTest.java b/src/test/java/javacrypt/RoundtripTest.java
new file mode 100644
index 0000000..ddd225e
--- /dev/null
+++ b/src/test/java/javacrypt/RoundtripTest.java
@@ -0,0 +1,77 @@
+package javacrypt;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Random;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+class RoundtripTest {
+
+ @TempDir
+ Path dir;
+
+ private Path priv;
+ private Path pub;
+
+ @BeforeEach
+ void generateKeys() {
+ priv = dir.resolve("priv.key");
+ pub = dir.resolve("pub.key");
+ new RunGenKeys().run(Arrays.asList(priv.toString(), pub.toString()));
+ }
+
+ private byte[] roundtrip(byte[] plain) throws Exception {
+ Path in = dir.resolve("in.dat");
+ Path enc = dir.resolve("enc.dat");
+ Path dec = dir.resolve("dec.dat");
+ Files.write(in, plain);
+
+ new RunEncrypt().run(Arrays.asList(pub.toString(), in.toString(), enc.toString()));
+ new RunDecrypt().run(Arrays.asList(priv.toString(), enc.toString(), dec.toString()));
+
+ return Files.readAllBytes(dec);
+ }
+
+ @Test
+ void genKeysWritesBothKeyFiles() throws Exception {
+ assertTrue(Files.size(priv) > 0);
+ assertTrue(Files.size(pub) > 0);
+ }
+
+ @Test
+ void shortTextSurvivesRoundtrip() throws Exception {
+ byte[] plain = "Dear readers, today's weather is wet.".getBytes(StandardCharsets.UTF_8);
+
+ assertArrayEquals(plain, roundtrip(plain));
+ }
+
+ @Test
+ void ciphertextDiffersFromPlaintext() throws Exception {
+ Path in = dir.resolve("in.dat");
+ Path enc = dir.resolve("enc.dat");
+ byte[] plain = "secret".getBytes(StandardCharsets.UTF_8);
+ Files.write(in, plain);
+
+ new RunEncrypt().run(Arrays.asList(pub.toString(), in.toString(), enc.toString()));
+
+ byte[] cipherText = Files.readAllBytes(enc);
+ assertEquals(RunnableBase.KEY_LENGTH / 8, cipherText.length);
+ assertTrue(!Arrays.equals(plain, cipherText));
+ }
+
+ @Test
+ void largeFileSurvivesRoundtrip() throws Exception {
+ byte[] plain = new byte[1000];
+ new Random(42).nextBytes(plain);
+
+ assertArrayEquals(plain, roundtrip(plain));
+ }
+}