Browse Source

Userstory21 (#13)

* added InkCanvas

* fixed some bugs

* added some UITests

* Test changes and Bugfix:
- Added tests
- Added support for coverage report generation in CI
- Fixed a bug where points would appear out of nowhere and sometimes not
appear when they are suppose to
- Cleaned up userstories
m-edlund 5 years ago
parent
commit
68f620d518

+ 8 - 1
.appveyor.yml

@@ -7,12 +7,19 @@ install:
  - "nuget install MSTest.TestFramework"
  - "nuget restore SketchAssistant/SketchAssistant.sln"
  
-#artifacts:
+before_build:
+ - ps: .\screenres.ps1
+ 
+artifacts:
+ - path: \SketchAssistant\GeneratedReports\
 #  - path: SketchAssistant/WhiteTests/test_input_files/whitelisted/*.svg
 #    name: whitelisted svg files for testing
 #  - path: SketchAssistant/WhiteTests/test_input_files/blacklisted/*.svg
 #    name: blacklisted svg files for testing
 
+before_test:
+ - ps: "SketchAssistant/GenerateCoverageReport.bat"
+
 test:
     #don't run tests depending on [DeploymentItem] and filesystem access
     categories:

+ 13 - 0
Finished Userstories/userstory15.md

@@ -0,0 +1,13 @@
+# Userstory 15  
+ 
+|**ID**|15|  
+|-|-|
+|**Name**|Schöne Linien|
+|**Beschreibung**|Als Auftraggeber wünsche ich, dass die Linien die gezeichnet werden, an den Enden dünner und in der Mitte dicker sind, um einem echten Stift zu ähneln.|
+|**Akzeptanzkriterium**|Die Userstory wird akzeptiert, wenn die vom Anwender gezeichnete Line am Anfang schmaler ist, im Verlauf etwas breiter wird und am Ende wieder so schmal wie am Anfang ist.|
+|Geschätzter Aufwand (Story Points)|10|
+|Entwickler|Tim|
+|Umgesetzt in Iteration|9|
+|Tatsächlicher Aufwand (Std.)|keine|
+|Velocity (Std./Story Point)|keine|
+|Bemerkungen|Wurde durch Userstory 16 umgesetzt|

+ 3 - 3
userstory16.md → Finished Userstories/userstory16.md

@@ -7,7 +7,7 @@
 |**Akzeptanzkriterium**|Die Userstory ist akzeptiert, wenn die UI eine Zeichenfläche enthält und eine Fläche zum Anzeigen von Grafiken, sowie alle vorherig verfügbare Knöpfe. Zusätzlich ist erforderlich, dass die UI statt auf WinForms auf WPF basiert. Auch sollen die Knöpfe in der Toolbar durch Symbole, statt durch Text markiert sein, und es einen Edit Button gibt in welchem zusätzlich die Funktionen vorhanden sind.|
 |Geschätzter Aufwand (Story Points)|10|
 |Entwickler|Martin Edlund|
-|Umgesetzt in Iteration|keine|
-|Tatsächlicher Aufwand (Std.)|keine|
-|Velocity (Std./Story Point)|keine|
+|Umgesetzt in Iteration|9|
+|Tatsächlicher Aufwand (Std.)|30|
+|Velocity (Std./Story Point)|3|
 |Bemerkungen|Keine|

+ 13 - 0
Finished Userstories/userstory18.md

@@ -0,0 +1,13 @@
+# Userstory 18  
+ 
+|**ID**|18|  
+|-|-|
+|**Name**|Antialiasing|
+|**Beschreibung**|Als Auftraggeber möchte ich eine möglichst saubere Linie auf dem Bildschrim sehen, das heißt, dass die Kanten der Pixel geglättet werden.|
+|**Akzeptanzkriterium**|Die Breite der Linie wird an die Größe des Bildes in der rechten Picturebox angepasst. Zudem soll Antialiasing auf die Linien angewendet werden, damit diese glatte Kanten aufweisen.|
+|Geschätzter Aufwand (Story Points)|keine|
+|Entwickler|keiner|
+|Umgesetzt in Iteration|9|
+|Tatsächlicher Aufwand (Std.)|keine|
+|Velocity (Std./Story Point)|keine|
+|Bemerkungen|Wurde durch Userstory 16 gelöst|

+ 13 - 0
Finished Userstories/userstory21.md

@@ -0,0 +1,13 @@
+# Userstory 21  
+ 
+|**ID**|21|  
+|-|-|
+|**Name**|InkCanvas|
+|**Beschreibung**|Bei den manuellen Tests haben sich beim zeichnen auserhalb der vorgesehnen Zeichenoberfläche Fehler ergeben. Als Programmierer benötige ich eine Oberfläche, die resistenter gegen solche Fehler ist.|
+|**Akzeptanzkriterium**|InkCanvas soll verwendet werden, um die aktuelle Linie zu zeichnen und anzuzeigen.|
+|Geschätzter Aufwand (Story Points)|5|
+|Entwickler|Tim Reischl|
+|Umgesetzt in Iteration|13|
+|Tatsächlicher Aufwand (Std.)|30|
+|Velocity (Std./Story Point)|6|
+|Bemerkungen|Keine|

+ 4 - 1
README.md

@@ -1 +1,4 @@
-# BP-SketchAssistant
+# Interactive Sketch Assistant
+[![Build status](https://ci.appveyor.com/api/projects/status/yvefugnjqwc8649y?svg=true)](https://ci.appveyor.com/project/m-edlund/bp-sketchassistant)
+
+A Sketch Assistant to help you with sketching

+ 13 - 0
SketchAssistant/GenerateCoverageReport.bat

@@ -0,0 +1,13 @@
+if not exist "%~dp0GeneratedReports" mkdir "%~dp0GeneratedReports"
+
+"%~dp0\packages\OpenCover.4.7.922\tools\OpenCover.Console.exe" ^
+-register:user ^
+-target:"%~dp0\packages\Microsoft.TestPlatform.16.0.0\tools\net451\Common7\IDE\Extensions\TestPlatform\vstest.console.exe" ^
+-targetargs:"%~dp0\WhiteTests\bin\Debug\WhiteTests.dll" ^
+-filter:"+[SketchAssistantWPF*]*" ^
+-mergebyhash ^
+-output:"%~dp0\GeneratedReports\opencovertests.xml"
+
+"%~dp0\packages\ReportGenerator.4.0.14\tools\net47\ReportGenerator.exe" ^
+-reports:"%~dp0\GeneratedReports\opencovertests.xml" ^
+-targetdir:"%~dp0\GeneratedReports\ReportGeneratorOutput"

+ 5 - 0
SketchAssistant/SketchAssistant.sln

@@ -7,6 +7,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SketchAssistantWPF", "Sketc
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WhiteTests", "WhiteTests\WhiteTests.csproj", "{EB09C624-91F2-465F-825B-559BF7A7D5CB}"
 EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CoverageTools", "CoverageTools", "{A179F8A5-FBEB-4DBB-8AF4-DD1B5C714A8F}"
+	ProjectSection(SolutionItems) = preProject
+		GenerateCoverageReport.bat = GenerateCoverageReport.bat
+	EndProjectSection
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU

File diff suppressed because it is too large
+ 0 - 0
SketchAssistant/SketchAssistantWPF/DebugData.cs


+ 0 - 48
SketchAssistant/SketchAssistantWPF/GeometryCalculator.cs

@@ -12,54 +12,6 @@ namespace SketchAssistantWPF
     /// </summary>
     public static class GeometryCalculator
     {
-        /// <summary>
-        /// An implementation of the Bresenham Line Algorithm,
-        /// which calculates the points of a circle in a radius around a center point.
-        /// Implemented with the help of code examples on Wikipedia.
-        /// </summary>
-        /// <param name="center">The center of the circle.</param>
-        /// <param name="radius">The radius of the circle, 
-        /// when it is zero or less, only the midpoint is returned</param>
-        /// <returns>The HashSet containing all Points on the circle.</returns>
-        public static HashSet<Point> BresenhamCircleAlgorithm(Point center, int radius)
-        {
-            if (radius <= 0) { return new HashSet<Point> { center }; }
-
-            int x = radius - 1;
-            int y = 0;
-            int dx = 1;
-            int dy = 1;
-            int err = dx - (radius * 2);
-            HashSet<Point> returnSet = new HashSet<Point>();
-
-            while (x >= y)
-            {
-                returnSet.Add(new Point(center.X + x, center.Y + y));
-                returnSet.Add(new Point(center.X + y, center.Y + x));
-                returnSet.Add(new Point(center.X - y, center.Y + x));
-                returnSet.Add(new Point(center.X - x, center.Y + y));
-                returnSet.Add(new Point(center.X - x, center.Y - y));
-                returnSet.Add(new Point(center.X - y, center.Y - x));
-                returnSet.Add(new Point(center.X + y, center.Y - x));
-                returnSet.Add(new Point(center.X + x, center.Y - y));
-
-                if (err <= 0)
-                {
-                    y++;
-                    err += dy;
-                    dy += 2;
-                }
-
-                if (err > 0)
-                {
-                    x--;
-                    dx += 2;
-                    err += dx - (radius * 2);
-                }
-            }
-            return returnSet;
-        }
-
         /// <summary>
         /// A simple algorithm that returns a filled circle with a radius and a center point.
         /// </summary>

+ 20 - 11
SketchAssistant/SketchAssistantWPF/MVP_Model.cs

@@ -361,19 +361,27 @@ namespace SketchAssistantWPF
         /// <summary>
         /// Finish the current Line, when the pressed Mouse is released.
         /// </summary>
-        public void MouseUp()
+        /// <param name="valid">Whether the up event is valid or not</param>
+        public void MouseUp(bool valid)
         {
             mouseDown = false;
-            if (inDrawingMode && currentLine.Count > 0)
+            if (valid)
+            {
+                if (inDrawingMode && currentLine.Count > 0)
+                {
+                    InternalLine newLine = new InternalLine(currentLine, rightLineList.Count);
+                    rightLineList.Add(new Tuple<bool, InternalLine>(true, newLine));
+                    newLine.PopulateMatrixes(isFilledMatrix, linesMatrix);
+                    programPresenter.PassLastActionTaken(historyOfActions.AddNewAction(new SketchAction(SketchAction.ActionType.Draw, newLine.GetID())));
+                    //TODO: For the person implementing overlay: Add check if overlay needs to be added
+                    programPresenter.UpdateRightLines(rightLineList);
+                    currentLine.Clear();
+                    //programPresenter.UpdateCurrentLine(currentLine);
+                }
+            }
+            else
             {
-                InternalLine newLine = new InternalLine(currentLine, rightLineList.Count);
-                rightLineList.Add(new Tuple<bool, InternalLine>(true, newLine));
-                newLine.PopulateMatrixes(isFilledMatrix, linesMatrix);
-                programPresenter.PassLastActionTaken(historyOfActions.AddNewAction(new SketchAction(SketchAction.ActionType.Draw, newLine.GetID())));
-                //TODO: For the person implementing overlay: Add check if overlay needs to be added
-                programPresenter.UpdateRightLines(rightLineList);
                 currentLine.Clear();
-                programPresenter.UpdateCurrentLine(currentLine);
             }
             UpdateUI();
         }
@@ -390,7 +398,7 @@ namespace SketchAssistantWPF
             if (inDrawingMode && programPresenter.IsMousePressed())
             {
                 currentLine.Add(currentCursorPosition);
-                programPresenter.UpdateCurrentLine(currentLine);
+                //programPresenter.UpdateCurrentLine(currentLine);
             }
             //Deleting
             if (!inDrawingMode && programPresenter.IsMousePressed())
@@ -413,7 +421,7 @@ namespace SketchAssistantWPF
                 }
             }
         }
-
+        /*
         /// <summary>
         /// A helper Function that updates the markerRadius & deletionRadius, considering the size of the canvas.
         /// </summary>
@@ -444,6 +452,7 @@ namespace SketchAssistantWPF
                 deletionRadius = (int)(5 * zoomFactor);
             }
         }
+        */
 
         /// <summary>
         /// If there is unsaved progress.

+ 21 - 64
SketchAssistant/SketchAssistantWPF/MVP_Presenter.cs

@@ -43,6 +43,7 @@ namespace SketchAssistantWPF
             Click,
             Down,
             Up,
+            Up_Invalid,
             Move
         }
 
