diff --git a/tests/Furnace.Tests/Furnace.Tests.fsproj b/tests/Furnace.Tests/Furnace.Tests.fsproj
index 8f647df2..cc6b9840 100644
--- a/tests/Furnace.Tests/Furnace.Tests.fsproj
+++ b/tests/Furnace.Tests/Furnace.Tests.fsproj
@@ -34,6 +34,8 @@
+
+
diff --git a/tests/Furnace.Tests/TestPlotHelpers.fs b/tests/Furnace.Tests/TestPlotHelpers.fs
new file mode 100644
index 00000000..8b1159c1
--- /dev/null
+++ b/tests/Furnace.Tests/TestPlotHelpers.fs
@@ -0,0 +1,138 @@
+// Copyright (c) 2016- University of Oxford (Atılım Güneş Baydin )
+// and other contributors, see LICENSE in root of repository.
+//
+// BSD 2-Clause License. See LICENSE in root of repository.
+
+namespace Tests
+
+open System
+open System.IO
+open NUnit.Framework
+open Furnace
+open Furnace.Util
+
+[]
+type TestPlotHelpers() =
+
+ []
+ member _.TestHelpersPrintValFloat32() =
+ // Test printVal with Float32 values
+ let result1 = printVal (3.14f :> scalar)
+ let result2 = printVal (Single.NaN :> scalar)
+ let result3 = printVal (Single.PositiveInfinity :> scalar)
+
+ Assert.IsTrue(result1.Contains("3.14"))
+ Assert.AreEqual("float('nan')", result2)
+ Assert.AreEqual("float('inf')", result3)
+
+ []
+ member _.TestHelpersPrintValFloat64() =
+ // Test printVal with Float64 values
+ let result1 = printVal (2.718 :> scalar)
+ let result2 = printVal (Double.NaN :> scalar)
+ let result3 = printVal (Double.PositiveInfinity :> scalar)
+
+ Assert.IsTrue(result1.Contains("2.718"))
+ Assert.AreEqual("float('nan')", result2)
+ Assert.AreEqual("float('inf')", result3)
+
+ []
+ member _.TestHelpersPrintValIntegers() =
+ // Test printVal with various integer types
+ let result1 = printVal (42 :> scalar) // Int32
+ let result2 = printVal (42L :> scalar) // Int64
+ let result3 = printVal (42s :> scalar) // Int16
+ let result4 = printVal (42uy :> scalar) // Byte
+ let result5 = printVal (42y :> scalar) // SByte
+
+ Assert.AreEqual("42", result1)
+ Assert.AreEqual("42", result2)
+ Assert.AreEqual("42", result3)
+ Assert.AreEqual("42", result4)
+ Assert.AreEqual("42", result5)
+
+ []
+ member _.TestHelpersPrintValBoolean() =
+ // Test printVal with Boolean values
+ let resultTrue = printVal (true :> scalar)
+ let resultFalse = printVal (false :> scalar)
+
+ Assert.AreEqual("True", resultTrue)
+ Assert.AreEqual("False", resultFalse)
+
+ []
+ member _.TestHelpersToPythonBool() =
+ // Test toPython with boolean values
+ let resultTrue = toPython true
+ let resultFalse = toPython false
+
+ Assert.AreEqual("True", resultTrue)
+ Assert.AreEqual("False", resultFalse)
+
+ []
+ member _.TestHelpersToPythonScalarTensor() =
+ // Test toPython with scalar tensor
+ let t = FurnaceImage.scalar(42.0f)
+ let result = toPython t
+
+ Assert.IsTrue(result.Contains("42"))
+
+ []
+ member _.TestHelpersToPython1DTensor() =
+ // Test toPython with 1D tensor
+ let t = FurnaceImage.tensor([1.0f; 2.0f; 3.0f])
+ let result = toPython t
+
+ // Should be in Python list format: [1.000000, 2.000000, 3.000000]
+ Assert.IsTrue(result.StartsWith("["))
+ Assert.IsTrue(result.EndsWith("]"))
+ Assert.IsTrue(result.Contains("1."))
+ Assert.IsTrue(result.Contains("2."))
+ Assert.IsTrue(result.Contains("3."))
+
+ []
+ member _.TestHelpersToPython2DTensor() =
+ // Test toPython with 2D tensor
+ let t = FurnaceImage.tensor([[1.0f; 2.0f]; [3.0f; 4.0f]])
+ let result = toPython t
+
+ // Should be nested list format: [[1., 2.], [3., 4.]]
+ Assert.IsTrue(result.StartsWith("["))
+ Assert.IsTrue(result.EndsWith("]"))
+ // Should contain at least two opening brackets for nested structure
+ let openBrackets = result.ToCharArray() |> Array.filter (fun c -> c = '[') |> Array.length
+ Assert.GreaterOrEqual(openBrackets, 2)
+
+ []
+ member _.TestHelpersToPythonOtherTypes() =
+ // Test toPython with other types (should fall back to ToString)
+ let result = toPython "hello world"
+ Assert.AreEqual("hello world", result)
+
+ let result2 = toPython 123
+ Assert.AreEqual("123", result2)
+
+ []
+ member _.TestHelpersRunScriptSuccess() =
+ // Test runScript with successful execution (echo command)
+ let tempDir = Path.GetTempPath()
+ let lines = [| "echo 'test'" |]
+
+ // This should not throw an exception
+ Assert.DoesNotThrow(fun () -> runScript "echo" lines 1000)
+
+ []
+ member _.TestHelpersRunScriptTimeout() =
+ // Test runScript with timeout (should handle gracefully)
+ let lines = [| "sleep 5" |] // Command that takes longer than timeout
+
+ // This should not throw an exception, just print warning
+ Assert.DoesNotThrow(fun () -> runScript "sleep" lines 100)
+
+ []
+ member _.TestHelpersRunScriptInvalidExecutable() =
+ // Test runScript with invalid executable (should handle gracefully)
+ let lines = [| "test" |]
+
+ // This should not throw an exception, just print warning
+ Assert.DoesNotThrow(fun () -> runScript "nonexistent_executable_12345" lines 1000)
\ No newline at end of file
diff --git a/tests/Furnace.Tests/TestPyplot.fs b/tests/Furnace.Tests/TestPyplot.fs
new file mode 100644
index 00000000..3c9b8163
--- /dev/null
+++ b/tests/Furnace.Tests/TestPyplot.fs
@@ -0,0 +1,171 @@
+// Copyright (c) 2016- University of Oxford (Atılım Güneş Baydin )
+// and other contributors, see LICENSE in root of repository.
+//
+// BSD 2-Clause License. See LICENSE in root of repository.
+
+namespace Tests
+
+open System
+open System.IO
+open NUnit.Framework
+open Furnace
+open Furnace.Util
+
+[]
+type TestPyplot() =
+
+ []
+ member _.TestPyplotConstructorDefaults() =
+ // Test Pyplot constructor with default parameters
+ let plt = Pyplot()
+ Assert.IsNotNull(plt)
+
+ []
+ member _.TestPyplotConstructorCustomParameters() =
+ // Test Pyplot constructor with custom parameters
+ let plt = Pyplot(pythonExecutable="python3", timeoutMilliseconds=5000)
+ Assert.IsNotNull(plt)
+
+ []
+ member _.TestPyplotFigure() =
+ // Test figure method
+ let plt = Pyplot()
+ Assert.DoesNotThrow(fun () -> plt.figure())
+
+ []
+ member _.TestPyplotFigureWithSize() =
+ // Test figure method with size
+ let plt = Pyplot()
+ Assert.DoesNotThrow(fun () -> plt.figure((10.0, 6.0)))
+
+ []
+ member _.TestPyplotPlotTensor() =
+ // Test plot method with tensor
+ let plt = Pyplot()
+ let data = FurnaceImage.tensor([1.0f; 2.0f; 3.0f; 2.0f; 1.0f])
+ Assert.DoesNotThrow(fun () -> plt.plot(data))
+
+ []
+ member _.TestPyplotPlotTensorWithLabel() =
+ // Test plot method with tensor and label
+ let plt = Pyplot()
+ let data = FurnaceImage.tensor([1.0f; 2.0f; 3.0f])
+ Assert.DoesNotThrow(fun () -> plt.plot(data, label="test data"))
+
+ []
+ member _.TestPyplotPlotTensorWithAlpha() =
+ // Test plot method with tensor and alpha
+ let plt = Pyplot()
+ let data = FurnaceImage.tensor([1.0f; 2.0f; 3.0f])
+ Assert.DoesNotThrow(fun () -> plt.plot(data, alpha=0.7))
+
+ []
+ member _.TestPyplotPlotTensorWithLabelAndAlpha() =
+ // Test plot method with tensor, label and alpha
+ let plt = Pyplot()
+ let data = FurnaceImage.tensor([1.0f; 2.0f; 3.0f])
+ Assert.DoesNotThrow(fun () -> plt.plot(data, label="test", alpha=0.5))
+
+ []
+ member _.TestPyplotPlotXYTensors() =
+ // Test plot method with x and y tensors
+ let plt = Pyplot()
+ let x = FurnaceImage.tensor([1.0f; 2.0f; 3.0f])
+ let y = FurnaceImage.tensor([2.0f; 4.0f; 6.0f])
+ Assert.DoesNotThrow(fun () -> plt.plot(x, y))
+
+ []
+ member _.TestPyplotPlotXYTensorsWithLabel() =
+ // Test plot method with x, y tensors and label
+ let plt = Pyplot()
+ let x = FurnaceImage.tensor([1.0f; 2.0f; 3.0f])
+ let y = FurnaceImage.tensor([2.0f; 4.0f; 6.0f])
+ Assert.DoesNotThrow(fun () -> plt.plot(x, y, label="linear"))
+
+ []
+ member _.TestPyplotPlotXYTensorsWithAlpha() =
+ // Test plot method with x, y tensors and alpha
+ let plt = Pyplot()
+ let x = FurnaceImage.tensor([1.0f; 2.0f; 3.0f])
+ let y = FurnaceImage.tensor([2.0f; 4.0f; 6.0f])
+ Assert.DoesNotThrow(fun () -> plt.plot(x, y, alpha=0.8))
+
+ []
+ member _.TestPyplotPlotXYTensorsWithLabelAndAlpha() =
+ // Test plot method with x, y tensors, label and alpha
+ let plt = Pyplot()
+ let x = FurnaceImage.tensor([1.0f; 2.0f; 3.0f])
+ let y = FurnaceImage.tensor([2.0f; 4.0f; 6.0f])
+ Assert.DoesNotThrow(fun () -> plt.plot(x, y, label="data", alpha=0.6))
+
+ []
+ member _.TestPyplotHistTensor() =
+ // Test hist method with tensor
+ let plt = Pyplot()
+ let data = FurnaceImage.randn([100])
+ Assert.DoesNotThrow(fun () -> plt.hist(data))
+
+ []
+ member _.TestPyplotHistTensorWithBins() =
+ // Test hist method with tensor and bins
+ let plt = Pyplot()
+ let data = FurnaceImage.randn([50])
+ Assert.DoesNotThrow(fun () -> plt.hist(data, bins=20))
+
+ []
+ member _.TestPyplotHistTensorWithLabel() =
+ // Test hist method with tensor and label
+ let plt = Pyplot()
+ let data = FurnaceImage.randn([30])
+ Assert.DoesNotThrow(fun () -> plt.hist(data, label="test data"))
+
+ []
+ member _.TestPyplotHistTensorWithBinsAndLabel() =
+ // Test hist method with tensor, bins and label
+ let plt = Pyplot()
+ let data = FurnaceImage.randn([40])
+ Assert.DoesNotThrow(fun () -> plt.hist(data, bins=15, label="histogram"))
+
+ []
+ member _.TestPyplotXlabelYlabel() =
+ // Test xlabel and ylabel methods
+ let plt = Pyplot()
+ Assert.DoesNotThrow(fun () -> plt.xlabel("X Axis"))
+ Assert.DoesNotThrow(fun () -> plt.ylabel("Y Axis"))
+
+ []
+ member _.TestPyplotLegend() =
+ // Test legend method
+ let plt = Pyplot()
+ Assert.DoesNotThrow(fun () -> plt.legend())
+
+ []
+ member _.TestPyplotTightLayout() =
+ // Test tightLayout method
+ let plt = Pyplot()
+ Assert.DoesNotThrow(fun () -> plt.tightLayout())
+
+ []
+ member _.TestPyplotSavefig() =
+ // Test savefig method - should not throw but may print warning
+ let plt = Pyplot()
+ let tempFile = Path.GetTempFileName() + ".png"
+ Assert.DoesNotThrow(fun () -> plt.savefig(tempFile))
+
+ []
+ member _.TestPyplotCompleteWorkflow() =
+ // Test complete plotting workflow
+ let plt = Pyplot()
+ let x = FurnaceImage.tensor([1.0f; 2.0f; 3.0f; 4.0f; 5.0f])
+ let y = FurnaceImage.tensor([1.0f; 4.0f; 9.0f; 16.0f; 25.0f])
+
+ Assert.DoesNotThrow(fun () ->
+ plt.figure((8.0, 6.0))
+ plt.plot(x, y, label="quadratic", alpha=0.8)
+ plt.xlabel("X values")
+ plt.ylabel("Y values")
+ plt.legend()
+ plt.tightLayout()
+ let tempFile = Path.GetTempFileName() + ".png"
+ plt.savefig(tempFile)
+ )
\ No newline at end of file