diff --git a/exercises/book-store/BookStoreTests.dpr b/exercises/book-store/BookStoreTests.dpr new file mode 100644 index 00000000..361e9dca --- /dev/null +++ b/exercises/book-store/BookStoreTests.dpr @@ -0,0 +1,60 @@ +program BookStoreTests; + +{$IFNDEF TESTINSIGHT} +{$APPTYPE CONSOLE} +{$ENDIF}{$STRONGLINKTYPES ON} +uses + System.SysUtils, + {$IFDEF TESTINSIGHT} + TestInsight.DUnitX, + {$ENDIF } + DUnitX.Loggers.Console, + DUnitX.Loggers.Xml.NUnit, + DUnitX.TestFramework, + uBookStoreTests in 'uBookStoreTests.pas', + uBookStore in 'uBookStore.pas'; + +var + runner : ITestRunner; + results : IRunResults; + logger : ITestLogger; + nunitLogger : ITestLogger; +begin +{$IFDEF TESTINSIGHT} + TestInsight.DUnitX.RunRegisteredTests; + exit; +{$ENDIF} + try + //Check command line options, will exit if invalid + TDUnitX.CheckCommandLine; + //Create the test runner + runner := TDUnitX.CreateRunner; + //Tell the runner to use RTTI to find Fixtures + runner.UseRTTI := True; + //tell the runner how we will log things + //Log to the console window + logger := TDUnitXConsoleLogger.Create(true); + runner.AddLogger(logger); + //Generate an NUnit compatible XML File + nunitLogger := TDUnitXXMLNUnitFileLogger.Create(TDUnitX.Options.XMLOutputFile); + runner.AddLogger(nunitLogger); + runner.FailsOnNoAsserts := False; //When true, Assertions must be made during tests; + + //Run tests + results := runner.Execute; + if not results.AllPassed then + System.ExitCode := EXIT_ERRORS; + + {$IFNDEF CI} + //We don't want this happening when running under CI. + if TDUnitX.Options.ExitBehavior = TDUnitXExitBehavior.Pause then + begin + System.Write('Done.. press key to quit.'); + System.Readln; + end; + {$ENDIF} + except + on E: Exception do + System.Writeln(E.ClassName, ': ', E.Message); + end; +end. diff --git a/exercises/book-store/BookStoreTests.dproj b/exercises/book-store/BookStoreTests.dproj new file mode 100644 index 00000000..4f25ec56 --- /dev/null +++ b/exercises/book-store/BookStoreTests.dproj @@ -0,0 +1,176 @@ + + + {00833D4E-9C22-4AE8-8578-81B63523F423} + 15.3 + None + BookStoreTests.dpr + True + Debug + Win32 + 1 + Console + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + $(BDS)\bin\delphi_PROJECTICNS.icns + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + $(DUnitX);$(DCC_UnitSearchPath) + .\$(Platform)\$(Config) + .\$(Platform)\$(Config) + false + false + false + false + false + + + FireDACASADriver;FireDACSqliteDriver;bindcompfmx;DBXSqliteDriver;FireDACPgDriver;FireDACODBCDriver;fmx;rtl;dbrtl;DbxClientDriver;IndySystem;FireDACCommon;bindcomp;DBXInterBaseDriver;DataSnapCommon;xmlrtl;ibxpress;DbxCommonDriver;IndyProtocols;dbxcds;DBXMySQLDriver;FireDACCommonDriver;soaprtl;bindengine;bindcompdbx;FMXTee;fmxFireDAC;FireDACADSDriver;CustomIPTransport;FireDAC;dsnap;IndyIPServer;fmxase;IndyCore;IndyIPCommon;CloudService;FireDACIBDriver;FmxTeeUI;inet;fmxobj;FireDACMySQLDriver;inetdbxpress;fmxdae;RESTComponents;FireDACMSAccDriver;dbexpress;IndyIPClient;$(DCC_UsePackage) + + + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + 1033 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + frxDB19;L208_r140;FireDACASADriver;FireDACSqliteDriver;bindcompfmx;DBXSqliteDriver;AbbreviaVCL;vcldbx;FireDACPgDriver;FireDACODBCDriver;S404_r;fmx;rtl;dbrtl;DbxClientDriver;IndySystem;FireDACCommon;bindcomp;TeeDB;frx19;vclib;inetdbbde;DBXInterBaseDriver;Tee;DataSnapCommon;vclFireDAC;Rave110VCL;xmlrtl;svnui;ibxpress;DbxCommonDriver;vclimg;IndyProtocols;dbxcds;DBXMySQLDriver;FireDACCommonDriver;MetropolisUILiveTile;O408_r;soaprtl;bindengine;vclactnband;vcldb;bindcompdbx;vcldsnap;bindcompvcl;FMXTee;TeeUI;AsyncPro;vclie;XMLPartner_PR;fmxFireDAC;FireDACADSDriver;vcltouch;CustomIPTransport;G114_R140;vclribbon;VclSmp;FireDAC;dsnap;IndyIPServer;Intraweb;fmxase;vcl;IndyCore;VCLRESTComponents;IndyIPCommon;CloudService;CodeSiteExpressPkg;dsnapcon;FireDACIBDriver;FmxTeeUI;inet;fmxobj;E111_R120;FireDACMySQLDriver;vclx;inetdbxpress;svn;fmxdae;frxe19;RESTComponents;bdertl;FireDACMSAccDriver;adortl;dbexpress;IndyIPClient;$(DCC_UsePackage) + + + FireDACASADriver;FireDACSqliteDriver;bindcompfmx;DBXSqliteDriver;AbbreviaVCL;FireDACPgDriver;FireDACODBCDriver;fmx;rtl;dbrtl;DbxClientDriver;IndySystem;FireDACCommon;bindcomp;TeeDB;vclib;DBXInterBaseDriver;Tee;DataSnapCommon;vclFireDAC;xmlrtl;ibxpress;DbxCommonDriver;vclimg;IndyProtocols;dbxcds;DBXMySQLDriver;FireDACCommonDriver;MetropolisUILiveTile;O408_r;soaprtl;bindengine;vclactnband;vcldb;bindcompdbx;vcldsnap;bindcompvcl;FMXTee;TeeUI;vclie;fmxFireDAC;FireDACADSDriver;vcltouch;CustomIPTransport;vclribbon;VclSmp;FireDAC;dsnap;IndyIPServer;Intraweb;fmxase;vcl;IndyCore;VCLRESTComponents;IndyIPCommon;CloudService;dsnapcon;FireDACIBDriver;FmxTeeUI;inet;fmxobj;FireDACMySQLDriver;vclx;inetdbxpress;fmxdae;RESTComponents;FireDACMSAccDriver;adortl;dbexpress;IndyIPClient;$(DCC_UsePackage) + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + false + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + + MainSource + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + + + + + False + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 1033 + 1252 + + + + + 1.0.0.0 + + + + + + 1.0.0.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + BookStoreTests.dpr + + + + + False + True + False + + + 12 + + + + diff --git a/exercises/book-store/uBookStoreExample.pas b/exercises/book-store/uBookStoreExample.pas new file mode 100644 index 00000000..d3f28216 --- /dev/null +++ b/exercises/book-store/uBookStoreExample.pas @@ -0,0 +1,144 @@ +unit uBookStore; + +interface + +type + IBasket = interface(IInvokable) + ['{22B4BAF3-88E6-456D-9DE5-F6BAC743A655}'] + function Total:extended; + end; + +function NewBasket(aBasket: TArray): IBasket; + +implementation +uses System.SysUtils; + +const + seriesBooks = '12345'; + cNumberOfBooks = 5; + cDiscounts: array[1..cNumberOfBooks] of extended = (1.00, + 0.95, + 0.90, + 0.80, + 0.75); + +type + TBasket = class(TInterfacedObject, IBasket) + private + fSingleBookPrice: extended; + fBasket: string; + class function Head(inStr: string): string; static; + class function Tail(inStr: string): string; static; + class function ConvertIntArrayToString(const aIntArray: TArray): string; static; + function DiscountPercentage(inStr : string): extended; + function GroupBasket:TArray; + function NumberOfDifferentBooks(inStr : string):integer; + public + function Total:extended; + constructor Create(aBasket: TArray); + end; + +function NewBasket(aBasket: TArray): IBasket; +begin + result := TBasket.Create(aBasket); +end; + +class function TBasket.Head(inStr : string):string; +begin + result := inStr.Remove(1); +end; + +class function TBasket.Tail(inStr : string):string; +begin + result := inStr.Remove(0,1); +end; + +class function TBasket.ConvertIntArrayToString(const aIntArray: TArray): string; +var arrayItem: integer; +begin + result := ''; + if length(aIntArray) > 0 then + for arrayItem in aIntArray do + result := result + arrayItem.ToString; +end; + +constructor TBasket.Create(aBasket: TArray); +begin + fSingleBookPrice := 8; + fBasket := ConvertIntArrayToString(aBasket); +end; + +function TBasket.GroupBasket:TArray; +var lStrArray: TArray; + wrkBasket: string; + tmpStr : string; + thisBook : string; + Index : integer; + StrCount : integer; +begin + wrkBasket := fBasket; + StrCount := 1; + SetLength(lStrArray,StrCount); + thisBook := Head(wrkBasket); + while wrkBasket.Length > 0 do + begin + Index := 0; + repeat + tmpStr := lStrArray[Index]; + if thisBook.Length > 0 then + begin + if not tmpStr.Contains(thisBook) then + begin + tmpStr := tmpStr + thisBook; + lStrArray[Index] := tmpStr; + wrkBasket := Tail(wrkBasket); + thisBook := Head(wrkBasket); + end + else + if (Index = StrCount - 1) then + begin + inc(StrCount); + SetLength(lStrArray,StrCount); + end; + inc(Index); + end; + until (Index = StrCount) or wrkBasket.IsEmpty; + end; + result := lStrArray; +end; + +function TBasket.Total:extended; +var hpBook : char; + totalBooks : integer; + subBaskets : TArray; + wrkSubBasket: string; + subTotal : extended; +begin + subBaskets := GroupBasket; + result := 0; + for wrkSubBasket in subBaskets do + begin + totalBooks := wrkSubBasket.Length; + subTotal := totalBooks * (fSingleBookPrice * DiscountPercentage(wrkSubBasket)); + result := result + subTotal; + end; +end; + +function TBasket.DiscountPercentage(inStr : string):extended; +var numDiffBooks: integer; +begin + result := 1; + numDiffBooks := NumberOfDifferentBooks(inStr); + result := CDiscounts[numDiffBooks]; +end; + +function TBasket.NumberOfDifferentBooks(inStr : string):integer; +var Book: char; +begin + result := 0; + for Book in seriesBooks do + if inStr.Contains(Book) then + inc(result); +end; + +end. diff --git a/exercises/book-store/uBookStoreTests.pas b/exercises/book-store/uBookStoreTests.pas new file mode 100644 index 00000000..cb6523c7 --- /dev/null +++ b/exercises/book-store/uBookStoreTests.pas @@ -0,0 +1,56 @@ +unit uBookStoreTests; + +interface +uses + DUnitX.TestFramework; + +type + [TestFixture] + hpTests = class(TObject) + private + class function ConvertStrToIntArray(aString: string): TArray; static; + public + [Test] + [TestCase('Test total basket price single book', '1, 8.0')] +// [TestCase('Test total basket Price two of same book', '2|2, 16.0')] +// [TestCase('Test total basket price empty basket', ', 0.0')] +// [TestCase('Test basket with two different books', '1|2, 15.2')] +// [TestCase('Test basket with three different books', '1|2|3, 21.6')] +// [TestCase('Test basket with four different books', '1|2|3|4, 25.6')] +// [TestCase('Test basket with five different books', '1|2|3|4|5, 30.0')] +// [TestCase('Test basket w/2 copies of books 1..3 and one copy of books 4 and 5', '1|1|2|2|3|3|4|5, 51.20')] +// [TestCase('Test basket w/2 copies of books 1..4 and one copy of book 5', '1|1|2|2|3|3|4|4|5, 55.60')] +// [TestCase('Test basket w/2 copies of each book', '1|1|2|2|3|3|4|4|5|5, 60.00')] +// [TestCase('Test basket w/2 copies of each book plus one more copy of book 1', '1|1|2|2|3|3|4|4|5|5|1, 68.00')] +// [TestCase('Test basket w/2 copies of each book plus one more copy of books 1 and 2', '1|1|2|2|3|3|4|4|5|5|1|2, 75.20')] + procedure SeveralBasketTestCases(const aBasket: string; const Expected: extended); + end; + +implementation +uses System.SysUtils, uBookStore; + +const MinDelta = 0.005; //cents + +class function hpTests.ConvertStrToIntArray(aString: string): TArray; +var splitStr: TArray; + i: integer; +begin + result := nil; + splitStr := aString.Split(['|']); + SetLength(result, length(splitStr)); + for i := Low(splitStr) to High(splitStr) do + result[i] := splitStr[i].ToInteger; +end; + +procedure hpTests.SeveralBasketTestCases(const aBasket: string; const Expected: extended); +var Basket: TArray; + fBasket: IBasket; +begin + Basket := ConvertStrToIntArray(aBasket); + fBasket := NewBasket(Basket); + assert.AreEqual(Expected, FBasket.Total, MinDelta, format('Total should be %0.2f',[Expected])); +end; + +initialization + TDUnitX.RegisterTestFixture(hpTests); +end.