diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..812c3f9
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..aa00ffa
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
new file mode 100644
index 0000000..712ab9d
--- /dev/null
+++ b/.idea/jarRepositories.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..856f2f3
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 2182652..33ea6f3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -14,4 +14,23 @@
UTF-8
+
+
+ org.junit.jupiter
+ junit-jupiter
+ 5.12.2
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.5.3
+
+
+
+
\ No newline at end of file
diff --git a/src/test/java/banking/TestA.java b/src/test/java/banking/TestA.java
index ce2cd6b..35659eb 100644
--- a/src/test/java/banking/TestA.java
+++ b/src/test/java/banking/TestA.java
@@ -1,4 +1,49 @@
package banking;
+import dev.banking.model.BankAccount;
+import org.junit.jupiter.api.Test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
public class TestA {
-}
+
+ @Test
+ void concurrentDeposits() throws Exception {
+
+ BankAccount account = new BankAccount(1, 0);
+
+ int threadCount = 100;
+ int depositsPerThread = 1000;
+
+ ExecutorService executor =
+ Executors.newFixedThreadPool(threadCount);
+
+ CountDownLatch latch =
+ new CountDownLatch(threadCount);
+
+ for (int i = 0; i < threadCount; i++) {
+
+ executor.submit(() -> {
+
+ for (int j = 0; j < depositsPerThread; j++) {
+ account.deposit(1);
+ }
+
+ latch.countDown();
+ });
+ }
+
+ latch.await();
+
+ executor.shutdown();
+
+ assertEquals(
+ threadCount * depositsPerThread,
+ account.getBalance()
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/banking/TestB.java b/src/test/java/banking/TestB.java
index b5104e3..66daa64 100644
--- a/src/test/java/banking/TestB.java
+++ b/src/test/java/banking/TestB.java
@@ -1,4 +1,56 @@
package banking;
+import dev.banking.model.BankAccount;
+import org.junit.jupiter.api.Test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
public class TestB {
-}
+
+ @Test
+ void concurrentDepositWithdraw() throws Exception {
+
+ BankAccount account =
+ new BankAccount(1, 1_000_000);
+
+ ExecutorService executor =
+ Executors.newFixedThreadPool(100);
+
+ CountDownLatch latch =
+ new CountDownLatch(100);
+
+ for (int i = 0; i < 50; i++) {
+
+ executor.submit(() -> {
+
+ for (int j = 0; j < 10000; j++) {
+ account.deposit(1);
+ }
+
+ latch.countDown();
+ });
+
+ executor.submit(() -> {
+
+ for (int j = 0; j < 10000; j++) {
+ account.withdraw(1);
+ }
+
+ latch.countDown();
+ });
+ }
+
+ latch.await();
+
+ executor.shutdown();
+
+ assertEquals(
+ 1_000_000,
+ account.getBalance()
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/banking/TestC.java b/src/test/java/banking/TestC.java
index 84da5d5..93ea4a2 100644
--- a/src/test/java/banking/TestC.java
+++ b/src/test/java/banking/TestC.java
@@ -1,4 +1,98 @@
package banking;
+import dev.banking.model.BankAccount;
+import org.junit.jupiter.api.Test;
+
+import java.time.Duration;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.jupiter.api.Assertions.*;
+
public class TestC {
-}
+
+ @Test
+ void transferConservation() throws Exception {
+
+ BankAccount a =
+ new BankAccount(1, 100_000);
+
+ BankAccount b =
+ new BankAccount(2, 100_000);
+
+ ExecutorService executor =
+ Executors.newFixedThreadPool(50);
+
+ CountDownLatch latch =
+ new CountDownLatch(100);
+
+ for (int i = 0; i < 50; i++) {
+
+ executor.submit(() -> {
+
+ for (int j = 0; j < 1000; j++) {
+ a.transfer(b, 1);
+ }
+
+ latch.countDown();
+ });
+
+ executor.submit(() -> {
+
+ for (int j = 0; j < 1000; j++) {
+ b.transfer(a, 1);
+ }
+
+ latch.countDown();
+ });
+ }
+
+ latch.await();
+
+ executor.shutdown();
+
+ long total =
+ a.getBalance() + b.getBalance();
+
+ assertEquals(200_000, total);
+ }
+
+ @Test
+ void deadlockFreeTransfers() {
+
+ assertTimeoutPreemptively(
+ Duration.ofSeconds(5),
+ () -> {
+
+ BankAccount a =
+ new BankAccount(1, 1_000_000);
+
+ BankAccount b =
+ new BankAccount(2, 1_000_000);
+
+ ExecutorService executor =
+ Executors.newFixedThreadPool(50);
+
+ for (int i = 0; i < 50000; i++) {
+
+ executor.submit(() ->
+ a.transfer(b, 1));
+
+ executor.submit(() ->
+ b.transfer(a, 1));
+ }
+
+ executor.shutdown();
+
+ assertTrue(
+ executor.awaitTermination(
+ 5,
+ TimeUnit.SECONDS
+ )
+ );
+ }
+ );
+ }
+}
\ No newline at end of file