1.1.8 - Fix MQTT undefined settings and add unit tests

This commit is contained in:
Ryan Wagoner 2019-12-27 13:57:17 -05:00
parent a5e9fae32e
commit 7d87416915
9 changed files with 371 additions and 60 deletions

View file

@ -1,6 +1,7 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Mail; using System.Net.Mail;
using System.Reflection;
namespace OmniLinkBridge namespace OmniLinkBridge
{ {
@ -75,5 +76,10 @@ namespace OmniLinkBridge
// Pushover Notifications // Pushover Notifications
public static string pushover_token; public static string pushover_token;
public static string[] pushover_user; public static string[] pushover_user;
public static object GetValue(string propName)
{
return typeof(Global).GetField(propName, BindingFlags.Public | BindingFlags.Static).GetValue(null);
}
} }
} }

View file

@ -58,7 +58,7 @@ namespace OmniLinkBridge
try try
{ {
Settings.LoadSettings(); Settings.LoadSettings(Global.config_file);
} }
catch catch
{ {

View file

@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.1.7.0")] [assembly: AssemblyVersion("1.1.8.0")]
[assembly: AssemblyFileVersion("1.1.7.0")] [assembly: AssemblyFileVersion("1.1.8.0")]

View file

@ -16,10 +16,18 @@ namespace OmniLinkBridge
public static bool ShowDebug { get; set; } public static bool ShowDebug { get; set; }
public static bool UseEnvironment { 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 // HAI / Leviton Omni Controller
Global.controller_address = settings.ValidateHasValue("controller_address"); Global.controller_address = settings.ValidateHasValue("controller_address");
Global.controller_port = settings.ValidatePort("controller_port"); Global.controller_port = settings.ValidatePort("controller_port");
@ -122,10 +130,12 @@ namespace OmniLinkBridge
{ {
ConcurrentDictionary<int, T> ret = new ConcurrentDictionary<int, T>(); ConcurrentDictionary<int, T> ret = new ConcurrentDictionary<int, T>();
if (settings.CheckEnv(section) == null) string value = settings.CheckEnv(section);
if (string.IsNullOrEmpty(value))
return ret; return ret;
string[] ids = settings.CheckEnv(section).Split(','); string[] ids = value.Split(',');
for (int i = 0; i < ids.Length; i++) for (int i = 0; i < ids.Length; i++)
{ {
@ -195,6 +205,11 @@ namespace OmniLinkBridge
{ {
try try
{ {
string value = settings.CheckEnv(section);
if (string.IsNullOrEmpty(value))
return new HashSet<int>();
return new HashSet<int>(settings.CheckEnv(section).ParseRanges()); return new HashSet<int>(settings.CheckEnv(section).ParseRanges());
} }
catch catch
@ -241,7 +256,7 @@ namespace OmniLinkBridge
{ {
string value = settings.CheckEnv(section); string value = settings.CheckEnv(section);
if (value == null) if (string.IsNullOrEmpty(value))
return new MailAddress[] {}; return new MailAddress[] {};
string[] emails = value.Split(','); string[] emails = value.Split(',');
@ -292,31 +307,12 @@ namespace OmniLinkBridge
} }
} }
private static NameValueCollection LoadCollection(string sFile) private static NameValueCollection LoadCollection(string[] lines)
{ {
NameValueCollection settings = new NameValueCollection(); NameValueCollection settings = new NameValueCollection();
if (ShowDebug) foreach(string line in lines)
log.Debug($"Using settings file {sFile}");
if(!File.Exists(sFile))
{ {
log.Warn($"Unable to locate settings file {sFile}");
return settings;
}
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("#")) if (line.StartsWith("#"))
continue; continue;
@ -331,16 +327,29 @@ namespace OmniLinkBridge
settings.Add(key, value); settings.Add(key, value);
} }
sr.Close(); return settings;
fs.Close(); }
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 new NameValueCollection();
}
try
{
return LoadCollection(File.ReadAllLines(sFile));
} }
catch (FileNotFoundException ex) catch (FileNotFoundException ex)
{ {
log.Error("Error parsing settings file " + sFile, ex); log.Error("Error parsing settings file " + sFile, ex);
throw; throw;
} }
return settings;
} }
} }
} }

View file

@ -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();
}
}
}

View file

@ -9,11 +9,40 @@ namespace OmniLinkBridgeTest
[TestClass] [TestClass]
public class ExtensionTest 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] [TestMethod]
public void TestParseRange() public void TestParseRange()
{ {
List<int> blank = "".ParseRanges(); List<int> empty = "".ParseRanges();
Assert.AreEqual(0, blank.Count); Assert.AreEqual(0, empty.Count);
List<int> range = "1-3,5,6".ParseRanges(); List<int> range = "1-3,5,6".ParseRanges();
CollectionAssert.AreEqual(new List<int>(new int[] { 1, 2, 3, 5, 6 }), range); CollectionAssert.AreEqual(new List<int>(new int[] { 1, 2, 3, 5, 6 }), range);

View file

@ -3,6 +3,8 @@ using System.Text;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using OmniLinkBridge.Notifications; using OmniLinkBridge.Notifications;
using OmniLinkBridge;
using System.Net.Mail;
namespace OmniLinkBridgeTest namespace OmniLinkBridgeTest
{ {
@ -12,6 +14,16 @@ namespace OmniLinkBridgeTest
[TestMethod] [TestMethod]
public void SendNotification() 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"); Notification.Notify("Title", "Description");
} }
} }

View file

@ -47,11 +47,11 @@
<Reference Include="System.Core" /> <Reference Include="System.Core" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="AssemblyTestHarness.cs" />
<Compile Include="ExtensionTest.cs" /> <Compile Include="ExtensionTest.cs" />
<Compile Include="Mock\MockOmniLinkII.cs" /> <Compile Include="Mock\MockOmniLinkII.cs" />
<Compile Include="Mock\SendCommandEventArgs.cs" /> <Compile Include="Mock\SendCommandEventArgs.cs" />
<Compile Include="MQTTTest.cs" /> <Compile Include="MQTTTest.cs" />
<Compile Include="SettingsTest.cs" />
<Compile Include="NotificationTest.cs" /> <Compile Include="NotificationTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>

View file

@ -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<Exception>(() => Settings.LoadSettings(new string[]
{
"controller_address="
}));
List<string> lines = new List<string>(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<string> lines = new List<string>(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<string> lines = new List<string>(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<string> lines = new List<string>(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<int, OmniLinkBridge.WebAPI.OverrideZone> override_zone = new Dictionary<int, OmniLinkBridge.WebAPI.OverrideZone>()
{
{ 20, new OmniLinkBridge.WebAPI.OverrideZone { device_type = OmniLinkBridge.WebAPI.DeviceType.motion }}
};
Assert.AreEqual(override_zone.Count, Global.webapi_override_zone.Count);
foreach (KeyValuePair<int, OmniLinkBridge.WebAPI.OverrideZone> 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<string> lines = new List<string>(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<int, OmniLinkBridge.MQTT.OverrideZone> override_zone = new Dictionary<int, OmniLinkBridge.MQTT.OverrideZone>()
{
{ 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<int, OmniLinkBridge.MQTT.OverrideZone> 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<string> lines = new List<string>(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<string> lines = new List<string>(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<string> lines = new List<string>(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<string> lines = new List<string>(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]);
}
}
}