-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement exercise bank-account #1260
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
5aba8c9
Implement exercise bank-account
5aab377
bank-account: generate README using configlet
fea46c2
bank-account: fix typo and comments
fc62d6f
Merge branch 'master' into impl-bank-account
mrcfps 36d93da
Merge branch 'master' into impl-bank-account
cmccandless File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
# Bank Account | ||
|
||
Simulate a bank account supporting opening/closing, withdrawals, and deposits | ||
of money. Watch out for concurrent transactions! | ||
|
||
A bank account can be accessed in multiple ways. Clients can make | ||
deposits and withdrawals using the internet, mobile phones, etc. Shops | ||
can charge against the account. | ||
|
||
Create an account that can be accessed from multiple threads/processes | ||
(terminology depends on your programming language). | ||
|
||
It should be possible to close an account; operations against a closed | ||
account must fail. | ||
|
||
## Instructions | ||
|
||
Run the test file, and fix each of the errors in turn. When you get the | ||
first test to pass, go to the first pending or skipped test, and make | ||
that pass as well. When all of the tests are passing, feel free to | ||
submit. | ||
|
||
Remember that passing code is just the first step. The goal is to work | ||
towards a solution that is as readable and expressive as you can make | ||
it. | ||
|
||
Have fun! | ||
|
||
## Exception messages | ||
|
||
Sometimes it is necessary to raise an exception. When you do this, you should include a meaningful error message to | ||
indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. Not | ||
every exercise will require you to raise an exception, but for those that do, the tests will only pass if you include | ||
a message. | ||
|
||
To raise a message with an exception, just write it as an argument to the exception type. For example, instead of | ||
`raise Exception`, you shold write: | ||
|
||
```python | ||
raise Exception("Meaningful message indicating the source of the error") | ||
``` | ||
|
||
## Submitting Exercises | ||
|
||
Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/bank-account` directory. | ||
|
||
You can find your Exercism workspace by running `exercism debug` and looking for the line that starts with `Workspace`. | ||
|
||
For more detailed information about running tests, code style and linting, | ||
please see the [help page](http://exercism.io/languages/python). | ||
|
||
## Submitting Incomplete Solutions | ||
It's possible to submit an incomplete solution so you can see how others have completed the exercise. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
class BankAccount(object): | ||
def __init__(self): | ||
pass | ||
|
||
def get_balance(self): | ||
pass | ||
|
||
def open(self): | ||
pass | ||
|
||
def deposit(self, amount): | ||
pass | ||
|
||
def withdraw(self, amount): | ||
pass | ||
|
||
def close(self): | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
import sys | ||
import threading | ||
import time | ||
import unittest | ||
|
||
from bank_account import BankAccount | ||
|
||
|
||
class BankAccountTests(unittest.TestCase): | ||
|
||
def setUp(self): | ||
self.account = BankAccount() | ||
|
||
def test_newly_opened_account_has_zero_balance(self): | ||
self.account.open() | ||
self.assertEqual(self.account.get_balance(), 0) | ||
|
||
def test_can_deposit_money(self): | ||
self.account.open() | ||
self.account.deposit(100) | ||
self.assertEqual(self.account.get_balance(), 100) | ||
|
||
def test_can_deposit_money_sequentially(self): | ||
self.account.open() | ||
self.account.deposit(100) | ||
self.account.deposit(50) | ||
|
||
self.assertEqual(self.account.get_balance(), 150) | ||
|
||
def test_can_withdraw_money(self): | ||
self.account.open() | ||
self.account.deposit(100) | ||
self.account.withdraw(50) | ||
|
||
self.assertEqual(self.account.get_balance(), 50) | ||
|
||
def test_can_withdraw_money_sequentially(self): | ||
self.account.open() | ||
self.account.deposit(100) | ||
self.account.withdraw(20) | ||
self.account.withdraw(80) | ||
|
||
self.assertEqual(self.account.get_balance(), 0) | ||
|
||
def test_checking_balance_of_closed_account_throws_error(self): | ||
self.account.open() | ||
self.account.close() | ||
|
||
with self.assertRaises(ValueError): | ||
self.account.get_balance() | ||
|
||
def test_deposit_into_closed_account(self): | ||
self.account.open() | ||
self.account.close() | ||
|
||
with self.assertRaises(ValueError): | ||
self.account.deposit(50) | ||
|
||
def test_withdraw_from_closed_account(self): | ||
self.account.open() | ||
self.account.close() | ||
|
||
with self.assertRaises(ValueError): | ||
self.account.withdraw(50) | ||
|
||
def test_cannot_withdraw_more_than_deposited(self): | ||
self.account.open() | ||
self.account.deposit(25) | ||
|
||
with self.assertRaises(ValueError): | ||
self.account.withdraw(50) | ||
|
||
def test_cannot_withdraw_negative(self): | ||
self.account.open() | ||
self.account.deposit(100) | ||
|
||
with self.assertRaises(ValueError): | ||
self.account.withdraw(-50) | ||
|
||
def test_cannot_deposit_negative(self): | ||
self.account.open() | ||
|
||
with self.assertRaises(ValueError): | ||
self.account.deposit(-50) | ||
|
||
def test_can_handle_concurrent_transactions(self): | ||
self.account.open() | ||
self.account.deposit(1000) | ||
|
||
for _ in range(10): | ||
self.adjust_balance_concurrently() | ||
|
||
def adjust_balance_concurrently(self): | ||
def transact(): | ||
self.account.deposit(5) | ||
time.sleep(0.001) | ||
self.account.withdraw(5) | ||
|
||
# Greatly improve the chance of an operation being interrupted | ||
# by thread switch, thus testing synchronization effectively | ||
try: | ||
sys.setswitchinterval(1e-12) | ||
except AttributeError: | ||
# For Python 2 compatibility | ||
sys.setcheckinterval(1) | ||
|
||
threads = [] | ||
for _ in range(1000): | ||
t = threading.Thread(target=transact) | ||
threads.append(t) | ||
t.start() | ||
|
||
for thread in threads: | ||
thread.join() | ||
|
||
self.assertEqual(self.account.get_balance(), 1000) | ||
|
||
|
||
if __name__ == '__main__': | ||
unittest.main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import threading | ||
|
||
|
||
class BankAccount(object): | ||
def __init__(self): | ||
self.is_open = False | ||
self.balance = 0 | ||
self.lock = threading.Lock() | ||
|
||
def get_balance(self): | ||
with self.lock: | ||
if self.is_open: | ||
return self.balance | ||
else: | ||
raise ValueError | ||
|
||
def open(self): | ||
self.is_open = True | ||
|
||
def deposit(self, amount): | ||
with self.lock: | ||
if self.is_open and amount > 0: | ||
self.balance += amount | ||
else: | ||
raise ValueError | ||
|
||
def withdraw(self, amount): | ||
with self.lock: | ||
if self.is_open and 0 < amount <= self.balance: | ||
self.balance -= amount | ||
else: | ||
raise ValueError | ||
|
||
def close(self): | ||
self.is_open = False |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use configlet to generate README files for exercises.