diff --git a/.gitignore b/.gitignore
index 3831d65f8..3d42e0c8a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,3 +40,7 @@ source/*.xml
 
 #InnoSetup Output Folder
 [Oo]utput/
+
+#NuGet for MS Unit Tests
+packages
+!packages/repositories.config
diff --git a/OpenBVE.sln b/OpenBVE.sln
index 7571bcde3..0c40eb266 100644
--- a/OpenBVE.sln
+++ b/OpenBVE.sln
@@ -1,7 +1,7 @@
 
 Microsoft Visual Studio Solution File, Format Version 12.00
 # Visual Studio 15
-VisualStudioVersion = 15.0.26430.15
+VisualStudioVersion = 15.0.27004.2006
 MinimumVisualStudioVersion = 10.0.40219.1
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", "{16553295-E70F-4596-AA78-848EEA576C4A}"
 EndProject
@@ -40,6 +40,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DevTools", "DevTools", "{3D
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CarXMLConvertor", "source\CarXMLConvertor\CarXMLConvertor.csproj", "{55901EEC-6FB3-4FB6-BDD9-C0E1042F3FBB}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenBve.Tests", "Tests\OpenBve.Tests.csproj", "{844BB77E-DC64-4405-8E20-8BA0EFCEAE7D}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -198,6 +200,18 @@ Global
 		{55901EEC-6FB3-4FB6-BDD9-C0E1042F3FBB}.Release|Mixed Platforms.Build.0 = Release|Any CPU
 		{55901EEC-6FB3-4FB6-BDD9-C0E1042F3FBB}.Release|x86.ActiveCfg = Release|Any CPU
 		{55901EEC-6FB3-4FB6-BDD9-C0E1042F3FBB}.Release|x86.Build.0 = Release|Any CPU
+		{844BB77E-DC64-4405-8E20-8BA0EFCEAE7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{844BB77E-DC64-4405-8E20-8BA0EFCEAE7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{844BB77E-DC64-4405-8E20-8BA0EFCEAE7D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+		{844BB77E-DC64-4405-8E20-8BA0EFCEAE7D}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+		{844BB77E-DC64-4405-8E20-8BA0EFCEAE7D}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{844BB77E-DC64-4405-8E20-8BA0EFCEAE7D}.Debug|x86.Build.0 = Debug|Any CPU
+		{844BB77E-DC64-4405-8E20-8BA0EFCEAE7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{844BB77E-DC64-4405-8E20-8BA0EFCEAE7D}.Release|Any CPU.Build.0 = Release|Any CPU
+		{844BB77E-DC64-4405-8E20-8BA0EFCEAE7D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+		{844BB77E-DC64-4405-8E20-8BA0EFCEAE7D}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+		{844BB77E-DC64-4405-8E20-8BA0EFCEAE7D}.Release|x86.ActiveCfg = Release|Any CPU
+		{844BB77E-DC64-4405-8E20-8BA0EFCEAE7D}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -210,4 +224,7 @@ Global
 		{06D89847-9C7A-47D5-8C7A-95AEBFFF5F1E} = {16553295-E70F-4596-AA78-848EEA576C4A}
 		{E9B64673-65B7-4D77-A4DB-4B441A7C9197} = {3D6660F8-52D2-4A30-8F1E-1922DDCF10F9}
 	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {FF9D9023-8499-47CB-A371-870A0E911645}
+	EndGlobalSection
 EndGlobal
diff --git a/Tests/OpenBve.Tests.csproj b/Tests/OpenBve.Tests.csproj
new file mode 100644
index 000000000..25320bd91
--- /dev/null
+++ b/Tests/OpenBve.Tests.csproj
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="..\packages\MSTest.TestAdapter.1.1.18\build\net45\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.1.1.18\build\net45\MSTest.TestAdapter.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{844BB77E-DC64-4405-8E20-8BA0EFCEAE7D}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>OpenBve.Tests</RootNamespace>
+    <AssemblyName>OpenBve.Tests</AssemblyName>
+    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
+    <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
+    <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
+    <IsCodedUITest>False</IsCodedUITest>
+    <TestProjectType>UnitTest</TestProjectType>
+    <NuGetPackageImportStamp>
+    </NuGetPackageImportStamp>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <HintPath>..\packages\MSTest.TestFramework.1.1.18\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <HintPath>..\packages\MSTest.TestFramework.1.1.18\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Vector3.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\source\OpenBveApi\OpenBveApi.csproj">
+      <Project>{27134980-4415-4375-a564-40a9014dfa5f}</Project>
+      <Name>OpenBveApi</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>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}.</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.1.18\build\net45\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.1.18\build\net45\MSTest.TestAdapter.props'))" />
+    <Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.1.18\build\net45\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.1.18\build\net45\MSTest.TestAdapter.targets'))" />
+  </Target>
+  <Import Project="..\packages\MSTest.TestAdapter.1.1.18\build\net45\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.1.1.18\build\net45\MSTest.TestAdapter.targets')" />
+</Project>
\ No newline at end of file
diff --git a/Tests/Properties/AssemblyInfo.cs b/Tests/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..12cec514d
--- /dev/null
+++ b/Tests/Properties/AssemblyInfo.cs
@@ -0,0 +1,20 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("UnitTestProject1")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("UnitTestProject1")]
+[assembly: AssemblyCopyright("Copyright ©  2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+[assembly: ComVisible(false)]
+
+[assembly: Guid("844bb77e-dc64-4405-8e20-8ba0efceae7d")]
+
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/Tests/Vector3.cs b/Tests/Vector3.cs
new file mode 100644
index 000000000..a9a5026f8
--- /dev/null
+++ b/Tests/Vector3.cs
@@ -0,0 +1,148 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using OpenBveApi.Math;
+
+namespace OpenBve.Tests
+{
+	using System;
+
+	[TestClass]
+	public class Vector3Tests
+	{
+		/// <summary>Tests the equality operator</summary>
+		[TestMethod]
+		public void Equals()
+		{
+			Vector3 v1 = new Vector3(0, 0, 0);
+			Vector3 v2 = new Vector3(0, 0, 0);
+			Assert.AreEqual(v1, v2);
+			Assert.IsTrue(v1 == v2);
+			Assert.IsFalse(v1 != v2);
+
+			v2 = new Vector3(3, 1, 5);
+			Assert.AreNotEqual(v1, v2);
+			Assert.IsFalse(v1 == v2);
+			Assert.IsTrue(v1 != v2);
+		}
+
+		/// <summary>Tests the addition operators</summary>
+		[TestMethod]
+		public void Addition()
+		{
+			//Simple addition
+			Vector3 v1 = new Vector3(4, 6, 2);
+			Vector3 v2 = new Vector3(1, 5, 7);
+			Vector3 v3 = v1 + v2;
+			Assert.AreEqual(new Vector3(5, 11, 9), v3);
+			v3 = v2 + v1;
+			Assert.AreEqual(new Vector3(5, 11, 9), v3);
+			
+			//Vector and scalar
+			v1 = new Vector3(7, 18, 4);
+			v1 += 5;
+			Assert.AreEqual(new Vector3(12, 23, 9), v1);
+
+			//Scalar and vector
+			v1 = new Vector3(5, 13, 9);
+			v2 = 5 + v1;
+			Assert.AreEqual(new Vector3(10, 18, 14), v2);
+
+		}
+
+		/// <summary>Tests the subtraction operators</summary>
+		[TestMethod]
+		public void Subtraction()
+		{
+			//Simple subtraction
+			Vector3 v1 = new Vector3(4, 6, 2);
+			Vector3 v2 = new Vector3(1, 5, 7);
+			Vector3 v3 = v1 - v2;
+			Assert.AreEqual(new Vector3(3, 1, -5), v3);
+			v3 = v2 - v1;
+			Assert.AreEqual(new Vector3(-3, -1, 5), v3);
+
+			//Vector and scalar
+			v1 = new Vector3(7, 18, 4);
+			v1 -= 5;
+			Assert.AreEqual(new Vector3(2, 13, -1), v1);
+
+			//Scalar and vector
+			v1 = new Vector3(5, 13, 9);
+			v2 = 5 - v1;
+			Assert.AreEqual(new Vector3(0, -8, -4), v2);
+		}
+
+		/// <summary>Tests the negation operator</summary>
+		[TestMethod]
+		public void Negation()
+		{
+			Vector3 v1 = new Vector3(8, 13, 27);
+			v1 = -v1;
+			Assert.AreEqual(new Vector3(-8, -13, -27), v1);
+
+			v1 = new Vector3(-12, -2, -30);
+			v1 = -v1;
+			Assert.AreEqual(new Vector3(12, 2, 30), v1);
+
+			v1 = new Vector3(-24, 27, -15);
+			v1 = -v1;
+			Assert.AreEqual(new Vector3(24, -27, 15), v1);
+		}
+
+		/// <summary>Tests the multiplication operator</summary>
+		[TestMethod]
+		public void Multiplication()
+		{
+			//Mutliplication
+			Vector3 v1 = new Vector3(4, 6, 2);
+			Vector3 v2 = new Vector3(1, 5, 7);
+			Vector3 v3 = v1 * v2;
+			Assert.AreEqual(new Vector3(4, 30, 14), v3);
+
+			v1 = new Vector3(8, 12 , 9);
+			v2 = v1 * 12.5;
+			Assert.AreEqual(new Vector3(100, 150, 112.5), v2);
+
+			v1 = new Vector3(14, 3.5, 8);
+			v2 = 3.5 * v1;
+			Assert.AreEqual(new Vector3(49, 12.25, 28), v2);
+		}
+
+		/// <summary>Tests the division operator</summary>
+		[TestMethod]
+		public void Division()
+		{
+			Vector3 v1 = new Vector3(0, 0, 0);
+			Vector3 v2 = new Vector3(1, 5, 7);
+			Vector3 v3 = v1 / v2;
+			Assert.AreEqual(new Vector3(0, 0, 0), v3);
+
+			v1 = new Vector3(1, 6, 4);
+			v2 = new Vector3(2, 3, 5);
+			v3 = v1 / v2;
+			Assert.AreEqual(new Vector3(0.5, 2, 0.8), v3);
+
+			v1 = new Vector3(15.3, 9, 20.5);
+			v2 = v1 / 10;
+			Assert.AreEqual(new Vector3(1.53, 0.9, 2.05), v2);
+		}
+
+		/// <summary>Tests the division by zero vector exception</summary>
+		[TestMethod]
+		[ExpectedException(typeof(DivideByZeroException))]
+		public void DivisionByZeroVector()
+		{
+			Vector3 v1 = new Vector3(1, 5, 7);
+			Vector3 v2 = new Vector3(0, 0, 0);
+			Vector3 v3 = v1 / v2;
+		}
+		
+		/// <summary>Tests the division by zero exception</summary>
+		[TestMethod]
+		[ExpectedException(typeof(DivideByZeroException))]
+		public void DivisionByZero()
+		{
+			Vector3 v1 = new Vector3(1, 5, 7);
+			Vector3 v3 = v1 / 0;
+		}
+	}
+}
diff --git a/Tests/packages.config b/Tests/packages.config
new file mode 100644
index 000000000..d8c1b9099
--- /dev/null
+++ b/Tests/packages.config
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="MSTest.TestAdapter" version="1.1.18" targetFramework="net461" />
+  <package id="MSTest.TestFramework" version="1.1.18" targetFramework="net461" />
+</packages>
\ No newline at end of file
diff --git a/source/OpenBveApi/Vector2.cs b/source/OpenBveApi/Vector2.cs
index 86813c484..a9df09462 100644
--- a/source/OpenBveApi/Vector2.cs
+++ b/source/OpenBveApi/Vector2.cs
@@ -1,6 +1,4 @@
-#pragma warning disable 0660, 0661
-
-using System;
+using System;
 
 namespace OpenBveApi.Math {
 	/// <summary>Represents a two-dimensional vector.</summary>
@@ -168,10 +166,43 @@ public Vector2(double x, double y) {
 			if (a.Y != b.Y) return true;
 			return false;
 		}
-		
-		
+
+		/// <summary>Returns the hashcode for this instance.</summary>
+		/// <returns>An integer containing the unique hashcode for this instance.</returns>
+		public override int GetHashCode()
+		{
+			unchecked
+			{
+				return (this.X.GetHashCode() * 397) ^ this.Y.GetHashCode();
+			}
+		}
+
+		/// <summary>Indicates whether this instance and a specified object are equal.</summary>
+		/// <param name="obj">The object to compare to.</param>
+		/// <returns>True if the instances are equal; false otherwise.</returns>
+		public override bool Equals(object obj)
+		{
+			if (!(obj is Vector2))
+			{
+				return false;
+			}
+
+			return this.Equals((Vector2)obj);
+		}
+
+		/// <summary>Checks whether the current vector is equal to the specified vector.</summary>
+		/// <param name="b">The specified vector.</param>
+		/// <returns>Whether the two vectors are equal.</returns>
+		public bool Equals(Vector2 b)
+		{
+			if (this.X != b.X) return false;
+			if (this.Y != b.Y) return false;
+			return true;
+		}
+
+
 		// --- instance functions ---
-		
+
 		/// <summary>Normalizes the vector.</summary>
 		/// <exception cref="System.DivideByZeroException">Raised when the vector is a null vector.</exception>
 		public void Normalize() {
diff --git a/source/OpenBveApi/Vector3.cs b/source/OpenBveApi/Vector3.cs
index 4da859394..877b5b1be 100644
--- a/source/OpenBveApi/Vector3.cs
+++ b/source/OpenBveApi/Vector3.cs
@@ -1,6 +1,4 @@
-#pragma warning disable 0660, 0661
-
-using System;
+using System;
 
 namespace OpenBveApi.Math {
 	/// <summary>Represents a three-dimensional vector.</summary>
@@ -185,7 +183,44 @@ public static Vector3 LinearInterpolate(Vector3 Vector1, Vector3 Vector2, double
 			if (a.Z != b.Z) return false;
 			return true;
 		}
-		
+
+		/// <summary>Returns the hashcode for this instance.</summary>
+		/// <returns>An integer representing the unique hashcode for this instance.</returns>
+		public override int GetHashCode()
+		{
+			unchecked
+			{
+				var hashCode = this.X.GetHashCode();
+				hashCode = (hashCode * 397) ^ this.Y.GetHashCode();
+				hashCode = (hashCode * 397) ^ this.Z.GetHashCode();
+				return hashCode;
+			}
+		}
+
+		/// <summary>Indicates whether this instance and a specified object are equal.</summary>
+		/// <param name="obj">The object to compare to.</param>
+		/// <returns>True if the instances are equal; false otherwise.</returns>
+		public override bool Equals(object obj)
+		{
+			if (!(obj is Vector3))
+			{
+				return false;
+			}
+
+			return this.Equals((Vector3)obj);
+		}
+
+		/// <summary>Checks whether the current vector is equal to the specified vector.</summary>
+		/// <param name="b">The specified vector.</param>
+		/// <returns>Whether the two vectors are equal.</returns>
+		public bool Equals(Vector3 b)
+		{
+			if (this.X != b.X) return false;
+			if (this.Y != b.Y) return false;
+			if (this.Z != b.Z) return false;
+			return true;
+		}
+
 		/// <summary>Checks whether the two specified vectors are unequal.</summary>
 		/// <param name="a">The first vector.</param>
 		/// <param name="b">The second vector.</param>