From 7d874169155a064777acc6b9be1e8074582c4e3b Mon Sep 17 00:00:00 2001 From: Ryan Wagoner Date: Fri, 27 Dec 2019 13:57:17 -0500 Subject: [PATCH] 1.1.8 - Fix MQTT undefined settings and add unit tests --- OmniLinkBridge/Global.cs | 6 + OmniLinkBridge/Program.cs | 2 +- OmniLinkBridge/Properties/AssemblyInfo.cs | 4 +- OmniLinkBridge/Settings.cs | 79 +++--- OmniLinkBridgeTest/AssemblyTestHarness.cs | 19 -- OmniLinkBridgeTest/ExtensionTest.cs | 33 ++- OmniLinkBridgeTest/NotificationTest.cs | 12 + OmniLinkBridgeTest/OmniLinkBridgeTest.csproj | 2 +- OmniLinkBridgeTest/SettingsTest.cs | 274 +++++++++++++++++++ 9 files changed, 371 insertions(+), 60 deletions(-) delete mode 100644 OmniLinkBridgeTest/AssemblyTestHarness.cs create mode 100644 OmniLinkBridgeTest/SettingsTest.cs diff --git a/OmniLinkBridge/Global.cs b/OmniLinkBridge/Global.cs index 9bbb00d..677154e 100644 --- a/OmniLinkBridge/Global.cs +++ b/OmniLinkBridge/Global.cs @@ -1,6 +1,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Net.Mail; +using System.Reflection; namespace OmniLinkBridge { @@ -75,5 +76,10 @@ namespace OmniLinkBridge // Pushover Notifications public static string pushover_token; public static string[] pushover_user; + + public static object GetValue(string propName) + { + return typeof(Global).GetField(propName, BindingFlags.Public | BindingFlags.Static).GetValue(null); + } } } diff --git a/OmniLinkBridge/Program.cs b/OmniLinkBridge/Program.cs index ea6795d..562fbb8 100644 --- a/OmniLinkBridge/Program.cs +++ b/OmniLinkBridge/Program.cs @@ -58,7 +58,7 @@ namespace OmniLinkBridge try { - Settings.LoadSettings(); + Settings.LoadSettings(Global.config_file); } catch { diff --git a/OmniLinkBridge/Properties/AssemblyInfo.cs b/OmniLinkBridge/Properties/AssemblyInfo.cs index 63c9d1a..370a390 100644 --- a/OmniLinkBridge/Properties/AssemblyInfo.cs +++ b/OmniLinkBridge/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.1.7.0")] -[assembly: AssemblyFileVersion("1.1.7.0")] +[assembly: AssemblyVersion("1.1.8.0")] +[assembly: AssemblyFileVersion("1.1.8.0")] diff --git a/OmniLinkBridge/Settings.cs b/OmniLinkBridge/Settings.cs index fd3cd80..457565a 100644 --- a/OmniLinkBridge/Settings.cs +++ b/OmniLinkBridge/Settings.cs @@ -16,10 +16,18 @@ namespace OmniLinkBridge public static bool ShowDebug { get; set; } public static bool UseEnvironment { get; set; } - public static void LoadSettings() + public static void LoadSettings(string file) { - NameValueCollection settings = LoadCollection(Global.config_file); + LoadSettings(LoadCollection(file)); + } + public static void LoadSettings(string[] lines) + { + LoadSettings(LoadCollection(lines)); + } + + public static void LoadSettings(NameValueCollection settings) + { // HAI / Leviton Omni Controller Global.controller_address = settings.ValidateHasValue("controller_address"); Global.controller_port = settings.ValidatePort("controller_port"); @@ -122,10 +130,12 @@ namespace OmniLinkBridge { ConcurrentDictionary ret = new ConcurrentDictionary(); - if (settings.CheckEnv(section) == null) + string value = settings.CheckEnv(section); + + if (string.IsNullOrEmpty(value)) return ret; - string[] ids = settings.CheckEnv(section).Split(','); + string[] ids = value.Split(','); for (int i = 0; i < ids.Length; i++) { @@ -195,6 +205,11 @@ namespace OmniLinkBridge { try { + string value = settings.CheckEnv(section); + + if (string.IsNullOrEmpty(value)) + return new HashSet(); + return new HashSet(settings.CheckEnv(section).ParseRanges()); } catch @@ -241,7 +256,7 @@ namespace OmniLinkBridge { string value = settings.CheckEnv(section); - if (value == null) + if (string.IsNullOrEmpty(value)) return new MailAddress[] {}; string[] emails = value.Split(','); @@ -292,55 +307,49 @@ namespace OmniLinkBridge } } - private static NameValueCollection LoadCollection(string sFile) + private static NameValueCollection LoadCollection(string[] lines) { NameValueCollection settings = new NameValueCollection(); + foreach(string line in lines) + { + if (line.StartsWith("#")) + continue; + + int pos = line.IndexOf('=', 0); + + if (pos == -1) + continue; + + string key = line.Substring(0, pos).Trim(); + string value = line.Substring(pos + 1).Trim(); + + settings.Add(key, value); + } + + return settings; + } + + private static NameValueCollection LoadCollection(string sFile) + { if (ShowDebug) log.Debug($"Using settings file {sFile}"); if(!File.Exists(sFile)) { log.Warn($"Unable to locate settings file {sFile}"); - return settings; + return new NameValueCollection(); } try { - FileStream fs = new FileStream(sFile, FileMode.Open, FileAccess.Read); - StreamReader sr = new StreamReader(fs); - - while (true) - { - string line = sr.ReadLine(); - - if (line == null) - break; - - if (line.StartsWith("#")) - continue; - - int pos = line.IndexOf('=', 0); - - if (pos == -1) - continue; - - string key = line.Substring(0, pos).Trim(); - string value = line.Substring(pos + 1).Trim(); - - settings.Add(key, value); - } - - sr.Close(); - fs.Close(); + return LoadCollection(File.ReadAllLines(sFile)); } catch (FileNotFoundException ex) { log.Error("Error parsing settings file " + sFile, ex); throw; } - - return settings; } } } diff --git a/OmniLinkBridgeTest/AssemblyTestHarness.cs b/OmniLinkBridgeTest/AssemblyTestHarness.cs deleted file mode 100644 index 23b5dff..0000000 --- a/OmniLinkBridgeTest/AssemblyTestHarness.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using OmniLinkBridge; -using System.IO; -using System.Reflection; - -namespace OmniLinkBridgeTest -{ - [TestClass] - public class AssemblyTestHarness - { - [AssemblyInitialize] - public static void InitializeAssembly(TestContext context) - { - Global.config_file = "OmniLinkBridge.ini"; - Settings.LoadSettings(); - } - } -} diff --git a/OmniLinkBridgeTest/ExtensionTest.cs b/OmniLinkBridgeTest/ExtensionTest.cs index 1bc6345..1f032b1 100644 --- a/OmniLinkBridgeTest/ExtensionTest.cs +++ b/OmniLinkBridgeTest/ExtensionTest.cs @@ -9,11 +9,40 @@ namespace OmniLinkBridgeTest [TestClass] public class ExtensionTest { + [TestMethod] + public void TestToCelsius() + { + Assert.AreEqual(-40, ((double)-40).ToCelsius()); + Assert.AreEqual(0, ((double)32).ToCelsius()); + Assert.AreEqual(50, ((double)122).ToCelsius()); + } + + [TestMethod] + public void TestToOmniTemp() + { + // -40C is 0 + double min = -40; + Assert.AreEqual(0, min.ToOmniTemp()); + + // 87.5C is 255 + double max = 87.5; + Assert.AreEqual(255, max.ToOmniTemp()); + } + + [TestMethod] + public void TestIsBitSet() + { + Assert.AreEqual(true, ((byte)1).IsBitSet(0)); + Assert.AreEqual(false, ((byte)2).IsBitSet(0)); + Assert.AreEqual(true, ((byte)3).IsBitSet(0)); + Assert.AreEqual(true, ((byte)3).IsBitSet(1)); + } + [TestMethod] public void TestParseRange() { - List blank = "".ParseRanges(); - Assert.AreEqual(0, blank.Count); + List empty = "".ParseRanges(); + Assert.AreEqual(0, empty.Count); List range = "1-3,5,6".ParseRanges(); CollectionAssert.AreEqual(new List(new int[] { 1, 2, 3, 5, 6 }), range); diff --git a/OmniLinkBridgeTest/NotificationTest.cs b/OmniLinkBridgeTest/NotificationTest.cs index ee21f49..5486afc 100644 --- a/OmniLinkBridgeTest/NotificationTest.cs +++ b/OmniLinkBridgeTest/NotificationTest.cs @@ -3,6 +3,8 @@ using System.Text; using System.Collections.Generic; using Microsoft.VisualStudio.TestTools.UnitTesting; using OmniLinkBridge.Notifications; +using OmniLinkBridge; +using System.Net.Mail; namespace OmniLinkBridgeTest { @@ -12,6 +14,16 @@ namespace OmniLinkBridgeTest [TestMethod] public void SendNotification() { + // This is an integration test + Global.mail_server = "localhost"; + Global.mail_tls = false; + Global.mail_port = 25; + Global.mail_from = new MailAddress("OmniLinkBridge@localhost"); + Global.mail_to = new MailAddress[] + { + new MailAddress("mailbox@localhost") + }; + Notification.Notify("Title", "Description"); } } diff --git a/OmniLinkBridgeTest/OmniLinkBridgeTest.csproj b/OmniLinkBridgeTest/OmniLinkBridgeTest.csproj index 60e954d..046b598 100644 --- a/OmniLinkBridgeTest/OmniLinkBridgeTest.csproj +++ b/OmniLinkBridgeTest/OmniLinkBridgeTest.csproj @@ -47,11 +47,11 @@ - + diff --git a/OmniLinkBridgeTest/SettingsTest.cs b/OmniLinkBridgeTest/SettingsTest.cs new file mode 100644 index 0000000..a537362 --- /dev/null +++ b/OmniLinkBridgeTest/SettingsTest.cs @@ -0,0 +1,274 @@ +using System; +using System.Text; +using System.Collections.Generic; +using System.Collections.Concurrent; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OmniLinkBridge; + +namespace OmniLinkBridgeTest +{ + [TestClass] + public class SettingsTest + { + private string[] RequiredSettings() + { + return new string[] + { + "controller_address = 1.1.1.1", + "controller_port = 4369", + "controller_key1 = 00-00-00-00-00-00-00-01", + "controller_key2 = 00-00-00-00-00-00-00-02", + }; + } + + [TestMethod] + public void TestControllerSettings() + { + Assert.ThrowsException(() => Settings.LoadSettings(new string[] + { + "controller_address=" + })); + + List lines = new List(RequiredSettings()); + lines.AddRange(new string[] + { + "controller_name = MyController" + }); + Settings.LoadSettings(lines.ToArray()); + Assert.AreEqual("1.1.1.1", Global.controller_address); + Assert.AreEqual(4369, Global.controller_port); + Assert.AreEqual("00-00-00-00-00-00-00-01", Global.controller_key1); + Assert.AreEqual("00-00-00-00-00-00-00-02", Global.controller_key2); + Assert.AreEqual("MyController", Global.controller_name); + } + + [TestMethod] + public void TestTimeSyncSettings() + { + // Default should be false + Settings.LoadSettings(RequiredSettings()); + Assert.AreEqual(false, Global.mqtt_enabled); + + // Test minimal settings + List lines = new List(RequiredSettings()); + lines.AddRange(new string[] + { + "time_sync = yes", + "time_interval = 60", + "time_drift = 10" + }); + Settings.LoadSettings(lines.ToArray()); + Assert.AreEqual(true, Global.time_sync); + Assert.AreEqual(60, Global.time_interval); + Assert.AreEqual(10, Global.time_drift); + } + + [TestMethod] + public void TestVerboseSettings() + { + // Default should be false + Settings.LoadSettings(RequiredSettings()); + Assert.AreEqual(false, Global.verbose_unhandled); + + // Check each setting correctly sets + foreach (string setting in new string[] { + "verbose_unhandled", + "verbose_event", + "verbose_area", + "verbose_zone", + "verbose_thermostat_timer", + "verbose_thermostat", + "verbose_unit", + "verbose_message" + }) + { + List lines = new List(RequiredSettings()); + lines.Add($"{setting} = yes"); + Settings.LoadSettings(lines.ToArray()); + Assert.AreEqual(true, Global.GetValue(setting)); + } + } + + [TestMethod] + public void TestWebAPISettings() + { + // Default should be false + Settings.LoadSettings(RequiredSettings()); + Assert.AreEqual(false, Global.mqtt_enabled); + + // Test minimal settings + List lines = new List(RequiredSettings()); + lines.AddRange(new string[] + { + "webapi_enabled = yes", + "webapi_port = 8000", + "webapi_override_zone = id=20;device_type=motion" + }); + Settings.LoadSettings(lines.ToArray()); + Assert.AreEqual(true, Global.webapi_enabled); + Assert.AreEqual(8000, Global.webapi_port); + + Dictionary override_zone = new Dictionary() + { + { 20, new OmniLinkBridge.WebAPI.OverrideZone { device_type = OmniLinkBridge.WebAPI.DeviceType.motion }} + }; + + Assert.AreEqual(override_zone.Count, Global.webapi_override_zone.Count); + foreach (KeyValuePair pair in override_zone) + { + Global.webapi_override_zone.TryGetValue(pair.Key, out OmniLinkBridge.WebAPI.OverrideZone value); + Assert.AreEqual(override_zone[pair.Key].device_type, value.device_type); + } + } + + [TestMethod] + public void TestMQTTSettings() + { + // Default should be false + Settings.LoadSettings(RequiredSettings()); + Assert.AreEqual(false, Global.mqtt_enabled); + + // Test minimal settings + List lines = new List(RequiredSettings()); + lines.AddRange(new string[] + { + "mqtt_enabled = yes", + "mqtt_server = localhost", + "mqtt_port = 1883" + }); + Settings.LoadSettings(lines.ToArray()); + Assert.AreEqual(true, Global.mqtt_enabled); + Assert.AreEqual("localhost", Global.mqtt_server); + Assert.AreEqual(1883, Global.mqtt_port); + Assert.AreEqual("omnilink", Global.mqtt_prefix); + Assert.AreEqual("homeassistant", Global.mqtt_discovery_prefix); + Assert.AreEqual(string.Empty, Global.mqtt_discovery_name_prefix); + + // Test additional settings + lines.AddRange(new string[] + { + "mqtt_username = myuser", + "mqtt_password = mypass", + "mqtt_prefix = myprefix", + "mqtt_discovery_prefix = mydiscoveryprefix", + "mqtt_discovery_name_prefix = mynameprefix", + "mqtt_discovery_ignore_zones = 1,2-3,4", + "mqtt_discovery_ignore_units = 2-5,7", + "mqtt_discovery_override_zone = id=5;device_class=garage_door", + "mqtt_discovery_override_zone = id=7;device_class=motion", + }); + Settings.LoadSettings(lines.ToArray()); + Assert.AreEqual("myuser", Global.mqtt_username); + Assert.AreEqual("mypass", Global.mqtt_password); + Assert.AreEqual("myprefix", Global.mqtt_prefix); + Assert.AreEqual("mydiscoveryprefix", Global.mqtt_discovery_prefix); + Assert.AreEqual("mynameprefix ", Global.mqtt_discovery_name_prefix); + Assert.IsTrue(Global.mqtt_discovery_ignore_zones.SetEquals(new int[] { 1, 2, 3, 4 })); + Assert.IsTrue(Global.mqtt_discovery_ignore_units.SetEquals(new int[] { 2, 3, 4, 5, 7 })); + + Dictionary override_zone = new Dictionary() + { + { 5, new OmniLinkBridge.MQTT.OverrideZone { device_class = OmniLinkBridge.MQTT.BinarySensor.DeviceClass.garage_door }}, + { 7, new OmniLinkBridge.MQTT.OverrideZone { device_class = OmniLinkBridge.MQTT.BinarySensor.DeviceClass.motion }} + }; + + Assert.AreEqual(override_zone.Count, Global.mqtt_discovery_override_zone.Count); + foreach (KeyValuePair pair in override_zone) + { + Global.mqtt_discovery_override_zone.TryGetValue(pair.Key, out OmniLinkBridge.MQTT.OverrideZone value); + Assert.AreEqual(override_zone[pair.Key].device_class, value.device_class); + } + } + + [TestMethod] + public void TestNotifySettings() + { + // Default should be false + Settings.LoadSettings(RequiredSettings()); + Assert.AreEqual(false, Global.verbose_unhandled); + + // Check each setting correctly sets + foreach (string setting in new string[] { + "notify_area", + "notify_message" + }) + { + List lines = new List(RequiredSettings()); + lines.Add($"{setting} = yes"); + Settings.LoadSettings(lines.ToArray()); + Assert.AreEqual(true, Global.GetValue(setting)); + } + } + + [TestMethod] + public void TestMailSettings() + { + // Default should be null + Settings.LoadSettings(RequiredSettings()); + Assert.AreEqual(null, Global.mail_server); + + // Test minimal settings + List lines = new List(RequiredSettings()); + lines.AddRange(new string[] + { + "mail_server = localhost", + "mail_port = 25", + "mail_from = from@localhost", + "mail_to = to1@localhost", + "mail_to = to2@localhost" + }); + Settings.LoadSettings(lines.ToArray()); + Assert.AreEqual("localhost", Global.mail_server); + Assert.AreEqual(false, Global.mail_tls); + Assert.AreEqual(25, Global.mail_port); + Assert.AreEqual("from@localhost", Global.mail_from.Address); + Assert.AreEqual(2, Global.mail_to.Length); + Assert.AreEqual("to1@localhost", Global.mail_to[0].Address); + Assert.AreEqual("to2@localhost", Global.mail_to[1].Address); + + // Test additional settings + lines.AddRange(new string[] + { + "mail_tls = yes", + "mail_username = myuser", + "mail_password = mypass" + }); + Settings.LoadSettings(lines.ToArray()); + Assert.AreEqual(true, Global.mail_tls); + Assert.AreEqual("myuser", Global.mail_username); + Assert.AreEqual("mypass", Global.mail_password); + } + + [TestMethod] + public void TestProwlSettings() + { + // Test minimal settings + List lines = new List(RequiredSettings()); + lines.AddRange(new string[] + { + "prowl_key = mykey1", + "prowl_key = mykey2", + }); + Settings.LoadSettings(lines.ToArray()); + Assert.AreEqual("mykey1", Global.prowl_key[0]); + Assert.AreEqual("mykey2", Global.prowl_key[1]); + } + + [TestMethod] + public void TestPushoverSettings() + { + // Test minimal settings + List lines = new List(RequiredSettings()); + lines.AddRange(new string[] + { + "pushover_token = mytoken", + "pushover_user = myuser1", + "pushover_user = myuser2", + }); + Settings.LoadSettings(lines.ToArray()); + Assert.AreEqual("mytoken", Global.pushover_token); + Assert.AreEqual("myuser1", Global.pushover_user[0]); + Assert.AreEqual("myuser2", Global.pushover_user[1]); + } + } +}