Quellcode durchsuchen

Merge pull request #662 from FridaTveit/BankAccountAddExercise

bank-account: add to track
Stuart Kent vor 9 Jahren
Ursprung
Commit
683b7dc1e2

+ 7
- 0
config.json Datei anzeigen

374
       ]
374
       ]
375
     },
375
     },
376
     {
376
     {
377
+    	"slug": "bank-account",
378
+    	"difficulty": 6,
379
+    	"topics": [
380
+
381
+    	]
382
+    },
383
+    {
377
       "slug": "anagram",
384
       "slug": "anagram",
378
       "difficulty": 7,
385
       "difficulty": 7,
379
       "topics": [
386
       "topics": [

+ 11
- 0
exercises/bank-account/HINTS.md Datei anzeigen

1
+This exercise introduces [concurrency](https://docs.oracle.com/javase/tutorial/essential/concurrency/index.html). 
2
+To pass the last test you might find the 
3
+[`synchronized` keyword or locks](https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html) useful.
4
+
5
+Problems arising from running code concurrently are often intermittent because they depend on the order the code is
6
+executed. Therefore the last test runs many [threads](https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html) 
7
+several times to increase the chances of catching a bug. That means this test should fail if your implementation is not
8
+[thread safe](https://en.wikipedia.org/wiki/Thread_safety), but there is a chance it will pass just because there was 
9
+no concurrent modification attempt. It is unlikely that this will occur several times 
10
+in a row since the order the code is executed should vary every time you run the test. So if you run the last test a 
11
+couple of times and it passes every time then you can be reasonably sure that your implementation is correct.

+ 18
- 0
exercises/bank-account/build.gradle Datei anzeigen

1
+apply plugin: "java"
2
+apply plugin: "eclipse"
3
+apply plugin: "idea"
4
+
5
+repositories {
6
+    mavenCentral()
7
+}
8
+
9
+dependencies {
10
+    testCompile "junit:junit:4.12"
11
+}
12
+
13
+test {
14
+    testLogging {
15
+        exceptionFormat = 'full'
16
+        events = ["passed", "failed", "skipped"]
17
+    }
18
+}

+ 53
- 0
exercises/bank-account/src/example/java/BankAccount.java Datei anzeigen

1
+class BankAccount {
2
+    private int balance = 0;
3
+    private boolean isClosed = true;
4
+
5
+    void open() {
6
+        isClosed = false;
7
+    }
8
+
9
+    void close() {
10
+        isClosed = true;
11
+    }
12
+
13
+    synchronized int getBalance() throws BankAccountActionInvalidException {
14
+        checkIfClosed();
15
+        return balance;
16
+    }
17
+
18
+    synchronized void deposit(int amount) throws BankAccountActionInvalidException {
19
+        checkIfClosed();
20
+        checkIfValidAmount(amount);
21
+
22
+        balance += amount;
23
+    }
24
+
25
+    synchronized void withdraw(int amount) throws BankAccountActionInvalidException {
26
+        checkIfClosed();
27
+        checkIfValidAmount(amount);
28
+        checkIfEnoughMoneyInAccount(amount);
29
+
30
+        balance -= amount;
31
+    }
32
+
33
+    private void checkIfValidAmount(int amount) throws BankAccountActionInvalidException {
34
+        if (amount < 0) {
35
+            throw new BankAccountActionInvalidException("Cannot deposit or withdraw negative amount");
36
+        }
37
+    }
38
+
39
+    private void checkIfEnoughMoneyInAccount(int amount) throws BankAccountActionInvalidException {
40
+        if (balance == 0) {
41
+            throw new BankAccountActionInvalidException("Cannot withdraw money from an empty account");
42
+        }
43
+        if (balance - amount < 0) {
44
+            throw new BankAccountActionInvalidException("Cannot withdraw more money than is currently in the account");
45
+        }
46
+    }
47
+
48
+    private void checkIfClosed() throws BankAccountActionInvalidException {
49
+        if (isClosed) {
50
+            throw new BankAccountActionInvalidException("Account closed");
51
+        }
52
+    }
53
+}

+ 6
- 0
exercises/bank-account/src/example/java/BankAccountActionInvalidException.java Datei anzeigen

1
+class BankAccountActionInvalidException extends Exception {
2
+
3
+    BankAccountActionInvalidException(String message) {
4
+        super(message);
5
+    }
6
+}

+ 6
- 0
exercises/bank-account/src/main/java/BankAccountActionInvalidException.java Datei anzeigen

1
+class BankAccountActionInvalidException extends Exception {
2
+
3
+    BankAccountActionInvalidException(String message) {
4
+        super(message);
5
+    }
6
+}

+ 201
- 0
exercises/bank-account/src/test/java/BankAccountTest.java Datei anzeigen

1
+import org.junit.Before;
2
+import org.junit.Ignore;
3
+import org.junit.Rule;
4
+import org.junit.Test;
5
+import org.junit.rules.ExpectedException;
6
+
7
+import java.util.Random;
8
+
9
+import static org.junit.Assert.assertEquals;
10
+import static org.junit.Assert.fail;
11
+
12
+public class BankAccountTest {
13
+    @Rule
14
+    public ExpectedException expectedException = ExpectedException.none();
15
+    private BankAccount bankAccount;
16
+
17
+    @Before
18
+    public void setup() {
19
+        bankAccount = new BankAccount();
20
+    }
21
+
22
+    @Test
23
+    public void newlyOpenedAccountHasEmptyBalance() throws BankAccountActionInvalidException {
24
+        bankAccount.open();
25
+
26
+        assertEquals(0, bankAccount.getBalance());
27
+    }
28
+
29
+    @Ignore("Remove to run test")
30
+    @Test
31
+    public void canDepositMoney() throws BankAccountActionInvalidException {
32
+        bankAccount.open();
33
+
34
+        bankAccount.deposit(10);
35
+
36
+        assertEquals(10, bankAccount.getBalance());
37
+    }
38
+
39
+    @Ignore("Remove to run test")
40
+    @Test
41
+    public void canDepositMoneySequentially() throws BankAccountActionInvalidException {
42
+        bankAccount.open();
43
+
44
+        bankAccount.deposit(5);
45
+        bankAccount.deposit(23);
46
+
47
+        assertEquals(28, bankAccount.getBalance());
48
+    }
49
+
50
+    @Ignore("Remove to run test")
51
+    @Test
52
+    public void canWithdrawMoney() throws BankAccountActionInvalidException {
53
+        bankAccount.open();
54
+        bankAccount.deposit(10);
55
+
56
+        bankAccount.withdraw(5);
57
+
58
+        assertEquals(5, bankAccount.getBalance());
59
+    }
60
+
61
+    @Ignore("Remove to run test")
62
+    @Test
63
+    public void canWithdrawMoneySequentially() throws BankAccountActionInvalidException {
64
+        bankAccount.open();
65
+        bankAccount.deposit(23);
66
+
67
+        bankAccount.withdraw(10);
68
+        bankAccount.withdraw(13);
69
+
70
+        assertEquals(0, bankAccount.getBalance());
71
+    }
72
+
73
+    @Ignore("Remove to run test")
74
+    @Test
75
+    public void cannotWithdrawMoneyFromEmptyAccount() throws BankAccountActionInvalidException {
76
+        bankAccount.open();
77
+
78
+        expectedException.expect(BankAccountActionInvalidException.class);
79
+        expectedException.expectMessage("Cannot withdraw money from an empty account");
80
+
81
+        bankAccount.withdraw(5);
82
+    }
83
+
84
+    @Ignore("Remove to run test")
85
+    @Test
86
+    public void cannotWithdrawMoreMoneyThanYouHave() throws BankAccountActionInvalidException {
87
+        bankAccount.open();
88
+        bankAccount.deposit(6);
89
+
90
+        expectedException.expect(BankAccountActionInvalidException.class);
91
+        expectedException.expectMessage("Cannot withdraw more money than is currently in the account");
92
+
93
+        bankAccount.withdraw(7);
94
+    }
95
+
96
+    @Ignore("Remove to run test")
97
+    @Test
98
+    public void cannotDepositNegativeAmount() throws BankAccountActionInvalidException {
99
+        bankAccount.open();
100
+
101
+        expectedException.expect(BankAccountActionInvalidException.class);
102
+        expectedException.expectMessage("Cannot deposit or withdraw negative amount");
103
+
104
+        bankAccount.deposit(-1);
105
+    }
106
+
107
+    @Ignore("Remove to run test")
108
+    @Test
109
+    public void cannotWithdrawNegativeAmount() throws BankAccountActionInvalidException {
110
+        bankAccount.open();
111
+        bankAccount.deposit(105);
112
+
113
+        expectedException.expect(BankAccountActionInvalidException.class);
114
+        expectedException.expectMessage("Cannot deposit or withdraw negative amount");
115
+
116
+        bankAccount.withdraw(-5);
117
+    }
118
+
119
+    @Ignore("Remove to run test")
120
+    @Test
121
+    public void cannotGetBalanceOfClosedAccount() throws BankAccountActionInvalidException {
122
+        bankAccount.open();
123
+        bankAccount.deposit(10);
124
+        bankAccount.close();
125
+
126
+        expectedException.expect(BankAccountActionInvalidException.class);
127
+        expectedException.expectMessage("Account closed");
128
+
129
+        bankAccount.getBalance();
130
+    }
131
+
132
+    @Ignore("Remove to run test")
133
+    @Test
134
+    public void cannotDepositMoneyIntoClosedAccount() throws BankAccountActionInvalidException {
135
+        bankAccount.open();
136
+        bankAccount.close();
137
+
138
+        expectedException.expect(BankAccountActionInvalidException.class);
139
+        expectedException.expectMessage("Account closed");
140
+
141
+        bankAccount.deposit(5);
142
+    }
143
+
144
+    @Ignore("Remove to run test")
145
+    @Test
146
+    public void cannotWithdrawMoneyFromClosedAccount() throws BankAccountActionInvalidException {
147
+        bankAccount.open();
148
+        bankAccount.deposit(20);
149
+        bankAccount.close();
150
+
151
+        expectedException.expect(BankAccountActionInvalidException.class);
152
+        expectedException.expectMessage("Account closed");
153
+
154
+        bankAccount.withdraw(5);
155
+    }
156
+
157
+    @Ignore("Remove to run test")
158
+    @Test
159
+    public void bankAccountIsClosedBeforeItIsOpened() throws BankAccountActionInvalidException {
160
+        expectedException.expect(BankAccountActionInvalidException.class);
161
+        expectedException.expectMessage("Account closed");
162
+
163
+        bankAccount.getBalance();
164
+    }
165
+
166
+    @Ignore("Remove to run test")
167
+    @Test
168
+    public void canAdjustBalanceConcurrently() throws BankAccountActionInvalidException, InterruptedException {
169
+        bankAccount.open();
170
+        bankAccount.deposit(1000);
171
+
172
+        for (int i = 0; i < 10; i++) {
173
+            adjustBalanceConcurrently();
174
+        }
175
+    }
176
+
177
+    private void adjustBalanceConcurrently() throws BankAccountActionInvalidException, InterruptedException {
178
+        Random random = new Random();
179
+
180
+        Thread[] threads = new Thread[1000];
181
+        for (int i = 0; i < 1000; i++) {
182
+            threads[i] = new Thread(() -> {
183
+                try {
184
+                    bankAccount.deposit(5);
185
+                    Thread.sleep(random.nextInt(10));
186
+                    bankAccount.withdraw(5);
187
+                } catch (BankAccountActionInvalidException e) {
188
+                    fail("Exception should not be thrown: " + e.getMessage());
189
+                } catch (InterruptedException ignored) {
190
+                }
191
+            });
192
+            threads[i].start();
193
+        }
194
+
195
+        for (Thread thread : threads) {
196
+            thread.join();
197
+        }
198
+
199
+        assertEquals(1000, bankAccount.getBalance());
200
+    }
201
+}

+ 1
- 0
exercises/settings.gradle Datei anzeigen

4
 include 'allergies'
4
 include 'allergies'
5
 include 'anagram'
5
 include 'anagram'
6
 include 'atbash-cipher'
6
 include 'atbash-cipher'
7
+include 'bank-account'
7
 include 'beer-song'
8
 include 'beer-song'
8
 include 'binary'
9
 include 'binary'
9
 include 'binary-search'
10
 include 'binary-search'