-
-
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 1 commit
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,21 @@ | ||
# 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. | ||
|
||
## Submitting Exercises | ||
|
||
Note that, when trying to submit an exercise, make sure the solution is in the `exercism/python/<exerciseName>` directory. | ||
|
||
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`. | ||
|
||
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 interuppted | ||
# by thread switch, thus testing synchronization effectively | ||
try: | ||
sys.setswitchinterval(1e-12) | ||
except AttributeError: | ||
# Python 2 compatible | ||
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.