@@ -77,7 +78,7 @@ namespace SketchAssistantWPF
         {
             CanvasSizeLeft.ChangeDimension(leftPBS.Item1, leftPBS.Item2);
             CanvasSizeRight.ChangeDimension(rightPBS.Item1, rightPBS.Item2);
-            programModel.UpdateSizes(CanvasSizeRight);
+            //programModel.UpdateSizes(CanvasSizeRight);
             programModel.ResizeEvent(CanvasSizeLeft, CanvasSizeRight);
         }
 
@@ -241,13 +242,16 @@ namespace SketchAssistantWPF
                 case MouseAction.Click:
                     programModel.MouseDown();
                     programModel.Tick();
-                    programModel.MouseUp();
+                    programModel.MouseUp(true);
                     break;
                 case MouseAction.Down:
                     programModel.MouseDown();
                     break;
                 case MouseAction.Up:
-                    programModel.MouseUp();
+                    programModel.MouseUp(true);
+                    break;
+                case MouseAction.Up_Invalid:
+                    programModel.MouseUp(false);
                     break;
                 default:
                     break;
@@ -267,18 +271,6 @@ namespace SketchAssistantWPF
             return programView.GetCursorPosition();
         }
 
-        /// <summary>
-        /// Updates the currentline
-        /// </summary>
-        /// <param name="linepoints">The points of the current line.</param>
-        public void UpdateCurrentLine(List<Point> linepoints)
-        {
-            Polyline currentLine = new Polyline();
-            currentLine.Stroke = Brushes.Black;
-            currentLine.Points = new PointCollection(linepoints);
-            programView.DisplayCurrLine(currentLine);
-        }
-
         /// <summary>
         /// Clears all Lines in the right canvas.
         /// </summary>
