Skip to content

Commit

Permalink
[PTRun][System]Fix delay on many net interfaces (#17490)
Browse files Browse the repository at this point in the history
* code changes

* small text fixes

* update docs

* comment improvements

* update tests

* fix typo

* change

* fix typo

* fix error msg

* fix bug

* second fix
  • Loading branch information
htcfreek committed Apr 4, 2022
1 parent 4416562 commit fd01ee3
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 87 deletions.
5 changes: 5 additions & 0 deletions doc/devdocs/modules/launcher/plugins/system.md
Expand Up @@ -68,6 +68,11 @@ Available commands:
### Network results on global queries
- The network results (IP and MAC address) are only shown on global queries, if the search term starts with either IP, MAC or Address. (We compare case-insensitive.)

### Returning results
We return the results in two steps:
1. All results which we can create very fast like shutdown or logoff via [`Main.Query(Query query)`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Main.cs).
2. All results which need some time to create like the network results (IP, MAC) via [`Main.Query(Query query, bool delayedExecution)`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Main.cs).

## [Unit Tests](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System.UnitTests)
We have a [Unit Test project](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System.UnitTests) that executes various test to ensure that the plugin works as expected.

Expand Down
Expand Up @@ -28,10 +28,10 @@ public void Setup()
[DataRow("hibernate", "Images\\sleep.dark.png")]
[DataRow("empty recycle", "Images\\recyclebin.dark.png")]
[DataRow("uefi firmware settings", "Images\\firmwareSettings.dark.png")]
[DataRow("ip v4 addr", "Images\\networkAdapter.dark.png")]
[DataRow("ip v6 addr", "Images\\networkAdapter.dark.png")]
[DataRow("mac addr", "Images\\networkAdapter.dark.png")]
public void IconThemeDarkTest(string typedString, string expectedResult)
[DataRow("ip v4 addr", "Images\\networkAdapter.dark.png", true)]
[DataRow("ip v6 addr", "Images\\networkAdapter.dark.png", true)]
[DataRow("mac addr", "Images\\networkAdapter.dark.png", true)]
public void IconThemeDarkTest(string typedString, string expectedResult, bool isDelayed = default)
{
// Setup
Mock<Main> main = new Mock<Main>();
Expand All @@ -40,7 +40,7 @@ public void IconThemeDarkTest(string typedString, string expectedResult)
Query expectedQuery = new Query(typedString);

// Act
var result = main.Object.Query(expectedQuery).FirstOrDefault().IcoPath;
var result = !isDelayed ? main.Object.Query(expectedQuery).FirstOrDefault().IcoPath : main.Object.Query(expectedQuery, true).FirstOrDefault().IcoPath;

// Assert
Assert.AreEqual(expectedResult, result);
Expand All @@ -55,10 +55,10 @@ public void IconThemeDarkTest(string typedString, string expectedResult)
[DataRow("hibernate", "Images\\sleep.light.png")]
[DataRow("empty recycle", "Images\\recyclebin.light.png")]
[DataRow("uefi firmware settings", "Images\\firmwareSettings.light.png")]
[DataRow("ipv4 addr", "Images\\networkAdapter.light.png")]
[DataRow("ipv6 addr", "Images\\networkAdapter.light.png")]
[DataRow("mac addr", "Images\\networkAdapter.light.png")]
public void IconThemeLightTest(string typedString, string expectedResult)
[DataRow("ipv4 addr", "Images\\networkAdapter.light.png", true)]
[DataRow("ipv6 addr", "Images\\networkAdapter.light.png", true)]
[DataRow("mac addr", "Images\\networkAdapter.light.png", true)]
public void IconThemeLightTest(string typedString, string expectedResult, bool isDelayed = default)
{
// Setup
Mock<Main> main = new Mock<Main>();
Expand All @@ -67,7 +67,7 @@ public void IconThemeLightTest(string typedString, string expectedResult)
Query expectedQuery = new Query(typedString);

// Act
var result = main.Object.Query(expectedQuery).FirstOrDefault().IcoPath;
var result = !isDelayed ? main.Object.Query(expectedQuery).FirstOrDefault().IcoPath : main.Object.Query(expectedQuery, true).FirstOrDefault().IcoPath;

// Assert
Assert.AreEqual(expectedResult, result);
Expand Down
Expand Up @@ -28,19 +28,33 @@ public void Setup()
[DataRow("sleep", "Put computer to sleep")]
[DataRow("hibernate", "Hibernate computer")]
[DataRow("empty recycle", "Empty Recycle Bin")]
public void EnvironmentIndependentQueryResults(string typedString, string expectedResult)
{
// Setup
Mock<Main> main = new Mock<Main>();
Query expectedQuery = new Query(typedString);

// Act
var result = main.Object.Query(expectedQuery).FirstOrDefault().SubTitle;

// Assert
Assert.IsTrue(result.StartsWith(expectedResult, StringComparison.OrdinalIgnoreCase));
}

[DataTestMethod]
[DataRow("ip", "IPv4 address of")]
[DataRow("address", "IPv4 address of")] // searching for address should show ipv4 first
[DataRow("ip v4", "IPv4 address of")]
[DataRow("ip v6", "IPv6 address of")]
[DataRow("mac addr", "MAC address of")]
public void EnvironmentIndependentQueryResults(string typedString, string expectedResult)
public void DelayedQueryResults(string typedString, string expectedResult)
{
// Setup
Mock<Main> main = new Mock<Main>();
Query expectedQuery = new Query(typedString);

// Act
var result = main.Object.Query(expectedQuery).FirstOrDefault().SubTitle;
var result = main.Object.Query(expectedQuery, true).FirstOrDefault().SubTitle;

// Assert
Assert.IsTrue(result.StartsWith(expectedResult, StringComparison.OrdinalIgnoreCase));
Expand Down
Expand Up @@ -2,10 +2,9 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net.NetworkInformation;
using System.Windows;
using System.Windows.Interop;
using Microsoft.PowerToys.Run.Plugin.System.Properties;
Expand All @@ -28,6 +27,11 @@ internal static class Commands
internal const int EWXPOWEROFF = 0x00000008;
internal const int EWXFORCEIFHUNG = 0x00000010;

// Cache for network interface information to save query time
private const int UpdateCacheIntervalSeconds = 5;
private static List<NetworkConnectionProperties> networkPropertiesCache = new List<NetworkConnectionProperties>();
private static DateTime timeOfLastNetworkQuery;

/// <summary>
/// Returns a list with all system command results
/// </summary>
Expand Down Expand Up @@ -154,11 +158,16 @@ internal static List<Result> GetNetworkConnectionResults(string iconTheme, Cultu
{
var results = new List<Result>();

var interfaces = NetworkInterface.GetAllNetworkInterfaces().Where(x => x.NetworkInterfaceType != NetworkInterfaceType.Loopback && x.GetPhysicalAddress() != null);
foreach (NetworkInterface i in interfaces)
// We update the cache only if the last query is older than 'updateCacheIntervalSeconds' seconds
DateTime timeOfLastNetworkQueryBefore = timeOfLastNetworkQuery;
timeOfLastNetworkQuery = DateTime.Now; // Set time of last query to this query
if ((timeOfLastNetworkQuery - timeOfLastNetworkQueryBefore).TotalSeconds >= UpdateCacheIntervalSeconds)
{
NetworkConnectionProperties intInfo = new NetworkConnectionProperties(i);
networkPropertiesCache = NetworkConnectionProperties.GetList();
}

foreach (NetworkConnectionProperties intInfo in networkPropertiesCache)
{
if (!string.IsNullOrEmpty(intInfo.IPv4))
{
results.Add(new Result()
Expand Down
Expand Up @@ -4,7 +4,10 @@

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using Microsoft.PowerToys.Run.Plugin.System.Properties;
Expand Down Expand Up @@ -94,28 +97,29 @@ internal class NetworkConnectionProperties
/// <summary>
/// Gets the list of gateway IPs as string
/// </summary>
internal List<string> Gateways { get; private set; } = new List<string>();
internal List<IPAddress> Gateways { get; private set; } = new List<IPAddress>();

/// <summary>
/// Gets the list of DHCP server IPs as string
/// </summary>
internal List<string> DhcpServers { get; private set; } = new List<string>();
internal IPAddressCollection DhcpServers { get; private set; }

/// <summary>
/// Gets the list of DNS server IPs as string
/// </summary>
internal List<string> DnsServers { get; private set; } = new List<string>();
internal IPAddressCollection DnsServers { get; private set; }

/// <summary>
/// Gets the list of WINS server IPs as string
/// </summary>
internal List<string> WinsServers { get; private set; } = new List<string>();
internal IPAddressCollection WinsServers { get; private set; }

/// <summary>
/// Initializes a new instance of the <see cref="NetworkConnectionProperties"/> class.
/// This private constructor is used when we crete the list of adapter (properties) by calling <see cref="NetworkConnectionProperties.GetList()"/>.
/// </summary>
/// <param name="networkInterface">Network interface of the connection</param>
internal NetworkConnectionProperties(NetworkInterface networkInterface)
private NetworkConnectionProperties(NetworkInterface networkInterface)
{
// Setting adapter properties
Adapter = networkInterface.Description;
Expand All @@ -133,6 +137,23 @@ internal NetworkConnectionProperties(NetworkInterface networkInterface)
}
}

/// <summary>
/// Creates a list with all network adapters and their properties
/// </summary>
/// <returns>List containing all network adapters</returns>
internal static List<NetworkConnectionProperties> GetList()
{
List<NetworkConnectionProperties> list = new List<NetworkConnectionProperties>();

var interfaces = NetworkInterface.GetAllNetworkInterfaces().Where(x => x.NetworkInterfaceType != NetworkInterfaceType.Loopback && x.GetPhysicalAddress() != null);
foreach (NetworkInterface i in interfaces)
{
list.Add(new NetworkConnectionProperties(i));
}

return list;
}

/// <summary>
/// Gets a formatted string with the adapter details
/// </summary>
Expand Down Expand Up @@ -179,64 +200,59 @@ internal string GetConnectionDetails()
/// <param name="properties">Element of the type <see cref="IPInterfaceProperties"/>.</param>
private void SetIpProperties(IPInterfaceProperties properties)
{
var ipList = properties.UnicastAddresses;
DateTime t = DateTime.Now;

UnicastIPAddressInformationCollection ipList = properties.UnicastAddresses;
GatewayIPAddressInformationCollection gwList = properties.GatewayAddresses;
DhcpServers = properties.DhcpServerAddresses;
DnsServers = properties.DnsAddresses;
WinsServers = properties.WinsServersAddresses;

foreach (var ip in ipList)
for (int i = 0; i < ipList.Count; i++)
{
if (ip.Address.AddressFamily == AddressFamily.InterNetwork)
IPAddress ip = ipList[i].Address;

if (ip.AddressFamily == AddressFamily.InterNetwork)
{
IPv4 = ip.Address.ToString();
IPv4Mask = ip.IPv4Mask.ToString();
IPv4 = ip.ToString();
IPv4Mask = ipList[i].IPv4Mask.ToString();
}
else if (ip.Address.AddressFamily == AddressFamily.InterNetworkV6)
else if (ip.AddressFamily == AddressFamily.InterNetworkV6)
{
if (string.IsNullOrEmpty(IPv6Primary))
{
IPv6Primary = ip.Address.ToString();
IPv6Primary = ip.ToString();
}

if (ip.Address.IsIPv6LinkLocal)
if (ip.IsIPv6LinkLocal)
{
IPv6LinkLocal = ip.Address.ToString();
IPv6LinkLocal = ip.ToString();
}
else if (ip.Address.IsIPv6SiteLocal)
else if (ip.IsIPv6SiteLocal)
{
IPv6SiteLocal = ip.Address.ToString();
IPv6SiteLocal = ip.ToString();
}
else if (ip.Address.IsIPv6UniqueLocal)
else if (ip.IsIPv6UniqueLocal)
{
IPv6UniqueLocal = ip.Address.ToString();
IPv6UniqueLocal = ip.ToString();
}
else if (ip.SuffixOrigin == SuffixOrigin.Random)
else if (ipList[i].SuffixOrigin == SuffixOrigin.Random)
{
IPv6Temporary = ip.Address.ToString();
IPv6Temporary = ip.ToString();
}
else
{
IPv6Global = ip.Address.ToString();
IPv6Global = ip.ToString();
}
}
}

foreach (var ip in properties.GatewayAddresses)
{
Gateways.Add(ip.Address.ToString());
}

foreach (var ip in properties.DhcpServerAddresses)
{
DhcpServers.Add(ip.ToString());
}

foreach (var ip in properties.DnsAddresses)
for (int i = 0; i < gwList.Count; i++)
{
DnsServers.Add(ip.ToString());
Gateways.Add(gwList[i].Address);
}

foreach (var ip in properties.WinsServersAddresses)
{
WinsServers.Add(ip.ToString());
}
Debug.Print($"time for getting ips: {DateTime.Now - t}");
}

/// <summary>
Expand Down Expand Up @@ -284,21 +300,20 @@ private static string GetFormattedSpeedValue(long speed)
/// <exception cref="ArgumentException">If the parameter <paramref name="property"/> is not of the type <see cref="string"/> or <see cref="List{String}"/>.</exception>
private static string CreateIpInfoForDetailsText(string title, dynamic property)
{
if (property is string)
switch (property)
{
return $"\n{title}{property}";
}
else if (property is List<string> list)
{
return list.Count == 0 ? string.Empty : $"\n{title}{string.Join("\n\t", property)}";
}
else if (property is null)
{
return string.Empty;
}
else
{
throw new ArgumentException($"'{property}' is not of type 'string' or 'List<string>'.", nameof(property));
case string:
return $"\n{title}{property}";
case List<string> listString:
return listString.Count == 0 ? string.Empty : $"\n{title}{string.Join("\n\t", property)}";
case List<IPAddress> listIP:
return listIP.Count == 0 ? string.Empty : $"\n{title}{string.Join("\n\t", property)}";
case IPAddressCollection collectionIP:
return collectionIP.Count == 0 ? string.Empty : $"\n{title}{string.Join("\n\t", property)}";
case null:
return string.Empty;
default:
throw new ArgumentException($"'{property}' is not of type 'string', 'List<string>', 'List<IPAddress>' or 'IPAddressCollection'.", nameof(property));
}
}
}
Expand Down

0 comments on commit fd01ee3

Please sign in to comment.