-
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature(error-handling): add error-handling practice exercise
- Loading branch information
Showing
8 changed files
with
332 additions
and
0 deletions.
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,8 @@ | ||
# Instructions | ||
|
||
Implement various kinds of error handling and resource management. | ||
|
||
An important point of programming is how to handle errors and close resources even if errors occur. | ||
|
||
This exercise requires you to handle various errors. | ||
Because error handling is rather programming language specific you'll have to refer to the tests for your track to see what's exactly required. |
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,9 @@ | ||
{ | ||
"authors": ["ajborla"], | ||
"files": { | ||
"solution": ["error_handling.prg"], | ||
"test": ["error_handling_test.prg"], | ||
"example": [".meta/example.prg"] | ||
}, | ||
"blurb": "Implement various kinds of error handling and resource management." | ||
} |
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,13 @@ | ||
* ---------------------------------------------------------------------------- | ||
* exercism.org | ||
* Harbour Track Exercise: error-handling | ||
* Contributed: Anthony J. Borla ([email protected]) | ||
* ---------------------------------------------------------------------------- | ||
|
||
function HandleError(name) | ||
* Reject missing, or wrong number of arguments | ||
if PCOUNT() <> 1 ; return NIL ; endif | ||
|
||
* Perform expected processing | ||
return "Hello, " + name | ||
|
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,153 @@ | ||
* ---------------------------------------------------------------------------- | ||
* Harbour Unit Test Worker | ||
* Anthony J. Borla ([email protected]) | ||
* ---------------------------------------------------------------------------- | ||
|
||
#ifndef UTILS_PRG | ||
#include "utils.prg" | ||
#endif | ||
|
||
procedure MakeTestDatabaseStructure(dbfName) | ||
* Creation overwites any existing database | ||
create &dbfName | ||
|
||
* Each record describes the structure of a FIELD | ||
append blank | ||
replace Field_name with "NAME", Field_type with "C",; | ||
Field_Len with 80, Field_dec with 0 | ||
append blank | ||
replace Field_name with "CMPOP", Field_type with "C",; | ||
Field_Len with 2, Field_dec with 0 | ||
append blank | ||
replace Field_name with "EXPVALUE", Field_type with "C",; | ||
Field_Len with 80, Field_dec with 0 | ||
append blank | ||
replace Field_name with "CMDSTR", Field_type with "C",; | ||
Field_Len with 80, Field_dec with 0 | ||
|
||
* Ensure data written to disk | ||
close &dbfName | ||
return | ||
|
||
procedure MakeTestDatabase(dbfName) | ||
local dbfStructure := dbfName + "_STRUCTURE" | ||
|
||
* Build test database from database structure file | ||
do MakeTestDatabaseStructure with dbfStructure | ||
create &dbfName from &dbfStructure | ||
|
||
* Ensure database structure file is removed | ||
dbfStructure := dbfStructure + ".dbf" | ||
erase &dbfStructure | ||
return | ||
|
||
procedure AddTestDatabase(dbfName, testName, cmpOp, expValue, cmdStr) | ||
* Load a test data record into tests database (note use of 'Wrap' to | ||
* preserve spaces in expected value string) | ||
use &dbfName | ||
append blank | ||
replace &dbfName->NAME with testName | ||
replace &dbfName->CMPOP with cmpOp | ||
replace &dbfName->EXPVALUE with Wrap(expValue) | ||
replace &dbfName->CMDSTR with cmdStr | ||
close &dbfName | ||
return | ||
|
||
function RunTests(dbfName, keepTestDBF, outputJSON) | ||
local testName, cmpOp, expValue, cmdStr, retValue, testExpr | ||
local success := .T. | ||
|
||
use &dbfName | ||
|
||
* Determine, and print, number of tests (required for TAP) | ||
if outputJSON == NIL .OR. !outputJSON | ||
? "1.." + LTRIM(STR(LASTREC())) | ||
endif | ||
|
||
* Execute unit tests | ||
do while !EOF() | ||
* Extract test data (note use of 'Unwrap' to extract space-preserved | ||
* expected value string) | ||
testName := ALLTRIM(&dbfName->NAME) | ||
cmpOp := &dbfName->CMPOP | ||
expValue := Unwrap(ALLTRIM(&dbfName->EXPVALUE)) | ||
cmdStr := ALLTRIM(&dbfName->CMDSTR) | ||
|
||
* Execute test, and build test expression | ||
retValue := TypeToS(&cmdStr) | ||
testExpr := '"' + retValue + '" ' + cmpOp + ' "' + expValue + '"' | ||
|
||
* If the parameter flag, outputJSON, is omitted, or set to .F., then | ||
* emit test report in TAP format | ||
if outputJSON == NIL .OR. !outputJSON | ||
* Report test outcome - TAP | ||
if &testExpr | ||
? "OK " + LTRIM(STR(RECNO())) + " - " + testName | ||
else | ||
* Single test failure signals failure of whole suite | ||
success := .F. | ||
? "FAIL " + LTRIM(STR(RECNO())) + " - " + testName | ||
endif | ||
else | ||
* Report test outcome - JSON | ||
? "JSON" | ||
endif | ||
|
||
* ... next test | ||
skip | ||
enddo | ||
|
||
close &dbfName | ||
|
||
* If the parameter flag, keepTestDBF, is omitted, or set to .F., then | ||
* remove the tests database | ||
if keepTestDBF == NIL .OR. !keepTestDBF | ||
dbfName := dbfName + ".dbf" | ||
erase &dbfName | ||
endif | ||
|
||
return success | ||
|
||
function TypeToS(value) | ||
* Use VALTYPE() instead of TYPE() to check type | ||
local typeValue := VALTYPE(value) | ||
|
||
switch typeValue | ||
* Array type (assume 1D array of non-aggregate elements), | ||
* returns the concatenation of elements as a string | ||
case "A" ; return ArrToS(value) | ||
|
||
* Character type returned untouched | ||
case "C" ; return value | ||
|
||
* Date as "yyyymmdd" | ||
case "D" ; return DTOS(value) | ||
|
||
* Logical as literal string representation of self | ||
case "L" ; return IIF(value, ".T.", ".F.") | ||
|
||
* String-converted numerics are right-justified, so ensure are | ||
* returned trimmed | ||
case "N" ; return ALLTRIM(STR(value)) | ||
|
||
* Support use of NIL return type (usually to indicate error) | ||
case "U" ; return "NIL" | ||
endswitch | ||
|
||
* Ignore the remaining types, just return NIL (likely runtime error) | ||
return NIL | ||
|
||
* Utilities to preserve leading and trailing spaces in strings as they | ||
* are stored into, and extracted from, database fields | ||
function Wrap(string) ; return WrapString(string, .F., "[", "]") | ||
function Unwrap(string) ; return WrapString(string, .T.) | ||
|
||
function WrapString(string, doUnwrap, wrapStart, wrapEnd) | ||
local uws | ||
if doUnwrap | ||
uws := SUBSTR(SUBSTR(string, 2), 1, LEN(string) - 2) | ||
else | ||
uws := wrapStart + string + wrapEnd | ||
endif | ||
return uws | ||
|
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,8 @@ | ||
* ---------------------------------------------------------------------------- | ||
* exercism.org | ||
* Harbour Track Exercise: error-handling | ||
* ---------------------------------------------------------------------------- | ||
|
||
function HandleError(name) | ||
return "" | ||
|
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,47 @@ | ||
* ---------------------------------------------------------------------------- | ||
* Harbour Unit Test Runner [error_handling.prg] | ||
* ---------------------------------------------------------------------------- | ||
|
||
* Variable declarations | ||
memvar TESTS, SUCCESS | ||
|
||
* Test database name | ||
TESTS := IIF(PCOUNT() > 0, hb_PValue(1), "TESTS") | ||
|
||
* Create tests database | ||
do MakeTestDatabase with TESTS | ||
|
||
* Add test data into tests database. Each test case stub should be altered | ||
* to follow this format: | ||
* | ||
* do AddTestDatabase with TESTS, "say Hi!", "==", "Hello, World!", "HelloWorld()" | ||
* do AddTestDatabase with TESTS, "add 5 and 3", "==", "8", "Add_5_And_3(5, 3)" | ||
* | ||
* Note: | ||
* 1st field is the test description (already supplied) | ||
* 2nd field is comparator operator, usually "==" | ||
* 3rd field is the function return result, always written as a string | ||
* 4th field is the function (optionally with arguments), always written as a string | ||
* | ||
|
||
* Add test data into tests database | ||
do AddTestDatabase with TESTS, "Correct arguments", "==", "Hello, Alice", "HandleError('Alice')" | ||
do AddTestDatabase with TESTS, "One long argument", "==", "Hello, Alice and Bob", "HandleError('Alice and Bob')" | ||
do AddTestDatabase with TESTS, "Incorrect arguments", "==", "NIL", "HandleError('Alice', 'Bob')" | ||
do AddTestDatabase with TESTS, "Return error indicator with no value given", "==", "NIL", "HandleError()" | ||
do AddTestDatabase with TESTS, "Empty argument", "==", "Hello, ", "HandleError('')" | ||
|
||
* Execute unit tests. Arguments: | ||
* - Tests database name | ||
* - Database retention flag (.T. to not delete test database on test end) | ||
* - JSON output flag (.T. to emit test results in JSON format [default is TAP]) | ||
SUCCESS := RunTests(TESTS, SToBool(hb_PValue(2)), SToBool(hb_PValue(3))) | ||
|
||
* Return success status to OS | ||
ERRORLEVEL(IIF(SUCCESS, 0, 1)) | ||
|
||
* Code under test (CUT) | ||
#include "error_handling.prg" | ||
|
||
* Unit Test Framework | ||
#include "PRGUNIT.prg" |
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,86 @@ | ||
* ---------------------------------------------------------------------------- | ||
* Harbour Miscellaneous Utilities | ||
* Anthony J. Borla ([email protected]) | ||
* ---------------------------------------------------------------------------- | ||
|
||
#define UTILS_PRG | ||
|
||
* | ||
* Given a string, charSet, interpreted as a set of individual characters, | ||
* and a string, string, returns a copy of string with all occurrences of | ||
* the characters in charSet, removed. | ||
* | ||
function RemoveCharSet(charSet, string) | ||
local i, clen := LEN(charSet) | ||
for i := 1 to clen | ||
string := STRTRAN(string, SUBSTR(charSet, i, 1)) | ||
next | ||
return string | ||
|
||
* | ||
* Given a string, and a separator string (usually a single character) | ||
* returns an array of separator-split tokens, or the original string | ||
* if separation not possible. | ||
* | ||
function SToArr(string, separator) | ||
local array := {}, i, element | ||
|
||
* Return untouched string if no separator, or it is not in string | ||
if PCOUNT() < 2 .OR. separator == NIL ; return string ; endif | ||
i := AT(separator, string) ; if i == 0 ; return string ; endif | ||
|
||
* Parse the string, extracting each element, and adding to array | ||
do while i <> 0 | ||
element := LEFT(string, i - 1) | ||
if !EMPTY(element) ; AADD(array, element) ; endif | ||
string := SUBSTR(string, i + 1) | ||
i := AT(separator, string) | ||
enddo | ||
|
||
* Handle last element, and return array | ||
if !EMPTY(string) ; AADD(array, string) ; endif | ||
return array | ||
|
||
* | ||
* Given an array whose elements are non-aggregate types, returns a | ||
* string of those elements separated by separator. If a string | ||
* cannot be built, an empty string is returned. | ||
* | ||
function ArrToS(array, separator) | ||
local i, element, string := "", arrlen := LEN(array) | ||
if PCOUNT() < 2 .OR. separator == NIL ; separator := "" ; endif | ||
if arrlen < 1 ; return "" ; endif | ||
for i := 1 to arrlen | ||
element := IIF(VALTYPE(array[i]) <> "C", ALLTRIM(STR(array[i])), array[i]) | ||
string += element + separator | ||
next | ||
return ; | ||
IIF(EMPTY(separator), string, SUBSTR(string, 1, RAT(separator, string) - 1)) | ||
|
||
* | ||
* Given a string, returns the Boolean status indicating whether | ||
* it is convertible to an integer. Non-numeric and floating | ||
* point values will both return .F. | ||
* | ||
function IsINTString(s) | ||
local slen, i | ||
if PCOUNT() <> 1 .OR. VALTYPE(s) <> "C" ; return .F. ; endif | ||
slen := LEN(s) | ||
if AT(".", s) <> 0 .OR. slen < 1 ; return .F. ; endif | ||
if VAL(s) <> 0 ; return .T. ; endif | ||
for i := 1 to slen | ||
if !(SUBSTR(s, i, 1) $ "0123456789") ; return .F. ; endif | ||
next | ||
return .T. | ||
|
||
* | ||
* Given a string, returns the Boolean value represented. | ||
* | ||
function SToBool(s) | ||
return ; | ||
IIF(VALTYPE(s) <> "C", NIL, ; | ||
IIF(UPPER(s) == ".T.", .T., ; | ||
IIF(UPPER(s) == ".F.", .F., ; | ||
IIF(SUBSTR(s, 1, 1) $ 'Tt', .T., ; | ||
IIF(SUBSTR(s, 1, 1) $ 'Ff', .F., NIL))))) | ||
|