@@ -309,16 +301,27 @@ namespace SketchAssistantWPF
                     else
                     {
                         Ellipse newPoint = new Ellipse();
-                        newPoint.SetValue(Canvas.LeftProperty, line.point.X);
-                        newPoint.SetValue(Canvas.TopProperty, line.point.Y);
                         rightPolyLines.Add(line.GetID(), newPoint);
-                        programView.AddNewPointRight(newPoint);
+                        programView.AddNewPointRight(newPoint, line);
                     }
                 }
                 SetVisibility(rightPolyLines[line.GetID()], status);
             }
         }
 
+        /*
+        /// <summary>
+        /// Updates the currentline
+        /// </summary>
+        /// <param name="linepoints">The points of the current line.</param>
+        public void UpdateCurrentLine(List<Point> linepoints)
+        {
+            Polyline currentLine = new Polyline();
+            currentLine.Stroke = Brushes.Black;
+            currentLine.Points = new PointCollection(linepoints);
+            programView.DisplayCurrLine(currentLine);
+        }
+        */
         /// <summary>
         /// A function to update the displayed lines in the left canvas.
         /// </summary>
@@ -422,51 +425,5 @@ namespace SketchAssistantWPF
                 line.Opacity = 1;
             }
         }
-
-        /// <summary>
-        /// A function that calculates the coordinates of a point on a zoomed in image.
-        /// </summary>
-        /// <param name="">The position of the mouse cursor</param>
-        /// <returns>The real coordinates of the mouse cursor on the image</returns>
-        private Point ConvertCoordinates(Point cursorPosition)
-        {
-            if (!programModel.canvasActive) { return cursorPosition; }
-            if (programModel.canvasActive && !programModel.graphicLoaded) { return cursorPosition; }
-            ImageDimension rightImageDimensions = programModel.rightImageSize;
-            Point realCoordinates = new Point(0, 0);
-
-            int widthImage = rightImageDimensions.Width;
-            int heightImage = rightImageDimensions.Height;
-            int widthBox = programModel.rightImageBoxWidth;
-            int heightBox = programModel.rightImageBoxHeight;
-
-            if (heightImage == 0 && widthImage == 0)
-            {
-                return cursorPosition;
-            }
-
-            float imageRatio = (float)widthImage / (float)heightImage;
-            float containerRatio = (float)widthBox / (float)heightBox;
-
-            if (imageRatio >= containerRatio)
-            {
-                //Image is wider than it is high
-                float zoomFactor = (float)widthImage / (float)widthBox;
-                float scaledHeight = heightImage / zoomFactor;
-                float filler = (heightBox - scaledHeight) / 2;
-                realCoordinates.X = (int)(cursorPosition.X * zoomFactor);
-                realCoordinates.Y = (int)((cursorPosition.Y - filler) * zoomFactor);
-            }
-            else
-            {
-                //Image is higher than it is wide
-                float zoomFactor = (float)heightImage / (float)heightBox;
-                float scaledWidth = widthImage / zoomFactor;
-                float filler = (widthBox - scaledWidth) / 2;
-                realCoordinates.X = (int)((cursorPosition.X - filler) * zoomFactor);
-                realCoordinates.Y = (int)(cursorPosition.Y * zoomFactor);
-            }
-            return realCoordinates;
-        }
     }
 }

+ 2 - 1
SketchAssistant/SketchAssistantWPF/MVP_View.cs

