diff --git a/.gitignore b/.gitignore index 3e759b7..cb05819 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore +# Project Specific Items +SavingTestArea/ + # User-specific files *.suo *.user diff --git a/SmoulderingBeachBall.sln b/SmoulderingBeachBall.sln index 8328c68..0e40a48 100644 --- a/SmoulderingBeachBall.sln +++ b/SmoulderingBeachBall.sln @@ -7,6 +7,8 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "SmoulderingBeachBall", "Smo EndProject Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "SmoulderingBeachBallCLI", "SmoulderingBeachBallCLI\SmoulderingBeachBallCLI.fsproj", "{5C00D583-EF09-4FE6-A3FE-EB01454C0607}" EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "TestCentre", "TestCentre\TestCentre.fsproj", "{9EAEE0E6-1E90-4C44-AD22-E8CA727B3392}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,6 +23,10 @@ Global {5C00D583-EF09-4FE6-A3FE-EB01454C0607}.Debug|Any CPU.Build.0 = Debug|Any CPU {5C00D583-EF09-4FE6-A3FE-EB01454C0607}.Release|Any CPU.ActiveCfg = Release|Any CPU {5C00D583-EF09-4FE6-A3FE-EB01454C0607}.Release|Any CPU.Build.0 = Release|Any CPU + {9EAEE0E6-1E90-4C44-AD22-E8CA727B3392}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9EAEE0E6-1E90-4C44-AD22-E8CA727B3392}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9EAEE0E6-1E90-4C44-AD22-E8CA727B3392}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9EAEE0E6-1E90-4C44-AD22-E8CA727B3392}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/SmoulderingBeachBall/InternalServices.fs b/SmoulderingBeachBall/InternalServices.fs index 260e608..7402b30 100644 --- a/SmoulderingBeachBall/InternalServices.fs +++ b/SmoulderingBeachBall/InternalServices.fs @@ -44,7 +44,6 @@ let penPath = createBorderPath pen.Width spec graphics.DrawLines (pen, penPath) - let drawFullOverlay (graphics: Graphics) (pen: Pen) spec = drawBorder graphics pen spec printfn "[INFO.] Adding full overlay to image..." @@ -70,7 +69,6 @@ sb.Append ".png" |> ignore (sb.ToString ()) - let drawImage spec = let bitmap = new Bitmap (spec.width, spec.height) let graphics = Graphics.FromImage (bitmap) diff --git a/SmoulderingBeachBall/ScratchPad.fsx b/SmoulderingBeachBall/ScratchPad.fsx index 544dcb6..6d870b3 100644 --- a/SmoulderingBeachBall/ScratchPad.fsx +++ b/SmoulderingBeachBall/ScratchPad.fsx @@ -34,7 +34,7 @@ let imageSpec = { width = 500; height = 500; colour = Brushes.Yellow; - filePath = "C:/users/craig/desktop/test.png"; + filePath = "C:/users/craig/desktop/"; // Change this to flip between border/full overlay or None. overlay = Some fullOverlay } diff --git a/SmoulderingBeachBallCLI/Validation.fs b/SmoulderingBeachBallCLI/Validation.fs index 3820173..47460e6 100644 --- a/SmoulderingBeachBallCLI/Validation.fs +++ b/SmoulderingBeachBallCLI/Validation.fs @@ -1,4 +1,4 @@ -module internal Validation +module Validation open System.Drawing open SmoulderingBeachBall.Domain.DomainTypes @@ -16,11 +16,11 @@ "yellow", Brushes.Yellow;] |> Map.ofList - let isColourValid (colour: string) = + let private isColourValid (colour: string) = colourList |> Map.containsKey (colour.ToLower()) - let parseColour colour = + let private parseColour colour = match (isColourValid colour) with | true -> colourList @@ -33,7 +33,7 @@ let getDesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) - let parsePath (path: string) = + let private parsePath (path: string) = match path with | path when ((path.ToLower()).Equals "desktop") -> getDesktopPath | path when ((path.ToLower()).Equals "d") -> getDesktopPath @@ -52,7 +52,7 @@ } spec - let parseOverlay (oType: string) = + let private parseOverlay (oType: string) = match oType.ToLower() with | "border" -> Border | "b" -> Border @@ -60,16 +60,16 @@ | "f" -> Full | _ -> invalidArg "Overlay Type" "The overlay type must be either 'border' or 'full'." - let buildOverlaySpec oColour (oType: string) = + let private buildOverlaySpec oColour (oType: string) = let oSpec = - { colour = parseColour oColour; + { colour = parseColour oColour overlayType = parseOverlay oType } oSpec - let buildMainSpec iWidth iHeight mainColour path oSpec = + let private buildMainSpec iWidth iHeight mainColour path oSpec = let spec = - { width = iWidth; - height = iHeight; + { width = iWidth + height = iHeight colour = parseColour mainColour filePath = parsePath path overlay = oSpec } diff --git a/TestCentre/AssemblyInfo.fs b/TestCentre/AssemblyInfo.fs new file mode 100644 index 0000000..bfea358 --- /dev/null +++ b/TestCentre/AssemblyInfo.fs @@ -0,0 +1,41 @@ +namespace TestCentre.AssemblyInfo + +open System.Reflection +open System.Runtime.CompilerServices +open System.Runtime.InteropServices + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[] +[] +[] +[] +[] +[] +[] +[] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [] +[] +[] + +do + () \ No newline at end of file diff --git a/TestCentre/ConsoleTests.fs b/TestCentre/ConsoleTests.fs new file mode 100644 index 0000000..a1705c3 --- /dev/null +++ b/TestCentre/ConsoleTests.fs @@ -0,0 +1,123 @@ +namespace ConsoleTests + + module PropertyTests = + + open FsCheck.Xunit + open Validation + open System + open SmoulderingBeachBall.Domain.DomainTypes + open System.Drawing + + (* The functions without the "Property" attribute are not for testing. + They generate the test data used in the (property) tests. + Please be careful when removing any of these types of functions. + They are the ones not marked with the "Property" attribute. + Also, the intention is to keep these functions seperate from the tests. + Until it is not practical to keep them in this module, the default place to keep them is above the tests. With that said, it is just a guide. + Use your best judgement when adding new "helper" functions. + This applies to the UnitTests module, thanks. *) + + let randomInt () = Random().Next() + + let randomColour () = + colourList + |> Map.toList + |> List.item (Random().Next(colourList.Count)) + + let overlays = [|"none"; "n"; "b"; "border"; "full"; "f"|] + + let noOverlay () = overlays.[Random().Next(0, 1)] + + let borderOverlay () = overlays.[Random().Next(2, 3)] + + let fullOverlay () = overlays.[Random().Next(4, 5)] + + [] + let ``Can create an intended default image spec`` () = + let width = randomInt () + let height = randomInt () + let oSpec = + { colour = Brushes.Black + overlayType = Full } + let defaultSpec = + { width = width + height = height + colour = Brushes.AntiqueWhite + filePath = getDesktopPath + overlay = Some oSpec + } + let spec = buildDefaultSpec width height + defaultSpec = spec + + [] + let ``Can create an image spec with border`` () = + let width = randomInt () + let height = randomInt () + let mainColour = randomColour () + let overlayColour = randomColour () + let overlay = borderOverlay () + let oSpec = + { colour = (snd) overlayColour + overlayType = Border } + let expectedSpec = + { width = width + height = height + colour = (snd) mainColour + filePath = getDesktopPath + overlay = Some oSpec + } + let spec = + buildSpec width height (fst mainColour) (fst overlayColour) overlay getDesktopPath + expectedSpec = spec + + [] + let ``Can create an image spec with full overlay`` () = + let width = randomInt () + let height = randomInt () + let mainColour = randomColour () + let overlayColour = randomColour () + let overlay = fullOverlay () + let oSpec = + { colour = snd overlayColour + overlayType = Full } + let expectedSpec = + { width = width + height = height + colour = snd mainColour + filePath = getDesktopPath + overlay = Some oSpec } + let spec = + buildSpec width height (fst mainColour) (fst overlayColour) overlay getDesktopPath + expectedSpec = spec + + [] + let ``Can create an image spec with no overlay`` () = + let width = randomInt () + let height = randomInt () + let colour = randomColour () + let overlay = noOverlay () + let expectedSpec = + { width = width + height = height + colour = snd colour + filePath = getDesktopPath + overlay = option.None } + let spec = + buildSpec width height (fst colour) (fst colour) overlay getDesktopPath + expectedSpec = spec + + module UnitTests = + + open Xunit + open Validation + open System + + [] + let ``Colour map is not empty`` () = + Assert.NotEmpty colourList + + [] + let ``Can retrieve the path to users desktop`` () = + let expectedPath = + Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + Assert.Equal (expectedPath, getDesktopPath) \ No newline at end of file diff --git a/TestCentre/LibraryTests.fs b/TestCentre/LibraryTests.fs new file mode 100644 index 0000000..78c7b9a --- /dev/null +++ b/TestCentre/LibraryTests.fs @@ -0,0 +1,105 @@ +namespace LibraryTests + + module PropertyTests = + + open FsCheck.Xunit + open System + open System.Drawing + open SmoulderingBeachBall.Domain.DomainTypes + open System.Reflection + open SmoulderingBeachBall.Services + open System.IO + open System.Threading + open FsCheck + + (* '3000' is an arbitary value. It is used to keep the testing times sane. + This function is used to randomly generate image sizes but you do not need to use it. + The project can handle larger numbers than '3000' but processing takes longer. + If you decide to change it, please keep an eye on the saving time. + When a image is too big, the project cannot write it to disc fast enough -- + sometimes it is because of IO constraints. + This means the tests checking for this fail/error will think the image does not exist. + To resolved this, add a Thread.Sleep call to the affected tests. + Just be aware, the length of sleep-time depends on the size of the image. + Also, problems tends to arise around the 15,000 mark. + *) + let randomInt () = Random().Next(3000) + + let saveLocation = __SOURCE_DIRECTORY__ + "/SavingTestArea/" + + let allColours = + let properties = + typeof.GetProperties(BindingFlags.Public ||| BindingFlags.Static) + let colours = + seq { for prop in properties -> prop} + |> Seq.toArray + colours + + let randomColour () = + let item = allColours.[Random().Next(allColours.Length)] + item.GetValue(null, null) + + let fileSaved width height = + saveLocation + width + "x" + height + ".png" + |> File.Exists + + // To manually clear out the SavingTestArea folder, use this function in script.fsx. + let resetSavingTestArea () = + let files = Directory.GetFileSystemEntries(saveLocation) + match files.Length with + | 0 -> () + | _ -> + files + |> Array.iter (fun f -> File.Delete(f)) + + let buildBorderOverlay () = + Thread.Sleep 100 // Helps generate better colour variation. + { colour = randomColour () :?> Brush + overlayType = Border } + + let buildFullOverlay () = + Thread.Sleep 100 // Helps generate better colour variation. + { colour = randomColour () :?> Brush + overlayType = Full } + + (* Please non-test functions above the test functions. + See ConsoleTests.fs for the extended comment -- regarding function placement. *) + + [] + let ``Can create an image with no overlay`` () = + resetSavingTestArea () + let spec = + { // See note accompanying 'randomInt' function for constraints information. + width = randomInt () + height = randomInt () + colour = randomColour () :?> Brush + filePath = saveLocation + overlay = None } + makeImage spec |> Async.RunSynchronously + fileSaved (spec.width.ToString()) (spec.height.ToString()) = true + + [] + let ``Can create an image with border`` () = + resetSavingTestArea () + let spec = + { // See note accompanying 'randomInt' function for constraints information. + width = randomInt () + height = randomInt () + colour = randomColour () :?> Brush + filePath = saveLocation + overlay = Some (buildBorderOverlay ()) } + makeImage spec |> Async.RunSynchronously + fileSaved (spec.width.ToString()) (spec.height.ToString()) = true + + [] + let ``Can create an image with a full overlay`` () = + resetSavingTestArea () + let spec = + { // See note accompanying 'randomInt' function for constraints information. + width = randomInt () + height = randomInt () + colour = randomColour () :?> Brush + filePath = saveLocation + overlay = Some (buildFullOverlay ()) } + makeImage spec |> Async.RunSynchronously + fileSaved (spec.width.ToString()) (spec.height.ToString()) = true diff --git a/TestCentre/Script.fsx b/TestCentre/Script.fsx new file mode 100644 index 0000000..526b141 --- /dev/null +++ b/TestCentre/Script.fsx @@ -0,0 +1,15 @@ +// Learn more about F# at http://fsharp.org +// See the 'F# Tutorial' project for more help. +open System.IO + +let saveLocation = __SOURCE_DIRECTORY__ + "/SavingTestArea/" + +let resetSavingTestArea () = + let files = Directory.GetFileSystemEntries(saveLocation) + match files.Length with + | 0 -> () + | _ -> + files + |> Array.iter (fun f -> File.Delete(f)) + +resetSavingTestArea () \ No newline at end of file diff --git a/TestCentre/TestCentre.fsproj b/TestCentre/TestCentre.fsproj new file mode 100644 index 0000000..01d14c5 --- /dev/null +++ b/TestCentre/TestCentre.fsproj @@ -0,0 +1,116 @@ + + + + + + + Debug + AnyCPU + 2.0 + 9eaee0e6-1e90-4c44-ad22-e8ca727b3392 + Library + TestCentre + TestCentre + true + v4.7.1 + true + TestCentre + + + + + true + full + false + false + bin\$(Configuration)\ + DEBUG;TRACE + 3 + bin\$(Configuration)\$(AssemblyName).XML + + + pdbonly + true + true + bin\$(Configuration)\ + TRACE + 3 + bin\$(Configuration)\$(AssemblyName).XML + + + 11 + + + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets + + + + + + + + + + + + ..\packages\FsCheck.2.11.0\lib\net452\FsCheck.dll + + + ..\packages\FsCheck.Xunit.2.11.0\lib\net452\FsCheck.Xunit.dll + + + ..\packages\FSharp.Core.4.5.2\lib\net45\FSharp.Core.dll + + + + + + + ..\packages\System.Drawing.Common.4.5.0\lib\net461\System.Drawing.Common.dll + + + + True + + + ..\packages\xunit.abstractions.2.0.3\lib\net35\xunit.abstractions.dll + + + ..\packages\xunit.assert.2.4.0\lib\netstandard2.0\xunit.assert.dll + + + ..\packages\xunit.extensibility.core.2.4.0\lib\net452\xunit.core.dll + + + ..\packages\xunit.extensibility.execution.2.4.0\lib\net452\xunit.execution.desktop.dll + + + + + SmoulderingBeachBallCLI + {5c00d583-ef09-4fe6-a3fe-eb01454c0607} + True + + + SmoulderingBeachBall + {dfc3cbca-3da7-4cf4-a8bc-bccb740fa6cd} + True + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + + \ No newline at end of file diff --git a/TestCentre/packages.config b/TestCentre/packages.config new file mode 100644 index 0000000..6de53e1 --- /dev/null +++ b/TestCentre/packages.config @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file