@@ -106,7 +106,8 @@ namespace SketchAssistantWPF
         /// Adds a point to the right canvas
         /// </summary>
         /// <param name="newPoint">The point</param>
-        void AddNewPointRight(Ellipse newPoint);
+        /// <param name="line">The original line object</param>
+        void AddNewPointRight(Ellipse newPoint, InternalLine line);
 
         /// <summary>
         /// Adds a point to the left canvas

+ 5 - 3
SketchAssistant/SketchAssistantWPF/MainWindow.xaml

@@ -38,6 +38,7 @@
                         <MenuItem x:Name="DebugOne" Header="Debug 1" Click="DebugOne_Click"/>
                         <MenuItem x:Name="DebugTwo" Header="Debug 2" Click="DebugTwo_Click"/>
                         <MenuItem x:Name="DebugThree" Header="Debug 3" Click="DebugThree_Click"/>
+                        <MenuItem x:Name="DebugFour" Header="Debug 4" Click="DebugFour_Click"/>
                     </MenuItem>
                 </MenuItem>
             </Menu>
@@ -178,9 +179,10 @@
             </Button>
         </ToolBar>
         <local:CustomCanvas x:Name="LeftCanvas" Background="SlateGray" Grid.Column="2" Grid.Row="1" Height="auto" Grid.ColumnSpan="3"/>
-        <Canvas Name="CanvasSeperator" Grid.Column="5" Grid.Row="1" Background="LightGray"/>
-        <local:CustomCanvas x:Name="RightCanvas" Background="SlateGray" Grid.Column="6" Grid.Row="1" Height="auto"
-            MouseDown="RightCanvas_MouseDown" MouseUp="RightCanvas_MouseUp" MouseMove="RightCanvas_MouseMove" Grid.ColumnSpan="2" MouseLeave="RightCanvas_MouseLeave" MouseEnter="RightCanvas_MouseEnter" TouchEnter="RightCanvas_TouchEnter" TouchLeave="RightCanvas_TouchLeave"/>
+        <Canvas Name="CanvasSeperator" Grid.Column="5" Grid.Row="1" Background="LightGray" />
+        <InkCanvas x:Name="RightCanvas" Background="SlateGray" Grid.Column="6" Grid.Row="1" Height="auto"
+            PreviewMouseDown="RightCanvas_MouseDown" MouseUp="RightCanvas_MouseUp" MouseMove="RightCanvas_MouseMove" Grid.ColumnSpan="2" 
+                    StrokeCollected="RightCanvas_StrokeCollection" EditingMode="None"/>
 
 
         <DockPanel Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="8">

+ 93 - 79
SketchAssistant/SketchAssistantWPF/MainWindow.xaml.cs

@@ -18,6 +18,7 @@ using System.Windows.Media.Imaging;
 using System.Windows.Navigation;
 using System.Windows.Shapes;
 using System.Windows.Threading;
+using System.Windows.Ink;
 
 namespace SketchAssistantWPF
 {
@@ -87,6 +88,10 @@ namespace SketchAssistantWPF
         /// Point collections for debugging.
         /// </summary>
         DebugData debugDat = new DebugData();
+        /// <summary>
+        /// Stores Lines drawn on RightCanvas.
+        /// </summary>
+        StrokeCollection strokeCollection = new StrokeCollection();
 
         /********************************************/
         /*** WINDOW SPECIFIC FUNCTIONS START HERE ***/
@@ -101,6 +106,15 @@ namespace SketchAssistantWPF
                 new Tuple<int, int>((int)RightCanvas.ActualWidth, (int)RightCanvas.ActualHeight));
         }
 
+        /// <summary>
+        /// Collects all Strokes on RightCanvas
+        /// </summary>
+        public void RightCanvas_StrokeCollection(object sender, InkCanvasStrokeCollectedEventArgs e)
+        {
+            strokeCollection.Add(e.Stroke);
+            System.Diagnostics.Debug.WriteLine(strokeCollection.Count);
+        }
+
         /// <summary>
         /// Redo an Action.
         /// </summary>
@@ -123,6 +137,7 @@ namespace SketchAssistantWPF
         private void DeleteButton_Click(object sender, RoutedEventArgs e)
         {
             ProgramPresenter.ChangeState(false);
+            RightCanvas.EditingMode = InkCanvasEditingMode.EraseByStroke;
         }
 
         /// <summary>
@@ -131,6 +146,7 @@ namespace SketchAssistantWPF
         private void DrawButton_Click(object sender, RoutedEventArgs e)
         {
             ProgramPresenter.ChangeState(true);
+            RightCanvas.EditingMode = InkCanvasEditingMode.Ink;
         }
 
         /// <summary>
@@ -147,48 +163,18 @@ namespace SketchAssistantWPF
         /// </summary>
         private void RightCanvas_MouseUp(object sender, MouseButtonEventArgs e)
         {
-            ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Up);
-            //System.Diagnostics.Debug.WriteLine("ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Up);");
-        }
-
-        /// <summary>
-        /// If the mouse leaves the canvas, it is treated as if the mouse was released.
-        /// </summary>
-        private void RightCanvas_MouseLeave(object sender, MouseEventArgs e)
-        {
-            ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Up);
-        }
-
-        /// <summary>
-        /// If the cursor enters the canvas, it is treated as if the cursor was just pressed if the cursor is pressed.
-        /// </summary>
-        private void RightCanvas_MouseEnter(object sender, MouseEventArgs e)
-        {
-            if (IsMousePressed())
+            if(strokeCollection.Count == 0)
             {
-                ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Move, Mouse.GetPosition(RightCanvas));
-                ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Down);
+                ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Up_Invalid);
             }
-        }
-
-        /// <summary>
-        /// If the finger enters the canvas, it is treated as if the finger was just pressed if the finger is pressed.
-        /// </summary>
-        private void RightCanvas_TouchEnter(object sender, TouchEventArgs e)
-        {
-            if (IsMousePressed())
+            else
             {
-                ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Move, Mouse.GetPosition(RightCanvas));
-                ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Down);
+                ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Up);
+                RightCanvas.Strokes.RemoveAt(0);
+                strokeCollection.RemoveAt(0);
+                System.Diagnostics.Debug.WriteLine(strokeCollection.Count);
             }
-        }
-
-        /// <summary>
-        /// If the finger leaves the canvas, it is treated as if the finger was released.
-        /// </summary>
-        private void RightCanvas_TouchLeave(object sender, TouchEventArgs e)
-        {
-            ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Up);
+            //System.Diagnostics.Debug.WriteLine("ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Up);");
         }
 
         /// <summary>
@@ -208,33 +194,8 @@ namespace SketchAssistantWPF
         private void CanvasButton_Click(object sender, RoutedEventArgs e)
         {
             ProgramPresenter.NewCanvas();
-        }
-
-        /// <summary>
-        /// Sends inputs to the presenter simulating drawing, used for testing and debugging.
-        /// Takes 7000ms
-        /// </summary>
-        private void DebugOne_Click(object sender, RoutedEventArgs e)
-        {
-            Debug(1);
-        }
-
-        /// <summary>
-        /// Sends inputs to the presenter simulating drawing, used for testing and debugging.
-        /// Takes 24000ms
-        /// </summary>
-        private void DebugTwo_Click(object sender, RoutedEventArgs e)
-        {
-            Debug(2);
-        }
-
-        /// <summary>
-        /// Sends inputs to the presenter simulating drawing, used for testing and debugging.
-        /// Takes 7000ms
-        /// </summary>
-        private void DebugThree_Click(object sender, RoutedEventArgs e)
-        {
-            Debug(3);
+            RightCanvas.EditingMode = InkCanvasEditingMode.Ink;
+            RightCanvas.Strokes.Clear();
         }
 
         /// <summary>
@@ -350,11 +311,12 @@ namespace SketchAssistantWPF
         /// Adds a point to the right canvas
         /// </summary>
         /// <param name="newPoint">The point</param>
-        public void AddNewPointRight(Ellipse newPoint)
+        public void AddNewPointRight(Ellipse newPoint, InternalLine line)
         {
             newPoint.Height = 3; newPoint.Width = 3;
             newPoint.Fill = Brushes.Black;
             RightCanvas.Children.Add(newPoint);
+            newPoint.Margin = new Thickness(line.point.X - 1.5, line.point.Y - 1.5, 0,0);
         }
 
         /// <summary>
@@ -512,31 +474,75 @@ namespace SketchAssistantWPF
         /// <param name="active">Whether or not the canvas is active.</param>
         public void SetCanvasState(string canvasName, bool active)
         {
-            Canvas canvas;
-            switch (canvasName){
+            switch (canvasName)
+            {
                 case ("LeftCanvas"):
-                    canvas = LeftCanvas;
+                    if (active)
+                    {
+                        LeftCanvas.Background = Brushes.White;
+                    }
+                    else
+                    {
+                        LeftCanvas.Background = Brushes.SlateGray;
+                    }
                     break;
                 case ("RightCanvas"):
-                    canvas = RightCanvas;
+                    if (active)
+                    {
+                        RightCanvas.Background = Brushes.White;
+                    }
+                    else
+                    {
+                        RightCanvas.Background = Brushes.SlateGray;
+                    }
                     break;
                 default:
                     throw new InvalidOperationException("Unknown canvas name, Check that the canvas passed is either LeftCanvas or RightCanvas");
             }
-            if (active)
-            {
-                canvas.Background = Brushes.White;
-            }
-            else
-            {
-                canvas.Background = Brushes.SlateGray;
-            }
         }
 
         /************************/
         /*** HELPING FUNCTION ***/
         /************************/
 
+
+
+        /// <summary>
+        /// Sends inputs to the presenter simulating drawing, used for testing and debugging.
+        /// Takes 7000ms
+        /// </summary>
+        private void DebugOne_Click(object sender, RoutedEventArgs e)
+        {
+            Debug(1);
+        }
+
+        /// <summary>
+        /// Sends inputs to the presenter simulating drawing, used for testing and debugging.
+        /// Takes 24000ms
+        /// </summary>
+        private void DebugTwo_Click(object sender, RoutedEventArgs e)
+        {
+            Debug(2);
+        }
+
+        /// <summary>
+        /// Sends inputs to the presenter simulating drawing, used for testing and debugging.
+        /// Takes 4000ms
+        /// </summary>
+        private void DebugThree_Click(object sender, RoutedEventArgs e)
+        {
+            Debug(3);
+        }
+
+        /// <summary>
+        /// Sends inputs to the presenter simulating drawing, used for testing and debugging.
+        /// Takes 
+        /// </summary>
+        private void DebugFour_Click(object sender, RoutedEventArgs e)
+        {
+            Debug(4);
+        }
+
         /// <summary>
         /// A function which simulates canvas input for debugging.
         /// </summary>
@@ -544,6 +550,7 @@ namespace SketchAssistantWPF
         private async void Debug(int option)
         {
             Point[] points;
+            Point start = new Point(50, 50);
             switch (option)
             {
                 case 1:
@@ -552,13 +559,20 @@ namespace SketchAssistantWPF
                 case 2:
                     points = debugDat.debugPoints2;
                     break;
+                case 3:
+                    points = debugDat.debugPoints3;
+                    break;
+                case 4:
+                    points = debugDat.debugPoints4;
+                    start = new Point(284, 148);
+                    break;
                 default:
                     return;
             }
             dispatcherTimer.Stop();
             debugRunning = true;
             ProgramPresenter.Tick(); await Task.Delay(10);
-            ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Move, new Point(50, 50));
+            ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Move, start);
             ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Down); await Task.Delay(10);
             for (int x = 0; x < points.Length; x++)
             {

+ 6 - 0
SketchAssistant/SketchAssistantWPF/packages.config

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="Microsoft.TestPlatform" version="16.0.0" targetFramework="net461" />
+  <package id="OpenCover" version="4.7.922" targetFramework="net461" />
+  <package id="ReportGenerator" version="4.0.14" targetFramework="net461" />
+</packages>

+ 397 - 32
SketchAssistant/WhiteTests/UITest.cs

@@ -13,14 +13,74 @@ using System.Diagnostics;
 using TestStack.White.UIItems.WindowStripControls;
 using TestStack.White.UIItems.MenuItems;
 using System.Collections.Generic;
+using System.Text.RegularExpressions;
+using WindowsInput;
+using WindowsInput.Native;
 
 namespace WhiteTests
 {
+
     [TestClass]
     public class UITest
     {
         private TestStack.White.Application application;
 
+        /// <summary>
+        /// The directory of the input files, saved for repeated use
+        /// </summary>
+        private String input_file_dir = null;
+        /// <summary>
+        /// instance of TestContext to be able to access deployed files
+        /// </summary>
+        private TestContext testContextInstance;
+        /// <summary>
+        ///Gets or sets the test context which provides
+        ///information about and functionality for the current test run.
+        ///</summary>
+        public TestContext TestContext
+        {
+            get
+            {
+                return testContextInstance;
+            }
+            set
+            {
+                testContextInstance = value;
+            }
+        }
+
+        /// <summary>
+        /// A function that returns the path to the test_input_files folder. 
+        /// Do with it what you want.
+        /// </summary>
+        /// <returns>the path to the test_input_files folder</returns>
+        public String getSketchAssistantDirectory()
+        {
+            Regex rx = new Regex(@"^(.*\\SketchAssistant\\)");
+            Match match = rx.Match(TestContext.DeploymentDirectory);
+            String SketchAssistDir = match.Groups[1].Value;
+            if (input_file_dir == null)
+            {
+                if (Directory.Exists(SketchAssistDir + @"WhiteTests\test_input_files\"))
+                {
+                    input_file_dir = SketchAssistDir + @"WhiteTests\test_input_files\";
+                }
+                else if (Directory.Exists(SketchAssistDir + @"WhiteTests\bin\Debug\test_input_files\"))
+                {
+                    input_file_dir = SketchAssistDir + @"WhiteTests\bin\Debug\test_input_files\";
+                }
+                else
+                {
+                    Regex rx_0 = new Regex(@"^(.*\\projects\\)");
+                    Match match_0 = rx_0.Match(TestContext.DeploymentDirectory);
+                    String ProjectsDir = match_0.Groups[1].Value;
+                    var dirs = Directory.GetDirectories(ProjectsDir, "test_input_files", SearchOption.AllDirectories);
+                    input_file_dir = dirs[0];
+                }
+            }
+            return input_file_dir;
+        }
+
         public Window setupapp()
         {
             string outputDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
@@ -64,34 +124,36 @@ namespace WhiteTests
             mainWindow.Close();
         }
 
-        /*[TestMethod]
-         public void DeleteLineTest()
-         {
-             Window mainWindow = setupapp();
-             Thread.Sleep(20);
-             Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
-             mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
-             Thread.Sleep(20);
-             Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
-             mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
-             Thread.Sleep(20);
-             mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
-             Thread.Sleep(20);
-             mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugOne")).Click();
-             Thread.Sleep(7000);
-             Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
-             Thread.Sleep(20);
-             mainWindow.Get<Button>(SearchCriteria.ByAutomationId("DeleteButton")).Click();
-             Thread.Sleep(20);
-             mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
-             Thread.Sleep(20);
-             mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
-             Thread.Sleep(20);
-             mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugThree")).Click();
-             Thread.Sleep(24000);
-             Assert.AreEqual("Last Action: Line number 0 was deleted", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
-             mainWindow.Close();
-         }*/
+        [TestMethod]
+        public void DeleteLineTest()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("DrawButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugOne")).Click();
+            Thread.Sleep(7000);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("DeleteButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugOne")).Click();
+            Thread.Sleep(7000);
+            Assert.AreEqual("Last Action: Line number 0 was deleted.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Close();
+        }
 
         [TestMethod]
         [TestCategory("bla")]
@@ -144,13 +206,285 @@ namespace WhiteTests
             Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
             mainWindow.Close();
         }
+
+        [TestMethod]
+        public void DrawSeveralLines()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugOne")).Click();
+            Thread.Sleep(7000);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugTwo")).Click();
+            Thread.Sleep(30000);
+            Assert.AreEqual("Last Action: Line number 1 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Close();
+        }
+
+        [TestMethod]
+        public void DeleteSeveralLines()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugOne")).Click();
+            Thread.Sleep(7000);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugTwo")).Click();
+            Thread.Sleep(24000);
+            Assert.AreEqual("Last Action: Line number 1 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("DeleteButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugThree")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 1 was deleted.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(10000);
+            Assert.AreEqual("Last Action: Line number 0 was deleted.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Close();
+        }
+
+        [TestMethod]
+        public void UndoSeveralLines()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugOne")).Click();
+            Thread.Sleep(7000);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugTwo")).Click();
+            Thread.Sleep(30000);
+            Assert.AreEqual("Last Action: Line number 1 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("UndoButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("UndoButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Close();
+        }
+
+        [TestMethod]
+        public void RedoSeveralLines()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugOne")).Click();
+            Thread.Sleep(7000);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugTwo")).Click();
+            Thread.Sleep(24000);
+            Assert.AreEqual("Last Action: Line number 1 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("UndoButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("UndoButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("RedoButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("RedoButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 1 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Close();
+        }
+
+        [TestMethod]
+        public void UndoAndRedoTests()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugOne")).Click();
+            Thread.Sleep(7000);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugTwo")).Click();
+            Thread.Sleep(24000);
+            Assert.AreEqual("Last Action: Line number 1 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("UndoButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("UndoButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("RedoButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("RedoButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 1 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("DeleteButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugThree")).Click();
+            Thread.Sleep(7000);
+            Assert.AreEqual("Last Action: Line number 0 was deleted.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("UndoButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 1 was deleted.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Close();
+        }
+
+        [TestMethod]
+        public void PointDraw()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugFour")).Click();
+            Thread.Sleep(4000);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Close();
+        }
+
+        [TestMethod]
+        public void NewCanvasAfterDraw()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugThree")).Click();
+            Thread.Sleep(4000);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            // Click on No button in warning
+            Window messageBox0 = mainWindow.MessageBox("Warning");
+            messageBox0.Get<Button>(SearchCriteria.ByText("No")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            // close warning
+            Window messageBox1 = mainWindow.MessageBox("Warning");
+            messageBox1.Close();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            // click yes button on warning
+            Window messageBox2 = mainWindow.MessageBox("Warning");
+            messageBox2.Get<Button>(SearchCriteria.ByText("Yes")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Close();
+        }
     }
 
     [TestClass]
     [DeploymentItem(@"WhiteTests\test_input_files\")]
     public class FileImporterTests
     {
-
+        /// <summary>
+        /// The directory of the input files, saved for repeated use
+        /// </summary>
+        private String input_file_dir = null;
         /// <summary>
         /// instance of TestContext to be able to access deployed files
         /// </summary>
@@ -171,6 +505,38 @@ namespace WhiteTests
             }
         }
 
+        /// <summary>
+        /// A function that returns the path to the test_input_files folder. 
+        /// Do with it what you want.
+        /// </summary>
+        /// <returns>the path to the test_input_files folder</returns>
+        public String getSketchAssistantDirectory()
+        {
+            Regex rx = new Regex(@"^(.*\\SketchAssistant\\)");
+            Match match = rx.Match(TestContext.DeploymentDirectory);
+            String SketchAssistDir = match.Groups[1].Value;
+            if(input_file_dir == null)
+            {
+                if (Directory.Exists(SketchAssistDir + @"\WhiteTests\test_input_files\"))
+                {
+                    input_file_dir = SketchAssistDir + @"\WhiteTests\test_input_files\";
+                }
+                else if (Directory.Exists(SketchAssistDir + @"\WhiteTests\bin\Debug\test_input_files\"))
+                {
+                    input_file_dir = SketchAssistDir + @"\WhiteTests\bin\Debug\test_input_files\";
+                }
+                else
+                {
+                    Regex rx_0 = new Regex(@"^(.*\\projects\\)");
+                    Match match_0 = rx_0.Match(TestContext.DeploymentDirectory);
+                    String ProjectsDir = match_0.Groups[1].Value;
+                    var dirs = Directory.GetDirectories(ProjectsDir, "test_input_files", SearchOption.AllDirectories);
+                    input_file_dir = dirs[0];
+                }
+            }
+            return input_file_dir;
+        }
+
         /// <summary>
         /// creates a valid .isad file from the given sets of coordinates (number divisible by 3) by creating a line for every three consecutive points, parses the file and verifies that all lines and their points have been parsed correctly
         /// </summary>
@@ -265,8 +631,7 @@ namespace WhiteTests
         public void ParseSVGInputNoErrorForWhitelistedFilesTest()
         {
             FileImporter uut = new FileImporter();
-
-            string[] files = Directory.GetFiles(TestContext.DeploymentDirectory + @"\test_input_files\whitelisted", "*.svg", SearchOption.AllDirectories);
+            string[] files = Directory.GetFiles(getSketchAssistantDirectory() + @"\whitelisted", "*.svg", SearchOption.AllDirectories);
             
             Assert.IsTrue(files.Length > 0);
 
@@ -295,7 +660,7 @@ namespace WhiteTests
         {
             FileImporter uut = new FileImporter();
 
-            string[] files = Directory.GetFiles(TestContext.DeploymentDirectory + @"\test_input_files\blacklisted", "*.svg", SearchOption.AllDirectories);
+            string[] files = Directory.GetFiles(getSketchAssistantDirectory() + @"\blacklisted", "*.svg", SearchOption.AllDirectories);
             Assert.IsTrue(files.Length > 0);
             foreach (string s in files) //parse each of the blacklisted files
             {

+ 7 - 0
SketchAssistant/WhiteTests/WhiteTests.csproj

@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="..\packages\ReportGenerator.4.0.14\build\netstandard2.0\ReportGenerator.props" Condition="Exists('..\packages\ReportGenerator.4.0.14\build\netstandard2.0\ReportGenerator.props')" />
   <Import Project="..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.props')" />
   <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
   <PropertyGroup>
@@ -54,10 +55,15 @@
     <Reference Include="System" />
     <Reference Include="System.Configuration" />
     <Reference Include="System.Core" />
+    <Reference Include="System.Windows" />
+    <Reference Include="System.Windows.Forms" />
     <Reference Include="TestStack.White, Version=0.13.0.0, Culture=neutral, PublicKeyToken=2672efbf3e161801, processorArchitecture=MSIL">
       <HintPath>..\packages\TestStack.White.0.13.3\lib\net40\TestStack.White.dll</HintPath>
     </Reference>
     <Reference Include="WindowsBase" />
+    <Reference Include="WindowsInput, Version=1.0.4.0, Culture=neutral, PublicKeyToken=9b287f7dc5073cad, processorArchitecture=MSIL">
+      <HintPath>..\packages\InputSimulator.1.0.4.0\lib\net20\WindowsInput.dll</HintPath>
+    </Reference>
   </ItemGroup>
   <ItemGroup>
     <Compile Include="UITest.cs" />
@@ -112,6 +118,7 @@
     </PropertyGroup>
     <Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.props'))" />
     <Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.targets'))" />
+    <Error Condition="!Exists('..\packages\ReportGenerator.4.0.14\build\netstandard2.0\ReportGenerator.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\ReportGenerator.4.0.14\build\netstandard2.0\ReportGenerator.props'))" />
   </Target>
   <Import Project="..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.targets')" />
   <PropertyGroup>

+ 4 - 0
SketchAssistant/WhiteTests/packages.config

@@ -1,7 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
   <package id="Castle.Core" version="4.3.1" targetFramework="net461" />
+  <package id="InputSimulator" version="1.0.4.0" targetFramework="net461" />
+  <package id="Microsoft.TestPlatform" version="16.0.0" targetFramework="net461" />
   <package id="MSTest.TestAdapter" version="1.4.0" targetFramework="net461" />
   <package id="MSTest.TestFramework" version="1.4.0" targetFramework="net461" />
+  <package id="OpenCover" version="4.7.922" targetFramework="net461" />
+  <package id="ReportGenerator" version="4.0.14" targetFramework="net461" />
   <package id="TestStack.White" version="0.13.3" targetFramework="net461" />
 </packages>

+ 126 - 0
screenres.ps1

@@ -0,0 +1,126 @@
+# http://blogs.technet.com/b/heyscriptingguy/archive/2010/07/07/hey-scripting-guy-how-can-i-change-my-desktop-monitor-resolution-via-windows-powershell.aspx
+
+Function Set-ScreenResolution { 
+param ( 
+[Parameter(Mandatory=$true, 
+           Position = 0)] 
+[int] 
+$Width, 
+[Parameter(Mandatory=$true, 
+           Position = 1)] 
+[int] 
+$Height 
+) 
+$pinvokeCode = @" 
+using System; 
+using System.Runtime.InteropServices; 
+namespace Resolution 
+{ 
+    [StructLayout(LayoutKind.Sequential)] 
+    public struct DEVMODE1 
+    { 
+        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] 
+        public string dmDeviceName; 
+        public short dmSpecVersion; 
+        public short dmDriverVersion; 
+        public short dmSize; 
+        public short dmDriverExtra; 
+        public int dmFields; 
+        public short dmOrientation; 
+        public short dmPaperSize; 
+        public short dmPaperLength; 
+        public short dmPaperWidth; 
+        public short dmScale; 
+        public short dmCopies; 
+        public short dmDefaultSource; 
+        public short dmPrintQuality; 
+        public short dmColor; 
+        public short dmDuplex; 
+        public short dmYResolution; 
+        public short dmTTOption; 
+        public short dmCollate; 
+        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] 
+        public string dmFormName; 
+        public short dmLogPixels; 
+        public short dmBitsPerPel; 
+        public int dmPelsWidth; 
+        public int dmPelsHeight; 
+        public int dmDisplayFlags; 
+        public int dmDisplayFrequency; 
+        public int dmICMMethod; 
+        public int dmICMIntent; 
+        public int dmMediaType; 
+        public int dmDitherType; 
+        public int dmReserved1; 
+        public int dmReserved2; 
+        public int dmPanningWidth; 
+        public int dmPanningHeight; 
+    }; 
+    class User_32 
+    { 
+        [DllImport("user32.dll")] 
+        public static extern int EnumDisplaySettings(string deviceName, int modeNum, ref DEVMODE1 devMode); 
+        [DllImport("user32.dll")] 
+        public static extern int ChangeDisplaySettings(ref DEVMODE1 devMode, int flags); 
+        public const int ENUM_CURRENT_SETTINGS = -1; 
+        public const int CDS_UPDATEREGISTRY = 0x01; 
+        public const int CDS_TEST = 0x02; 
+        public const int DISP_CHANGE_SUCCESSFUL = 0; 
+        public const int DISP_CHANGE_RESTART = 1; 
+        public const int DISP_CHANGE_FAILED = -1; 
+    } 
+    public class PrmaryScreenResolution 
+    { 
+        static public string ChangeResolution(int width, int height) 
+        { 
+            DEVMODE1 dm = GetDevMode1(); 
+            if (0 != User_32.EnumDisplaySettings(null, User_32.ENUM_CURRENT_SETTINGS, ref dm)) 
+            { 
+                dm.dmPelsWidth = width; 
+                dm.dmPelsHeight = height; 
+                int iRet = User_32.ChangeDisplaySettings(ref dm, User_32.CDS_TEST); 
+                if (iRet == User_32.DISP_CHANGE_FAILED) 
+                { 
+                    return "Unable To Process Your Request. Sorry For This Inconvenience."; 
+                } 
+                else 
+                { 
+                    iRet = User_32.ChangeDisplaySettings(ref dm, User_32.CDS_UPDATEREGISTRY); 
+                    switch (iRet) 
+                    { 
+                        case User_32.DISP_CHANGE_SUCCESSFUL: 
+                            { 
+                                return "Success"; 
+                            } 
+                        case User_32.DISP_CHANGE_RESTART: 
+                            { 
+                                return "You Need To Reboot For The Change To Happen.\n If You Feel Any Problem After Rebooting Your Machine\nThen Try To Change Resolution In Safe Mode."; 
+                            } 
+                        default: 
+                            { 
+                                return "Failed To Change The Resolution"; 
+                            } 
+                    } 
+                } 
+            } 
+            else 
+            { 
+                return "Failed To Change The Resolution."; 
+            } 
+        } 
+        private static DEVMODE1 GetDevMode1() 
+        { 
+            DEVMODE1 dm = new DEVMODE1(); 
+            dm.dmDeviceName = new String(new char[32]); 
+            dm.dmFormName = new String(new char[32]); 
+            dm.dmSize = (short)Marshal.SizeOf(dm); 
+            return dm; 
+        } 
+    } 
+} 
+"@ 
+Add-Type $pinvokeCode -ErrorAction SilentlyContinue 
+[Resolution.PrmaryScreenResolution]::ChangeResolution($width,$height) 
+}
+
+Set-ScreenResolution 1920 1080

Some files were not shown because too many files changed in this diff