From 5751d90b48386fe6ec4ba0784945166ad9308755 Mon Sep 17 00:00:00 2001
From: evgenyfedorov2 <25526458+evgenyfedorov2@users.noreply.github.com>
Date: Thu, 15 Aug 2024 14:43:07 +0200
Subject: [PATCH 01/13] wip - add tests
---
eng/MSBuild/Shared.props | 4 +
.../ITcpTableInfo.cs | 22 +
.../Linux/IFileSystem.cs | 14 +-
.../Linux/Network/LinuxNetworkMetrics.cs | 81 ++
.../Network/LinuxNetworkUtilizationParser.cs | 147 +++
.../Linux/Network/LinuxTcpState.cs | 43 +
.../Linux/Network/LinuxTcpTableInfo.cs | 51 +
.../Linux/OSFileSystem.cs | 51 +
...ions.Diagnostics.ResourceMonitoring.csproj | 5 +-
...ceMonitoringServiceCollectionExtensions.cs | 10 +-
.../ResourceUtilizationInstruments.cs | 8 +
.../{Windows/Network => }/TcpStateInfo.cs | 2 +-
.../{ => Network}/WindowsNetworkMetrics.cs | 16 +-
...TcpTableInfo.cs => WindowsTcpTableInfo.cs} | 8 +-
src/Shared/BufferWriterPool/BufferWriter.cs | 2 -
.../BufferWriterPooledObjectPolicy.cs | 2 -
.../Data.Validation/TimeSpanAttribute.cs | 2 -
src/Shared/Debugger/DebuggerExtensions.cs | 2 -
src/Shared/RentedSpan/RentedSpan.cs | 2 -
src/Shared/StringSplit/README.md | 11 +
src/Shared/StringSplit/StringRange.cs | 164 ++++
.../StringSplit/StringSplitExtensions.cs | 891 ++++++++++++++++++
.../Resources/FileNamesOnlyFileSystem.cs | 5 +
.../Resources/HardcodedValueFileSystem.cs | 5 +
...ceMonitoringOptionsCustomValidatorTests.cs | 2 +-
.../Windows/Tcp6TableInfoTests.cs | 16 +-
.../Windows/TcpTableInfoTests.cs | 18 +-
.../Windows/WindowsCountersTests.cs | 4 +-
.../StringSplit/SplitExtensionsTests.cs | 426 +++++++++
test/Shared/StringSplit/StringRangeTests.cs | 42 +
30 files changed, 2006 insertions(+), 50 deletions(-)
create mode 100644 src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ITcpTableInfo.cs
create mode 100644 src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkMetrics.cs
create mode 100644 src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkUtilizationParser.cs
create mode 100644 src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxTcpState.cs
create mode 100644 src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxTcpTableInfo.cs
rename src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/{Windows/Network => }/TcpStateInfo.cs (95%)
rename src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/{ => Network}/WindowsNetworkMetrics.cs (91%)
rename src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Network/{TcpTableInfo.cs => WindowsTcpTableInfo.cs} (97%)
create mode 100644 src/Shared/StringSplit/README.md
create mode 100644 src/Shared/StringSplit/StringRange.cs
create mode 100644 src/Shared/StringSplit/StringSplitExtensions.cs
create mode 100644 test/Shared/StringSplit/SplitExtensionsTests.cs
create mode 100644 test/Shared/StringSplit/StringRangeTests.cs
diff --git a/eng/MSBuild/Shared.props b/eng/MSBuild/Shared.props
index 2662152ff85..14fa8b868cd 100644
--- a/eng/MSBuild/Shared.props
+++ b/eng/MSBuild/Shared.props
@@ -38,4 +38,8 @@
+
+
+
+
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ITcpTableInfo.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ITcpTableInfo.cs
new file mode 100644
index 00000000000..90ec8c5664f
--- /dev/null
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ITcpTableInfo.cs
@@ -0,0 +1,22 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring;
+
+///
+/// An interface for getting TCP/IP table information.
+///
+internal interface ITcpTableInfo
+{
+ ///
+ /// Gets the last known snapshot of TCP/IP v4 state info on the system.
+ ///
+ /// An instance of .
+ TcpStateInfo GetIpV4CachingSnapshot();
+
+ ///
+ /// Gets the last known snapshot of TCP/IP v6 state info on the system.
+ ///
+ /// An instance of .
+ TcpStateInfo GetIpV6CachingSnapshot();
+}
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/IFileSystem.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/IFileSystem.cs
index bce4929dfe0..2cb34d777ca 100644
--- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/IFileSystem.cs
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/IFileSystem.cs
@@ -16,20 +16,20 @@ internal interface IFileSystem
///
/// Checks for file existence.
///
- /// True/False.
+ /// or .
bool Exists(FileInfo fileInfo);
///
/// Get directory names on the filesystem based on the provided pattern.
///
- /// string.
+ /// IReadOnlyCollection.
IReadOnlyCollection GetDirectoryNames(string directory, string pattern);
///
/// Reads content from the file.
///
///
- /// Chars written.
+ /// Number of chars written.
///
int Read(FileInfo file, int length, Span destination);
@@ -42,4 +42,12 @@ internal interface IFileSystem
/// Reads first line from the file.
///
void ReadFirstLine(FileInfo file, BufferWriter destination);
+
+ ///
+ /// Reads all content from a file by line.
+ ///
+ ///
+ /// IEnumerable.
+ ///
+ IEnumerable> ReadAllByLines(FileInfo file, BufferWriter destination);
}
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkMetrics.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkMetrics.cs
new file mode 100644
index 00000000000..07c71fa039c
--- /dev/null
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkMetrics.cs
@@ -0,0 +1,81 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.Metrics;
+
+namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network;
+
+internal sealed class LinuxNetworkMetrics
+{
+ private readonly ITcpTableInfo _tcpTableInfo;
+
+ public LinuxNetworkMetrics(IMeterFactory meterFactory, ITcpTableInfo tcpTableInfo)
+ {
+ _tcpTableInfo = tcpTableInfo;
+
+#pragma warning disable CA2000 // Dispose objects before losing scope
+ // We don't dispose the meter because IMeterFactory handles that
+ // An issue on analyzer side: https://github.com/dotnet/roslyn-analyzers/issues/6912
+ // Related documentation: https://github.com/dotnet/docs/pull/37170
+ var meter = meterFactory.Create(nameof(ResourceMonitoring));
+#pragma warning restore CA2000 // Dispose objects before losing scope
+
+ var tcpTag = new KeyValuePair("network.transport", "tcp");
+ var commonTags = new TagList
+ {
+ tcpTag
+ };
+
+ // The metric is aligned with
+ // https://github.com/open-telemetry/semantic-conventions/blob/main/docs/system/system-metrics.md#metric-systemnetworkconnections
+ _ = meter.CreateObservableUpDownCounter(
+ ResourceUtilizationInstruments.SystemNetworkConnections,
+ GetMeasurements,
+ unit: "{connection}",
+ description: null,
+ tags: commonTags);
+ }
+
+ private IEnumerable> GetMeasurements()
+ {
+ const string NetworkStateKey = "system.network.state";
+
+ // These are covered in https://github.com/open-telemetry/semantic-conventions/blob/main/docs/rpc/rpc-metrics.md#attributes:
+ var tcpVersionFourTag = new KeyValuePair("network.type", "ipv4");
+ var tcpVersionSixTag = new KeyValuePair("network.type", "ipv6");
+
+ var measurements = new List>(24);
+
+ // IPv4:
+ TcpStateInfo snapshotV4 = _tcpTableInfo.GetIpV4CachingSnapshot();
+ measurements.Add(new Measurement(snapshotV4.ClosedCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "close") }));
+ measurements.Add(new Measurement(snapshotV4.ListenCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "listen") }));
+ measurements.Add(new Measurement(snapshotV4.SynSentCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "syn_sent") }));
+ measurements.Add(new Measurement(snapshotV4.SynRcvdCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "syn_recv") }));
+ measurements.Add(new Measurement(snapshotV4.EstabCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "established") }));
+ measurements.Add(new Measurement(snapshotV4.FinWait1Count, new TagList { tcpVersionFourTag, new(NetworkStateKey, "fin_wait_1") }));
+ measurements.Add(new Measurement(snapshotV4.FinWait2Count, new TagList { tcpVersionFourTag, new(NetworkStateKey, "fin_wait_2") }));
+ measurements.Add(new Measurement(snapshotV4.CloseWaitCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "close_wait") }));
+ measurements.Add(new Measurement(snapshotV4.ClosingCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "closing") }));
+ measurements.Add(new Measurement(snapshotV4.LastAckCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "last_ack") }));
+ measurements.Add(new Measurement(snapshotV4.TimeWaitCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "time_wait") }));
+
+ // IPv6:
+ TcpStateInfo snapshotV6 = _tcpTableInfo.GetIpV6CachingSnapshot();
+ measurements.Add(new Measurement(snapshotV6.ClosedCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "close") }));
+ measurements.Add(new Measurement(snapshotV6.ListenCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "listen") }));
+ measurements.Add(new Measurement(snapshotV6.SynSentCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "syn_sent") }));
+ measurements.Add(new Measurement(snapshotV6.SynRcvdCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "syn_recv") }));
+ measurements.Add(new Measurement(snapshotV6.EstabCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "established") }));
+ measurements.Add(new Measurement(snapshotV6.FinWait1Count, new TagList { tcpVersionSixTag, new(NetworkStateKey, "fin_wait_1") }));
+ measurements.Add(new Measurement(snapshotV6.FinWait2Count, new TagList { tcpVersionSixTag, new(NetworkStateKey, "fin_wait_2") }));
+ measurements.Add(new Measurement(snapshotV6.CloseWaitCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "close_wait") }));
+ measurements.Add(new Measurement(snapshotV6.ClosingCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "closing") }));
+ measurements.Add(new Measurement(snapshotV6.LastAckCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "last_ack") }));
+ measurements.Add(new Measurement(snapshotV6.TimeWaitCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "time_wait") }));
+
+ return measurements;
+ }
+}
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkUtilizationParser.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkUtilizationParser.cs
new file mode 100644
index 00000000000..48f5ee90c29
--- /dev/null
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkUtilizationParser.cs
@@ -0,0 +1,147 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.ComponentModel;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using Microsoft.Extensions.ObjectPool;
+#if !NET8_0_OR_GREATER
+using Microsoft.Shared.StringSplit;
+#endif
+using Microsoft.Shared.Diagnostics;
+using Microsoft.Shared.Pools;
+
+namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network;
+
+internal class LinuxNetworkUtilizationParser
+{
+ private static readonly ObjectPool> _sharedBufferWriterPool = BufferWriterPool.CreateBufferWriterPool();
+
+ ///
+ /// File that provide information about currently active TCP_IPv4 connections.
+ ///
+ private static readonly FileInfo _tcp = new("/proc/net/tcp");
+
+ ///
+ /// File that provide information about currently active TCP_IPv6 connections.
+ ///
+ private static readonly FileInfo _tcp6 = new("/proc/net/tcp6");
+
+ private readonly IFileSystem _fileSystem;
+
+ ///
+ /// Reads the contents of a file located at _tcp4 and parses it to extract information about the TCP/IP state info on the system.
+ ///
+ public TcpStateInfo GetTcpIPv4StateInfo() => GetTcpStateInfo(_tcp);
+
+ ///
+ /// Reads the contents of a file located at _tcp6 and parses it to extract information about the TCP/IP state info on the system.
+ ///
+ public TcpStateInfo GetTcpIPv6StateInfo() => GetTcpStateInfo(_tcp6);
+
+ public LinuxNetworkUtilizationParser(IFileSystem fileSystem)
+ {
+ _fileSystem = fileSystem;
+ }
+
+ ///
+ /// The method is used in the LinuxUtilizationParser class to read Span data and calculate the TCP state info.
+ /// Refer proc net tcp.
+ ///
+ [SuppressMessage("Major Code Smell", "S109:Magic numbers should not be used",
+ Justification = "We are adding another digit, so we need to multiply by ten.")]
+ private static void UpdateTcpStateInfo(ReadOnlySpan buffer, TcpStateInfo tcpStateInfo)
+ {
+ ReadOnlySpan line = buffer.TrimStart();
+ const int Target = 4;
+
+#if NET8_0_OR_GREATER
+ Span range = stackalloc Range[Target];
+ int numRanges = line.Split(range, ' ');
+#else
+ Span range = stackalloc StringRange[Target];
+ _ = line.TrySplit(" ", range, out int numRanges, StringComparison.OrdinalIgnoreCase, StringSplitOptions.RemoveEmptyEntries);
+#endif
+ if (numRanges < Target)
+ {
+ Throw.InvalidOperationException($"Could not split contents. We expected every line to contain more than {Target - 1} elements, but it has only {numRanges} elements.");
+ }
+
+#if NET8_0_OR_GREATER
+ ReadOnlySpan tcpConnectionState = line.Slice(range[Target - 1].Start.Value, range[Target - 1].End.Value - range[Target - 1].Start.Value);
+#else
+ ReadOnlySpan tcpConnectionState = line.Slice(range[Target - 1].Index, range[Target - 1].Count);
+#endif
+
+ // until this API proposal is implemented https://github.com/dotnet/runtime/issues/61397
+ // we have to allocate & throw away memory using ToString():
+ switch ((LinuxTcpState)Convert.ToInt32(tcpConnectionState.ToString(), 16))
+ {
+ case LinuxTcpState.ESTABLISHED:
+ tcpStateInfo.EstabCount++;
+ break;
+ case LinuxTcpState.SYN_SENT:
+ tcpStateInfo.SynSentCount++;
+ break;
+ case LinuxTcpState.SYN_RECV:
+ tcpStateInfo.SynRcvdCount++;
+ break;
+ case LinuxTcpState.FIN_WAIT1:
+ tcpStateInfo.FinWait1Count++;
+ break;
+ case LinuxTcpState.FIN_WAIT2:
+ tcpStateInfo.FinWait2Count++;
+ break;
+ case LinuxTcpState.TIME_WAIT:
+ tcpStateInfo.TimeWaitCount++;
+ break;
+ case LinuxTcpState.CLOSE:
+ tcpStateInfo.ClosedCount++;
+ break;
+ case LinuxTcpState.CLOSE_WAIT:
+ tcpStateInfo.CloseWaitCount++;
+ break;
+ case LinuxTcpState.LAST_ACK:
+ tcpStateInfo.LastAckCount++;
+ break;
+ case LinuxTcpState.LISTEN:
+ tcpStateInfo.ListenCount++;
+ break;
+ case LinuxTcpState.CLOSING:
+ tcpStateInfo.ClosingCount++;
+ break;
+ default:
+ throw new InvalidEnumArgumentException($"Cannot find status: {tcpConnectionState} in LinuxTcpState");
+ }
+ }
+
+ ///
+ /// Reads the contents of a file located at file and parses it to extract information about the TCP/IP state info on the system.
+ ///
+ private TcpStateInfo GetTcpStateInfo(FileInfo file)
+ {
+ // The value we are interested in starts with this. We just want to make sure it is true.
+ const string Sl = "sl";
+ var tcpStateInfo = new TcpStateInfo();
+ using ReturnableBufferWriter bufferWriter = new(_sharedBufferWriterPool);
+ using var enumerableLines = _fileSystem.ReadAllByLines(file, bufferWriter.Buffer).GetEnumerator();
+ if (!enumerableLines.MoveNext())
+ {
+ Throw.InvalidOperationException($"Could not parse '{file}'. File was empty.");
+ }
+
+ var firstLine = enumerableLines.Current.TrimStart().Span;
+ if (!firstLine.StartsWith(Sl, StringComparison.Ordinal))
+ {
+ Throw.InvalidOperationException($"Could not parse '{file}'. We expected first line of the file to start with '{Sl}' but it was '{firstLine.ToString()}' instead.");
+ }
+
+ while (enumerableLines.MoveNext())
+ {
+ UpdateTcpStateInfo(enumerableLines.Current.Span, tcpStateInfo);
+ }
+
+ return tcpStateInfo;
+ }
+}
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxTcpState.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxTcpState.cs
new file mode 100644
index 00000000000..3f9ea5acc1d
--- /dev/null
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxTcpState.cs
@@ -0,0 +1,43 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network;
+
+///
+/// Enumerates all possible TCP states on Linux.
+///
+internal enum LinuxTcpState
+{
+ /// The TCP connection was established.
+ ESTABLISHED = 1,
+
+ /// The TCP connection has sent a SYN packet.
+ SYN_SENT = 2,
+
+ /// The TCP connection has received a SYN packet.
+ SYN_RECV = 3,
+
+ /// The TCP connection is waiting for a FIN packet.
+ FIN_WAIT1 = 4,
+
+ /// The TCP connection is waiting for a FIN packet.
+ FIN_WAIT2 = 5,
+
+ /// The TCP connection is in the time wait state.
+ TIME_WAIT = 6,
+
+ /// The TCP connection is closed.
+ CLOSE = 7,
+
+ /// The TCP connection is in the close wait state.
+ CLOSE_WAIT = 8,
+
+ /// The TCP connection is in the last ACK state.
+ LAST_ACK = 9,
+
+ /// The TCP connection is in the listen state.
+ LISTEN = 10,
+
+ /// The TCP connection is closing.
+ CLOSING = 11
+}
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxTcpTableInfo.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxTcpTableInfo.cs
new file mode 100644
index 00000000000..69b796b1e7a
--- /dev/null
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxTcpTableInfo.cs
@@ -0,0 +1,51 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using Microsoft.Extensions.Options;
+
+namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network;
+
+internal class LinuxTcpTableInfo : ITcpTableInfo
+{
+ private readonly object _lock = new();
+ private readonly TimeSpan _samplingInterval;
+ private readonly LinuxNetworkUtilizationParser _parser;
+ private readonly TimeProvider _timeProvider;
+
+ private TcpStateInfo _iPv4Snapshot = new();
+ private TcpStateInfo _iPv6Snapshot = new();
+ private DateTimeOffset _refreshAfter;
+
+ public LinuxTcpTableInfo(IOptions options, LinuxNetworkUtilizationParser parser, TimeProvider timeProvider)
+ {
+ _samplingInterval = options.Value.SamplingInterval;
+ _parser = parser;
+ _timeProvider = timeProvider;
+ }
+
+ public TcpStateInfo GetIpV4CachingSnapshot()
+ {
+ RefreshSnapshotIfNeeded();
+ return _iPv4Snapshot;
+ }
+
+ public TcpStateInfo GetIpV6CachingSnapshot()
+ {
+ RefreshSnapshotIfNeeded();
+ return _iPv6Snapshot;
+ }
+
+ private void RefreshSnapshotIfNeeded()
+ {
+ lock (_lock)
+ {
+ if (_refreshAfter < _timeProvider.GetUtcNow())
+ {
+ _iPv4Snapshot = _parser.GetTcpIPv4StateInfo();
+ _iPv6Snapshot = _parser.GetTcpIPv6StateInfo();
+ _refreshAfter = _timeProvider.GetUtcNow().Add(_samplingInterval);
+ }
+ }
+ }
+}
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/OSFileSystem.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/OSFileSystem.cs
index b49f6d77df5..010dea4c9f0 100644
--- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/OSFileSystem.cs
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/OSFileSystem.cs
@@ -2,10 +2,12 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
+using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Shared.Pools;
@@ -45,6 +47,55 @@ public void ReadFirstLine(FileInfo file, BufferWriter destination)
public void ReadAll(FileInfo file, BufferWriter destination)
=> ReadUntilTerminatorOrEnd(file, destination, null);
+ public IEnumerable> ReadAllByLines(FileInfo file, BufferWriter destination)
+ {
+ const int MaxStackalloc = 256;
+
+ if (!file.Exists)
+ {
+ throw new FileNotFoundException();
+ }
+
+ using FileStream stream = file.OpenRead();
+
+ Memory buffer = ArrayPool.Shared.Rent(MaxStackalloc);
+ int read = stream.Read(buffer.Span);
+ while (read > 0)
+ {
+ var start = 0;
+ var end = 0;
+
+ for (end = 0; end < read; end++)
+ {
+ if (buffer.Span[end] == (byte)'\n')
+ {
+ var length = end - start;
+ _ = Encoding.ASCII.GetChars(buffer.Span.Slice(start, length), destination.GetSpan(length));
+ destination.Advance(length);
+ start = end + 1;
+ yield return destination.WrittenMemory;
+ destination.Reset();
+ }
+ }
+
+ // Set the comparison in the while loop to end when the file has not been completely read into the buffer.
+ // It will then advance the last character to the destination for the next time yield return is called.
+ if (start < read)
+ {
+ var length = read - start;
+ _ = Encoding.ASCII.GetChars(buffer.Span.Slice(start, length), destination.GetSpan(length));
+ destination.Advance(length);
+ }
+
+ read = stream.Read(buffer.Span);
+ }
+
+ if (MemoryMarshal.TryGetArray(buffer, out ArraySegment arraySegment) && arraySegment.Array != null)
+ {
+ ArrayPool.Shared.Return(arraySegment.Array);
+ }
+ }
+
[SkipLocalsInit]
private static void ReadUntilTerminatorOrEnd(FileInfo file, BufferWriter destination, byte? terminator)
{
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Microsoft.Extensions.Diagnostics.ResourceMonitoring.csproj b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Microsoft.Extensions.Diagnostics.ResourceMonitoring.csproj
index beffa2e9ac8..a98c18191be 100644
--- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Microsoft.Extensions.Diagnostics.ResourceMonitoring.csproj
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Microsoft.Extensions.Diagnostics.ResourceMonitoring.csproj
@@ -1,4 +1,4 @@
-
+Microsoft.Extensions.Diagnostics.ResourceMonitoringMeasures processor and memory usage.
@@ -14,6 +14,7 @@
truetruetrue
+ true
@@ -23,7 +24,7 @@
-
+
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringServiceCollectionExtensions.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringServiceCollectionExtensions.cs
index f370d3bb289..83db13a3cd4 100644
--- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringServiceCollectionExtensions.cs
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringServiceCollectionExtensions.cs
@@ -7,6 +7,8 @@
using Microsoft.Extensions.Diagnostics.ResourceMonitoring;
#if !NETFRAMEWORK
using Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux;
+using Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network;
+
#endif
using Microsoft.Extensions.Diagnostics.ResourceMonitoring.Windows;
using Microsoft.Extensions.Diagnostics.ResourceMonitoring.Windows.Interop;
@@ -91,7 +93,7 @@ private static ResourceMonitorBuilder AddWindowsProvider(this ResourceMonitorBui
.AddActivatedSingleton();
_ = builder.Services
- .AddActivatedSingleton();
+ .AddActivatedSingleton();
return builder;
}
@@ -120,6 +122,12 @@ private static ResourceMonitorBuilder AddLinuxProvider(this ResourceMonitorBuild
builder.Services.TryAddSingleton();
builder.PickLinuxParser();
+ _ = builder.Services
+ .AddActivatedSingleton();
+
+ _ = builder.Services
+ .AddActivatedSingleton();
+
return builder;
}
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceUtilizationInstruments.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceUtilizationInstruments.cs
index aa3fd012029..1669967f41a 100644
--- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceUtilizationInstruments.cs
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceUtilizationInstruments.cs
@@ -53,4 +53,12 @@ internal static class ResourceUtilizationInstruments
/// The type of an instrument is .
///
public const string ProcessMemoryUtilization = "dotnet.process.memory.virtual.utilization";
+
+ ///
+ /// The name of an instrument to retrieve network connections information.
+ ///
+ ///
+ /// The type of an instrument is .
+ ///
+ public const string SystemNetworkConnections = "system.network.connections";
}
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Network/TcpStateInfo.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/TcpStateInfo.cs
similarity index 95%
rename from src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Network/TcpStateInfo.cs
rename to src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/TcpStateInfo.cs
index 245ebfd5e37..83dbe92af86 100644
--- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Network/TcpStateInfo.cs
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/TcpStateInfo.cs
@@ -1,7 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Windows.Network;
+namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring;
///
/// TcpStateInfo contains different possible TCP state counts.
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/WindowsNetworkMetrics.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Network/WindowsNetworkMetrics.cs
similarity index 91%
rename from src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/WindowsNetworkMetrics.cs
rename to src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Network/WindowsNetworkMetrics.cs
index 115f7c95962..810eba9c2a2 100644
--- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/WindowsNetworkMetrics.cs
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Network/WindowsNetworkMetrics.cs
@@ -4,15 +4,14 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Metrics;
-using Microsoft.Extensions.Diagnostics.ResourceMonitoring.Windows.Network;
-namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Windows;
+namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Windows.Network;
internal sealed class WindowsNetworkMetrics
{
- private readonly TcpTableInfo _tcpTableInfo;
+ private readonly ITcpTableInfo _tcpTableInfo;
- public WindowsNetworkMetrics(IMeterFactory meterFactory, TcpTableInfo tcpTableInfo)
+ public WindowsNetworkMetrics(IMeterFactory meterFactory, ITcpTableInfo tcpTableInfo)
{
_tcpTableInfo = tcpTableInfo;
@@ -20,7 +19,7 @@ public WindowsNetworkMetrics(IMeterFactory meterFactory, TcpTableInfo tcpTableIn
// We don't dispose the meter because IMeterFactory handles that
// An issue on analyzer side: https://github.com/dotnet/roslyn-analyzers/issues/6912
// Related documentation: https://github.com/dotnet/docs/pull/37170
- var meter = meterFactory.Create(nameof(Microsoft.Extensions.Diagnostics.ResourceMonitoring));
+ var meter = meterFactory.Create(nameof(ResourceMonitoring));
#pragma warning restore CA2000 // Dispose objects before losing scope
var tcpTag = new KeyValuePair("network.transport", "tcp");
@@ -31,9 +30,8 @@ public WindowsNetworkMetrics(IMeterFactory meterFactory, TcpTableInfo tcpTableIn
// The metric is aligned with
// https://github.com/open-telemetry/semantic-conventions/blob/main/docs/system/system-metrics.md#metric-systemnetworkconnections
-
_ = meter.CreateObservableUpDownCounter(
- "system.network.connections",
+ ResourceUtilizationInstruments.SystemNetworkConnections,
GetMeasurements,
unit: "{connection}",
description: null,
@@ -51,7 +49,7 @@ private IEnumerable> GetMeasurements()
var measurements = new List>(24);
// IPv4:
- var snapshotV4 = _tcpTableInfo.GetIPv4CachingSnapshot();
+ var snapshotV4 = _tcpTableInfo.GetIpV4CachingSnapshot();
measurements.Add(new Measurement(snapshotV4.ClosedCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "close") }));
measurements.Add(new Measurement(snapshotV4.ListenCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "listen") }));
measurements.Add(new Measurement(snapshotV4.SynSentCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "syn_sent") }));
@@ -66,7 +64,7 @@ private IEnumerable> GetMeasurements()
measurements.Add(new Measurement(snapshotV4.DeleteTcbCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "delete") }));
// IPv6:
- var snapshotV6 = _tcpTableInfo.GetIPv6CachingSnapshot();
+ var snapshotV6 = _tcpTableInfo.GetIpV6CachingSnapshot();
measurements.Add(new Measurement(snapshotV6.ClosedCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "close") }));
measurements.Add(new Measurement(snapshotV6.ListenCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "listen") }));
measurements.Add(new Measurement(snapshotV6.SynSentCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "syn_sent") }));
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Network/TcpTableInfo.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Network/WindowsTcpTableInfo.cs
similarity index 97%
rename from src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Network/TcpTableInfo.cs
rename to src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Network/WindowsTcpTableInfo.cs
index ef8f7ed617b..e203f5f0828 100644
--- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Network/TcpTableInfo.cs
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Network/WindowsTcpTableInfo.cs
@@ -11,7 +11,7 @@
namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Windows.Network;
-internal sealed class TcpTableInfo
+internal sealed class WindowsTcpTableInfo : ITcpTableInfo
{
private readonly object _lock = new();
private readonly FrozenSet _localIPAddresses;
@@ -25,7 +25,7 @@ internal sealed class TcpTableInfo
private static TimeProvider TimeProvider => TimeProvider.System;
private DateTimeOffset _refreshAfter;
- public TcpTableInfo(IOptions options)
+ public WindowsTcpTableInfo(IOptions options)
{
var stringAddresses = options.Value.SourceIpAddresses;
_localIPAddresses = stringAddresses
@@ -42,13 +42,13 @@ public TcpTableInfo(IOptions options)
_refreshAfter = default;
}
- public TcpStateInfo GetIPv4CachingSnapshot()
+ public TcpStateInfo GetIpV4CachingSnapshot()
{
RefreshSnapshotIfNeeded();
return _iPv4Snapshot;
}
- public TcpStateInfo GetIPv6CachingSnapshot()
+ public TcpStateInfo GetIpV6CachingSnapshot()
{
RefreshSnapshotIfNeeded();
return _iPv6Snapshot;
diff --git a/src/Shared/BufferWriterPool/BufferWriter.cs b/src/Shared/BufferWriterPool/BufferWriter.cs
index 18269616f49..f8dc2b7c42a 100644
--- a/src/Shared/BufferWriterPool/BufferWriter.cs
+++ b/src/Shared/BufferWriterPool/BufferWriter.cs
@@ -8,9 +8,7 @@
using System.Runtime.CompilerServices;
#endif
-#pragma warning disable CA1716
namespace Microsoft.Shared.Pools;
-#pragma warning restore CA1716
///
/// Represents an output sink into which data can be written.
diff --git a/src/Shared/BufferWriterPool/BufferWriterPooledObjectPolicy.cs b/src/Shared/BufferWriterPool/BufferWriterPooledObjectPolicy.cs
index 50533f74b2b..d11d41a6930 100644
--- a/src/Shared/BufferWriterPool/BufferWriterPooledObjectPolicy.cs
+++ b/src/Shared/BufferWriterPool/BufferWriterPooledObjectPolicy.cs
@@ -4,9 +4,7 @@
using Microsoft.Extensions.ObjectPool;
using Microsoft.Shared.Diagnostics;
-#pragma warning disable CA1716
namespace Microsoft.Shared.Pools;
-#pragma warning restore CA1716
///
/// An object pooling policy designed for .
diff --git a/src/Shared/Data.Validation/TimeSpanAttribute.cs b/src/Shared/Data.Validation/TimeSpanAttribute.cs
index 8830a4120e5..bc181e645cf 100644
--- a/src/Shared/Data.Validation/TimeSpanAttribute.cs
+++ b/src/Shared/Data.Validation/TimeSpanAttribute.cs
@@ -7,9 +7,7 @@
using System.Globalization;
using Microsoft.Shared.Diagnostics;
-#pragma warning disable CA1716
namespace Microsoft.Shared.Data.Validation;
-#pragma warning restore CA1716
///
/// Provides boundary validation for .
diff --git a/src/Shared/Debugger/DebuggerExtensions.cs b/src/Shared/Debugger/DebuggerExtensions.cs
index cc197be12c2..4aa63ee590a 100644
--- a/src/Shared/Debugger/DebuggerExtensions.cs
+++ b/src/Shared/Debugger/DebuggerExtensions.cs
@@ -5,9 +5,7 @@
using Microsoft.Extensions.DependencyInjection.Extensions;
using Throw = Microsoft.Shared.Diagnostics.Throw;
-#pragma warning disable CA1716
namespace Microsoft.Shared.Diagnostics;
-#pragma warning restore CA1716
///
/// Adds debugger to DI container.
diff --git a/src/Shared/RentedSpan/RentedSpan.cs b/src/Shared/RentedSpan/RentedSpan.cs
index c7b429b0b67..efebc2e2ffe 100644
--- a/src/Shared/RentedSpan/RentedSpan.cs
+++ b/src/Shared/RentedSpan/RentedSpan.cs
@@ -5,9 +5,7 @@
using System.Buffers;
using System.Runtime.CompilerServices;
-#pragma warning disable CA1716
namespace Microsoft.Shared.Pools;
-#pragma warning restore CA1716
///
/// Represents a span that's potentially created over a rented array.
diff --git a/src/Shared/StringSplit/README.md b/src/Shared/StringSplit/README.md
new file mode 100644
index 00000000000..9c9f1110e7e
--- /dev/null
+++ b/src/Shared/StringSplit/README.md
@@ -0,0 +1,11 @@
+# Numeric Extensions
+
+`StringSplit` API to get allocation free string splitting for .NET runtime before .NET 8
+
+To use this in your project, add the following to your `.csproj` file:
+
+```xml
+
+ true
+
+```
diff --git a/src/Shared/StringSplit/StringRange.cs b/src/Shared/StringSplit/StringRange.cs
new file mode 100644
index 00000000000..2acd9317f77
--- /dev/null
+++ b/src/Shared/StringSplit/StringRange.cs
@@ -0,0 +1,164 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#if !NET8_0_OR_GREATER
+
+using System;
+using Microsoft.Shared.Diagnostics;
+
+namespace Microsoft.Shared.StringSplit;
+
+#if !SHARED_PROJECT
+[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
+#endif
+internal readonly struct StringRange : IComparable, IComparable, IEquatable
+{
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// Starting index of the segment.
+ /// Number of characters in the segment.
+ public StringRange(int index, int count)
+ {
+ _ = Throw.IfLessThan(index, 0);
+ _ = Throw.IfLessThan(count, 0);
+
+ Index = index;
+ Count = count;
+ }
+
+ ///
+ /// Gets the starting index of the string.
+ ///
+ public int Index { get; }
+
+ ///
+ /// Gets the number of characters in the segment.
+ ///
+ public int Count { get; }
+
+ ///
+ /// Compare current instance of to another.
+ ///
+ /// Segment to compare.
+ ///
+ /// Returns a value less than zero if this less than other, zero if this equal to other,
+ /// or a value greater than zero if this greater than other.
+ ///
+ public int CompareTo(StringRange other) => Index.CompareTo(other.Index);
+
+ ///
+ /// Compare current instance of to another object.
+ ///
+ /// Segment to compare.
+ ///
+ /// Returns a value less than zero if this less than other, zero if this equal to other,
+ /// or a value greater than zero if this greater than other.
+ /// Null is considered to be less than any instance.
+ ///
+ ///
+ /// If object is not of same type.
+ ///
+ public int CompareTo(object? obj)
+ {
+ if (obj is StringRange ss)
+ {
+ return CompareTo(ss);
+ }
+
+ if (obj != null)
+ {
+ Throw.ArgumentException(nameof(obj), $"Provided value must be of type {typeof(StringRange)}, but was of type {obj.GetType()}.");
+ }
+
+ return 1;
+ }
+
+ ///
+ /// Compares two string segments.
+ ///
+ /// Segment to compare.
+ /// when equal, otherwise.
+ public bool Equals(StringRange other) => other.Index == Index && other.Count == Count;
+
+ ///
+ /// Compares two string segments.
+ ///
+ /// Segment to compare.
+ /// when equal, otherwise.
+ public override bool Equals(object? obj) => obj is StringRange ss && Equals(ss);
+
+ ///
+ /// Returns the hashcode for this instance.
+ ///
+ /// Hash code.
+ public override int GetHashCode() => HashCode.Combine(Index, Count);
+
+ ///
+ /// Compares two string segments.
+ ///
+ /// Left segment to compare.
+ /// Right segment to compare.
+ /// when equal, otherwise.
+ public static bool operator ==(StringRange left, StringRange right)
+ {
+ return left.Equals(right);
+ }
+
+ ///
+ /// Compares two string segments.
+ ///
+ /// Left segment to compare.
+ /// Right segment to compare.
+ /// when not equal, otherwise.
+ public static bool operator !=(StringRange left, StringRange right)
+ {
+ return !(left == right);
+ }
+
+ ///
+ /// Compares two string segments.
+ ///
+ /// Left segment to compare.
+ /// Right segment to compare.
+ /// when first segment is before the second, otherwise.
+ public static bool operator <(StringRange left, StringRange right)
+ {
+ return left.Index < right.Index;
+ }
+
+ ///
+ /// Compares two string segments.
+ ///
+ /// Left segment to compare.
+ /// Right segment to compare.
+ /// when first segment is after the second, otherwise.
+ public static bool operator >(StringRange left, StringRange right)
+ {
+ return left.Index > right.Index;
+ }
+
+ ///
+ /// Compares two string segments.
+ ///
+ /// Left segment to compare.
+ /// Right segment to compare.
+ /// when first segment is before or at the same index as the second, otherwise.
+ public static bool operator <=(StringRange left, StringRange right)
+ {
+ return left.Index <= right.Index;
+ }
+
+ ///
+ /// Compares two string segments.
+ ///
+ /// Left segment to compare.
+ /// Right segment to compare.
+ /// when first segment is at the same index or after the second, otherwise.
+ public static bool operator >=(StringRange left, StringRange right)
+ {
+ return left.Index >= right.Index;
+ }
+}
+
+#endif
diff --git a/src/Shared/StringSplit/StringSplitExtensions.cs b/src/Shared/StringSplit/StringSplitExtensions.cs
new file mode 100644
index 00000000000..cbc8aedc805
--- /dev/null
+++ b/src/Shared/StringSplit/StringSplitExtensions.cs
@@ -0,0 +1,891 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#if !NET8_0_OR_GREATER
+
+using System;
+using Microsoft.Shared.Diagnostics;
+
+namespace Microsoft.Shared.StringSplit;
+
+#if !SHARED_PROJECT
+[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
+#endif
+internal static class StringSplitExtensions
+{
+ ///
+ /// Splits a string into a number of string segments.
+ ///
+ /// The string to split.
+ /// A character that delimits the substrings in this instance.
+ /// A span to receive the individual string segments.
+ /// The number of string segments copied to the output.
+ /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
+ /// if there was enough space in the output array, otherwise .
+ public static bool TrySplit(
+ this ReadOnlySpan input,
+ char separator,
+ Span result,
+ out int numSegments,
+ StringSplitOptions options = StringSplitOptions.None)
+ {
+ const int SeparatorLen = 1;
+
+ CheckStringSplitOptions(options);
+
+ numSegments = 0;
+
+ int start = 0;
+ while (true)
+ {
+ int index = input.Slice(start).IndexOf(separator);
+ var sp = index < 0 ? input.Slice(start) : input.Slice(start, index);
+
+ var rangeStart = start;
+#if NET5_0_OR_GREATER
+ if ((options & StringSplitOptions.TrimEntries) != 0)
+ {
+ var len = sp.Length;
+ sp = sp.TrimStart();
+ rangeStart = start + len - sp.Length;
+ sp = sp.TrimEnd();
+ }
+#endif
+
+ if (sp.Length > 0 || (options & StringSplitOptions.RemoveEmptyEntries) == 0)
+ {
+ if (numSegments >= result.Length)
+ {
+ return false;
+ }
+
+ result[numSegments++] = new StringRange(rangeStart, sp.Length);
+ }
+
+ if (index < 0)
+ {
+ return true;
+ }
+
+ start += index + SeparatorLen;
+ }
+ }
+
+ ///
+ /// Splits a string into a number of string segments.
+ ///
+ /// The string to split.
+ /// The characters that delimit the substrings in this instance. This is not treated as a string, this is used as an array of individual characters.
+ /// A span to receive the individual string segments.
+ /// The number of string segments copied to the output.
+ /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
+ /// if there was enough space in the output array, otherwise .
+ public static bool TrySplit(
+ this ReadOnlySpan input,
+ ReadOnlySpan separators,
+ Span result,
+ out int numSegments,
+ StringSplitOptions options = StringSplitOptions.None)
+ {
+ const int SeparatorLen = 1;
+
+ CheckStringSplitOptions(options);
+
+ numSegments = 0;
+
+ int start = 0;
+ while (true)
+ {
+ int index = input.Slice(start).IndexOfAny(separators);
+ var sp = index < 0 ? input.Slice(start) : input.Slice(start, index);
+
+ var rangeStart = start;
+#if NET5_0_OR_GREATER
+ if ((options & StringSplitOptions.TrimEntries) != 0)
+ {
+ var len = sp.Length;
+ sp = sp.TrimStart();
+ rangeStart = start + len - sp.Length;
+ sp = sp.TrimEnd();
+ }
+#endif
+
+ if (sp.Length > 0 || (options & StringSplitOptions.RemoveEmptyEntries) == 0)
+ {
+ if (numSegments >= result.Length)
+ {
+ return false;
+ }
+
+ result[numSegments++] = new StringRange(rangeStart, sp.Length);
+ }
+
+ if (index < 0)
+ {
+ return true;
+ }
+
+ start += index + SeparatorLen;
+ }
+ }
+
+ ///
+ /// Splits a string into a number of string segments.
+ ///
+ /// The string to split.
+ /// The strings that delimit the substrings in this instance.
+ /// A span to receive the individual string segments.
+ /// The number of string segments copied to the output.
+ /// The kind of string comparison to apply to the separator strings.
+ /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
+ /// if there was enough space in the output array, otherwise .
+ public static bool TrySplit(
+ this ReadOnlySpan input,
+ string[] separators,
+ Span result,
+ out int numSegments,
+ StringComparison comparison = StringComparison.Ordinal,
+ StringSplitOptions options = StringSplitOptions.None)
+ {
+ _ = Throw.IfNull(separators);
+ CheckStringSplitOptions(options);
+
+ numSegments = 0;
+
+ int start = 0;
+ while (true)
+ {
+ int index = -1;
+ int separatorLen = 0;
+ foreach (var sep in separators)
+ {
+ int found = input.Slice(start).IndexOf(sep.AsSpan(), comparison);
+ if (found >= 0)
+ {
+ if (found < index || index < 0)
+ {
+ separatorLen = sep.Length;
+ index = found;
+ }
+ }
+ }
+
+ var sp = index < 0 ? input.Slice(start) : input.Slice(start, index);
+
+ var rangeStart = start;
+#if NET5_0_OR_GREATER
+ if ((options & StringSplitOptions.TrimEntries) != 0)
+ {
+ var len = sp.Length;
+ sp = sp.TrimStart();
+ rangeStart = start + len - sp.Length;
+ sp = sp.TrimEnd();
+ }
+#endif
+
+ if (sp.Length > 0 || (options & StringSplitOptions.RemoveEmptyEntries) == 0)
+ {
+ if (numSegments >= result.Length)
+ {
+ return false;
+ }
+
+ result[numSegments++] = new StringRange(rangeStart, sp.Length);
+ }
+
+ if (index < 0)
+ {
+ return true;
+ }
+
+ start += index + separatorLen;
+ }
+ }
+
+ ///
+ /// Splits a string into a number of string segments.
+ ///
+ /// The string to split.
+ /// The string that delimits the substrings in this instance.
+ /// A span to receive the individual string segments.
+ /// The number of string segments copied to the output.
+ /// The kind of string comparison to apply to the separator strings.
+ /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
+ /// if there was enough space in the output array, otherwise .
+ public static bool TrySplit(
+ this ReadOnlySpan input,
+ string separator,
+ Span result,
+ out int numSegments,
+ StringComparison comparison = StringComparison.Ordinal,
+ StringSplitOptions options = StringSplitOptions.None)
+ {
+ _ = Throw.IfNull(separator);
+ CheckStringSplitOptions(options);
+
+ numSegments = 0;
+
+ int start = 0;
+ while (true)
+ {
+ int index = -1;
+ int separatorLen = 0;
+
+ int found = input.Slice(start).IndexOf(separator.AsSpan(), comparison);
+ if (found >= 0)
+ {
+ if (found < index || index < 0)
+ {
+ separatorLen = separator.Length;
+ index = found;
+ }
+ }
+
+ var sp = index < 0 ? input.Slice(start) : input.Slice(start, index);
+
+ var rangeStart = start;
+#if NET5_0_OR_GREATER
+ if ((options & StringSplitOptions.TrimEntries) != 0)
+ {
+ var len = sp.Length;
+ sp = sp.TrimStart();
+ rangeStart = start + len - sp.Length;
+ sp = sp.TrimEnd();
+ }
+#endif
+
+ if (sp.Length > 0 || (options & StringSplitOptions.RemoveEmptyEntries) == 0)
+ {
+ if (numSegments >= result.Length)
+ {
+ return false;
+ }
+
+ result[numSegments++] = new StringRange(rangeStart, sp.Length);
+ }
+
+ if (index < 0)
+ {
+ return true;
+ }
+
+ start += index + separatorLen;
+ }
+ }
+
+ ///
+ /// Splits a string into a number of string segments.
+ ///
+ /// The string to split.
+ /// A span to receive the individual string segments.
+ /// The number of string segments copied to the output.
+ /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
+ /// if there was enough space in the output array, otherwise .
+ /// This uses whitespace as a separator of individual substrings.
+ public static bool TrySplit(
+ this ReadOnlySpan input,
+ Span result,
+ out int numSegments,
+ StringSplitOptions options = StringSplitOptions.None)
+ {
+ const int SeparatorLen = 1;
+
+ CheckStringSplitOptions(options);
+
+ numSegments = 0;
+
+ int start = 0;
+ while (true)
+ {
+ int index = -1;
+ for (int i = start; i < input.Length; i++)
+ {
+ if (char.IsWhiteSpace(input[i]))
+ {
+ index = i - start;
+ break;
+ }
+ }
+
+ var sp = index < 0 ? input.Slice(start) : input.Slice(start, index);
+
+ var rangeStart = start;
+#if NET5_0_OR_GREATER
+ if ((options & StringSplitOptions.TrimEntries) != 0)
+ {
+ var len = sp.Length;
+ sp = sp.TrimStart();
+ rangeStart = start + len - sp.Length;
+ sp = sp.TrimEnd();
+ }
+#endif
+
+ if (sp.Length > 0 || (options & StringSplitOptions.RemoveEmptyEntries) == 0)
+ {
+ if (numSegments >= result.Length)
+ {
+ return false;
+ }
+
+ result[numSegments++] = new StringRange(rangeStart, sp.Length);
+ }
+
+ if (index < 0)
+ {
+ return true;
+ }
+
+ start += index + SeparatorLen;
+ }
+ }
+
+ ///
+ /// Splits a string into a number of string segments.
+ ///
+ /// The string to split.
+ /// A character that delimits the substrings in this instance.
+ /// A span to receive the individual string segments.
+ /// The number of string segments copied to the output.
+ /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
+ /// if there was enough space in the output array, otherwise .
+ public static bool TrySplit(
+ this string input,
+ char separator,
+ Span result,
+ out int numSegments,
+ StringSplitOptions options = StringSplitOptions.None)
+ => TrySplit(input.AsSpan(), separator, result, out numSegments, options);
+
+ ///
+ /// Splits a string into a number of string segments.
+ ///
+ /// The string to split.
+ /// The characters that delimit the substrings in this instance. This is not treated as a string, this is used as an array of individual characters.
+ /// A span to receive the individual string segments.
+ /// The number of string segments copied to the output.
+ /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
+ /// if there was enough space in the output array, otherwise .
+ public static bool TrySplit(
+ this string input,
+ ReadOnlySpan separators,
+ Span result,
+ out int numSegments,
+ StringSplitOptions options = StringSplitOptions.None)
+ => TrySplit(input.AsSpan(), separators, result, out numSegments, options);
+
+ ///
+ /// Splits a string into a number of string segments.
+ ///
+ /// The string to split.
+ /// The strings that delimit the substrings in this instance.
+ /// A span to receive the individual string segments.
+ /// The number of string segments copied to the output.
+ /// The kind of string comparison to apply to the separator strings.
+ /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
+ /// if there was enough space in the output array, otherwise .
+ public static bool TrySplit(
+ this string input,
+ string[] separators,
+ Span result,
+ out int numSegments,
+ StringComparison comparison = StringComparison.Ordinal,
+ StringSplitOptions options = StringSplitOptions.None)
+ => TrySplit(input.AsSpan(), separators, result, out numSegments, comparison, options);
+
+ ///
+ /// Splits a string into a number of string segments.
+ ///
+ /// The string to split.
+ /// The string that delimits the substrings in this instance.
+ /// A span to receive the individual string segments.
+ /// The number of string segments copied to the output.
+ /// The kind of string comparison to apply to the separator strings.
+ /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
+ /// if there was enough space in the output array, otherwise .
+ public static bool TrySplit(
+ this string input,
+ string separator,
+ Span result,
+ out int numSegments,
+ StringComparison comparison = StringComparison.Ordinal,
+ StringSplitOptions options = StringSplitOptions.None)
+ => TrySplit(input.AsSpan(), separator, result, out numSegments, comparison, options);
+
+ ///
+ /// Splits a string into a number of string segments.
+ ///
+ /// The string to split.
+ /// A span to receive the individual string segments.
+ /// The number of string segments copied to the output.
+ /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
+ /// if there was enough space in the output array, otherwise .
+ /// This uses whitespace as a separator of individual substrings.
+ public static bool TrySplit(
+ this string input,
+ Span result,
+ out int numSegments,
+ StringSplitOptions options = StringSplitOptions.None) => TrySplit(input.AsSpan(), result, out numSegments, options);
+
+ private static void CheckStringSplitOptions(StringSplitOptions options)
+ {
+#if NET5_0_OR_GREATER
+ const StringSplitOptions AllValidFlags = StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries;
+#else
+ const StringSplitOptions AllValidFlags = StringSplitOptions.RemoveEmptyEntries;
+#endif
+
+ if ((options & ~AllValidFlags) != 0)
+ {
+ // at least one invalid flag was set
+ Throw.ArgumentException(nameof(options), "Invalid split options specified");
+ }
+ }
+
+ ///
+ /// The delegate that gets invoked when visiting the splits of a string.
+ ///
+ /// Type of the context value given to the delegate.
+ /// The span of characters that makes up the split.
+ /// The monotonically increasing split count.
+ /// The user-defined context object.
+ public delegate void SplitVisitor(ReadOnlySpan split, int segmentNum, T context);
+
+ ///
+ /// Invokes a delegate for individual string segments.
+ ///
+ /// The string to split.
+ /// A character that delimits the substrings in this instance.
+ /// An object that can be used to pass state to the visitor.
+ /// A delegate that gets invoked for each individual segment.
+ /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
+ /// The type of the visitor's context.
+ ///
+ /// The visitor delegate is invoked for each segment in the input. It is given as parameter the
+ /// value of the argument, the segment index, and a range for the segment.
+ ///
+ public static void VisitSplits(
+ this ReadOnlySpan input,
+ char separator,
+ TContext context,
+ SplitVisitor visitor,
+ StringSplitOptions options = StringSplitOptions.None)
+ {
+ const int SeparatorLen = 1;
+
+ _ = Throw.IfNull(visitor);
+ CheckStringSplitOptions(options);
+
+ int numSegments = 0;
+ int start = 0;
+ while (true)
+ {
+ int index = input.Slice(start).IndexOf(separator);
+ var sp = index < 0 ? input.Slice(start) : input.Slice(start, index);
+
+ var rangeStart = start;
+#if NET5_0_OR_GREATER
+ if ((options & StringSplitOptions.TrimEntries) != 0)
+ {
+ var len = sp.Length;
+ sp = sp.TrimStart();
+ rangeStart = start + len - sp.Length;
+ sp = sp.TrimEnd();
+ }
+#endif
+
+ if (sp.Length > 0 || (options & StringSplitOptions.RemoveEmptyEntries) == 0)
+ {
+ visitor(input.Slice(rangeStart, sp.Length), numSegments++, context);
+ }
+
+ if (index < 0)
+ {
+ return;
+ }
+
+ start += index + SeparatorLen;
+ }
+ }
+
+ ///
+ /// Invokes a delegate for individual string segments.
+ ///
+ /// The string to split.
+ /// The characters that delimit the substrings in this instance.
+ /// An object that can be used to pass state to the visitor.
+ /// A delegate that gets invoked for each individual segment.
+ /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
+ /// The type of the visitor's context.
+ ///
+ /// The visitor delegate is invoked for each segment in the input. It is given as parameter the
+ /// value of the argument, the segment index, and a range for the segment.
+ ///
+ public static void VisitSplits(
+ this ReadOnlySpan input,
+ ReadOnlySpan separators,
+ TContext context,
+ SplitVisitor visitor,
+ StringSplitOptions options = StringSplitOptions.None)
+ {
+ const int SeparatorLen = 1;
+
+ _ = Throw.IfNull(visitor);
+ CheckStringSplitOptions(options);
+
+ int numSegments = 0;
+ int start = 0;
+ while (true)
+ {
+ int index = input.Slice(start).IndexOfAny(separators);
+ var sp = index < 0 ? input.Slice(start) : input.Slice(start, index);
+
+ var rangeStart = start;
+#if NET5_0_OR_GREATER
+ if ((options & StringSplitOptions.TrimEntries) != 0)
+ {
+ var len = sp.Length;
+ sp = sp.TrimStart();
+ rangeStart = start + len - sp.Length;
+ sp = sp.TrimEnd();
+ }
+#endif
+
+ if (sp.Length > 0 || (options & StringSplitOptions.RemoveEmptyEntries) == 0)
+ {
+ visitor(input.Slice(rangeStart, sp.Length), numSegments++, context);
+ }
+
+ if (index < 0)
+ {
+ return;
+ }
+
+ start += index + SeparatorLen;
+ }
+ }
+
+ ///
+ /// Invokes a delegate for individual string segments.
+ ///
+ /// The string to split.
+ /// The strings that delimit the substrings in this instance.
+ /// An object that can be used to pass state to the visitor.
+ /// A delegate that gets invoked for each individual segment.
+ /// The kind of string comparison to apply to the separator strings.
+ /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
+ /// The type of the visitor's context.
+ ///
+ /// The visitor delegate is invoked for each segment in the input. It is given as parameter the
+ /// value of the argument, the segment index, and a range for the segment.
+ ///
+ public static void VisitSplits(
+ this ReadOnlySpan input,
+ string[] separators,
+ TContext context,
+ SplitVisitor visitor,
+ StringComparison comparison = StringComparison.Ordinal,
+ StringSplitOptions options = StringSplitOptions.None)
+ {
+ _ = Throw.IfNull(separators);
+ _ = Throw.IfNull(visitor);
+ CheckStringSplitOptions(options);
+
+ int numSegments = 0;
+ int start = 0;
+ while (true)
+ {
+ int index = -1;
+ int separatorLen = 0;
+ foreach (var sep in separators)
+ {
+ int found = input.Slice(start).IndexOf(sep.AsSpan(), comparison);
+ if (found >= 0)
+ {
+ if (found < index || index < 0)
+ {
+ separatorLen = sep.Length;
+ index = found;
+ }
+ }
+ }
+
+ var sp = index < 0 ? input.Slice(start) : input.Slice(start, index);
+
+ var rangeStart = start;
+#if NET5_0_OR_GREATER
+ if ((options & StringSplitOptions.TrimEntries) != 0)
+ {
+ var len = sp.Length;
+ sp = sp.TrimStart();
+ rangeStart = start + len - sp.Length;
+ sp = sp.TrimEnd();
+ }
+#endif
+
+ if (sp.Length > 0 || (options & StringSplitOptions.RemoveEmptyEntries) == 0)
+ {
+ visitor(input.Slice(rangeStart, sp.Length), numSegments++, context);
+ }
+
+ if (index < 0)
+ {
+ return;
+ }
+
+ start += index + separatorLen;
+ }
+ }
+
+ ///
+ /// Invokes a delegate for individual string segments.
+ ///
+ /// The string to split.
+ /// The string that delimits the substrings in this instance.
+ /// An object that can be used to pass state to the visitor.
+ /// A delegate that gets invoked for each individual segment.
+ /// The kind of string comparison to apply to the separator strings.
+ /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
+ /// The type of the visitor's context.
+ ///
+ /// The visitor delegate is invoked for each segment in the input. It is given as parameter the
+ /// value of the argument, the segment index, and a range for the segment.
+ ///
+#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
+ public static void VisitSplits(
+ this ReadOnlySpan input,
+ string separator,
+ TContext context,
+ SplitVisitor visitor,
+ StringComparison comparison = StringComparison.Ordinal,
+ StringSplitOptions options = StringSplitOptions.None)
+#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
+ {
+ _ = Throw.IfNull(separator);
+ _ = Throw.IfNull(visitor);
+ CheckStringSplitOptions(options);
+
+ int numSegments = 0;
+ int start = 0;
+ while (true)
+ {
+ int index = -1;
+ int separatorLen = 0;
+
+ int found = input.Slice(start).IndexOf(separator.AsSpan(), comparison);
+ if (found >= 0)
+ {
+ if (found < index || index < 0)
+ {
+ separatorLen = separator.Length;
+ index = found;
+ }
+ }
+
+ var sp = index < 0 ? input.Slice(start) : input.Slice(start, index);
+
+ var rangeStart = start;
+#if NET5_0_OR_GREATER
+ if ((options & StringSplitOptions.TrimEntries) != 0)
+ {
+ var len = sp.Length;
+ sp = sp.TrimStart();
+ rangeStart = start + len - sp.Length;
+ sp = sp.TrimEnd();
+ }
+#endif
+
+ if (sp.Length > 0 || (options & StringSplitOptions.RemoveEmptyEntries) == 0)
+ {
+ visitor(input.Slice(rangeStart, sp.Length), numSegments++, context);
+ }
+
+ if (index < 0)
+ {
+ return;
+ }
+
+ start += index + separatorLen;
+ }
+ }
+
+ ///
+ /// Invokes a delegate for individual string segments.
+ ///
+ /// The string to split.
+ /// An object that can be used to pass state to the visitor.
+ /// A delegate that gets invoked for each individual segment.
+ /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
+ /// The type of the visitor's context.
+ ///
+ /// This uses whitespace as a separator of individual substrings.
+ ///
+ /// The visitor delegate is invoked for each segment in the input. It is given as parameter the
+ /// value of the argument, the segment index, and a range for the segment.
+ ///
+ public static void VisitSplits(
+ this ReadOnlySpan input,
+ TContext context,
+ SplitVisitor visitor,
+ StringSplitOptions options = StringSplitOptions.None)
+ {
+ const int SeparatorLen = 1;
+
+ _ = Throw.IfNull(visitor);
+ CheckStringSplitOptions(options);
+
+ int numSegments = 0;
+ int start = 0;
+ while (true)
+ {
+ int index = -1;
+ for (int i = start; i < input.Length; i++)
+ {
+ if (char.IsWhiteSpace(input[i]))
+ {
+ index = i - start;
+ break;
+ }
+ }
+
+ var sp = index < 0 ? input.Slice(start) : input.Slice(start, index);
+
+ var rangeStart = start;
+#if NET5_0_OR_GREATER
+ if ((options & StringSplitOptions.TrimEntries) != 0)
+ {
+ var len = sp.Length;
+ sp = sp.TrimStart();
+ rangeStart = start + len - sp.Length;
+ sp = sp.TrimEnd();
+ }
+#endif
+
+ if (sp.Length > 0 || (options & StringSplitOptions.RemoveEmptyEntries) == 0)
+ {
+ visitor(input.Slice(rangeStart, sp.Length), numSegments++, context);
+ }
+
+ if (index < 0)
+ {
+ return;
+ }
+
+ start += index + SeparatorLen;
+ }
+ }
+
+ ///
+ /// Invokes a delegate for individual string segments.
+ ///
+ /// The string to split.
+ /// A character that delimits the substrings in this instance.
+ /// An object that can be used to pass state to the visitor.
+ /// A delegate that gets invoked for each individual segment.
+ /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
+ /// The type of the visitor's context.
+ ///
+ /// The visitor delegate is invoked for each segment in the input. It is given as parameter the
+ /// value of the argument, the segment index, and a range for the segment.
+ ///
+#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
+ public static void VisitSplits(
+ this string input,
+ char separator,
+ TContext context,
+ SplitVisitor visitor,
+ StringSplitOptions options = StringSplitOptions.None)
+ => VisitSplits(input.AsSpan(), separator, context, visitor, options);
+#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
+
+ ///
+ /// Invokes a delegate for individual string segments.
+ ///
+ /// The string to split.
+ /// The characters that delimit the substrings in this instance.
+ /// An object that can be used to pass state to the visitor.
+ /// A delegate that gets invoked for each individual segment.
+ /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
+ /// The type of the visitor's context.
+ ///
+ /// The visitor delegate is invoked for each segment in the input. It is given as parameter the
+ /// value of the argument, the segment index, and a range for the segment.
+ ///
+#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
+ public static void VisitSplits(
+ this string input,
+ ReadOnlySpan separators,
+ TContext context,
+ SplitVisitor visitor,
+ StringSplitOptions options = StringSplitOptions.None)
+ => VisitSplits(input.AsSpan(), separators, context, visitor, options);
+#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
+
+ ///
+ /// Invokes a delegate for individual string segments.
+ ///
+ /// The string to split.
+ /// The strings that delimit the substrings in this instance.
+ /// An object that can be used to pass state to the visitor.
+ /// A delegate that gets invoked for each individual segment.
+ /// The kind of string comparison to apply to the separator strings.
+ /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
+ /// The type of the visitor's context.
+ ///
+ /// The visitor delegate is invoked for each segment in the input. It is given as parameter the
+ /// value of the argument, the segment index, and a range for the segment.
+ ///
+ public static void VisitSplits(
+ this string input,
+ string[] separators,
+ TContext context,
+ SplitVisitor visitor,
+ StringComparison comparison = StringComparison.Ordinal,
+ StringSplitOptions options = StringSplitOptions.None)
+ => VisitSplits(input.AsSpan(), separators, context, visitor, comparison, options);
+
+ ///
+ /// Invokes a delegate for individual string segments.
+ ///
+ /// The string to split.
+ /// The string that delimits the substrings in this instance.
+ /// An object that can be used to pass state to the visitor.
+ /// A delegate that gets invoked for each individual segment.
+ /// The kind of string comparison to apply to the separator strings.
+ /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
+ /// The type of the visitor's context.
+ ///
+ /// The visitor delegate is invoked for each segment in the input. It is given as parameter the
+ /// value of the argument, the segment index, and a range for the segment.
+ ///
+ public static void VisitSplits(
+ this string input,
+ string separator,
+ TContext context,
+ SplitVisitor visitor,
+ StringComparison comparison = StringComparison.Ordinal,
+ StringSplitOptions options = StringSplitOptions.None)
+ => VisitSplits(input.AsSpan(), separator, context, visitor, comparison, options);
+
+ ///
+ /// Invokes a delegate for individual string segments.
+ ///
+ /// The string to split.
+ /// An object that can be used to pass state to the visitor.
+ /// A delegate that gets invoked for each individual segment.
+ /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
+ /// The type of the visitor's context.
+ ///
+ /// This uses whitespace as a separator of individual substrings.
+ ///
+ /// The visitor delegate is invoked for each segment in the input. It is given as parameter the
+ /// value of the argument, the segment index, and a range for the segment.
+ ///
+ public static void VisitSplits(
+ this string input,
+ TContext context,
+ SplitVisitor visitor,
+ StringSplitOptions options = StringSplitOptions.None)
+ => VisitSplits(input.AsSpan(), context, visitor, options);
+}
+
+#endif
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Resources/FileNamesOnlyFileSystem.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Resources/FileNamesOnlyFileSystem.cs
index 9dbe57266a4..0ced1c50c4a 100644
--- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Resources/FileNamesOnlyFileSystem.cs
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Resources/FileNamesOnlyFileSystem.cs
@@ -55,4 +55,9 @@ public int Read(FileInfo file, int length, Span destination)
return min;
}
+
+ public IEnumerable> ReadAllByLines(FileInfo file, BufferWriter destination)
+ {
+ return Enumerable.Empty>();
+ }
}
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Resources/HardcodedValueFileSystem.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Resources/HardcodedValueFileSystem.cs
index 6450ff2b2f9..3b20e575af3 100644
--- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Resources/HardcodedValueFileSystem.cs
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Resources/HardcodedValueFileSystem.cs
@@ -91,4 +91,9 @@ public void ReplaceFileContent(FileInfo file, string value)
{
_fileContent[file.FullName] = value;
}
+
+ public IEnumerable> ReadAllByLines(FileInfo file, BufferWriter destination)
+ {
+ return Enumerable.Empty>();
+ }
}
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/ResourceMonitoringOptionsCustomValidatorTests.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/ResourceMonitoringOptionsCustomValidatorTests.cs
index c6dce066a3f..35583f17224 100644
--- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/ResourceMonitoringOptionsCustomValidatorTests.cs
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/ResourceMonitoringOptionsCustomValidatorTests.cs
@@ -20,7 +20,7 @@ public void Test_WindowsCountersOptionsCustomValidator_With_Wrong_IP_Address()
{
SourceIpAddresses = new HashSet { "" }
};
- var tcpTableInfo = new TcpTableInfo(Options.Options.Create(options));
+ var tcpTableInfo = new WindowsTcpTableInfo(Options.Options.Create(options));
var validator = new ResourceMonitoringOptionsCustomValidator();
var result = validator.Validate("", options);
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/Tcp6TableInfoTests.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/Tcp6TableInfoTests.cs
index 2c584c7ea08..5f30cc9d8fd 100644
--- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/Tcp6TableInfoTests.cs
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/Tcp6TableInfoTests.cs
@@ -234,11 +234,11 @@ public void Test_Tcp6TableInfo_Get_UnsuccessfulStatus_All_The_Time()
SourceIpAddresses = new HashSet { "[::1]" },
SamplingInterval = DefaultTimeSpan
};
- TcpTableInfo tcp6TableInfo = new TcpTableInfo(Options.Options.Create(options));
+ WindowsTcpTableInfo tcp6TableInfo = new WindowsTcpTableInfo(Options.Options.Create(options));
tcp6TableInfo.SetGetTcp6TableDelegate(FakeGetTcp6TableWithUnsuccessfulStatusAllTheTime);
Assert.Throws(() =>
{
- var tcpStateInfo = tcp6TableInfo.GetIPv6CachingSnapshot();
+ var tcpStateInfo = tcp6TableInfo.GetIpV6CachingSnapshot();
});
}
@@ -250,11 +250,11 @@ public void Test_Tcp6TableInfo_Get_InsufficientBuffer_Then_Get_InvalidParameter(
SourceIpAddresses = new HashSet { "[::1]" },
SamplingInterval = DefaultTimeSpan
};
- TcpTableInfo tcp6TableInfo = new TcpTableInfo(Options.Options.Create(options));
+ WindowsTcpTableInfo tcp6TableInfo = new WindowsTcpTableInfo(Options.Options.Create(options));
tcp6TableInfo.SetGetTcp6TableDelegate(FakeGetTcp6TableWithInsufficientBufferAndInvalidParameter);
Assert.Throws(() =>
{
- var tcpStateInfo = tcp6TableInfo.GetIPv6CachingSnapshot();
+ var tcpStateInfo = tcp6TableInfo.GetIpV6CachingSnapshot();
});
}
@@ -268,9 +268,9 @@ public void Test_Tcp6TableInfo_Get_Correct_Information()
SourceIpAddresses = new HashSet { "[::1]" },
SamplingInterval = DefaultTimeSpan
};
- TcpTableInfo tcp6TableInfo = new TcpTableInfo(Options.Options.Create(options));
+ WindowsTcpTableInfo tcp6TableInfo = new WindowsTcpTableInfo(Options.Options.Create(options));
tcp6TableInfo.SetGetTcp6TableDelegate(FakeGetTcp6TableWithFakeInformation);
- var tcpStateInfo = tcp6TableInfo.GetIPv6CachingSnapshot();
+ var tcpStateInfo = tcp6TableInfo.GetIpV6CachingSnapshot();
Assert.NotNull(tcpStateInfo);
Assert.Equal(1, tcpStateInfo.ClosedCount);
Assert.Equal(1, tcpStateInfo.ListenCount);
@@ -286,7 +286,7 @@ public void Test_Tcp6TableInfo_Get_Correct_Information()
Assert.Equal(1, tcpStateInfo.DeleteTcbCount);
// Second calling in a small interval.
- tcpStateInfo = tcp6TableInfo.GetIPv6CachingSnapshot();
+ tcpStateInfo = tcp6TableInfo.GetIpV6CachingSnapshot();
Assert.NotNull(tcpStateInfo);
Assert.Equal(1, tcpStateInfo.ClosedCount);
Assert.Equal(1, tcpStateInfo.ListenCount);
@@ -303,7 +303,7 @@ public void Test_Tcp6TableInfo_Get_Correct_Information()
// Third calling in a long interval.
Thread.Sleep(6000);
- tcpStateInfo = tcp6TableInfo.GetIPv6CachingSnapshot();
+ tcpStateInfo = tcp6TableInfo.GetIpV6CachingSnapshot();
Assert.NotNull(tcpStateInfo);
Assert.Equal(2, tcpStateInfo.ClosedCount);
Assert.Equal(2, tcpStateInfo.ListenCount);
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/TcpTableInfoTests.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/TcpTableInfoTests.cs
index 6ca26cf324a..8f6309085ec 100644
--- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/TcpTableInfoTests.cs
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/TcpTableInfoTests.cs
@@ -177,11 +177,11 @@ public void Test_TcpTableInfo_Get_UnsuccessfulStatus_All_The_Time()
SourceIpAddresses = new HashSet { "127.0.0.1" },
SamplingInterval = DefaultTimeSpan
};
- TcpTableInfo tcpTableInfo = new TcpTableInfo(Options.Options.Create(options));
+ WindowsTcpTableInfo tcpTableInfo = new WindowsTcpTableInfo(Options.Options.Create(options));
tcpTableInfo.SetGetTcpTableDelegate(FakeGetTcpTableWithUnsuccessfulStatusAllTheTime);
Assert.Throws(() =>
{
- var tcpStateInfo = tcpTableInfo.GetIPv4CachingSnapshot();
+ var tcpStateInfo = tcpTableInfo.GetIpV4CachingSnapshot();
});
}
@@ -193,11 +193,11 @@ public void Test_TcpTableInfo_Get_InsufficientBuffer_Then_Get_InvalidParameter()
SourceIpAddresses = new HashSet { "127.0.0.1" },
SamplingInterval = DefaultTimeSpan
};
- TcpTableInfo tcpTableInfo = new TcpTableInfo(Options.Options.Create(options));
+ WindowsTcpTableInfo tcpTableInfo = new WindowsTcpTableInfo(Options.Options.Create(options));
tcpTableInfo.SetGetTcpTableDelegate(FakeGetTcpTableWithInsufficientBufferAndInvalidParameter);
Assert.Throws(() =>
{
- var tcpStateInfo = tcpTableInfo.GetIPv4CachingSnapshot();
+ var tcpStateInfo = tcpTableInfo.GetIpV4CachingSnapshot();
});
}
@@ -211,9 +211,9 @@ public void Test_TcpTableInfo_Get_Correct_Information()
SourceIpAddresses = new HashSet { "127.0.0.1" },
SamplingInterval = DefaultTimeSpan
};
- TcpTableInfo tcpTableInfo = new TcpTableInfo(Options.Options.Create(options));
+ WindowsTcpTableInfo tcpTableInfo = new WindowsTcpTableInfo(Options.Options.Create(options));
tcpTableInfo.SetGetTcpTableDelegate(FakeGetTcpTableWithFakeInformation);
- var tcpStateInfo = tcpTableInfo.GetIPv4CachingSnapshot();
+ var tcpStateInfo = tcpTableInfo.GetIpV4CachingSnapshot();
Assert.NotNull(tcpStateInfo);
Assert.Equal(1, tcpStateInfo.ClosedCount);
Assert.Equal(1, tcpStateInfo.ListenCount);
@@ -229,7 +229,7 @@ public void Test_TcpTableInfo_Get_Correct_Information()
Assert.Equal(1, tcpStateInfo.DeleteTcbCount);
// Second calling in a small interval.
- tcpStateInfo = tcpTableInfo.GetIPv4CachingSnapshot();
+ tcpStateInfo = tcpTableInfo.GetIpV4CachingSnapshot();
Assert.NotNull(tcpStateInfo);
Assert.Equal(1, tcpStateInfo.ClosedCount);
Assert.Equal(1, tcpStateInfo.ListenCount);
@@ -246,7 +246,7 @@ public void Test_TcpTableInfo_Get_Correct_Information()
// Third calling in a long interval.
Thread.Sleep(6000);
- tcpStateInfo = tcpTableInfo.GetIPv4CachingSnapshot();
+ tcpStateInfo = tcpTableInfo.GetIpV4CachingSnapshot();
Assert.NotNull(tcpStateInfo);
Assert.Equal(2, tcpStateInfo.ClosedCount);
Assert.Equal(2, tcpStateInfo.ListenCount);
@@ -268,7 +268,7 @@ public void Test_TcpTableInfo_CalculateCount_default_branch()
TcpStateInfo tcpStateInfo = new();
// Add this case to increase coverage, but 0 will not happen in actual case.
- TcpTableInfo.CalculateCount(tcpStateInfo, 0);
+ WindowsTcpTableInfo.CalculateCount(tcpStateInfo, 0);
Assert.NotNull(tcpStateInfo);
Assert.Equal(0, tcpStateInfo.ClosedCount);
Assert.Equal(0, tcpStateInfo.ListenCount);
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/WindowsCountersTests.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/WindowsCountersTests.cs
index 6370e26cb09..7eb1fdec01e 100644
--- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/WindowsCountersTests.cs
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/WindowsCountersTests.cs
@@ -32,7 +32,7 @@ public void WindowsCounters_Registers_Instruments()
meterFactoryMock.Setup(x => x.Create(It.IsAny()))
.Returns(meter);
- var tcpTableInfo = new TcpTableInfo(Options.Options.Create(options));
+ var tcpTableInfo = new WindowsTcpTableInfo(Options.Options.Create(options));
tcpTableInfo.SetGetTcpTableDelegate(TcpTableInfoTests.FakeGetTcpTableWithFakeInformation);
tcpTableInfo.SetGetTcp6TableDelegate(Tcp6TableInfoTests.FakeGetTcp6TableWithFakeInformation);
var windowsCounters = new WindowsNetworkMetrics(meterFactoryMock.Object, tcpTableInfo);
@@ -74,7 +74,7 @@ public void WindowsCounters_Got_Unsuccessful()
meterFactoryMock.Setup(x => x.Create(It.IsAny()))
.Returns(meter);
- var tcpTableInfo = new TcpTableInfo(Options.Options.Create(options));
+ var tcpTableInfo = new WindowsTcpTableInfo(Options.Options.Create(options));
tcpTableInfo.SetGetTcpTableDelegate(TcpTableInfoTests.FakeGetTcpTableWithUnsuccessfulStatusAllTheTime);
tcpTableInfo.SetGetTcp6TableDelegate(Tcp6TableInfoTests.FakeGetTcp6TableWithUnsuccessfulStatusAllTheTime);
var windowsCounters = new WindowsNetworkMetrics(meterFactoryMock.Object, tcpTableInfo);
diff --git a/test/Shared/StringSplit/SplitExtensionsTests.cs b/test/Shared/StringSplit/SplitExtensionsTests.cs
new file mode 100644
index 00000000000..bb96612658a
--- /dev/null
+++ b/test/Shared/StringSplit/SplitExtensionsTests.cs
@@ -0,0 +1,426 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#if !NET8_0_OR_GREATER
+
+using System;
+using System.Collections.Generic;
+using Xunit;
+
+namespace Microsoft.Shared.StringSplit.Test;
+
+public static class SplitExtensionsTests
+{
+ public static IEnumerable
-
+
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkUtilizationParser.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkUtilizationParser.cs
index 3669e8c4602..f622133eed9 100644
--- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkUtilizationParser.cs
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkUtilizationParser.cs
@@ -45,7 +45,7 @@ public LinuxNetworkUtilizationParser(IFileSystem fileSystem)
}
///
- /// The method is used in the LinuxUtilizationParser class to read Span data and calculate the TCP state info.
+ /// The method is used to read Span data and calculate the TCP state info.
/// Refer proc net tcp.
///
private static void UpdateTcpStateInfo(ReadOnlySpan buffer, TcpStateInfo tcpStateInfo)
@@ -124,11 +124,11 @@ private static void UpdateTcpStateInfo(ReadOnlySpan buffer, TcpStateInfo t
}
///
- /// Reads the contents of a file located at file and parses it to extract information about the TCP/IP state info on the system.
+ /// Reads the contents of a file and parses it to extract information about the TCP/IP state info on the system.
///
private TcpStateInfo GetTcpStateInfo(FileInfo file)
{
- // The value we are interested in starts with this. We just want to make sure it is true.
+ // The value we are interested in starts with this "sl".
const string Sl = "sl";
var tcpStateInfo = new TcpStateInfo();
using ReturnableBufferWriter bufferWriter = new(_sharedBufferWriterPool);
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringServiceCollectionExtensions.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringServiceCollectionExtensions.cs
index ab206eeda11..6e1343aa9b6 100644
--- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringServiceCollectionExtensions.cs
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringServiceCollectionExtensions.cs
@@ -90,9 +90,7 @@ private static ResourceMonitorBuilder AddWindowsProvider(this ResourceMonitorBui
builder.PickWindowsSnapshotProvider();
_ = builder.Services
- .AddActivatedSingleton();
-
- _ = builder.Services
+ .AddActivatedSingleton()
.AddActivatedSingleton();
return builder;
@@ -120,13 +118,11 @@ private static ResourceMonitorBuilder AddLinuxProvider(this ResourceMonitorBuild
builder.Services.TryAddSingleton();
builder.Services.TryAddSingleton();
- builder.Services.TryAddSingleton();
builder.PickLinuxParser();
_ = builder.Services
- .AddActivatedSingleton();
-
- _ = builder.Services
+ .AddActivatedSingleton()
+ .AddActivatedSingleton()
.AddActivatedSingleton();
return builder;
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/LinuxCountersTests.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/LinuxCountersTests.cs
index 70aebaedadb..40ffa7e011d 100644
--- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/LinuxCountersTests.cs
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/LinuxCountersTests.cs
@@ -19,101 +19,59 @@ public void LinuxNetworkCounters_Registers_Instruments()
{
var fileSystem = new HardcodedValueFileSystem(new Dictionary
{
- {
- new FileInfo("/proc/net/tcp"), " sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\r\n" +
- " 0: 030011AC:8AF2 C1B17822:01BB 01 00000000:00000000 02:000000D1 00000000 472 0 2481276 2 00000000c62511cb 28 4 26 10 -1\r\n" +
- " 1: 030011AC:DEAC 856CC7B9:01BB 02 00000000:00000000 02:000000D1 00000000 472 0 2483767 2 0000000014121fd6 28 4 30 10 -1\r\n" +
- " 0: 030011AC:8AF3 C1B17823:01BB 03 00000000:00000000 02:000000D1 00000000 472 0 2481276 2 00000000c62511cb 28 4 26 10 -1\r\n" +
- " 1: 030011AC:DEAD 856CC7BA:01BB 04 00000000:00000000 02:000000D1 00000000 472 0 2483767 2 0000000014121fd6 28 4 30 10 -1\r\n" +
- " 0: 030011AC:8AF4 C1B17824:01BB 05 00000000:00000000 02:000000D1 00000000 472 0 2481276 2 00000000c62511cb 28 4 26 10 -1\r\n" +
- " 1: 030011AC:DEAE 856CC7BB:01BB 06 00000000:00000000 02:000000D1 00000000 472 0 2483767 2 0000000014121fd6 28 4 30 10 -1\r\n" +
- " 0: 030011AC:8AF5 C1B17825:01BB 07 00000000:00000000 02:000000D1 00000000 472 0 2481276 2 00000000c62511cb 28 4 26 10 -1\r\n" +
- " 1: 030011AC:DEAF 856CC7BC:01BB 08 00000000:00000000 02:000000D1 00000000 472 0 2483767 2 0000000014121fd6 28 4 30 10 -1\r\n" +
- " 0: 030011AC:8AF6 C1B17826:01BB 09 00000000:00000000 02:000000D1 00000000 472 0 2481276 2 00000000c62511cb 28 4 26 10 -1\r\n" +
- " 1: 030011AC:DEA1 856CC7BD:01BB 0A 00000000:00000000 02:000000D1 00000000 472 0 2483767 2 0000000014121fd6 28 4 30 10 -1\r\n" +
- " 0: 030011AC:8AF7 C1B17827:01BB 0B 00000000:00000000 02:000000D1 00000000 472 0 2481276 2 00000000c62511cb 28 4 26 10 -1\r\n" +
- " 0: 030011AC:8AF2 C1B17822:01BB 01 00000000:00000000 02:000000D1 00000000 472 0 2481276 2 00000000c62511cb 28 4 26 10 -1\r\n" +
- " 1: 030011AC:DEAC 856CC7B9:01BB 02 00000000:00000000 02:000000D1 00000000 472 0 2483767 2 0000000014121fd6 28 4 30 10 -1\r\n" +
- " 0: 030011AC:8AF3 C1B17823:01BB 03 00000000:00000000 02:000000D1 00000000 472 0 2481276 2 00000000c62511cb 28 4 26 10 -1\r\n" +
- " 1: 030011AC:DEAD 856CC7BA:01BB 04 00000000:00000000 02:000000D1 00000000 472 0 2483767 2 0000000014121fd6 28 4 30 10 -1\r\n" +
- " 0: 030011AC:8AF4 C1B17824:01BB 05 00000000:00000000 02:000000D1 00000000 472 0 2481276 2 00000000c62511cb 28 4 26 10 -1\r\n" +
- " 1: 030011AC:DEAE 856CC7BB:01BB 06 00000000:00000000 02:000000D1 00000000 472 0 2483767 2 0000000014121fd6 28 4 30 10 -1\r\n" +
- " 0: 030011AC:8AF5 C1B17825:01BB 07 00000000:00000000 02:000000D1 00000000 472 0 2481276 2 00000000c62511cb 28 4 26 10 -1\r\n" +
- " 1: 030011AC:DEAF 856CC7BC:01BB 08 00000000:00000000 02:000000D1 00000000 472 0 2483767 2 0000000014121fd6 28 4 30 10 -1\r\n" +
- " 0: 030011AC:8AF6 C1B17826:01BB 09 00000000:00000000 02:000000D1 00000000 472 0 2481276 2 00000000c62511cb 28 4 26 10 -1\r\n" +
- " 1: 030011AC:DEA1 856CC7BD:01BB 0A 00000000:00000000 02:000000D1 00000000 472 0 2483767 2 0000000014121fd6 28 4 30 10 -1\r\n" +
- " 0: 030011AC:8AF7 C1B17827:01BB 0B 00000000:00000000 02:000000D1 00000000 472 0 2481276 2 00000000c62511cb 28 4 26 10 -1\r\n"
- },
- {
- new FileInfo("/proc/net/tcp6"),
- " sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\r\n" +
- " 0: 00000000000000000000000000000000:0BB8 00000000000000000000000000000000:0000 " +
- "0A 00000000:00000000 00:00000000 00000000 " +
- "472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
- " 0: 00000000000000000000000000000000:0BB9 00000000000000000000000000000000:0001 " +
- "0B 00000000:00000000 00:00000000 00000000 " +
- "472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
- " 0: 00000000000000000000000000000000:0BBB 00000000000000000000000000000000:0003 " +
- "01 00000000:00000000 00:00000000 00000000 " +
- "472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
- " 0: 00000000000000000000000000000000:0BBC 00000000000000000000000000000000:0004 " +
- "02 00000000:00000000 00:00000000 00000000 " +
- "472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
- " 0: 00000000000000000000000000000000:0BBD 00000000000000000000000000000000:0005 " +
- "03 00000000:00000000 00:00000000 00000000 " +
- "472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
- " 0: 00000000000000000000000000000000:0BBE 00000000000000000000000000000000:0006 " +
- "04 00000000:00000000 00:00000000 00000000 " +
- "472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
- " 0: 00000000000000000000000000000000:0BB1 00000000000000000000000000000000:0007 " +
- "05 00000000:00000000 00:00000000 00000000 " +
- "472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
- " 0: 00000000000000000000000000000000:0BB2 00000000000000000000000000000000:0008 " +
- "06 00000000:00000000 00:00000000 00000000 " +
- "472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
- " 0: 00000000000000000000000000000000:0BB3 00000000000000000000000000000000:0009 " +
- "07 00000000:00000000 00:00000000 00000000 " +
- "472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
- " 0: 00000000000000000000000000000000:0BB4 00000000000000000000000000000000:000A " +
- "08 00000000:00000000 00:00000000 00000000 " +
- "472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
- " 0: 00000000000000000000000000000000:0BB5 00000000000000000000000000000000:000B " +
- "09 00000000:00000000 00:00000000 00000000 " +
- "472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
- " 0: 00000000000000000000000000000000:0BB8 00000000000000000000000000000000:0000 " +
- "0A 00000000:00000000 00:00000000 00000000 " +
- "472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
- " 0: 00000000000000000000000000000000:0BB9 00000000000000000000000000000000:0001 " +
- "0B 00000000:00000000 00:00000000 00000000 " +
- "472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
- " 0: 00000000000000000000000000000000:0BBB 00000000000000000000000000000000:0003 " +
- "01 00000000:00000000 00:00000000 00000000 " +
- "472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
- " 0: 00000000000000000000000000000000:0BBC 00000000000000000000000000000000:0004 " +
- "02 00000000:00000000 00:00000000 00000000 " +
- "472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
- " 0: 00000000000000000000000000000000:0BBD 00000000000000000000000000000000:0005 " +
- "03 00000000:00000000 00:00000000 00000000 " +
- "472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
- " 0: 00000000000000000000000000000000:0BBE 00000000000000000000000000000000:0006 " +
- "04 00000000:00000000 00:00000000 00000000 " +
- "472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
- " 0: 00000000000000000000000000000000:0BB1 00000000000000000000000000000000:0007 " +
- "05 00000000:00000000 00:00000000 00000000 " +
- "472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
- " 0: 00000000000000000000000000000000:0BB2 00000000000000000000000000000000:0008 " +
- "06 00000000:00000000 00:00000000 00000000 " +
- "472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
- " 0: 00000000000000000000000000000000:0BB3 00000000000000000000000000000000:0009 " +
- "07 00000000:00000000 00:00000000 00000000 " +
- "472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
- " 0: 00000000000000000000000000000000:0BB4 00000000000000000000000000000000:000A " +
- "08 00000000:00000000 00:00000000 00000000 " +
- "472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
- " 0: 00000000000000000000000000000000:0BB5 00000000000000000000000000000000:000B " +
- "09 00000000:00000000 00:00000000 00000000 " +
- "472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n"
- },
+ {
+ new FileInfo("/proc/net/tcp"),
+ " sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\r\n" +
+ " 0: 030011AC:8AF2 C1B17822:01BB 01 00000000:00000000 02:000000D1 00000000 472 0 2481276 2 00000000c62511cb 28 4 26 10 -1\r\n" +
+ " 1: 030011AC:DEAC 856CC7B9:01BB 02 00000000:00000000 02:000000D1 00000000 472 0 2483767 2 0000000014121fd6 28 4 30 10 -1\r\n" +
+ " 0: 030011AC:8AF3 C1B17823:01BB 03 00000000:00000000 02:000000D1 00000000 472 0 2481276 2 00000000c62511cb 28 4 26 10 -1\r\n" +
+ " 1: 030011AC:DEAD 856CC7BA:01BB 04 00000000:00000000 02:000000D1 00000000 472 0 2483767 2 0000000014121fd6 28 4 30 10 -1\r\n" +
+ " 0: 030011AC:8AF4 C1B17824:01BB 05 00000000:00000000 02:000000D1 00000000 472 0 2481276 2 00000000c62511cb 28 4 26 10 -1\r\n" +
+ " 1: 030011AC:DEAE 856CC7BB:01BB 06 00000000:00000000 02:000000D1 00000000 472 0 2483767 2 0000000014121fd6 28 4 30 10 -1\r\n" +
+ " 0: 030011AC:8AF5 C1B17825:01BB 07 00000000:00000000 02:000000D1 00000000 472 0 2481276 2 00000000c62511cb 28 4 26 10 -1\r\n" +
+ " 1: 030011AC:DEAF 856CC7BC:01BB 08 00000000:00000000 02:000000D1 00000000 472 0 2483767 2 0000000014121fd6 28 4 30 10 -1\r\n" +
+ " 0: 030011AC:8AF6 C1B17826:01BB 09 00000000:00000000 02:000000D1 00000000 472 0 2481276 2 00000000c62511cb 28 4 26 10 -1\r\n" +
+ " 1: 030011AC:DEA1 856CC7BD:01BB 0A 00000000:00000000 02:000000D1 00000000 472 0 2483767 2 0000000014121fd6 28 4 30 10 -1\r\n" +
+ " 0: 030011AC:8AF7 C1B17827:01BB 0B 00000000:00000000 02:000000D1 00000000 472 0 2481276 2 00000000c62511cb 28 4 26 10 -1\r\n" +
+ " 0: 030011AC:8AF2 C1B17822:01BB 01 00000000:00000000 02:000000D1 00000000 472 0 2481276 2 00000000c62511cb 28 4 26 10 -1\r\n" +
+ " 1: 030011AC:DEAC 856CC7B9:01BB 02 00000000:00000000 02:000000D1 00000000 472 0 2483767 2 0000000014121fd6 28 4 30 10 -1\r\n" +
+ " 0: 030011AC:8AF3 C1B17823:01BB 03 00000000:00000000 02:000000D1 00000000 472 0 2481276 2 00000000c62511cb 28 4 26 10 -1\r\n" +
+ " 1: 030011AC:DEAD 856CC7BA:01BB 04 00000000:00000000 02:000000D1 00000000 472 0 2483767 2 0000000014121fd6 28 4 30 10 -1\r\n" +
+ " 0: 030011AC:8AF4 C1B17824:01BB 05 00000000:00000000 02:000000D1 00000000 472 0 2481276 2 00000000c62511cb 28 4 26 10 -1\r\n" +
+ " 1: 030011AC:DEAE 856CC7BB:01BB 06 00000000:00000000 02:000000D1 00000000 472 0 2483767 2 0000000014121fd6 28 4 30 10 -1\r\n" +
+ " 0: 030011AC:8AF5 C1B17825:01BB 07 00000000:00000000 02:000000D1 00000000 472 0 2481276 2 00000000c62511cb 28 4 26 10 -1\r\n" +
+ " 1: 030011AC:DEAF 856CC7BC:01BB 08 00000000:00000000 02:000000D1 00000000 472 0 2483767 2 0000000014121fd6 28 4 30 10 -1\r\n" +
+ " 0: 030011AC:8AF6 C1B17826:01BB 09 00000000:00000000 02:000000D1 00000000 472 0 2481276 2 00000000c62511cb 28 4 26 10 -1\r\n" +
+ " 1: 030011AC:DEA1 856CC7BD:01BB 0A 00000000:00000000 02:000000D1 00000000 472 0 2483767 2 0000000014121fd6 28 4 30 10 -1\r\n" +
+ " 0: 030011AC:8AF7 C1B17827:01BB 0B 00000000:00000000 02:000000D1 00000000 472 0 2481276 2 00000000c62511cb 28 4 26 10 -1\r\n"
+ },
+ {
+#pragma warning disable S103 // Lines should not be too long - disabled for better readability
+ new FileInfo("/proc/net/tcp6"),
+ " sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\r\n" +
+ " 0: 00000000000000000000000000000000:0BB8 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000 472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
+ " 0: 00000000000000000000000000000000:0BB9 00000000000000000000000000000000:0001 0B 00000000:00000000 00:00000000 00000000 472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
+ " 0: 00000000000000000000000000000000:0BBB 00000000000000000000000000000000:0003 01 00000000:00000000 00:00000000 00000000 472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
+ " 0: 00000000000000000000000000000000:0BBC 00000000000000000000000000000000:0004 02 00000000:00000000 00:00000000 00000000 472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
+ " 0: 00000000000000000000000000000000:0BBD 00000000000000000000000000000000:0005 03 00000000:00000000 00:00000000 00000000 472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
+ " 0: 00000000000000000000000000000000:0BBE 00000000000000000000000000000000:0006 04 00000000:00000000 00:00000000 00000000 472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
+ " 0: 00000000000000000000000000000000:0BB1 00000000000000000000000000000000:0007 05 00000000:00000000 00:00000000 00000000 472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
+ " 0: 00000000000000000000000000000000:0BB2 00000000000000000000000000000000:0008 06 00000000:00000000 00:00000000 00000000 472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
+ " 0: 00000000000000000000000000000000:0BB3 00000000000000000000000000000000:0009 07 00000000:00000000 00:00000000 00000000 472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
+ " 0: 00000000000000000000000000000000:0BB4 00000000000000000000000000000000:000A 08 00000000:00000000 00:00000000 00000000 472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
+ " 0: 00000000000000000000000000000000:0BB5 00000000000000000000000000000000:000B 09 00000000:00000000 00:00000000 00000000 472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
+ " 0: 00000000000000000000000000000000:0BB8 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000 472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
+ " 0: 00000000000000000000000000000000:0BB9 00000000000000000000000000000000:0001 0B 00000000:00000000 00:00000000 00000000 472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
+ " 0: 00000000000000000000000000000000:0BBB 00000000000000000000000000000000:0003 01 00000000:00000000 00:00000000 00000000 472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
+ " 0: 00000000000000000000000000000000:0BBC 00000000000000000000000000000000:0004 02 00000000:00000000 00:00000000 00000000 472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
+ " 0: 00000000000000000000000000000000:0BBD 00000000000000000000000000000000:0005 03 00000000:00000000 00:00000000 00000000 472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
+ " 0: 00000000000000000000000000000000:0BBE 00000000000000000000000000000000:0006 04 00000000:00000000 00:00000000 00000000 472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
+ " 0: 00000000000000000000000000000000:0BB1 00000000000000000000000000000000:0007 05 00000000:00000000 00:00000000 00000000 472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
+ " 0: 00000000000000000000000000000000:0BB2 00000000000000000000000000000000:0008 06 00000000:00000000 00:00000000 00000000 472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
+ " 0: 00000000000000000000000000000000:0BB3 00000000000000000000000000000000:0009 07 00000000:00000000 00:00000000 00000000 472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
+ " 0: 00000000000000000000000000000000:0BB4 00000000000000000000000000000000:000A 08 00000000:00000000 00:00000000 00000000 472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
+ " 0: 00000000000000000000000000000000:0BB5 00000000000000000000000000000000:000B 09 00000000:00000000 00:00000000 00000000 472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n"
+ },
});
var parser = new LinuxNetworkUtilizationParser(fileSystem);
@@ -166,15 +124,9 @@ public void Tcp_State_Info_Changes_After_Time()
{
new FileInfo("/proc/net/tcp6"),
" sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\r\n" +
- " 0: 00000000000000000000000000000000:0BBB 00000000000000000000000000000000:0003 " +
- "01 00000000:00000000 00:00000000 00000000 " +
- "472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
- " 0: 00000000000000000000000000000000:0BB6 0000000000000000FFFF000000000000:000C " +
- "01 00000000:00000000 00:00000000 00000000 " +
- "472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
- " 0: 00000000000000000000000000000000:0BB7 0000000000000000FFFF000000000000:000D " +
- "01 00000000:00000000 00:00000000 00000000 " +
- "472 0 0 3 0000000000000000\r\n"
+ " 0: 00000000000000000000000000000000:0BBB 00000000000000000000000000000000:0003 01 00000000:00000000 00:00000000 00000000 472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
+ " 0: 00000000000000000000000000000000:0BB6 0000000000000000FFFF000000000000:000C 01 00000000:00000000 00:00000000 00000000 472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
+ " 0: 00000000000000000000000000000000:0BB7 0000000000000000FFFF000000000000:000D 01 00000000:00000000 00:00000000 00000000 472 0 0 3 0000000000000000\r\n"
},
});
@@ -194,15 +146,9 @@ public void Tcp_State_Info_Changes_After_Time()
" 0: 030011AC:8AF8 C1B17828:01BB 02 00000000:00000000 02:000000D1 00000000 472 0 2481276 2 00000000c62511cb 28 4 26 10 -1\r\n";
var tcp6File =
" sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\r\n" +
- " 0: 00000000000000000000000000000000:0BBB 00000000000000000000000000000000:0003 " +
- "01 00000000:00000000 00:00000000 00000000 " +
- "472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
- " 0: 00000000000000000000000000000000:0BB6 0000000000000000FFFF000000000000:000C " +
- "01 00000000:00000000 00:00000000 00000000 " +
- "472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
- " 0: 00000000000000000000000000000000:0BB7 0000000000000000FFFF000000000000:000D " +
- "02 00000000:00000000 00:00000000 00000000 " +
- "472 0 0 3 0000000000000000\r\n";
+ " 0: 00000000000000000000000000000000:0BBB 00000000000000000000000000000000:0003 01 00000000:00000000 00:00000000 00000000 472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
+ " 0: 00000000000000000000000000000000:0BB6 0000000000000000FFFF000000000000:000C 01 00000000:00000000 00:00000000 00000000 472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n" +
+ " 0: 00000000000000000000000000000000:0BB7 0000000000000000FFFF000000000000:000D 02 00000000:00000000 00:00000000 00000000 472 0 0 3 0000000000000000\r\n";
fileSystem.ReplaceFileContent(new FileInfo("/proc/net/tcp"), tcpFile);
fileSystem.ReplaceFileContent(new FileInfo("/proc/net/tcp6"), tcp6File);
Thread.Sleep(3000);
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/LinuxNetworkUtilizationParserTests.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/LinuxNetworkUtilizationParserTests.cs
index 10382c3182e..1aaf633b755 100644
--- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/LinuxNetworkUtilizationParserTests.cs
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/LinuxNetworkUtilizationParserTests.cs
@@ -1,72 +1,74 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
using System.Collections.Generic;
-using System.ComponentModel;
using System.IO;
+using System.Threading.Tasks;
using Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network;
+using VerifyXunit;
using Xunit;
namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Test;
+[UsesVerify]
public sealed class LinuxNetworkUtilizationParserTests
{
+ private const string VerifiedDataDirectory = "Verified";
+
[Theory]
[InlineData("DFIJEUWGHFWGBWEFWOMDOWKSLA")]
[InlineData("")]
[InlineData("________________________Asdasdasdas dd")]
[InlineData(" ")]
[InlineData("!@#!$%!@")]
- public void Parser_Throws_When_Data_Is_Invalid(string line)
+ public async Task Parser_Throws_When_Data_Is_Invalid(string line)
{
var parser = new LinuxNetworkUtilizationParser(new HardcodedValueFileSystem(line));
- Assert.Throws(() => parser.GetTcpIPv4StateInfo());
- Assert.Throws(() => parser.GetTcpIPv6StateInfo());
+ await Verifier.Verify(Record.Exception(parser.GetTcpIPv4StateInfo)).UseParameters(line).UseMethodName("ipv4").UseDirectory(VerifiedDataDirectory);
+ await Verifier.Verify(Record.Exception(parser.GetTcpIPv4StateInfo)).UseParameters(line).UseMethodName("ipv6").UseDirectory(VerifiedDataDirectory);
}
[Fact]
- public void Parser_Tcp_State_Info_Exception()
+ public async Task Parser_Tcp_State_Info_Exception()
{
var fileSystem = new HardcodedValueFileSystem(new Dictionary
{
- {
- new FileInfo("/proc/net/tcp"), " sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\r\n" +
- " 0: 030011AC:8AF2\r\n"
- },
- {
- new FileInfo("/proc/net/tcp6"),
- " sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\r\n" +
- " 0: 00000000000000000000000000000000:0BB8 00000000000000000000000000000000:0000\r\n"
- },
+ {
+ new FileInfo("/proc/net/tcp"),
+ " sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\r\n" +
+ " 0: 030011AC:8AF2\r\n"
+ },
+ {
+ new FileInfo("/proc/net/tcp6"),
+ " sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\r\n" +
+ " 0: 00000000000000000000000000000000:0BB8 00000000000000000000000000000000:0000\r\n"
+ },
});
var parser = new LinuxNetworkUtilizationParser(fileSystem);
- // Exception Message: Could not split contents. We expected every line to contains more than 4 elements, but it has only 2 elements.
- Assert.Throws(() => parser.GetTcpIPv4StateInfo());
+ // Expected every line to contain more than 4 elements, but it has only 2 elements.
+ await Verifier.Verify(Record.Exception(parser.GetTcpIPv4StateInfo)).UseMethodName("1").UseDirectory(VerifiedDataDirectory);
- // Exception Message: Could not split contents. We expected every line to contains more than 4 elements, but it has only 3 elements.
- Assert.Throws(() => parser.GetTcpIPv6StateInfo());
+ // Expected every line to contain more than 4 elements, but it has only 3 elements.
+ await Verifier.Verify(Record.Exception(parser.GetTcpIPv4StateInfo)).UseMethodName("2").UseDirectory(VerifiedDataDirectory);
string tcpFile =
" sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\r\n" +
" 0: 030011AC:8AF2 C1B17822:01BB 00 00000000:00000000 02:000000D1 00000000 472 0 2481276 2 00000000c62511cb 28 4 26 10 -1\r\n";
string tcp6File =
" sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\r\n" +
- " 0: 00000000000000000000000000000000:0BBB 00000000000000000000000000000000:0003 " +
- "12 00000000:00000000 00:00000000 00000000 " +
- "472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n";
+ " 0: 00000000000000000000000000000000:0BBB 00000000000000000000000000000000:0003 12 00000000:00000000 00:00000000 00000000 472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n";
fileSystem.ReplaceFileContent(new FileInfo("/proc/net/tcp"), tcpFile);
fileSystem.ReplaceFileContent(new FileInfo("/proc/net/tcp6"), tcp6File);
// Cannot find status: 00 in LINUX_TCP_STATE
- Assert.Throws(() => parser.GetTcpIPv4StateInfo());
+ await Verifier.Verify(Record.Exception(parser.GetTcpIPv4StateInfo)).UseMethodName("3").UseDirectory(VerifiedDataDirectory);
// Cannot find status: 12 in LINUX_TCP_STATE
- Assert.Throws(() => parser.GetTcpIPv6StateInfo());
+ await Verifier.Verify(Record.Exception(parser.GetTcpIPv6StateInfo)).UseMethodName("4").UseDirectory(VerifiedDataDirectory);
tcpFile = "";
tcp6File = "";
@@ -75,17 +77,17 @@ public void Parser_Tcp_State_Info_Exception()
fileSystem.ReplaceFileContent(new FileInfo("/proc/net/tcp6"), tcp6File);
// Could not parse '/proc/net/tcp'. File was empty.
- Assert.Throws(() => parser.GetTcpIPv4StateInfo());
+ await Verifier.Verify(Record.Exception(parser.GetTcpIPv4StateInfo)).UseMethodName("5").UseDirectory(VerifiedDataDirectory);
// Could not parse '/proc/net/tcp6'. File was empty.
- Assert.Throws(() => parser.GetTcpIPv6StateInfo());
+ await Verifier.Verify(Record.Exception(parser.GetTcpIPv6StateInfo)).UseMethodName("6").UseDirectory(VerifiedDataDirectory);
}
[Fact]
public void Parser_Tcp_State_Info()
{
var parser = new LinuxNetworkUtilizationParser(new FileNamesOnlyFileSystem(TestResources.TestFilesLocation));
- var tcp4StateInfo = parser.GetTcpIPv4StateInfo();
+ TcpStateInfo tcp4StateInfo = parser.GetTcpIPv4StateInfo();
Assert.Equal(2, tcp4StateInfo.EstabCount);
Assert.Equal(1, tcp4StateInfo.SynSentCount);
@@ -112,5 +114,4 @@ public void Parser_Tcp_State_Info()
Assert.Equal(1, tcp6StateInfo.ListenCount);
Assert.Equal(1, tcp6StateInfo.ClosingCount);
}
-
}
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Resources/FileNamesOnlyFileSystem.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Resources/FileNamesOnlyFileSystem.cs
index a9e0fb0fbc3..41dee36f63c 100644
--- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Resources/FileNamesOnlyFileSystem.cs
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Resources/FileNamesOnlyFileSystem.cs
@@ -58,11 +58,11 @@ public int Read(FileInfo file, int length, Span destination)
public IEnumerable> ReadAllByLines(FileInfo file, BufferWriter destination)
{
- var a = File.ReadAllLines($"{_directory}/{file.Name}");
- foreach (var item in a)
+ string[] lines = File.ReadAllLines($"{_directory}/{file.Name}");
+ foreach (var line in lines)
{
destination.Reset();
- destination.Write(item);
+ destination.Write(line);
yield return destination.WrittenMemory;
}
}
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Resources/HardcodedValueFileSystem.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Resources/HardcodedValueFileSystem.cs
index ac83153ceeb..0ca972cd30c 100644
--- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Resources/HardcodedValueFileSystem.cs
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Resources/HardcodedValueFileSystem.cs
@@ -94,7 +94,7 @@ public void ReplaceFileContent(FileInfo file, string value)
public IEnumerable> ReadAllByLines(FileInfo file, BufferWriter destination)
{
- var flag = !_fileContent.TryGetValue(file.FullName, out var content);
+ bool flag = !_fileContent.TryGetValue(file.FullName, out var content);
if (_fileContent.Count == 0 || flag)
{
destination.Reset();
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.1.verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.1.verified.txt
new file mode 100644
index 00000000000..18fd7dd650a
--- /dev/null
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.1.verified.txt
@@ -0,0 +1,10 @@
+{
+ Type: InvalidOperationException,
+ Message: Could not split contents. We expected every line to contain more than 4 elements, but it has only 2 elements.,
+ StackTrace:
+at Microsoft.Shared.Diagnostics.Throw.InvalidOperationException(String message)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.UpdateTcpStateInfo(ReadOnlySpan`1 buffer, TcpStateInfo tcpStateInfo)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpStateInfo(FileInfo file)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpIPv4StateInfo()
+at Xunit.Record.Exception(Func`1 testCode)
+}
\ No newline at end of file
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.2.verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.2.verified.txt
new file mode 100644
index 00000000000..18fd7dd650a
--- /dev/null
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.2.verified.txt
@@ -0,0 +1,10 @@
+{
+ Type: InvalidOperationException,
+ Message: Could not split contents. We expected every line to contain more than 4 elements, but it has only 2 elements.,
+ StackTrace:
+at Microsoft.Shared.Diagnostics.Throw.InvalidOperationException(String message)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.UpdateTcpStateInfo(ReadOnlySpan`1 buffer, TcpStateInfo tcpStateInfo)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpStateInfo(FileInfo file)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpIPv4StateInfo()
+at Xunit.Record.Exception(Func`1 testCode)
+}
\ No newline at end of file
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.3.verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.3.verified.txt
new file mode 100644
index 00000000000..d221be2178e
--- /dev/null
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.3.verified.txt
@@ -0,0 +1,9 @@
+{
+ Type: InvalidEnumArgumentException,
+ Message: Cannot find status: 00 in LinuxTcpState,
+ StackTrace:
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.UpdateTcpStateInfo(ReadOnlySpan`1 buffer, TcpStateInfo tcpStateInfo)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpStateInfo(FileInfo file)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpIPv4StateInfo()
+at Xunit.Record.Exception(Func`1 testCode)
+}
\ No newline at end of file
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.4.verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.4.verified.txt
new file mode 100644
index 00000000000..b2bf541ce52
--- /dev/null
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.4.verified.txt
@@ -0,0 +1,9 @@
+{
+ Type: InvalidEnumArgumentException,
+ Message: Cannot find status: 12 in LinuxTcpState,
+ StackTrace:
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.UpdateTcpStateInfo(ReadOnlySpan`1 buffer, TcpStateInfo tcpStateInfo)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpStateInfo(FileInfo file)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpIPv6StateInfo()
+at Xunit.Record.Exception(Func`1 testCode)
+}
\ No newline at end of file
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.5.verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.5.verified.txt
new file mode 100644
index 00000000000..d05022da97a
--- /dev/null
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.5.verified.txt
@@ -0,0 +1,9 @@
+{
+ Type: InvalidOperationException,
+ Message: Could not parse '/proc/net/tcp'. File was empty.,
+ StackTrace:
+at Microsoft.Shared.Diagnostics.Throw.InvalidOperationException(String message)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpStateInfo(FileInfo file)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpIPv4StateInfo()
+at Xunit.Record.Exception(Func`1 testCode)
+}
\ No newline at end of file
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.6.verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.6.verified.txt
new file mode 100644
index 00000000000..745bbbf1397
--- /dev/null
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.6.verified.txt
@@ -0,0 +1,9 @@
+{
+ Type: InvalidOperationException,
+ Message: Could not parse '/proc/net/tcp6'. File was empty.,
+ StackTrace:
+at Microsoft.Shared.Diagnostics.Throw.InvalidOperationException(String message)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpStateInfo(FileInfo file)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpIPv6StateInfo()
+at Xunit.Record.Exception(Func`1 testCode)
+}
\ No newline at end of file
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid.verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid.verified.txt
new file mode 100644
index 00000000000..5f282702bb0
--- /dev/null
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid.verified.txt
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid_line= .verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid_line= .verified.txt
new file mode 100644
index 00000000000..5f282702bb0
--- /dev/null
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid_line= .verified.txt
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid_line=!@#!$%!@.verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid_line=!@#!$%!@.verified.txt
new file mode 100644
index 00000000000..5f282702bb0
--- /dev/null
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid_line=!@#!$%!@.verified.txt
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid_line=.verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid_line=.verified.txt
new file mode 100644
index 00000000000..5f282702bb0
--- /dev/null
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid_line=.verified.txt
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid_line=DFIJEUWGHFWGBWEFWOMDOWKSLA.verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid_line=DFIJEUWGHFWGBWEFWOMDOWKSLA.verified.txt
new file mode 100644
index 00000000000..247dbeafcb1
--- /dev/null
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid_line=DFIJEUWGHFWGBWEFWOMDOWKSLA.verified.txt
@@ -0,0 +1,9 @@
+{
+ Type: InvalidOperationException,
+ Message: Could not parse '/proc/net/tcp'. We expected first line of the file to start with 'sl' but it was 'DFIJEUWGHFWGBWEFWOMDOWKSLA' instead.,
+ StackTrace:
+at Microsoft.Shared.Diagnostics.Throw.InvalidOperationException(String message)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpStateInfo(FileInfo file)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpIPv4StateInfo()
+at Xunit.Record.Exception(Func`1 testCode)
+}
\ No newline at end of file
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid_line=________________________Asdasdasdas dd.verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid_line=________________________Asdasdasdas dd.verified.txt
new file mode 100644
index 00000000000..5f282702bb0
--- /dev/null
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid_line=________________________Asdasdasdas dd.verified.txt
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.ipv4_line= .verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.ipv4_line= .verified.txt
new file mode 100644
index 00000000000..715f6e120fd
--- /dev/null
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.ipv4_line= .verified.txt
@@ -0,0 +1,9 @@
+{
+ Type: InvalidOperationException,
+ Message: Could not parse '/proc/net/tcp'. We expected first line of the file to start with 'sl' but it was '' instead.,
+ StackTrace:
+at Microsoft.Shared.Diagnostics.Throw.InvalidOperationException(String message)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpStateInfo(FileInfo file)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpIPv4StateInfo()
+at Xunit.Record.Exception(Func`1 testCode)
+}
\ No newline at end of file
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.ipv4_line=!@#!$%!@.verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.ipv4_line=!@#!$%!@.verified.txt
new file mode 100644
index 00000000000..f48b75ef20b
--- /dev/null
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.ipv4_line=!@#!$%!@.verified.txt
@@ -0,0 +1,9 @@
+{
+ Type: InvalidOperationException,
+ Message: Could not parse '/proc/net/tcp'. We expected first line of the file to start with 'sl' but it was '!@#!$%!@' instead.,
+ StackTrace:
+at Microsoft.Shared.Diagnostics.Throw.InvalidOperationException(String message)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpStateInfo(FileInfo file)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpIPv4StateInfo()
+at Xunit.Record.Exception(Func`1 testCode)
+}
\ No newline at end of file
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.ipv4_line=.verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.ipv4_line=.verified.txt
new file mode 100644
index 00000000000..715f6e120fd
--- /dev/null
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.ipv4_line=.verified.txt
@@ -0,0 +1,9 @@
+{
+ Type: InvalidOperationException,
+ Message: Could not parse '/proc/net/tcp'. We expected first line of the file to start with 'sl' but it was '' instead.,
+ StackTrace:
+at Microsoft.Shared.Diagnostics.Throw.InvalidOperationException(String message)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpStateInfo(FileInfo file)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpIPv4StateInfo()
+at Xunit.Record.Exception(Func`1 testCode)
+}
\ No newline at end of file
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.ipv4_line=DFIJEUWGHFWGBWEFWOMDOWKSLA.verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.ipv4_line=DFIJEUWGHFWGBWEFWOMDOWKSLA.verified.txt
new file mode 100644
index 00000000000..247dbeafcb1
--- /dev/null
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.ipv4_line=DFIJEUWGHFWGBWEFWOMDOWKSLA.verified.txt
@@ -0,0 +1,9 @@
+{
+ Type: InvalidOperationException,
+ Message: Could not parse '/proc/net/tcp'. We expected first line of the file to start with 'sl' but it was 'DFIJEUWGHFWGBWEFWOMDOWKSLA' instead.,
+ StackTrace:
+at Microsoft.Shared.Diagnostics.Throw.InvalidOperationException(String message)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpStateInfo(FileInfo file)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpIPv4StateInfo()
+at Xunit.Record.Exception(Func`1 testCode)
+}
\ No newline at end of file
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.ipv4_line=________________________Asdasdasdas dd.verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.ipv4_line=________________________Asdasdasdas dd.verified.txt
new file mode 100644
index 00000000000..20ac0d91b3d
--- /dev/null
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.ipv4_line=________________________Asdasdasdas dd.verified.txt
@@ -0,0 +1,9 @@
+{
+ Type: InvalidOperationException,
+ Message: Could not parse '/proc/net/tcp'. We expected first line of the file to start with 'sl' but it was '________________________Asdasdasdas dd' instead.,
+ StackTrace:
+at Microsoft.Shared.Diagnostics.Throw.InvalidOperationException(String message)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpStateInfo(FileInfo file)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpIPv4StateInfo()
+at Xunit.Record.Exception(Func`1 testCode)
+}
\ No newline at end of file
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.ipv6_line= .verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.ipv6_line= .verified.txt
new file mode 100644
index 00000000000..715f6e120fd
--- /dev/null
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.ipv6_line= .verified.txt
@@ -0,0 +1,9 @@
+{
+ Type: InvalidOperationException,
+ Message: Could not parse '/proc/net/tcp'. We expected first line of the file to start with 'sl' but it was '' instead.,
+ StackTrace:
+at Microsoft.Shared.Diagnostics.Throw.InvalidOperationException(String message)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpStateInfo(FileInfo file)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpIPv4StateInfo()
+at Xunit.Record.Exception(Func`1 testCode)
+}
\ No newline at end of file
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.ipv6_line=!@#!$%!@.verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.ipv6_line=!@#!$%!@.verified.txt
new file mode 100644
index 00000000000..f48b75ef20b
--- /dev/null
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.ipv6_line=!@#!$%!@.verified.txt
@@ -0,0 +1,9 @@
+{
+ Type: InvalidOperationException,
+ Message: Could not parse '/proc/net/tcp'. We expected first line of the file to start with 'sl' but it was '!@#!$%!@' instead.,
+ StackTrace:
+at Microsoft.Shared.Diagnostics.Throw.InvalidOperationException(String message)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpStateInfo(FileInfo file)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpIPv4StateInfo()
+at Xunit.Record.Exception(Func`1 testCode)
+}
\ No newline at end of file
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.ipv6_line=.verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.ipv6_line=.verified.txt
new file mode 100644
index 00000000000..715f6e120fd
--- /dev/null
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.ipv6_line=.verified.txt
@@ -0,0 +1,9 @@
+{
+ Type: InvalidOperationException,
+ Message: Could not parse '/proc/net/tcp'. We expected first line of the file to start with 'sl' but it was '' instead.,
+ StackTrace:
+at Microsoft.Shared.Diagnostics.Throw.InvalidOperationException(String message)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpStateInfo(FileInfo file)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpIPv4StateInfo()
+at Xunit.Record.Exception(Func`1 testCode)
+}
\ No newline at end of file
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.ipv6_line=DFIJEUWGHFWGBWEFWOMDOWKSLA.verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.ipv6_line=DFIJEUWGHFWGBWEFWOMDOWKSLA.verified.txt
new file mode 100644
index 00000000000..247dbeafcb1
--- /dev/null
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.ipv6_line=DFIJEUWGHFWGBWEFWOMDOWKSLA.verified.txt
@@ -0,0 +1,9 @@
+{
+ Type: InvalidOperationException,
+ Message: Could not parse '/proc/net/tcp'. We expected first line of the file to start with 'sl' but it was 'DFIJEUWGHFWGBWEFWOMDOWKSLA' instead.,
+ StackTrace:
+at Microsoft.Shared.Diagnostics.Throw.InvalidOperationException(String message)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpStateInfo(FileInfo file)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpIPv4StateInfo()
+at Xunit.Record.Exception(Func`1 testCode)
+}
\ No newline at end of file
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.ipv6_line=________________________Asdasdasdas dd.verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.ipv6_line=________________________Asdasdasdas dd.verified.txt
new file mode 100644
index 00000000000..20ac0d91b3d
--- /dev/null
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.ipv6_line=________________________Asdasdasdas dd.verified.txt
@@ -0,0 +1,9 @@
+{
+ Type: InvalidOperationException,
+ Message: Could not parse '/proc/net/tcp'. We expected first line of the file to start with 'sl' but it was '________________________Asdasdasdas dd' instead.,
+ StackTrace:
+at Microsoft.Shared.Diagnostics.Throw.InvalidOperationException(String message)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpStateInfo(FileInfo file)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpIPv4StateInfo()
+at Xunit.Record.Exception(Func`1 testCode)
+}
\ No newline at end of file
From 7d01fc4c4c6c729fdb4cfd48418d2ff3e763ccd8 Mon Sep 17 00:00:00 2001
From: evgenyfedorov2 <25526458+evgenyfedorov2@users.noreply.github.com>
Date: Fri, 16 Aug 2024 13:33:49 +0200
Subject: [PATCH 04/13] Fix tests
---
src/Shared/BufferWriterPool/BufferWriter.cs | 2 ++
src/Shared/RentedSpan/RentedSpan.cs | 2 ++
src/Shared/StringSplit/StringRange.cs | 2 ++
.../Linux/LinuxNetworkUtilizationParserTests.cs | 4 ++--
...workUtilizationParserTests.1.DotNet6_0.verified.txt | 10 ++++++++++
...orkUtilizationParserTests.1.DotNet8_0.verified.txt} | 0
...workUtilizationParserTests.2.DotNet6_0.verified.txt | 10 ++++++++++
...orkUtilizationParserTests.2.DotNet8_0.verified.txt} | 0
8 files changed, 28 insertions(+), 2 deletions(-)
create mode 100644 test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.1.DotNet6_0.verified.txt
rename test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/{LinuxNetworkUtilizationParserTests.1.verified.txt => LinuxNetworkUtilizationParserTests.1.DotNet8_0.verified.txt} (100%)
create mode 100644 test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.2.DotNet6_0.verified.txt
rename test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/{LinuxNetworkUtilizationParserTests.2.verified.txt => LinuxNetworkUtilizationParserTests.2.DotNet8_0.verified.txt} (100%)
diff --git a/src/Shared/BufferWriterPool/BufferWriter.cs b/src/Shared/BufferWriterPool/BufferWriter.cs
index f8dc2b7c42a..18269616f49 100644
--- a/src/Shared/BufferWriterPool/BufferWriter.cs
+++ b/src/Shared/BufferWriterPool/BufferWriter.cs
@@ -8,7 +8,9 @@
using System.Runtime.CompilerServices;
#endif
+#pragma warning disable CA1716
namespace Microsoft.Shared.Pools;
+#pragma warning restore CA1716
///
/// Represents an output sink into which data can be written.
diff --git a/src/Shared/RentedSpan/RentedSpan.cs b/src/Shared/RentedSpan/RentedSpan.cs
index efebc2e2ffe..c7b429b0b67 100644
--- a/src/Shared/RentedSpan/RentedSpan.cs
+++ b/src/Shared/RentedSpan/RentedSpan.cs
@@ -5,7 +5,9 @@
using System.Buffers;
using System.Runtime.CompilerServices;
+#pragma warning disable CA1716
namespace Microsoft.Shared.Pools;
+#pragma warning restore CA1716
///
/// Represents a span that's potentially created over a rented array.
diff --git a/src/Shared/StringSplit/StringRange.cs b/src/Shared/StringSplit/StringRange.cs
index 2acd9317f77..e120dc3a869 100644
--- a/src/Shared/StringSplit/StringRange.cs
+++ b/src/Shared/StringSplit/StringRange.cs
@@ -6,7 +6,9 @@
using System;
using Microsoft.Shared.Diagnostics;
+#pragma warning disable CA1716
namespace Microsoft.Shared.StringSplit;
+#pragma warning restore CA1716
#if !SHARED_PROJECT
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/LinuxNetworkUtilizationParserTests.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/LinuxNetworkUtilizationParserTests.cs
index 1aaf633b755..71ef94168fd 100644
--- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/LinuxNetworkUtilizationParserTests.cs
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/LinuxNetworkUtilizationParserTests.cs
@@ -49,10 +49,10 @@ public async Task Parser_Tcp_State_Info_Exception()
var parser = new LinuxNetworkUtilizationParser(fileSystem);
// Expected every line to contain more than 4 elements, but it has only 2 elements.
- await Verifier.Verify(Record.Exception(parser.GetTcpIPv4StateInfo)).UseMethodName("1").UseDirectory(VerifiedDataDirectory);
+ await Verifier.Verify(Record.Exception(parser.GetTcpIPv4StateInfo)).UniqueForRuntimeAndVersion().UseMethodName("1").UseDirectory(VerifiedDataDirectory);
// Expected every line to contain more than 4 elements, but it has only 3 elements.
- await Verifier.Verify(Record.Exception(parser.GetTcpIPv4StateInfo)).UseMethodName("2").UseDirectory(VerifiedDataDirectory);
+ await Verifier.Verify(Record.Exception(parser.GetTcpIPv4StateInfo)).UniqueForRuntimeAndVersion().UseMethodName("2").UseDirectory(VerifiedDataDirectory);
string tcpFile =
" sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\r\n" +
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.1.DotNet6_0.verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.1.DotNet6_0.verified.txt
new file mode 100644
index 00000000000..09d6d8023a5
--- /dev/null
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.1.DotNet6_0.verified.txt
@@ -0,0 +1,10 @@
+{
+ Type: InvalidOperationException,
+ Message: Could not split contents. We expected every line to contain more than 3 elements, but it has only 2 elements.,
+ StackTrace:
+at Microsoft.Shared.Diagnostics.Throw.InvalidOperationException(String message)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.UpdateTcpStateInfo(ReadOnlySpan`1 buffer, TcpStateInfo tcpStateInfo)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpStateInfo(FileInfo file)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpIPv4StateInfo()
+at Xunit.Record.Exception(Func`1 testCode)
+}
\ No newline at end of file
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.1.verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.1.DotNet8_0.verified.txt
similarity index 100%
rename from test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.1.verified.txt
rename to test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.1.DotNet8_0.verified.txt
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.2.DotNet6_0.verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.2.DotNet6_0.verified.txt
new file mode 100644
index 00000000000..09d6d8023a5
--- /dev/null
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.2.DotNet6_0.verified.txt
@@ -0,0 +1,10 @@
+{
+ Type: InvalidOperationException,
+ Message: Could not split contents. We expected every line to contain more than 3 elements, but it has only 2 elements.,
+ StackTrace:
+at Microsoft.Shared.Diagnostics.Throw.InvalidOperationException(String message)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.UpdateTcpStateInfo(ReadOnlySpan`1 buffer, TcpStateInfo tcpStateInfo)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpStateInfo(FileInfo file)
+at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpIPv4StateInfo()
+at Xunit.Record.Exception(Func`1 testCode)
+}
\ No newline at end of file
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.2.verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.2.DotNet8_0.verified.txt
similarity index 100%
rename from test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.2.verified.txt
rename to test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.2.DotNet8_0.verified.txt
From 452842fbee14ede21f22e1a36d081d2d94c34ffd Mon Sep 17 00:00:00 2001
From: evgenyfedorov2 <25526458+evgenyfedorov2@users.noreply.github.com>
Date: Fri, 16 Aug 2024 13:37:08 +0200
Subject: [PATCH 05/13] fixes
---
src/Shared/BufferWriterPool/BufferWriterPooledObjectPolicy.cs | 2 ++
src/Shared/Data.Validation/TimeSpanAttribute.cs | 2 ++
src/Shared/Debugger/DebuggerExtensions.cs | 2 ++
3 files changed, 6 insertions(+)
diff --git a/src/Shared/BufferWriterPool/BufferWriterPooledObjectPolicy.cs b/src/Shared/BufferWriterPool/BufferWriterPooledObjectPolicy.cs
index d11d41a6930..50533f74b2b 100644
--- a/src/Shared/BufferWriterPool/BufferWriterPooledObjectPolicy.cs
+++ b/src/Shared/BufferWriterPool/BufferWriterPooledObjectPolicy.cs
@@ -4,7 +4,9 @@
using Microsoft.Extensions.ObjectPool;
using Microsoft.Shared.Diagnostics;
+#pragma warning disable CA1716
namespace Microsoft.Shared.Pools;
+#pragma warning restore CA1716
///
/// An object pooling policy designed for .
diff --git a/src/Shared/Data.Validation/TimeSpanAttribute.cs b/src/Shared/Data.Validation/TimeSpanAttribute.cs
index bc181e645cf..8830a4120e5 100644
--- a/src/Shared/Data.Validation/TimeSpanAttribute.cs
+++ b/src/Shared/Data.Validation/TimeSpanAttribute.cs
@@ -7,7 +7,9 @@
using System.Globalization;
using Microsoft.Shared.Diagnostics;
+#pragma warning disable CA1716
namespace Microsoft.Shared.Data.Validation;
+#pragma warning restore CA1716
///
/// Provides boundary validation for .
diff --git a/src/Shared/Debugger/DebuggerExtensions.cs b/src/Shared/Debugger/DebuggerExtensions.cs
index 4aa63ee590a..cc197be12c2 100644
--- a/src/Shared/Debugger/DebuggerExtensions.cs
+++ b/src/Shared/Debugger/DebuggerExtensions.cs
@@ -5,7 +5,9 @@
using Microsoft.Extensions.DependencyInjection.Extensions;
using Throw = Microsoft.Shared.Diagnostics.Throw;
+#pragma warning disable CA1716
namespace Microsoft.Shared.Diagnostics;
+#pragma warning restore CA1716
///
/// Adds debugger to DI container.
From 83f99e4b5a8bbb304cdad9484b5709bef5425d14 Mon Sep 17 00:00:00 2001
From: evgenyfedorov2 <25526458+evgenyfedorov2@users.noreply.github.com>
Date: Fri, 16 Aug 2024 13:50:30 +0200
Subject: [PATCH 06/13] fix warnings
---
.../Linux/Network/LinuxNetworkUtilizationParser.cs | 2 +-
.../Linux/Network/LinuxTcpTableInfo.cs | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkUtilizationParser.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkUtilizationParser.cs
index f622133eed9..29e6c42ad7f 100644
--- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkUtilizationParser.cs
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkUtilizationParser.cs
@@ -13,7 +13,7 @@
namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network;
-internal class LinuxNetworkUtilizationParser
+internal sealed class LinuxNetworkUtilizationParser
{
private static readonly ObjectPool> _sharedBufferWriterPool = BufferWriterPool.CreateBufferWriterPool();
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxTcpTableInfo.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxTcpTableInfo.cs
index 5ca031ff440..105c8bec9b5 100644
--- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxTcpTableInfo.cs
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxTcpTableInfo.cs
@@ -6,7 +6,7 @@
namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network;
-internal class LinuxTcpTableInfo : ITcpTableInfo
+internal sealed class LinuxTcpTableInfo : ITcpTableInfo
{
private readonly object _lock = new();
private readonly TimeSpan _samplingInterval;
From 5ae321cc53284dafdbead3f5f493f6ea39583267 Mon Sep 17 00:00:00 2001
From: evgenyfedorov2 <25526458+evgenyfedorov2@users.noreply.github.com>
Date: Wed, 21 Aug 2024 18:08:38 +0200
Subject: [PATCH 07/13] PR Comments
---
...pTableInfo.cs => ITcpStateInfoProvider.cs} | 12 ++--
.../Linux/IFileSystem.cs | 2 +-
.../Linux/Network/LinuxNetworkMetrics.cs | 67 +++++++++--------
.../Network/LinuxNetworkUtilizationParser.cs | 17 ++---
...uxTcpTableInfo.cs => LinuxTcpStateInfo.cs} | 8 +--
...ions.Diagnostics.ResourceMonitoring.csproj | 1 -
...ceMonitoringServiceCollectionExtensions.cs | 4 +-
.../Windows/Network/WindowsNetworkMetrics.cs | 71 +++++++++----------
...TcpTableInfo.cs => WindowsTcpStateInfo.cs} | 8 +--
.../Linux/LinuxCountersTests.cs | 4 +-
.../LinuxNetworkUtilizationParserTests.cs | 2 +-
...tworkUtilizationParserTests.3.verified.txt | 6 +-
...tworkUtilizationParserTests.4.verified.txt | 6 +-
...ceMonitoringOptionsCustomValidatorTests.cs | 2 +-
.../Windows/Tcp6TableInfoTests.cs | 16 ++---
.../Windows/TcpTableInfoTests.cs | 18 ++---
...apshotProvider_EmitsLogRecord.verified.txt | 68 ------------------
.../Windows/WindowsCountersTests.cs | 4 +-
18 files changed, 123 insertions(+), 193 deletions(-)
rename src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/{ITcpTableInfo.cs => ITcpStateInfoProvider.cs} (57%)
rename src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/{LinuxTcpTableInfo.cs => LinuxTcpStateInfo.cs} (85%)
rename src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Network/{WindowsTcpTableInfo.cs => WindowsTcpStateInfo.cs} (97%)
delete mode 100644 test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/Verified/WindowsContainerSnapshotProviderTests.SnapshotProvider_EmitsLogRecord.verified.txt
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ITcpTableInfo.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ITcpStateInfoProvider.cs
similarity index 57%
rename from src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ITcpTableInfo.cs
rename to src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ITcpStateInfoProvider.cs
index 90ec8c5664f..c7c0d999330 100644
--- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ITcpTableInfo.cs
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ITcpStateInfoProvider.cs
@@ -4,19 +4,19 @@
namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring;
///
-/// An interface for getting TCP/IP table information.
+/// An interface for getting TCP/IP state information.
///
-internal interface ITcpTableInfo
+internal interface ITcpStateInfoProvider
{
///
- /// Gets the last known snapshot of TCP/IP v4 state info on the system.
+ /// Gets the last known information about TCP/IP v4 state on the system.
///
/// An instance of .
- TcpStateInfo GetIpV4CachingSnapshot();
+ TcpStateInfo GetpIpV4TcpStateInfo();
///
- /// Gets the last known snapshot of TCP/IP v6 state info on the system.
+ /// Gets the last known information about TCP/IP v6 state on the system.
///
/// An instance of .
- TcpStateInfo GetIpV6CachingSnapshot();
+ TcpStateInfo GetpIpV6TcpStateInfo();
}
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/IFileSystem.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/IFileSystem.cs
index 2cb34d777ca..80e185a5e70 100644
--- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/IFileSystem.cs
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/IFileSystem.cs
@@ -16,7 +16,7 @@ internal interface IFileSystem
///
/// Checks for file existence.
///
- /// or .
+ /// if file exists; otherwise, .
bool Exists(FileInfo fileInfo);
///
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkMetrics.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkMetrics.cs
index 07c71fa039c..4476a7fc01f 100644
--- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkMetrics.cs
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkMetrics.cs
@@ -9,11 +9,11 @@ namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network;
internal sealed class LinuxNetworkMetrics
{
- private readonly ITcpTableInfo _tcpTableInfo;
+ private readonly ITcpStateInfoProvider _tcpStateInfoProvider;
- public LinuxNetworkMetrics(IMeterFactory meterFactory, ITcpTableInfo tcpTableInfo)
+ public LinuxNetworkMetrics(IMeterFactory meterFactory, ITcpStateInfoProvider tcpStateInfoProvider)
{
- _tcpTableInfo = tcpTableInfo;
+ _tcpStateInfoProvider = tcpStateInfoProvider;
#pragma warning disable CA2000 // Dispose objects before losing scope
// We don't dispose the meter because IMeterFactory handles that
@@ -22,11 +22,8 @@ public LinuxNetworkMetrics(IMeterFactory meterFactory, ITcpTableInfo tcpTableInf
var meter = meterFactory.Create(nameof(ResourceMonitoring));
#pragma warning restore CA2000 // Dispose objects before losing scope
- var tcpTag = new KeyValuePair("network.transport", "tcp");
- var commonTags = new TagList
- {
- tcpTag
- };
+ KeyValuePair tcpTag = new("network.transport", "tcp");
+ TagList commonTags = new() { tcpTag };
// The metric is aligned with
// https://github.com/open-telemetry/semantic-conventions/blob/main/docs/system/system-metrics.md#metric-systemnetworkconnections
@@ -43,38 +40,38 @@ private IEnumerable> GetMeasurements()
const string NetworkStateKey = "system.network.state";
// These are covered in https://github.com/open-telemetry/semantic-conventions/blob/main/docs/rpc/rpc-metrics.md#attributes:
- var tcpVersionFourTag = new KeyValuePair("network.type", "ipv4");
- var tcpVersionSixTag = new KeyValuePair("network.type", "ipv6");
+ KeyValuePair tcpVersionFourTag = new("network.type", "ipv4");
+ KeyValuePair tcpVersionSixTag = new("network.type", "ipv6");
- var measurements = new List>(24);
+ List> measurements = new(24);
// IPv4:
- TcpStateInfo snapshotV4 = _tcpTableInfo.GetIpV4CachingSnapshot();
- measurements.Add(new Measurement(snapshotV4.ClosedCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "close") }));
- measurements.Add(new Measurement(snapshotV4.ListenCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "listen") }));
- measurements.Add(new Measurement(snapshotV4.SynSentCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "syn_sent") }));
- measurements.Add(new Measurement(snapshotV4.SynRcvdCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "syn_recv") }));
- measurements.Add(new Measurement(snapshotV4.EstabCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "established") }));
- measurements.Add(new Measurement(snapshotV4.FinWait1Count, new TagList { tcpVersionFourTag, new(NetworkStateKey, "fin_wait_1") }));
- measurements.Add(new Measurement(snapshotV4.FinWait2Count, new TagList { tcpVersionFourTag, new(NetworkStateKey, "fin_wait_2") }));
- measurements.Add(new Measurement(snapshotV4.CloseWaitCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "close_wait") }));
- measurements.Add(new Measurement(snapshotV4.ClosingCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "closing") }));
- measurements.Add(new Measurement(snapshotV4.LastAckCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "last_ack") }));
- measurements.Add(new Measurement(snapshotV4.TimeWaitCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "time_wait") }));
+ TcpStateInfo stateV4 = _tcpStateInfoProvider.GetpIpV4TcpStateInfo();
+ measurements.Add(new Measurement(stateV4.ClosedCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "close") }));
+ measurements.Add(new Measurement(stateV4.ListenCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "listen") }));
+ measurements.Add(new Measurement(stateV4.SynSentCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "syn_sent") }));
+ measurements.Add(new Measurement(stateV4.SynRcvdCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "syn_recv") }));
+ measurements.Add(new Measurement(stateV4.EstabCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "established") }));
+ measurements.Add(new Measurement(stateV4.FinWait1Count, new TagList { tcpVersionFourTag, new(NetworkStateKey, "fin_wait_1") }));
+ measurements.Add(new Measurement(stateV4.FinWait2Count, new TagList { tcpVersionFourTag, new(NetworkStateKey, "fin_wait_2") }));
+ measurements.Add(new Measurement(stateV4.CloseWaitCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "close_wait") }));
+ measurements.Add(new Measurement(stateV4.ClosingCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "closing") }));
+ measurements.Add(new Measurement(stateV4.LastAckCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "last_ack") }));
+ measurements.Add(new Measurement(stateV4.TimeWaitCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "time_wait") }));
// IPv6:
- TcpStateInfo snapshotV6 = _tcpTableInfo.GetIpV6CachingSnapshot();
- measurements.Add(new Measurement(snapshotV6.ClosedCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "close") }));
- measurements.Add(new Measurement(snapshotV6.ListenCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "listen") }));
- measurements.Add(new Measurement(snapshotV6.SynSentCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "syn_sent") }));
- measurements.Add(new Measurement(snapshotV6.SynRcvdCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "syn_recv") }));
- measurements.Add(new Measurement(snapshotV6.EstabCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "established") }));
- measurements.Add(new Measurement(snapshotV6.FinWait1Count, new TagList { tcpVersionSixTag, new(NetworkStateKey, "fin_wait_1") }));
- measurements.Add(new Measurement(snapshotV6.FinWait2Count, new TagList { tcpVersionSixTag, new(NetworkStateKey, "fin_wait_2") }));
- measurements.Add(new Measurement(snapshotV6.CloseWaitCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "close_wait") }));
- measurements.Add(new Measurement(snapshotV6.ClosingCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "closing") }));
- measurements.Add(new Measurement(snapshotV6.LastAckCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "last_ack") }));
- measurements.Add(new Measurement(snapshotV6.TimeWaitCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "time_wait") }));
+ TcpStateInfo stateV6 = _tcpStateInfoProvider.GetpIpV6TcpStateInfo();
+ measurements.Add(new Measurement(stateV6.ClosedCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "close") }));
+ measurements.Add(new Measurement(stateV6.ListenCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "listen") }));
+ measurements.Add(new Measurement(stateV6.SynSentCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "syn_sent") }));
+ measurements.Add(new Measurement(stateV6.SynRcvdCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "syn_recv") }));
+ measurements.Add(new Measurement(stateV6.EstabCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "established") }));
+ measurements.Add(new Measurement(stateV6.FinWait1Count, new TagList { tcpVersionSixTag, new(NetworkStateKey, "fin_wait_1") }));
+ measurements.Add(new Measurement(stateV6.FinWait2Count, new TagList { tcpVersionSixTag, new(NetworkStateKey, "fin_wait_2") }));
+ measurements.Add(new Measurement(stateV6.CloseWaitCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "close_wait") }));
+ measurements.Add(new Measurement(stateV6.ClosingCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "closing") }));
+ measurements.Add(new Measurement(stateV6.LastAckCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "last_ack") }));
+ measurements.Add(new Measurement(stateV6.TimeWaitCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "time_wait") }));
return measurements;
}
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkUtilizationParser.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkUtilizationParser.cs
index 29e6c42ad7f..5fb2379d89f 100644
--- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkUtilizationParser.cs
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkUtilizationParser.cs
@@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
-using System.ComponentModel;
using System.IO;
using Microsoft.Extensions.ObjectPool;
#if !NET8_0_OR_GREATER
@@ -45,8 +44,8 @@ public LinuxNetworkUtilizationParser(IFileSystem fileSystem)
}
///
- /// The method is used to read Span data and calculate the TCP state info.
- /// Refer proc net tcp.
+ /// Parses the contents of the and updates the with the parsed information.
+ /// For the data format expected in the , refer proc net tcp.
///
private static void UpdateTcpStateInfo(ReadOnlySpan buffer, TcpStateInfo tcpStateInfo)
{
@@ -82,8 +81,9 @@ private static void UpdateTcpStateInfo(ReadOnlySpan buffer, TcpStateInfo t
#endif
// until this API proposal is implemented https://github.com/dotnet/runtime/issues/61397
- // we have to allocate & throw away memory using ToString():
- switch ((LinuxTcpState)Convert.ToInt32(tcpConnectionState.ToString(), Base16))
+ // we have to allocate & throw away memory using .ToString():
+ var state = (LinuxTcpState)Convert.ToInt32(tcpConnectionState.ToString(), Base16);
+ switch (state)
{
case LinuxTcpState.ESTABLISHED:
tcpStateInfo.EstabCount++;
@@ -119,7 +119,8 @@ private static void UpdateTcpStateInfo(ReadOnlySpan buffer, TcpStateInfo t
tcpStateInfo.ClosingCount++;
break;
default:
- throw new InvalidEnumArgumentException($"Cannot find status: {tcpConnectionState} in LinuxTcpState");
+ Throw.IfOutOfRange(state);
+ break;
}
}
@@ -130,7 +131,7 @@ private TcpStateInfo GetTcpStateInfo(FileInfo file)
{
// The value we are interested in starts with this "sl".
const string Sl = "sl";
- var tcpStateInfo = new TcpStateInfo();
+ TcpStateInfo tcpStateInfo = new();
using ReturnableBufferWriter bufferWriter = new(_sharedBufferWriterPool);
using var enumerableLines = _fileSystem.ReadAllByLines(file, bufferWriter.Buffer).GetEnumerator();
if (!enumerableLines.MoveNext())
@@ -138,7 +139,7 @@ private TcpStateInfo GetTcpStateInfo(FileInfo file)
Throw.InvalidOperationException($"Could not parse '{file}'. File was empty.");
}
- var firstLine = enumerableLines.Current.TrimStart().Span;
+ ReadOnlySpan firstLine = enumerableLines.Current.TrimStart().Span;
if (!firstLine.StartsWith(Sl, StringComparison.Ordinal))
{
Throw.InvalidOperationException($"Could not parse '{file}'. We expected first line of the file to start with '{Sl}' but it was '{firstLine.ToString()}' instead.");
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxTcpTableInfo.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxTcpStateInfo.cs
similarity index 85%
rename from src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxTcpTableInfo.cs
rename to src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxTcpStateInfo.cs
index 105c8bec9b5..390bcda64ea 100644
--- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxTcpTableInfo.cs
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxTcpStateInfo.cs
@@ -6,7 +6,7 @@
namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network;
-internal sealed class LinuxTcpTableInfo : ITcpTableInfo
+internal sealed class LinuxTcpStateInfo : ITcpStateInfoProvider
{
private readonly object _lock = new();
private readonly TimeSpan _samplingInterval;
@@ -17,19 +17,19 @@ internal sealed class LinuxTcpTableInfo : ITcpTableInfo
private TcpStateInfo _iPv6Snapshot = new();
private DateTimeOffset _refreshAfter;
- public LinuxTcpTableInfo(IOptions options, LinuxNetworkUtilizationParser parser)
+ public LinuxTcpStateInfo(IOptions options, LinuxNetworkUtilizationParser parser)
{
_samplingInterval = options.Value.SamplingInterval;
_parser = parser;
}
- public TcpStateInfo GetIpV4CachingSnapshot()
+ public TcpStateInfo GetpIpV4TcpStateInfo()
{
RefreshSnapshotIfNeeded();
return _iPv4Snapshot;
}
- public TcpStateInfo GetIpV6CachingSnapshot()
+ public TcpStateInfo GetpIpV6TcpStateInfo()
{
RefreshSnapshotIfNeeded();
return _iPv6Snapshot;
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Microsoft.Extensions.Diagnostics.ResourceMonitoring.csproj b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Microsoft.Extensions.Diagnostics.ResourceMonitoring.csproj
index a98c18191be..c8658a5945d 100644
--- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Microsoft.Extensions.Diagnostics.ResourceMonitoring.csproj
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Microsoft.Extensions.Diagnostics.ResourceMonitoring.csproj
@@ -45,7 +45,6 @@
-
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringServiceCollectionExtensions.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringServiceCollectionExtensions.cs
index 6e1343aa9b6..d145f718405 100644
--- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringServiceCollectionExtensions.cs
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringServiceCollectionExtensions.cs
@@ -91,7 +91,7 @@ private static ResourceMonitorBuilder AddWindowsProvider(this ResourceMonitorBui
_ = builder.Services
.AddActivatedSingleton()
- .AddActivatedSingleton();
+ .AddActivatedSingleton();
return builder;
}
@@ -123,7 +123,7 @@ private static ResourceMonitorBuilder AddLinuxProvider(this ResourceMonitorBuild
_ = builder.Services
.AddActivatedSingleton()
.AddActivatedSingleton()
- .AddActivatedSingleton();
+ .AddActivatedSingleton();
return builder;
}
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Network/WindowsNetworkMetrics.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Network/WindowsNetworkMetrics.cs
index 810eba9c2a2..60332880cd7 100644
--- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Network/WindowsNetworkMetrics.cs
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Network/WindowsNetworkMetrics.cs
@@ -9,11 +9,11 @@ namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Windows.Network;
internal sealed class WindowsNetworkMetrics
{
- private readonly ITcpTableInfo _tcpTableInfo;
+ private readonly ITcpStateInfoProvider _tcpStateInfoProvider;
- public WindowsNetworkMetrics(IMeterFactory meterFactory, ITcpTableInfo tcpTableInfo)
+ public WindowsNetworkMetrics(IMeterFactory meterFactory, ITcpStateInfoProvider tcpStateInfoProvider)
{
- _tcpTableInfo = tcpTableInfo;
+ _tcpStateInfoProvider = tcpStateInfoProvider;
#pragma warning disable CA2000 // Dispose objects before losing scope
// We don't dispose the meter because IMeterFactory handles that
@@ -22,11 +22,8 @@ public WindowsNetworkMetrics(IMeterFactory meterFactory, ITcpTableInfo tcpTableI
var meter = meterFactory.Create(nameof(ResourceMonitoring));
#pragma warning restore CA2000 // Dispose objects before losing scope
- var tcpTag = new KeyValuePair("network.transport", "tcp");
- var commonTags = new TagList
- {
- tcpTag
- };
+ KeyValuePair tcpTag = new("network.transport", "tcp");
+ TagList commonTags = new() { tcpTag };
// The metric is aligned with
// https://github.com/open-telemetry/semantic-conventions/blob/main/docs/system/system-metrics.md#metric-systemnetworkconnections
@@ -43,40 +40,40 @@ private IEnumerable> GetMeasurements()
const string NetworkStateKey = "system.network.state";
// These are covered in https://github.com/open-telemetry/semantic-conventions/blob/main/docs/rpc/rpc-metrics.md#attributes:
- var tcpVersionFourTag = new KeyValuePair("network.type", "ipv4");
- var tcpVersionSixTag = new KeyValuePair("network.type", "ipv6");
+ KeyValuePair tcpVersionFourTag = new("network.type", "ipv4");
+ KeyValuePair tcpVersionSixTag = new("network.type", "ipv6");
- var measurements = new List>(24);
+ List> measurements = new(24);
// IPv4:
- var snapshotV4 = _tcpTableInfo.GetIpV4CachingSnapshot();
- measurements.Add(new Measurement(snapshotV4.ClosedCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "close") }));
- measurements.Add(new Measurement(snapshotV4.ListenCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "listen") }));
- measurements.Add(new Measurement(snapshotV4.SynSentCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "syn_sent") }));
- measurements.Add(new Measurement(snapshotV4.SynRcvdCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "syn_recv") }));
- measurements.Add(new Measurement(snapshotV4.EstabCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "established") }));
- measurements.Add(new Measurement(snapshotV4.FinWait1Count, new TagList { tcpVersionFourTag, new(NetworkStateKey, "fin_wait_1") }));
- measurements.Add(new Measurement(snapshotV4.FinWait2Count, new TagList { tcpVersionFourTag, new(NetworkStateKey, "fin_wait_2") }));
- measurements.Add(new Measurement(snapshotV4.CloseWaitCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "close_wait") }));
- measurements.Add(new Measurement(snapshotV4.ClosingCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "closing") }));
- measurements.Add(new Measurement(snapshotV4.LastAckCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "last_ack") }));
- measurements.Add(new Measurement(snapshotV4.TimeWaitCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "time_wait") }));
- measurements.Add(new Measurement(snapshotV4.DeleteTcbCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "delete") }));
+ TcpStateInfo stateV4 = _tcpStateInfoProvider.GetpIpV4TcpStateInfo();
+ measurements.Add(new Measurement(stateV4.ClosedCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "close") }));
+ measurements.Add(new Measurement(stateV4.ListenCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "listen") }));
+ measurements.Add(new Measurement(stateV4.SynSentCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "syn_sent") }));
+ measurements.Add(new Measurement(stateV4.SynRcvdCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "syn_recv") }));
+ measurements.Add(new Measurement(stateV4.EstabCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "established") }));
+ measurements.Add(new Measurement(stateV4.FinWait1Count, new TagList { tcpVersionFourTag, new(NetworkStateKey, "fin_wait_1") }));
+ measurements.Add(new Measurement(stateV4.FinWait2Count, new TagList { tcpVersionFourTag, new(NetworkStateKey, "fin_wait_2") }));
+ measurements.Add(new Measurement(stateV4.CloseWaitCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "close_wait") }));
+ measurements.Add(new Measurement(stateV4.ClosingCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "closing") }));
+ measurements.Add(new Measurement(stateV4.LastAckCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "last_ack") }));
+ measurements.Add(new Measurement(stateV4.TimeWaitCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "time_wait") }));
+ measurements.Add(new Measurement(stateV4.DeleteTcbCount, new TagList { tcpVersionFourTag, new(NetworkStateKey, "delete") }));
// IPv6:
- var snapshotV6 = _tcpTableInfo.GetIpV6CachingSnapshot();
- measurements.Add(new Measurement(snapshotV6.ClosedCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "close") }));
- measurements.Add(new Measurement(snapshotV6.ListenCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "listen") }));
- measurements.Add(new Measurement(snapshotV6.SynSentCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "syn_sent") }));
- measurements.Add(new Measurement(snapshotV6.SynRcvdCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "syn_recv") }));
- measurements.Add(new Measurement(snapshotV6.EstabCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "established") }));
- measurements.Add(new Measurement(snapshotV6.FinWait1Count, new TagList { tcpVersionSixTag, new(NetworkStateKey, "fin_wait_1") }));
- measurements.Add(new Measurement(snapshotV6.FinWait2Count, new TagList { tcpVersionSixTag, new(NetworkStateKey, "fin_wait_2") }));
- measurements.Add(new Measurement(snapshotV6.CloseWaitCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "close_wait") }));
- measurements.Add(new Measurement(snapshotV6.ClosingCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "closing") }));
- measurements.Add(new Measurement(snapshotV6.LastAckCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "last_ack") }));
- measurements.Add(new Measurement(snapshotV6.TimeWaitCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "time_wait") }));
- measurements.Add(new Measurement(snapshotV6.DeleteTcbCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "delete") }));
+ TcpStateInfo stateV6 = _tcpStateInfoProvider.GetpIpV6TcpStateInfo();
+ measurements.Add(new Measurement(stateV6.ClosedCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "close") }));
+ measurements.Add(new Measurement(stateV6.ListenCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "listen") }));
+ measurements.Add(new Measurement(stateV6.SynSentCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "syn_sent") }));
+ measurements.Add(new Measurement(stateV6.SynRcvdCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "syn_recv") }));
+ measurements.Add(new Measurement(stateV6.EstabCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "established") }));
+ measurements.Add(new Measurement(stateV6.FinWait1Count, new TagList { tcpVersionSixTag, new(NetworkStateKey, "fin_wait_1") }));
+ measurements.Add(new Measurement(stateV6.FinWait2Count, new TagList { tcpVersionSixTag, new(NetworkStateKey, "fin_wait_2") }));
+ measurements.Add(new Measurement(stateV6.CloseWaitCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "close_wait") }));
+ measurements.Add(new Measurement(stateV6.ClosingCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "closing") }));
+ measurements.Add(new Measurement(stateV6.LastAckCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "last_ack") }));
+ measurements.Add(new Measurement(stateV6.TimeWaitCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "time_wait") }));
+ measurements.Add(new Measurement(stateV6.DeleteTcbCount, new TagList { tcpVersionSixTag, new(NetworkStateKey, "delete") }));
return measurements;
}
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Network/WindowsTcpTableInfo.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Network/WindowsTcpStateInfo.cs
similarity index 97%
rename from src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Network/WindowsTcpTableInfo.cs
rename to src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Network/WindowsTcpStateInfo.cs
index e203f5f0828..732c522cda5 100644
--- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Network/WindowsTcpTableInfo.cs
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Network/WindowsTcpStateInfo.cs
@@ -11,7 +11,7 @@
namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Windows.Network;
-internal sealed class WindowsTcpTableInfo : ITcpTableInfo
+internal sealed class WindowsTcpStateInfo : ITcpStateInfoProvider
{
private readonly object _lock = new();
private readonly FrozenSet _localIPAddresses;
@@ -25,7 +25,7 @@ internal sealed class WindowsTcpTableInfo : ITcpTableInfo
private static TimeProvider TimeProvider => TimeProvider.System;
private DateTimeOffset _refreshAfter;
- public WindowsTcpTableInfo(IOptions options)
+ public WindowsTcpStateInfo(IOptions options)
{
var stringAddresses = options.Value.SourceIpAddresses;
_localIPAddresses = stringAddresses
@@ -42,13 +42,13 @@ public WindowsTcpTableInfo(IOptions options)
_refreshAfter = default;
}
- public TcpStateInfo GetIpV4CachingSnapshot()
+ public TcpStateInfo GetpIpV4TcpStateInfo()
{
RefreshSnapshotIfNeeded();
return _iPv4Snapshot;
}
- public TcpStateInfo GetIpV6CachingSnapshot()
+ public TcpStateInfo GetpIpV6TcpStateInfo()
{
RefreshSnapshotIfNeeded();
return _iPv6Snapshot;
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/LinuxCountersTests.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/LinuxCountersTests.cs
index 40ffa7e011d..abe3098661d 100644
--- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/LinuxCountersTests.cs
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/LinuxCountersTests.cs
@@ -83,8 +83,8 @@ public void LinuxNetworkCounters_Registers_Instruments()
.Setup(x => x.Create(It.IsAny()))
.Returns(meter);
- var tcpTableInfo = new LinuxTcpTableInfo(options, parser);
- var lnm = new LinuxNetworkMetrics(meterFactoryMock.Object, tcpTableInfo);
+ var tcpStateInfo = new LinuxTcpStateInfo(options, parser);
+ var lnm = new LinuxNetworkMetrics(meterFactoryMock.Object, tcpStateInfo);
using var listener = new MeterListener
{
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/LinuxNetworkUtilizationParserTests.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/LinuxNetworkUtilizationParserTests.cs
index 71ef94168fd..ee3c60597c7 100644
--- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/LinuxNetworkUtilizationParserTests.cs
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/LinuxNetworkUtilizationParserTests.cs
@@ -59,7 +59,7 @@ public async Task Parser_Tcp_State_Info_Exception()
" 0: 030011AC:8AF2 C1B17822:01BB 00 00000000:00000000 02:000000D1 00000000 472 0 2481276 2 00000000c62511cb 28 4 26 10 -1\r\n";
string tcp6File =
" sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\r\n" +
- " 0: 00000000000000000000000000000000:0BBB 00000000000000000000000000000000:0003 12 00000000:00000000 00:00000000 00000000 472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n";
+ " 0: 00000000000000000000000000000000:0BBB 00000000000000000000000000000000:0003 0C 00000000:00000000 00:00000000 00000000 472 0 2455375 1 00000000f4cb7621 100 0 0 10 0\r\n";
fileSystem.ReplaceFileContent(new FileInfo("/proc/net/tcp"), tcpFile);
fileSystem.ReplaceFileContent(new FileInfo("/proc/net/tcp6"), tcp6File);
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.3.verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.3.verified.txt
index d221be2178e..cccdff9332f 100644
--- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.3.verified.txt
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.3.verified.txt
@@ -1,7 +1,9 @@
{
- Type: InvalidEnumArgumentException,
- Message: Cannot find status: 00 in LinuxTcpState,
+ Type: ArgumentOutOfRangeException,
+ Message: 0 is an invalid value for enum type Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxTcpState (Parameter 'state'),
+ ParamName: state,
StackTrace:
+at Microsoft.Shared.Diagnostics.Throw.ArgumentOutOfRangeException(String paramName, String message)
at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.UpdateTcpStateInfo(ReadOnlySpan`1 buffer, TcpStateInfo tcpStateInfo)
at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpStateInfo(FileInfo file)
at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpIPv4StateInfo()
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.4.verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.4.verified.txt
index b2bf541ce52..755a57b8e3c 100644
--- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.4.verified.txt
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.4.verified.txt
@@ -1,7 +1,9 @@
{
- Type: InvalidEnumArgumentException,
- Message: Cannot find status: 12 in LinuxTcpState,
+ Type: ArgumentOutOfRangeException,
+ Message: 12 is an invalid value for enum type Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxTcpState (Parameter 'state'),
+ ParamName: state,
StackTrace:
+at Microsoft.Shared.Diagnostics.Throw.ArgumentOutOfRangeException(String paramName, String message)
at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.UpdateTcpStateInfo(ReadOnlySpan`1 buffer, TcpStateInfo tcpStateInfo)
at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpStateInfo(FileInfo file)
at Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network.LinuxNetworkUtilizationParser.GetTcpIPv6StateInfo()
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/ResourceMonitoringOptionsCustomValidatorTests.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/ResourceMonitoringOptionsCustomValidatorTests.cs
index 35583f17224..2fabf0f9400 100644
--- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/ResourceMonitoringOptionsCustomValidatorTests.cs
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/ResourceMonitoringOptionsCustomValidatorTests.cs
@@ -20,7 +20,7 @@ public void Test_WindowsCountersOptionsCustomValidator_With_Wrong_IP_Address()
{
SourceIpAddresses = new HashSet { "" }
};
- var tcpTableInfo = new WindowsTcpTableInfo(Options.Options.Create(options));
+ var tcpStateInfo = new WindowsTcpStateInfo(Options.Options.Create(options));
var validator = new ResourceMonitoringOptionsCustomValidator();
var result = validator.Validate("", options);
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/Tcp6TableInfoTests.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/Tcp6TableInfoTests.cs
index 5f30cc9d8fd..3ef240e1274 100644
--- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/Tcp6TableInfoTests.cs
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/Tcp6TableInfoTests.cs
@@ -234,11 +234,11 @@ public void Test_Tcp6TableInfo_Get_UnsuccessfulStatus_All_The_Time()
SourceIpAddresses = new HashSet { "[::1]" },
SamplingInterval = DefaultTimeSpan
};
- WindowsTcpTableInfo tcp6TableInfo = new WindowsTcpTableInfo(Options.Options.Create(options));
+ WindowsTcpStateInfo tcp6TableInfo = new WindowsTcpStateInfo(Options.Options.Create(options));
tcp6TableInfo.SetGetTcp6TableDelegate(FakeGetTcp6TableWithUnsuccessfulStatusAllTheTime);
Assert.Throws(() =>
{
- var tcpStateInfo = tcp6TableInfo.GetIpV6CachingSnapshot();
+ var tcpStateInfo = tcp6TableInfo.GetpIpV6TcpStateInfo();
});
}
@@ -250,11 +250,11 @@ public void Test_Tcp6TableInfo_Get_InsufficientBuffer_Then_Get_InvalidParameter(
SourceIpAddresses = new HashSet { "[::1]" },
SamplingInterval = DefaultTimeSpan
};
- WindowsTcpTableInfo tcp6TableInfo = new WindowsTcpTableInfo(Options.Options.Create(options));
+ WindowsTcpStateInfo tcp6TableInfo = new WindowsTcpStateInfo(Options.Options.Create(options));
tcp6TableInfo.SetGetTcp6TableDelegate(FakeGetTcp6TableWithInsufficientBufferAndInvalidParameter);
Assert.Throws(() =>
{
- var tcpStateInfo = tcp6TableInfo.GetIpV6CachingSnapshot();
+ var tcpStateInfo = tcp6TableInfo.GetpIpV6TcpStateInfo();
});
}
@@ -268,9 +268,9 @@ public void Test_Tcp6TableInfo_Get_Correct_Information()
SourceIpAddresses = new HashSet { "[::1]" },
SamplingInterval = DefaultTimeSpan
};
- WindowsTcpTableInfo tcp6TableInfo = new WindowsTcpTableInfo(Options.Options.Create(options));
+ WindowsTcpStateInfo tcp6TableInfo = new WindowsTcpStateInfo(Options.Options.Create(options));
tcp6TableInfo.SetGetTcp6TableDelegate(FakeGetTcp6TableWithFakeInformation);
- var tcpStateInfo = tcp6TableInfo.GetIpV6CachingSnapshot();
+ var tcpStateInfo = tcp6TableInfo.GetpIpV6TcpStateInfo();
Assert.NotNull(tcpStateInfo);
Assert.Equal(1, tcpStateInfo.ClosedCount);
Assert.Equal(1, tcpStateInfo.ListenCount);
@@ -286,7 +286,7 @@ public void Test_Tcp6TableInfo_Get_Correct_Information()
Assert.Equal(1, tcpStateInfo.DeleteTcbCount);
// Second calling in a small interval.
- tcpStateInfo = tcp6TableInfo.GetIpV6CachingSnapshot();
+ tcpStateInfo = tcp6TableInfo.GetpIpV6TcpStateInfo();
Assert.NotNull(tcpStateInfo);
Assert.Equal(1, tcpStateInfo.ClosedCount);
Assert.Equal(1, tcpStateInfo.ListenCount);
@@ -303,7 +303,7 @@ public void Test_Tcp6TableInfo_Get_Correct_Information()
// Third calling in a long interval.
Thread.Sleep(6000);
- tcpStateInfo = tcp6TableInfo.GetIpV6CachingSnapshot();
+ tcpStateInfo = tcp6TableInfo.GetpIpV6TcpStateInfo();
Assert.NotNull(tcpStateInfo);
Assert.Equal(2, tcpStateInfo.ClosedCount);
Assert.Equal(2, tcpStateInfo.ListenCount);
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/TcpTableInfoTests.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/TcpTableInfoTests.cs
index 8f6309085ec..fb79d0cb839 100644
--- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/TcpTableInfoTests.cs
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/TcpTableInfoTests.cs
@@ -177,11 +177,11 @@ public void Test_TcpTableInfo_Get_UnsuccessfulStatus_All_The_Time()
SourceIpAddresses = new HashSet { "127.0.0.1" },
SamplingInterval = DefaultTimeSpan
};
- WindowsTcpTableInfo tcpTableInfo = new WindowsTcpTableInfo(Options.Options.Create(options));
+ WindowsTcpStateInfo tcpTableInfo = new WindowsTcpStateInfo(Options.Options.Create(options));
tcpTableInfo.SetGetTcpTableDelegate(FakeGetTcpTableWithUnsuccessfulStatusAllTheTime);
Assert.Throws(() =>
{
- var tcpStateInfo = tcpTableInfo.GetIpV4CachingSnapshot();
+ var tcpStateInfo = tcpTableInfo.GetpIpV4TcpStateInfo();
});
}
@@ -193,11 +193,11 @@ public void Test_TcpTableInfo_Get_InsufficientBuffer_Then_Get_InvalidParameter()
SourceIpAddresses = new HashSet { "127.0.0.1" },
SamplingInterval = DefaultTimeSpan
};
- WindowsTcpTableInfo tcpTableInfo = new WindowsTcpTableInfo(Options.Options.Create(options));
+ WindowsTcpStateInfo tcpTableInfo = new WindowsTcpStateInfo(Options.Options.Create(options));
tcpTableInfo.SetGetTcpTableDelegate(FakeGetTcpTableWithInsufficientBufferAndInvalidParameter);
Assert.Throws(() =>
{
- var tcpStateInfo = tcpTableInfo.GetIpV4CachingSnapshot();
+ var tcpStateInfo = tcpTableInfo.GetpIpV4TcpStateInfo();
});
}
@@ -211,9 +211,9 @@ public void Test_TcpTableInfo_Get_Correct_Information()
SourceIpAddresses = new HashSet { "127.0.0.1" },
SamplingInterval = DefaultTimeSpan
};
- WindowsTcpTableInfo tcpTableInfo = new WindowsTcpTableInfo(Options.Options.Create(options));
+ WindowsTcpStateInfo tcpTableInfo = new WindowsTcpStateInfo(Options.Options.Create(options));
tcpTableInfo.SetGetTcpTableDelegate(FakeGetTcpTableWithFakeInformation);
- var tcpStateInfo = tcpTableInfo.GetIpV4CachingSnapshot();
+ var tcpStateInfo = tcpTableInfo.GetpIpV4TcpStateInfo();
Assert.NotNull(tcpStateInfo);
Assert.Equal(1, tcpStateInfo.ClosedCount);
Assert.Equal(1, tcpStateInfo.ListenCount);
@@ -229,7 +229,7 @@ public void Test_TcpTableInfo_Get_Correct_Information()
Assert.Equal(1, tcpStateInfo.DeleteTcbCount);
// Second calling in a small interval.
- tcpStateInfo = tcpTableInfo.GetIpV4CachingSnapshot();
+ tcpStateInfo = tcpTableInfo.GetpIpV4TcpStateInfo();
Assert.NotNull(tcpStateInfo);
Assert.Equal(1, tcpStateInfo.ClosedCount);
Assert.Equal(1, tcpStateInfo.ListenCount);
@@ -246,7 +246,7 @@ public void Test_TcpTableInfo_Get_Correct_Information()
// Third calling in a long interval.
Thread.Sleep(6000);
- tcpStateInfo = tcpTableInfo.GetIpV4CachingSnapshot();
+ tcpStateInfo = tcpTableInfo.GetpIpV4TcpStateInfo();
Assert.NotNull(tcpStateInfo);
Assert.Equal(2, tcpStateInfo.ClosedCount);
Assert.Equal(2, tcpStateInfo.ListenCount);
@@ -268,7 +268,7 @@ public void Test_TcpTableInfo_CalculateCount_default_branch()
TcpStateInfo tcpStateInfo = new();
// Add this case to increase coverage, but 0 will not happen in actual case.
- WindowsTcpTableInfo.CalculateCount(tcpStateInfo, 0);
+ WindowsTcpStateInfo.CalculateCount(tcpStateInfo, 0);
Assert.NotNull(tcpStateInfo);
Assert.Equal(0, tcpStateInfo.ClosedCount);
Assert.Equal(0, tcpStateInfo.ListenCount);
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/Verified/WindowsContainerSnapshotProviderTests.SnapshotProvider_EmitsLogRecord.verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/Verified/WindowsContainerSnapshotProviderTests.SnapshotProvider_EmitsLogRecord.verified.txt
deleted file mode 100644
index 228dccfb1c4..00000000000
--- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/Verified/WindowsContainerSnapshotProviderTests.SnapshotProvider_EmitsLogRecord.verified.txt
+++ /dev/null
@@ -1,68 +0,0 @@
-[
- {
- Level: Information,
- Id: {
- Id: 1,
- Name: RunningInsideJobObject
- },
- State: [
- {
- {OriginalFormat}: Resource Monitoring is running inside a Job Object. For more information about Job Objects see https://aka.ms/job-objects
- }
- ],
- StructuredState: [
- {
- {OriginalFormat}: Resource Monitoring is running inside a Job Object. For more information about Job Objects see https://aka.ms/job-objects
- }
- ],
- Message: Resource Monitoring is running inside a Job Object. For more information about Job Objects see https://aka.ms/job-objects,
- Category: Microsoft.Extensions.Diagnostics.ResourceMonitoring.Windows.WindowsContainerSnapshotProvider,
- LevelEnabled: true,
- Timestamp: DateTimeOffset_1
- },
- {
- Level: Debug,
- Id: {
- Id: 6,
- Name: SystemResourcesInfo
- },
- State: [
- {
- {OriginalFormat}: System resources information: CpuLimit = {cpuLimit}, CpuRequest = {cpuRequest}, MemoryLimit = {memoryLimit}, MemoryRequest = {memoryRequest}.
- },
- {
- memoryRequest: 2000
- },
- {
- memoryLimit: 2000
- },
- {
- cpuRequest: 1
- },
- {
- cpuLimit: 1
- }
- ],
- StructuredState: [
- {
- {OriginalFormat}: System resources information: CpuLimit = {cpuLimit}, CpuRequest = {cpuRequest}, MemoryLimit = {memoryLimit}, MemoryRequest = {memoryRequest}.
- },
- {
- memoryRequest: 2000
- },
- {
- memoryLimit: 2000
- },
- {
- cpuRequest: 1
- },
- {
- cpuLimit: 1
- }
- ],
- Message: System resources information: CpuLimit = 1, CpuRequest = 1, MemoryLimit = 2000, MemoryRequest = 2000.,
- Category: Microsoft.Extensions.Diagnostics.ResourceMonitoring.Windows.WindowsContainerSnapshotProvider,
- LevelEnabled: true,
- Timestamp: DateTimeOffset_2
- }
-]
\ No newline at end of file
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/WindowsCountersTests.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/WindowsCountersTests.cs
index 7eb1fdec01e..4ef7d2c4038 100644
--- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/WindowsCountersTests.cs
+++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/WindowsCountersTests.cs
@@ -32,7 +32,7 @@ public void WindowsCounters_Registers_Instruments()
meterFactoryMock.Setup(x => x.Create(It.IsAny()))
.Returns(meter);
- var tcpTableInfo = new WindowsTcpTableInfo(Options.Options.Create(options));
+ var tcpTableInfo = new WindowsTcpStateInfo(Options.Options.Create(options));
tcpTableInfo.SetGetTcpTableDelegate(TcpTableInfoTests.FakeGetTcpTableWithFakeInformation);
tcpTableInfo.SetGetTcp6TableDelegate(Tcp6TableInfoTests.FakeGetTcp6TableWithFakeInformation);
var windowsCounters = new WindowsNetworkMetrics(meterFactoryMock.Object, tcpTableInfo);
@@ -74,7 +74,7 @@ public void WindowsCounters_Got_Unsuccessful()
meterFactoryMock.Setup(x => x.Create(It.IsAny()))
.Returns(meter);
- var tcpTableInfo = new WindowsTcpTableInfo(Options.Options.Create(options));
+ var tcpTableInfo = new WindowsTcpStateInfo(Options.Options.Create(options));
tcpTableInfo.SetGetTcpTableDelegate(TcpTableInfoTests.FakeGetTcpTableWithUnsuccessfulStatusAllTheTime);
tcpTableInfo.SetGetTcp6TableDelegate(Tcp6TableInfoTests.FakeGetTcp6TableWithUnsuccessfulStatusAllTheTime);
var windowsCounters = new WindowsNetworkMetrics(meterFactoryMock.Object, tcpTableInfo);
From 5adc7fd9544bd4c2f439c48c524d1dbbe7ce3a1e Mon Sep 17 00:00:00 2001
From: evgenyfedorov2 <25526458+evgenyfedorov2@users.noreply.github.com>
Date: Fri, 23 Aug 2024 12:42:56 +0200
Subject: [PATCH 08/13] PR comments
---
.../ITcpStateInfoProvider.cs | 4 ++--
.../Linux/Network/LinuxNetworkUtilizationParser.cs | 9 +++++----
...Tests.Parser_Throws_When_Data_Is_Invalid.verified.txt | 1 -
...arser_Throws_When_Data_Is_Invalid_line= .verified.txt | 1 -
...hrows_When_Data_Is_Invalid_line=!@#!$%!@.verified.txt | 1 -
...Parser_Throws_When_Data_Is_Invalid_line=.verified.txt | 1 -
..._________________Asdasdasdas dd.verified.txt | 1 -
7 files changed, 7 insertions(+), 11 deletions(-)
delete mode 100644 test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid.verified.txt
delete mode 100644 test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid_line= .verified.txt
delete mode 100644 test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid_line=!@#!$%!@.verified.txt
delete mode 100644 test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid_line=.verified.txt
delete mode 100644 test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid_line=________________________Asdasdasdas dd.verified.txt
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ITcpStateInfoProvider.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ITcpStateInfoProvider.cs
index c7c0d999330..0c51e5fc45a 100644
--- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ITcpStateInfoProvider.cs
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ITcpStateInfoProvider.cs
@@ -9,13 +9,13 @@ namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring;
internal interface ITcpStateInfoProvider
{
///
- /// Gets the last known information about TCP/IP v4 state on the system.
+ /// Gets the last known TCP/IP v4 state of the system.
///
/// An instance of .
TcpStateInfo GetpIpV4TcpStateInfo();
///
- /// Gets the last known information about TCP/IP v6 state on the system.
+ /// Gets the last known TCP/IP v6 state of the system.
///
/// An instance of .
TcpStateInfo GetpIpV6TcpStateInfo();
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkUtilizationParser.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkUtilizationParser.cs
index 5fb2379d89f..a08b2fd5c3e 100644
--- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkUtilizationParser.cs
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkUtilizationParser.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
+using System.Collections.Generic;
using System.IO;
using Microsoft.Extensions.ObjectPool;
#if !NET8_0_OR_GREATER
@@ -29,12 +30,12 @@ internal sealed class LinuxNetworkUtilizationParser
private readonly IFileSystem _fileSystem;
///
- /// Reads the contents of a file located at _tcp4 and parses it to extract information about the TCP/IP state info on the system.
+ /// Reads the contents of a file located at and parses it to extract information about the TCP/IP state info of the system.
///
public TcpStateInfo GetTcpIPv4StateInfo() => GetTcpStateInfo(_tcp);
///
- /// Reads the contents of a file located at _tcp6 and parses it to extract information about the TCP/IP state info on the system.
+ /// Reads the contents of a file located at and parses it to extract information about the TCP/IP state info of the system.
///
public TcpStateInfo GetTcpIPv6StateInfo() => GetTcpStateInfo(_tcp6);
@@ -125,7 +126,7 @@ private static void UpdateTcpStateInfo(ReadOnlySpan buffer, TcpStateInfo t
}
///
- /// Reads the contents of a file and parses it to extract information about the TCP/IP state info on the system.
+ /// Reads the contents of a file and parses it to extract information about the TCP/IP state info of the system.
///
private TcpStateInfo GetTcpStateInfo(FileInfo file)
{
@@ -133,7 +134,7 @@ private TcpStateInfo GetTcpStateInfo(FileInfo file)
const string Sl = "sl";
TcpStateInfo tcpStateInfo = new();
using ReturnableBufferWriter bufferWriter = new(_sharedBufferWriterPool);
- using var enumerableLines = _fileSystem.ReadAllByLines(file, bufferWriter.Buffer).GetEnumerator();
+ using IEnumerator> enumerableLines = _fileSystem.ReadAllByLines(file, bufferWriter.Buffer).GetEnumerator();
if (!enumerableLines.MoveNext())
{
Throw.InvalidOperationException($"Could not parse '{file}'. File was empty.");
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid.verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid.verified.txt
deleted file mode 100644
index 5f282702bb0..00000000000
--- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid.verified.txt
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid_line= .verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid_line= .verified.txt
deleted file mode 100644
index 5f282702bb0..00000000000
--- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid_line= .verified.txt
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid_line=!@#!$%!@.verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid_line=!@#!$%!@.verified.txt
deleted file mode 100644
index 5f282702bb0..00000000000
--- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid_line=!@#!$%!@.verified.txt
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid_line=.verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid_line=.verified.txt
deleted file mode 100644
index 5f282702bb0..00000000000
--- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid_line=.verified.txt
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid_line=________________________Asdasdasdas dd.verified.txt b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid_line=________________________Asdasdasdas dd.verified.txt
deleted file mode 100644
index 5f282702bb0..00000000000
--- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Verified/LinuxNetworkUtilizationParserTests.Parser_Throws_When_Data_Is_Invalid_line=________________________Asdasdasdas dd.verified.txt
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
From 8c20051acb9399e76fb0ed2df832393c74708f04 Mon Sep 17 00:00:00 2001
From: evgenyfedorov2 <25526458+evgenyfedorov2@users.noreply.github.com>
Date: Fri, 23 Aug 2024 14:58:42 +0200
Subject: [PATCH 09/13] PR comments
---
.../Linux/Network/LinuxNetworkUtilizationParser.cs | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkUtilizationParser.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkUtilizationParser.cs
index a08b2fd5c3e..39e0be31155 100644
--- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkUtilizationParser.cs
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkUtilizationParser.cs
@@ -51,6 +51,11 @@ public LinuxNetworkUtilizationParser(IFileSystem fileSystem)
private static void UpdateTcpStateInfo(ReadOnlySpan buffer, TcpStateInfo tcpStateInfo)
{
const int Base16 = 16;
+
+ // The buffer contains one line from /proc/net/tcp(6) file, e.g.:
+ // 0: 030011AC:8AF2 C1B17822:01BB 01 00000000:00000000 02:000000D1 00000000 472 0 2481276 2 00000000c62511cb 28 4 26 10 -1
+ // The line may contain leading spaces, so we have to trim those.
+ // tcpConnectionState is in the 4th column - i.e., "01".
ReadOnlySpan line = buffer.TrimStart();
#if NET8_0_OR_GREATER
@@ -81,8 +86,10 @@ private static void UpdateTcpStateInfo(ReadOnlySpan buffer, TcpStateInfo t
ReadOnlySpan tcpConnectionState = line.Slice(range[Target - 1].Index, range[Target - 1].Count);
#endif
- // until this API proposal is implemented https://github.com/dotnet/runtime/issues/61397
- // we have to allocate & throw away memory using .ToString():
+ // at this point, tcpConnectionState contains one of TCP connection states in hexadecimal format, e.g., "01",
+ // which we now need to convert to the LinuxTcpState enum.
+ // note: until this API proposal is implemented https://github.com/dotnet/runtime/issues/61397
+ // we have to allocate & throw away memory using .ToString()
var state = (LinuxTcpState)Convert.ToInt32(tcpConnectionState.ToString(), Base16);
switch (state)
{
From 149803b24922a09ef5d09b3a006dcf26477fbf75 Mon Sep 17 00:00:00 2001
From: evgenyfedorov2 <25526458+evgenyfedorov2@users.noreply.github.com>
Date: Fri, 23 Aug 2024 15:45:53 +0200
Subject: [PATCH 10/13] add try/finally
---
.../Linux/OSFileSystem.cs | 55 ++++++++++---------
1 file changed, 30 insertions(+), 25 deletions(-)
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/OSFileSystem.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/OSFileSystem.cs
index 010dea4c9f0..d9e76c0aa4c 100644
--- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/OSFileSystem.cs
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/OSFileSystem.cs
@@ -56,43 +56,48 @@ public IEnumerable> ReadAllByLines(FileInfo file, BufferWri
throw new FileNotFoundException();
}
- using FileStream stream = file.OpenRead();
-
Memory buffer = ArrayPool.Shared.Rent(MaxStackalloc);
- int read = stream.Read(buffer.Span);
- while (read > 0)
+ try
{
- var start = 0;
- var end = 0;
+ using FileStream stream = file.OpenRead();
- for (end = 0; end < read; end++)
+ int read = stream.Read(buffer.Span);
+ while (read > 0)
{
- if (buffer.Span[end] == (byte)'\n')
+ var start = 0;
+ var end = 0;
+
+ for (end = 0; end < read; end++)
+ {
+ if (buffer.Span[end] == (byte)'\n')
+ {
+ var length = end - start;
+ _ = Encoding.ASCII.GetChars(buffer.Span.Slice(start, length), destination.GetSpan(length));
+ destination.Advance(length);
+ start = end + 1;
+ yield return destination.WrittenMemory;
+ destination.Reset();
+ }
+ }
+
+ // Set the comparison in the while loop to end when the file has not been completely read into the buffer.
+ // It will then advance the last character to the destination for the next time yield return is called.
+ if (start < read)
{
- var length = end - start;
+ var length = read - start;
_ = Encoding.ASCII.GetChars(buffer.Span.Slice(start, length), destination.GetSpan(length));
destination.Advance(length);
- start = end + 1;
- yield return destination.WrittenMemory;
- destination.Reset();
}
- }
- // Set the comparison in the while loop to end when the file has not been completely read into the buffer.
- // It will then advance the last character to the destination for the next time yield return is called.
- if (start < read)
- {
- var length = read - start;
- _ = Encoding.ASCII.GetChars(buffer.Span.Slice(start, length), destination.GetSpan(length));
- destination.Advance(length);
+ read = stream.Read(buffer.Span);
}
-
- read = stream.Read(buffer.Span);
}
-
- if (MemoryMarshal.TryGetArray(buffer, out ArraySegment arraySegment) && arraySegment.Array != null)
+ finally
{
- ArrayPool.Shared.Return(arraySegment.Array);
+ if (MemoryMarshal.TryGetArray(buffer, out ArraySegment arraySegment) && arraySegment.Array != null)
+ {
+ ArrayPool.Shared.Return(arraySegment.Array);
+ }
}
}
From 53fc489a7876b560441263748f406aea23b21cfa Mon Sep 17 00:00:00 2001
From: evgenyfedorov2 <25526458+evgenyfedorov2@users.noreply.github.com>
Date: Fri, 23 Aug 2024 16:07:44 +0200
Subject: [PATCH 11/13] Update xml comments
---
.../Linux/IFileSystem.cs | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/IFileSystem.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/IFileSystem.cs
index 80e185a5e70..08cbfcf7e74 100644
--- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/IFileSystem.cs
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/IFileSystem.cs
@@ -20,34 +20,34 @@ internal interface IFileSystem
bool Exists(FileInfo fileInfo);
///
- /// Get directory names on the filesystem based on the provided pattern.
+ /// Get directory names on the filesystem based on the specified pattern.
///
- /// IReadOnlyCollection.
+ /// A read-only collection of the paths of directories that match the specified pattern, or an empty read-only collection if no directories are found.
IReadOnlyCollection GetDirectoryNames(string directory, string pattern);
///
- /// Reads content from the file.
+ /// Reads content of the given length from a file and writes the data in the destination buffer.
///
///
- /// Number of chars written.
+ /// The total number of bytes read into the destination buffer.
///
int Read(FileInfo file, int length, Span destination);
///
- /// Read all content from a file.
+ /// Read all content from a file and writes the data in the destination buffer.
///
void ReadAll(FileInfo file, BufferWriter destination);
///
- /// Reads first line from the file.
+ /// Reads first line from the file and writes the data in the destination buffer.
///
void ReadFirstLine(FileInfo file, BufferWriter destination);
///
- /// Reads all content from a file by line.
+ /// Reads all content from a file line by line.
///
///
- /// IEnumerable.
+ /// The enumerable that represents all the lines of the file.
///
IEnumerable> ReadAllByLines(FileInfo file, BufferWriter destination);
}
From 2cdcc52a2ac415010228e7a60abe020b86107bfd Mon Sep 17 00:00:00 2001
From: evgenyfedorov2 <25526458+evgenyfedorov2@users.noreply.github.com>
Date: Mon, 26 Aug 2024 10:42:56 +0200
Subject: [PATCH 12/13] PR comments
---
.../Linux/Network/LinuxNetworkMetrics.cs | 4 ++--
.../Network/LinuxNetworkUtilizationParser.cs | 14 ++++++-------
.../Windows/Network/WindowsNetworkMetrics.cs | 2 +-
src/Shared/StringSplit/README.md | 2 +-
src/Shared/StringSplit/StringRange.cs | 2 +-
.../StringSplit/StringSplitExtensions.cs | 20 +++++++++----------
6 files changed, 22 insertions(+), 22 deletions(-)
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkMetrics.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkMetrics.cs
index 4476a7fc01f..b5b60de894a 100644
--- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkMetrics.cs
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkMetrics.cs
@@ -17,8 +17,8 @@ public LinuxNetworkMetrics(IMeterFactory meterFactory, ITcpStateInfoProvider tcp
#pragma warning disable CA2000 // Dispose objects before losing scope
// We don't dispose the meter because IMeterFactory handles that
- // An issue on analyzer side: https://github.com/dotnet/roslyn-analyzers/issues/6912
- // Related documentation: https://github.com/dotnet/docs/pull/37170
+ // Is's a false-positive, see: https://github.com/dotnet/roslyn-analyzers/issues/6912
+ // Related documentation: https://github.com/dotnet/docs/pull/37170.
var meter = meterFactory.Create(nameof(ResourceMonitoring));
#pragma warning restore CA2000 // Dispose objects before losing scope
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkUtilizationParser.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkUtilizationParser.cs
index 39e0be31155..3b9860b31a7 100644
--- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkUtilizationParser.cs
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Network/LinuxNetworkUtilizationParser.cs
@@ -62,17 +62,17 @@ private static void UpdateTcpStateInfo(ReadOnlySpan buffer, TcpStateInfo t
const int Target = 5;
Span range = stackalloc Range[Target];
- // on .NET 8+, if capacity of destination range array is less than number of ranges found by ReadOnlySpan.Split(),
+ // In .NET 8+, if capacity of destination range array is less than number of ranges found by ReadOnlySpan.Split(),
// the last range in the array will get all the remaining elements of the ReadOnlySpan.
- // therefore we request 5 ranges instead of 4, and then range[Target - 2] will have the range we need without the remaining elements.
+ // Therefore, we request 5 ranges instead of 4, and then range[Target - 2] will have the range we need without the remaining elements.
int numRanges = line.Split(range, ' ', StringSplitOptions.RemoveEmptyEntries);
#else
const int Target = 4;
Span range = stackalloc StringRange[Target];
- // in our StringRange API, if capacity of destination range array is less than number of ranges found by ReadOnlySpan.TrySplit(),
+ // In our StringRange API, if capacity of destination range array is less than number of ranges found by ReadOnlySpan.TrySplit(),
// the last range in the array will get the last range as expected, and all remaining elements will be ignored.
- // hence range[Target - 1] will have the last range as we need.
+ // Hence range[Target - 1] will have the last range as we need.
_ = line.TrySplit(" ", range, out int numRanges, StringComparison.OrdinalIgnoreCase, StringSplitOptions.RemoveEmptyEntries);
#endif
if (numRanges < Target)
@@ -86,10 +86,10 @@ private static void UpdateTcpStateInfo(ReadOnlySpan buffer, TcpStateInfo t
ReadOnlySpan tcpConnectionState = line.Slice(range[Target - 1].Index, range[Target - 1].Count);
#endif
- // at this point, tcpConnectionState contains one of TCP connection states in hexadecimal format, e.g., "01",
+ // At this point, tcpConnectionState contains one of TCP connection states in hexadecimal format, e.g., "01",
// which we now need to convert to the LinuxTcpState enum.
- // note: until this API proposal is implemented https://github.com/dotnet/runtime/issues/61397
- // we have to allocate & throw away memory using .ToString()
+ // Note: until this API proposal is implemented https://github.com/dotnet/runtime/issues/61397
+ // we have to allocate and throw away memory using .ToString().
var state = (LinuxTcpState)Convert.ToInt32(tcpConnectionState.ToString(), Base16);
switch (state)
{
diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Network/WindowsNetworkMetrics.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Network/WindowsNetworkMetrics.cs
index 60332880cd7..be93f3b9fb1 100644
--- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Network/WindowsNetworkMetrics.cs
+++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Network/WindowsNetworkMetrics.cs
@@ -17,7 +17,7 @@ public WindowsNetworkMetrics(IMeterFactory meterFactory, ITcpStateInfoProvider t
#pragma warning disable CA2000 // Dispose objects before losing scope
// We don't dispose the meter because IMeterFactory handles that
- // An issue on analyzer side: https://github.com/dotnet/roslyn-analyzers/issues/6912
+ // Is's a false-positive, see: https://github.com/dotnet/roslyn-analyzers/issues/6912.
// Related documentation: https://github.com/dotnet/docs/pull/37170
var meter = meterFactory.Create(nameof(ResourceMonitoring));
#pragma warning restore CA2000 // Dispose objects before losing scope
diff --git a/src/Shared/StringSplit/README.md b/src/Shared/StringSplit/README.md
index 9c9f1110e7e..0ba6e2437eb 100644
--- a/src/Shared/StringSplit/README.md
+++ b/src/Shared/StringSplit/README.md
@@ -1,6 +1,6 @@
# Numeric Extensions
-`StringSplit` API to get allocation free string splitting for .NET runtime before .NET 8
+`StringSplit` API to get allocation-free string splitting for .NET runtime before .NET 8
To use this in your project, add the following to your `.csproj` file:
diff --git a/src/Shared/StringSplit/StringRange.cs b/src/Shared/StringSplit/StringRange.cs
index e120dc3a869..79d92a35c7e 100644
--- a/src/Shared/StringSplit/StringRange.cs
+++ b/src/Shared/StringSplit/StringRange.cs
@@ -68,7 +68,7 @@ public int CompareTo(object? obj)
return CompareTo(ss);
}
- if (obj != null)
+ if (obj is not null)
{
Throw.ArgumentException(nameof(obj), $"Provided value must be of type {typeof(StringRange)}, but was of type {obj.GetType()}.");
}
diff --git a/src/Shared/StringSplit/StringSplitExtensions.cs b/src/Shared/StringSplit/StringSplitExtensions.cs
index cbc8aedc805..c6ef8e6794a 100644
--- a/src/Shared/StringSplit/StringSplitExtensions.cs
+++ b/src/Shared/StringSplit/StringSplitExtensions.cs
@@ -21,7 +21,7 @@ internal static class StringSplitExtensions
/// A span to receive the individual string segments.
/// The number of string segments copied to the output.
/// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
- /// if there was enough space in the output array, otherwise .
+ /// if there was enough space in the output array; otherwise, .
public static bool TrySplit(
this ReadOnlySpan input,
char separator,
@@ -79,7 +79,7 @@ public static bool TrySplit(
/// A span to receive the individual string segments.
/// The number of string segments copied to the output.
/// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
- /// if there was enough space in the output array, otherwise .
+ /// if there was enough space in the output array; otherwise, .
public static bool TrySplit(
this ReadOnlySpan input,
ReadOnlySpan separators,
@@ -138,7 +138,7 @@ public static bool TrySplit(
/// The number of string segments copied to the output.
/// The kind of string comparison to apply to the separator strings.
/// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
- /// if there was enough space in the output array, otherwise .
+ /// if there was enough space in the output array; otherwise, .
public static bool TrySplit(
this ReadOnlySpan input,
string[] separators,
@@ -211,7 +211,7 @@ public static bool TrySplit(
/// The number of string segments copied to the output.
/// The kind of string comparison to apply to the separator strings.
/// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
- /// if there was enough space in the output array, otherwise .
+ /// if there was enough space in the output array; otherwise, .
public static bool TrySplit(
this ReadOnlySpan input,
string separator,
@@ -280,7 +280,7 @@ public static bool TrySplit(
/// A span to receive the individual string segments.
/// The number of string segments copied to the output.
/// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
- /// if there was enough space in the output array, otherwise .
+ /// if there was enough space in the output array; otherwise, .
/// This uses whitespace as a separator of individual substrings.
public static bool TrySplit(
this ReadOnlySpan input,
@@ -347,7 +347,7 @@ public static bool TrySplit(
/// A span to receive the individual string segments.
/// The number of string segments copied to the output.
/// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
- /// if there was enough space in the output array, otherwise .
+ /// if there was enough space in the output array; otherwise, .
public static bool TrySplit(
this string input,
char separator,
@@ -364,7 +364,7 @@ public static bool TrySplit(
/// A span to receive the individual string segments.
/// The number of string segments copied to the output.
/// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
- /// if there was enough space in the output array, otherwise .
+ /// if there was enough space in the output array; otherwise, .
public static bool TrySplit(
this string input,
ReadOnlySpan separators,
@@ -382,7 +382,7 @@ public static bool TrySplit(
/// The number of string segments copied to the output.
/// The kind of string comparison to apply to the separator strings.
/// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
- /// if there was enough space in the output array, otherwise .
+ /// if there was enough space in the output array; otherwise, .
public static bool TrySplit(
this string input,
string[] separators,
@@ -401,7 +401,7 @@ public static bool TrySplit(
/// The number of string segments copied to the output.
/// The kind of string comparison to apply to the separator strings.
/// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
- /// if there was enough space in the output array, otherwise .
+ /// if there was enough space in the output array; otherwise, .
public static bool TrySplit(
this string input,
string separator,
@@ -418,7 +418,7 @@ public static bool TrySplit(
/// A span to receive the individual string segments.
/// The number of string segments copied to the output.
/// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
- /// if there was enough space in the output array, otherwise .
+ /// if there was enough space in the output array; otherwise, .
/// This uses whitespace as a separator of individual substrings.
public static bool TrySplit(
this string input,
From 8f0215c499a22d6f176b237858b5a8247d1af168 Mon Sep 17 00:00:00 2001
From: evgenyfedorov2 <25526458+evgenyfedorov2@users.noreply.github.com>
Date: Tue, 27 Aug 2024 13:15:50 +0200
Subject: [PATCH 13/13] Remove extra stringSplit extensions
---
.../StringSplit/StringSplitExtensions.cs | 806 +-----------------
.../StringSplit/SplitExtensionsTests.cs | 349 +-------
2 files changed, 9 insertions(+), 1146 deletions(-)
diff --git a/src/Shared/StringSplit/StringSplitExtensions.cs b/src/Shared/StringSplit/StringSplitExtensions.cs
index c6ef8e6794a..cee4579d1e1 100644
--- a/src/Shared/StringSplit/StringSplitExtensions.cs
+++ b/src/Shared/StringSplit/StringSplitExtensions.cs
@@ -1,7 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-#if !NET8_0_OR_GREATER
+#if NET6_0
using System;
using Microsoft.Shared.Diagnostics;
@@ -13,195 +13,6 @@ namespace Microsoft.Shared.StringSplit;
#endif
internal static class StringSplitExtensions
{
- ///
- /// Splits a string into a number of string segments.
- ///
- /// The string to split.
- /// A character that delimits the substrings in this instance.
- /// A span to receive the individual string segments.
- /// The number of string segments copied to the output.
- /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
- /// if there was enough space in the output array; otherwise, .
- public static bool TrySplit(
- this ReadOnlySpan input,
- char separator,
- Span result,
- out int numSegments,
- StringSplitOptions options = StringSplitOptions.None)
- {
- const int SeparatorLen = 1;
-
- CheckStringSplitOptions(options);
-
- numSegments = 0;
-
- int start = 0;
- while (true)
- {
- int index = input.Slice(start).IndexOf(separator);
- var sp = index < 0 ? input.Slice(start) : input.Slice(start, index);
-
- var rangeStart = start;
-#if NET5_0_OR_GREATER
- if ((options & StringSplitOptions.TrimEntries) != 0)
- {
- var len = sp.Length;
- sp = sp.TrimStart();
- rangeStart = start + len - sp.Length;
- sp = sp.TrimEnd();
- }
-#endif
-
- if (sp.Length > 0 || (options & StringSplitOptions.RemoveEmptyEntries) == 0)
- {
- if (numSegments >= result.Length)
- {
- return false;
- }
-
- result[numSegments++] = new StringRange(rangeStart, sp.Length);
- }
-
- if (index < 0)
- {
- return true;
- }
-
- start += index + SeparatorLen;
- }
- }
-
- ///
- /// Splits a string into a number of string segments.
- ///
- /// The string to split.
- /// The characters that delimit the substrings in this instance. This is not treated as a string, this is used as an array of individual characters.
- /// A span to receive the individual string segments.
- /// The number of string segments copied to the output.
- /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
- /// if there was enough space in the output array; otherwise, .
- public static bool TrySplit(
- this ReadOnlySpan input,
- ReadOnlySpan separators,
- Span result,
- out int numSegments,
- StringSplitOptions options = StringSplitOptions.None)
- {
- const int SeparatorLen = 1;
-
- CheckStringSplitOptions(options);
-
- numSegments = 0;
-
- int start = 0;
- while (true)
- {
- int index = input.Slice(start).IndexOfAny(separators);
- var sp = index < 0 ? input.Slice(start) : input.Slice(start, index);
-
- var rangeStart = start;
-#if NET5_0_OR_GREATER
- if ((options & StringSplitOptions.TrimEntries) != 0)
- {
- var len = sp.Length;
- sp = sp.TrimStart();
- rangeStart = start + len - sp.Length;
- sp = sp.TrimEnd();
- }
-#endif
-
- if (sp.Length > 0 || (options & StringSplitOptions.RemoveEmptyEntries) == 0)
- {
- if (numSegments >= result.Length)
- {
- return false;
- }
-
- result[numSegments++] = new StringRange(rangeStart, sp.Length);
- }
-
- if (index < 0)
- {
- return true;
- }
-
- start += index + SeparatorLen;
- }
- }
-
- ///
- /// Splits a string into a number of string segments.
- ///
- /// The string to split.
- /// The strings that delimit the substrings in this instance.
- /// A span to receive the individual string segments.
- /// The number of string segments copied to the output.
- /// The kind of string comparison to apply to the separator strings.
- /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
- /// if there was enough space in the output array; otherwise, .
- public static bool TrySplit(
- this ReadOnlySpan input,
- string[] separators,
- Span result,
- out int numSegments,
- StringComparison comparison = StringComparison.Ordinal,
- StringSplitOptions options = StringSplitOptions.None)
- {
- _ = Throw.IfNull(separators);
- CheckStringSplitOptions(options);
-
- numSegments = 0;
-
- int start = 0;
- while (true)
- {
- int index = -1;
- int separatorLen = 0;
- foreach (var sep in separators)
- {
- int found = input.Slice(start).IndexOf(sep.AsSpan(), comparison);
- if (found >= 0)
- {
- if (found < index || index < 0)
- {
- separatorLen = sep.Length;
- index = found;
- }
- }
- }
-
- var sp = index < 0 ? input.Slice(start) : input.Slice(start, index);
-
- var rangeStart = start;
-#if NET5_0_OR_GREATER
- if ((options & StringSplitOptions.TrimEntries) != 0)
- {
- var len = sp.Length;
- sp = sp.TrimStart();
- rangeStart = start + len - sp.Length;
- sp = sp.TrimEnd();
- }
-#endif
-
- if (sp.Length > 0 || (options & StringSplitOptions.RemoveEmptyEntries) == 0)
- {
- if (numSegments >= result.Length)
- {
- return false;
- }
-
- result[numSegments++] = new StringRange(rangeStart, sp.Length);
- }
-
- if (index < 0)
- {
- return true;
- }
-
- start += index + separatorLen;
- }
- }
-
///
/// Splits a string into a number of string segments.
///
@@ -244,7 +55,6 @@ public static bool TrySplit(
var sp = index < 0 ? input.Slice(start) : input.Slice(start, index);
var rangeStart = start;
-#if NET5_0_OR_GREATER
if ((options & StringSplitOptions.TrimEntries) != 0)
{
var len = sp.Length;
@@ -252,7 +62,6 @@ public static bool TrySplit(
rangeStart = start + len - sp.Length;
sp = sp.TrimEnd();
}
-#endif
if (sp.Length > 0 || (options & StringSplitOptions.RemoveEmptyEntries) == 0)
{
@@ -273,619 +82,16 @@ public static bool TrySplit(
}
}
- ///
- /// Splits a string into a number of string segments.
- ///
- /// The string to split.
- /// A span to receive the individual string segments.
- /// The number of string segments copied to the output.
- /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
- /// if there was enough space in the output array; otherwise, .
- /// This uses whitespace as a separator of individual substrings.
- public static bool TrySplit(
- this ReadOnlySpan input,
- Span result,
- out int numSegments,
- StringSplitOptions options = StringSplitOptions.None)
+ private static void CheckStringSplitOptions(StringSplitOptions options)
{
- const int SeparatorLen = 1;
-
- CheckStringSplitOptions(options);
-
- numSegments = 0;
+ const StringSplitOptions AllValidFlags = StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries;
- int start = 0;
- while (true)
+ if ((options & ~AllValidFlags) != 0)
{
- int index = -1;
- for (int i = start; i < input.Length; i++)
- {
- if (char.IsWhiteSpace(input[i]))
- {
- index = i - start;
- break;
- }
- }
-
- var sp = index < 0 ? input.Slice(start) : input.Slice(start, index);
-
- var rangeStart = start;
-#if NET5_0_OR_GREATER
- if ((options & StringSplitOptions.TrimEntries) != 0)
- {
- var len = sp.Length;
- sp = sp.TrimStart();
- rangeStart = start + len - sp.Length;
- sp = sp.TrimEnd();
- }
-#endif
-
- if (sp.Length > 0 || (options & StringSplitOptions.RemoveEmptyEntries) == 0)
- {
- if (numSegments >= result.Length)
- {
- return false;
- }
-
- result[numSegments++] = new StringRange(rangeStart, sp.Length);
- }
-
- if (index < 0)
- {
- return true;
- }
-
- start += index + SeparatorLen;
+ // at least one invalid flag was set
+ Throw.ArgumentException(nameof(options), "Invalid split options specified");
}
}
-
- ///
- /// Splits a string into a number of string segments.
- ///
- /// The string to split.
- /// A character that delimits the substrings in this instance.
- /// A span to receive the individual string segments.
- /// The number of string segments copied to the output.
- /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
- /// if there was enough space in the output array; otherwise, .
- public static bool TrySplit(
- this string input,
- char separator,
- Span result,
- out int numSegments,
- StringSplitOptions options = StringSplitOptions.None)
- => TrySplit(input.AsSpan(), separator, result, out numSegments, options);
-
- ///
- /// Splits a string into a number of string segments.
- ///
- /// The string to split.
- /// The characters that delimit the substrings in this instance. This is not treated as a string, this is used as an array of individual characters.
- /// A span to receive the individual string segments.
- /// The number of string segments copied to the output.
- /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
- /// if there was enough space in the output array; otherwise, .
- public static bool TrySplit(
- this string input,
- ReadOnlySpan separators,
- Span result,
- out int numSegments,
- StringSplitOptions options = StringSplitOptions.None)
- => TrySplit(input.AsSpan(), separators, result, out numSegments, options);
-
- ///
- /// Splits a string into a number of string segments.
- ///
- /// The string to split.
- /// The strings that delimit the substrings in this instance.
- /// A span to receive the individual string segments.
- /// The number of string segments copied to the output.
- /// The kind of string comparison to apply to the separator strings.
- /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
- /// if there was enough space in the output array; otherwise, .
- public static bool TrySplit(
- this string input,
- string[] separators,
- Span result,
- out int numSegments,
- StringComparison comparison = StringComparison.Ordinal,
- StringSplitOptions options = StringSplitOptions.None)
- => TrySplit(input.AsSpan(), separators, result, out numSegments, comparison, options);
-
- ///
- /// Splits a string into a number of string segments.
- ///
- /// The string to split.
- /// The string that delimits the substrings in this instance.
- /// A span to receive the individual string segments.
- /// The number of string segments copied to the output.
- /// The kind of string comparison to apply to the separator strings.
- /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
- /// if there was enough space in the output array; otherwise, .
- public static bool TrySplit(
- this string input,
- string separator,
- Span result,
- out int numSegments,
- StringComparison comparison = StringComparison.Ordinal,
- StringSplitOptions options = StringSplitOptions.None)
- => TrySplit(input.AsSpan(), separator, result, out numSegments, comparison, options);
-
- ///
- /// Splits a string into a number of string segments.
- ///
- /// The string to split.
- /// A span to receive the individual string segments.
- /// The number of string segments copied to the output.
- /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
- /// if there was enough space in the output array; otherwise, .
- /// This uses whitespace as a separator of individual substrings.
- public static bool TrySplit(
- this string input,
- Span result,
- out int numSegments,
- StringSplitOptions options = StringSplitOptions.None) => TrySplit(input.AsSpan(), result, out numSegments, options);
-
- private static void CheckStringSplitOptions(StringSplitOptions options)
- {
-#if NET5_0_OR_GREATER
- const StringSplitOptions AllValidFlags = StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries;
-#else
- const StringSplitOptions AllValidFlags = StringSplitOptions.RemoveEmptyEntries;
-#endif
-
- if ((options & ~AllValidFlags) != 0)
- {
- // at least one invalid flag was set
- Throw.ArgumentException(nameof(options), "Invalid split options specified");
- }
- }
-
- ///
- /// The delegate that gets invoked when visiting the splits of a string.
- ///
- /// Type of the context value given to the delegate.
- /// The span of characters that makes up the split.
- /// The monotonically increasing split count.
- /// The user-defined context object.
- public delegate void SplitVisitor(ReadOnlySpan split, int segmentNum, T context);
-
- ///
- /// Invokes a delegate for individual string segments.
- ///
- /// The string to split.
- /// A character that delimits the substrings in this instance.
- /// An object that can be used to pass state to the visitor.
- /// A delegate that gets invoked for each individual segment.
- /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
- /// The type of the visitor's context.
- ///
- /// The visitor delegate is invoked for each segment in the input. It is given as parameter the
- /// value of the argument, the segment index, and a range for the segment.
- ///
- public static void VisitSplits(
- this ReadOnlySpan input,
- char separator,
- TContext context,
- SplitVisitor visitor,
- StringSplitOptions options = StringSplitOptions.None)
- {
- const int SeparatorLen = 1;
-
- _ = Throw.IfNull(visitor);
- CheckStringSplitOptions(options);
-
- int numSegments = 0;
- int start = 0;
- while (true)
- {
- int index = input.Slice(start).IndexOf(separator);
- var sp = index < 0 ? input.Slice(start) : input.Slice(start, index);
-
- var rangeStart = start;
-#if NET5_0_OR_GREATER
- if ((options & StringSplitOptions.TrimEntries) != 0)
- {
- var len = sp.Length;
- sp = sp.TrimStart();
- rangeStart = start + len - sp.Length;
- sp = sp.TrimEnd();
- }
-#endif
-
- if (sp.Length > 0 || (options & StringSplitOptions.RemoveEmptyEntries) == 0)
- {
- visitor(input.Slice(rangeStart, sp.Length), numSegments++, context);
- }
-
- if (index < 0)
- {
- return;
- }
-
- start += index + SeparatorLen;
- }
- }
-
- ///
- /// Invokes a delegate for individual string segments.
- ///
- /// The string to split.
- /// The characters that delimit the substrings in this instance.
- /// An object that can be used to pass state to the visitor.
- /// A delegate that gets invoked for each individual segment.
- /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
- /// The type of the visitor's context.
- ///
- /// The visitor delegate is invoked for each segment in the input. It is given as parameter the
- /// value of the argument, the segment index, and a range for the segment.
- ///
- public static void VisitSplits(
- this ReadOnlySpan input,
- ReadOnlySpan separators,
- TContext context,
- SplitVisitor visitor,
- StringSplitOptions options = StringSplitOptions.None)
- {
- const int SeparatorLen = 1;
-
- _ = Throw.IfNull(visitor);
- CheckStringSplitOptions(options);
-
- int numSegments = 0;
- int start = 0;
- while (true)
- {
- int index = input.Slice(start).IndexOfAny(separators);
- var sp = index < 0 ? input.Slice(start) : input.Slice(start, index);
-
- var rangeStart = start;
-#if NET5_0_OR_GREATER
- if ((options & StringSplitOptions.TrimEntries) != 0)
- {
- var len = sp.Length;
- sp = sp.TrimStart();
- rangeStart = start + len - sp.Length;
- sp = sp.TrimEnd();
- }
-#endif
-
- if (sp.Length > 0 || (options & StringSplitOptions.RemoveEmptyEntries) == 0)
- {
- visitor(input.Slice(rangeStart, sp.Length), numSegments++, context);
- }
-
- if (index < 0)
- {
- return;
- }
-
- start += index + SeparatorLen;
- }
- }
-
- ///
- /// Invokes a delegate for individual string segments.
- ///
- /// The string to split.
- /// The strings that delimit the substrings in this instance.
- /// An object that can be used to pass state to the visitor.
- /// A delegate that gets invoked for each individual segment.
- /// The kind of string comparison to apply to the separator strings.
- /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
- /// The type of the visitor's context.
- ///
- /// The visitor delegate is invoked for each segment in the input. It is given as parameter the
- /// value of the argument, the segment index, and a range for the segment.
- ///
- public static void VisitSplits(
- this ReadOnlySpan input,
- string[] separators,
- TContext context,
- SplitVisitor visitor,
- StringComparison comparison = StringComparison.Ordinal,
- StringSplitOptions options = StringSplitOptions.None)
- {
- _ = Throw.IfNull(separators);
- _ = Throw.IfNull(visitor);
- CheckStringSplitOptions(options);
-
- int numSegments = 0;
- int start = 0;
- while (true)
- {
- int index = -1;
- int separatorLen = 0;
- foreach (var sep in separators)
- {
- int found = input.Slice(start).IndexOf(sep.AsSpan(), comparison);
- if (found >= 0)
- {
- if (found < index || index < 0)
- {
- separatorLen = sep.Length;
- index = found;
- }
- }
- }
-
- var sp = index < 0 ? input.Slice(start) : input.Slice(start, index);
-
- var rangeStart = start;
-#if NET5_0_OR_GREATER
- if ((options & StringSplitOptions.TrimEntries) != 0)
- {
- var len = sp.Length;
- sp = sp.TrimStart();
- rangeStart = start + len - sp.Length;
- sp = sp.TrimEnd();
- }
-#endif
-
- if (sp.Length > 0 || (options & StringSplitOptions.RemoveEmptyEntries) == 0)
- {
- visitor(input.Slice(rangeStart, sp.Length), numSegments++, context);
- }
-
- if (index < 0)
- {
- return;
- }
-
- start += index + separatorLen;
- }
- }
-
- ///
- /// Invokes a delegate for individual string segments.
- ///
- /// The string to split.
- /// The string that delimits the substrings in this instance.
- /// An object that can be used to pass state to the visitor.
- /// A delegate that gets invoked for each individual segment.
- /// The kind of string comparison to apply to the separator strings.
- /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
- /// The type of the visitor's context.
- ///
- /// The visitor delegate is invoked for each segment in the input. It is given as parameter the
- /// value of the argument, the segment index, and a range for the segment.
- ///
-#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
- public static void VisitSplits(
- this ReadOnlySpan input,
- string separator,
- TContext context,
- SplitVisitor visitor,
- StringComparison comparison = StringComparison.Ordinal,
- StringSplitOptions options = StringSplitOptions.None)
-#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
- {
- _ = Throw.IfNull(separator);
- _ = Throw.IfNull(visitor);
- CheckStringSplitOptions(options);
-
- int numSegments = 0;
- int start = 0;
- while (true)
- {
- int index = -1;
- int separatorLen = 0;
-
- int found = input.Slice(start).IndexOf(separator.AsSpan(), comparison);
- if (found >= 0)
- {
- if (found < index || index < 0)
- {
- separatorLen = separator.Length;
- index = found;
- }
- }
-
- var sp = index < 0 ? input.Slice(start) : input.Slice(start, index);
-
- var rangeStart = start;
-#if NET5_0_OR_GREATER
- if ((options & StringSplitOptions.TrimEntries) != 0)
- {
- var len = sp.Length;
- sp = sp.TrimStart();
- rangeStart = start + len - sp.Length;
- sp = sp.TrimEnd();
- }
-#endif
-
- if (sp.Length > 0 || (options & StringSplitOptions.RemoveEmptyEntries) == 0)
- {
- visitor(input.Slice(rangeStart, sp.Length), numSegments++, context);
- }
-
- if (index < 0)
- {
- return;
- }
-
- start += index + separatorLen;
- }
- }
-
- ///
- /// Invokes a delegate for individual string segments.
- ///
- /// The string to split.
- /// An object that can be used to pass state to the visitor.
- /// A delegate that gets invoked for each individual segment.
- /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
- /// The type of the visitor's context.
- ///
- /// This uses whitespace as a separator of individual substrings.
- ///
- /// The visitor delegate is invoked for each segment in the input. It is given as parameter the
- /// value of the argument, the segment index, and a range for the segment.
- ///
- public static void VisitSplits(
- this ReadOnlySpan input,
- TContext context,
- SplitVisitor visitor,
- StringSplitOptions options = StringSplitOptions.None)
- {
- const int SeparatorLen = 1;
-
- _ = Throw.IfNull(visitor);
- CheckStringSplitOptions(options);
-
- int numSegments = 0;
- int start = 0;
- while (true)
- {
- int index = -1;
- for (int i = start; i < input.Length; i++)
- {
- if (char.IsWhiteSpace(input[i]))
- {
- index = i - start;
- break;
- }
- }
-
- var sp = index < 0 ? input.Slice(start) : input.Slice(start, index);
-
- var rangeStart = start;
-#if NET5_0_OR_GREATER
- if ((options & StringSplitOptions.TrimEntries) != 0)
- {
- var len = sp.Length;
- sp = sp.TrimStart();
- rangeStart = start + len - sp.Length;
- sp = sp.TrimEnd();
- }
-#endif
-
- if (sp.Length > 0 || (options & StringSplitOptions.RemoveEmptyEntries) == 0)
- {
- visitor(input.Slice(rangeStart, sp.Length), numSegments++, context);
- }
-
- if (index < 0)
- {
- return;
- }
-
- start += index + SeparatorLen;
- }
- }
-
- ///
- /// Invokes a delegate for individual string segments.
- ///
- /// The string to split.
- /// A character that delimits the substrings in this instance.
- /// An object that can be used to pass state to the visitor.
- /// A delegate that gets invoked for each individual segment.
- /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
- /// The type of the visitor's context.
- ///
- /// The visitor delegate is invoked for each segment in the input. It is given as parameter the
- /// value of the argument, the segment index, and a range for the segment.
- ///
-#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
- public static void VisitSplits(
- this string input,
- char separator,
- TContext context,
- SplitVisitor visitor,
- StringSplitOptions options = StringSplitOptions.None)
- => VisitSplits(input.AsSpan(), separator, context, visitor, options);
-#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
-
- ///
- /// Invokes a delegate for individual string segments.
- ///
- /// The string to split.
- /// The characters that delimit the substrings in this instance.
- /// An object that can be used to pass state to the visitor.
- /// A delegate that gets invoked for each individual segment.
- /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
- /// The type of the visitor's context.
- ///
- /// The visitor delegate is invoked for each segment in the input. It is given as parameter the
- /// value of the argument, the segment index, and a range for the segment.
- ///
-#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
- public static void VisitSplits(
- this string input,
- ReadOnlySpan separators,
- TContext context,
- SplitVisitor visitor,
- StringSplitOptions options = StringSplitOptions.None)
- => VisitSplits(input.AsSpan(), separators, context, visitor, options);
-#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
-
- ///
- /// Invokes a delegate for individual string segments.
- ///
- /// The string to split.
- /// The strings that delimit the substrings in this instance.
- /// An object that can be used to pass state to the visitor.
- /// A delegate that gets invoked for each individual segment.
- /// The kind of string comparison to apply to the separator strings.
- /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
- /// The type of the visitor's context.
- ///
- /// The visitor delegate is invoked for each segment in the input. It is given as parameter the
- /// value of the argument, the segment index, and a range for the segment.
- ///
- public static void VisitSplits(
- this string input,
- string[] separators,
- TContext context,
- SplitVisitor visitor,
- StringComparison comparison = StringComparison.Ordinal,
- StringSplitOptions options = StringSplitOptions.None)
- => VisitSplits(input.AsSpan(), separators, context, visitor, comparison, options);
-
- ///
- /// Invokes a delegate for individual string segments.
- ///
- /// The string to split.
- /// The string that delimits the substrings in this instance.
- /// An object that can be used to pass state to the visitor.
- /// A delegate that gets invoked for each individual segment.
- /// The kind of string comparison to apply to the separator strings.
- /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
- /// The type of the visitor's context.
- ///
- /// The visitor delegate is invoked for each segment in the input. It is given as parameter the
- /// value of the argument, the segment index, and a range for the segment.
- ///
- public static void VisitSplits(
- this string input,
- string separator,
- TContext context,
- SplitVisitor visitor,
- StringComparison comparison = StringComparison.Ordinal,
- StringSplitOptions options = StringSplitOptions.None)
- => VisitSplits(input.AsSpan(), separator, context, visitor, comparison, options);
-
- ///
- /// Invokes a delegate for individual string segments.
- ///
- /// The string to split.
- /// An object that can be used to pass state to the visitor.
- /// A delegate that gets invoked for each individual segment.
- /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
- /// The type of the visitor's context.
- ///
- /// This uses whitespace as a separator of individual substrings.
- ///
- /// The visitor delegate is invoked for each segment in the input. It is given as parameter the
- /// value of the argument, the segment index, and a range for the segment.
- ///
- public static void VisitSplits(
- this string input,
- TContext context,
- SplitVisitor visitor,
- StringSplitOptions options = StringSplitOptions.None)
- => VisitSplits(input.AsSpan(), context, visitor, options);
}
#endif
diff --git a/test/Shared/StringSplit/SplitExtensionsTests.cs b/test/Shared/StringSplit/SplitExtensionsTests.cs
index bb96612658a..fc64ad7ef34 100644
--- a/test/Shared/StringSplit/SplitExtensionsTests.cs
+++ b/test/Shared/StringSplit/SplitExtensionsTests.cs
@@ -1,7 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-#if !NET8_0_OR_GREATER
+#if NET6_0
using System;
using System.Collections.Generic;
@@ -11,146 +11,6 @@ namespace Microsoft.Shared.StringSplit.Test;
public static class SplitExtensionsTests
{
- public static IEnumerable SingleCharData => new List
- {
- new object[] { string.Empty, StringSplitOptions.None },
- new object[] { "A", StringSplitOptions.None },
- new object[] { "AA", StringSplitOptions.None },
- new object[] { "/", StringSplitOptions.None },
- new object[] { "A/", StringSplitOptions.None },
- new object[] { "AA/", StringSplitOptions.None },
- new object[] { "/A", StringSplitOptions.None },
- new object[] { "/AA", StringSplitOptions.None },
- new object[] { "AA/B", StringSplitOptions.None },
- new object[] { "AA//", StringSplitOptions.None },
- new object[] { "AA//BB", StringSplitOptions.None },
-
- new object[] { string.Empty, StringSplitOptions.RemoveEmptyEntries },
- new object[] { "A", StringSplitOptions.RemoveEmptyEntries },
- new object[] { "AA", StringSplitOptions.RemoveEmptyEntries },
- new object[] { "/", StringSplitOptions.RemoveEmptyEntries },
- new object[] { "A/", StringSplitOptions.RemoveEmptyEntries },
- new object[] { "AA/", StringSplitOptions.RemoveEmptyEntries },
- new object[] { "/A", StringSplitOptions.RemoveEmptyEntries },
- new object[] { "/AA", StringSplitOptions.RemoveEmptyEntries },
- new object[] { "AA/B", StringSplitOptions.RemoveEmptyEntries },
- new object[] { "AA//", StringSplitOptions.RemoveEmptyEntries },
- new object[] { "AA//BB", StringSplitOptions.RemoveEmptyEntries },
-
-#if NET5_0_OR_GREATER
- new object[] { string.Empty, StringSplitOptions.TrimEntries },
- new object[] { " ", StringSplitOptions.TrimEntries },
- new object[] { " A", StringSplitOptions.TrimEntries },
- new object[] { "AA", StringSplitOptions.TrimEntries },
- new object[] { "A A", StringSplitOptions.TrimEntries },
- new object[] { "/", StringSplitOptions.TrimEntries },
- new object[] { " /", StringSplitOptions.TrimEntries },
- new object[] { "A/", StringSplitOptions.TrimEntries },
- new object[] { "A /", StringSplitOptions.TrimEntries },
- new object[] { "AA/", StringSplitOptions.TrimEntries },
- new object[] { "/A", StringSplitOptions.TrimEntries },
- new object[] { " / A ", StringSplitOptions.TrimEntries },
- new object[] { "/AA", StringSplitOptions.TrimEntries },
- new object[] { "AA/B", StringSplitOptions.TrimEntries },
- new object[] { "AA//", StringSplitOptions.TrimEntries },
- new object[] { "AA//BB", StringSplitOptions.TrimEntries },
- new object[] { " abcde /fghijk /lmn", StringSplitOptions.TrimEntries },
-#endif
- };
-
- public static IEnumerable MultiCharData => new List
- {
- new object[] { string.Empty, StringSplitOptions.None },
- new object[] { "A", StringSplitOptions.None },
- new object[] { "AA", StringSplitOptions.None },
- new object[] { "/", StringSplitOptions.None },
- new object[] { "A\\", StringSplitOptions.None },
- new object[] { "AA/", StringSplitOptions.None },
- new object[] { "/A", StringSplitOptions.None },
- new object[] { "\\AA", StringSplitOptions.None },
- new object[] { "AA/B", StringSplitOptions.None },
- new object[] { "AA/\\", StringSplitOptions.None },
- new object[] { "AA//BB", StringSplitOptions.None },
-
- new object[] { string.Empty, StringSplitOptions.RemoveEmptyEntries },
- new object[] { "A", StringSplitOptions.RemoveEmptyEntries },
- new object[] { "AA", StringSplitOptions.RemoveEmptyEntries },
- new object[] { "/", StringSplitOptions.RemoveEmptyEntries },
- new object[] { "A/", StringSplitOptions.RemoveEmptyEntries },
- new object[] { "AA\\", StringSplitOptions.RemoveEmptyEntries },
- new object[] { "/A", StringSplitOptions.RemoveEmptyEntries },
- new object[] { "/AA", StringSplitOptions.RemoveEmptyEntries },
- new object[] { "AA/B", StringSplitOptions.RemoveEmptyEntries },
- new object[] { "AA//", StringSplitOptions.RemoveEmptyEntries },
- new object[] { "AA//BB", StringSplitOptions.RemoveEmptyEntries },
-
-#if NET5_0_OR_GREATER
- new object[] { string.Empty, StringSplitOptions.TrimEntries },
- new object[] { " ", StringSplitOptions.TrimEntries },
- new object[] { " A", StringSplitOptions.TrimEntries },
- new object[] { "AA", StringSplitOptions.TrimEntries },
- new object[] { "A A", StringSplitOptions.TrimEntries },
- new object[] { "/", StringSplitOptions.TrimEntries },
- new object[] { " /", StringSplitOptions.TrimEntries },
- new object[] { "A/", StringSplitOptions.TrimEntries },
- new object[] { "A /", StringSplitOptions.TrimEntries },
- new object[] { "AA/", StringSplitOptions.TrimEntries },
- new object[] { "/A", StringSplitOptions.TrimEntries },
- new object[] { " / A ", StringSplitOptions.TrimEntries },
- new object[] { "/AA", StringSplitOptions.TrimEntries },
- new object[] { "AA/B", StringSplitOptions.TrimEntries },
- new object[] { "AA//", StringSplitOptions.TrimEntries },
- new object[] { "AA//BB", StringSplitOptions.TrimEntries },
- new object[] { " abcde //fghijk //lmn", StringSplitOptions.TrimEntries },
-#endif
- };
-
- public static IEnumerable WhitespaceData => new List
- {
- new object[] { string.Empty, StringSplitOptions.None },
- new object[] { "A", StringSplitOptions.None },
- new object[] { "AA", StringSplitOptions.None },
- new object[] { " ", StringSplitOptions.None },
- new object[] { "A ", StringSplitOptions.None },
- new object[] { "AA ", StringSplitOptions.None },
- new object[] { " A", StringSplitOptions.None },
- new object[] { " AA", StringSplitOptions.None },
- new object[] { "AA B", StringSplitOptions.None },
- new object[] { "AA ", StringSplitOptions.None },
- new object[] { "AA BB", StringSplitOptions.None },
-
- new object[] { string.Empty, StringSplitOptions.RemoveEmptyEntries },
- new object[] { "A", StringSplitOptions.RemoveEmptyEntries },
- new object[] { "AA", StringSplitOptions.RemoveEmptyEntries },
- new object[] { " ", StringSplitOptions.RemoveEmptyEntries },
- new object[] { "A ", StringSplitOptions.RemoveEmptyEntries },
- new object[] { "AA ", StringSplitOptions.RemoveEmptyEntries },
- new object[] { " A", StringSplitOptions.RemoveEmptyEntries },
- new object[] { " AA", StringSplitOptions.RemoveEmptyEntries },
- new object[] { "AA B", StringSplitOptions.RemoveEmptyEntries },
- new object[] { "AA ", StringSplitOptions.RemoveEmptyEntries },
- new object[] { "AA BB", StringSplitOptions.RemoveEmptyEntries },
-
-#if NET5_0_OR_GREATER
- new object[] { string.Empty, StringSplitOptions.TrimEntries },
- new object[] { " ", StringSplitOptions.TrimEntries },
- new object[] { " A", StringSplitOptions.TrimEntries },
- new object[] { "AA", StringSplitOptions.TrimEntries },
- new object[] { "A A", StringSplitOptions.TrimEntries },
- new object[] { " ", StringSplitOptions.TrimEntries },
- new object[] { " ", StringSplitOptions.TrimEntries },
- new object[] { "A ", StringSplitOptions.TrimEntries },
- new object[] { "A ", StringSplitOptions.TrimEntries },
- new object[] { "AA ", StringSplitOptions.TrimEntries },
- new object[] { " A", StringSplitOptions.TrimEntries },
- new object[] { " A ", StringSplitOptions.TrimEntries },
- new object[] { " AA", StringSplitOptions.TrimEntries },
- new object[] { "AA B", StringSplitOptions.TrimEntries },
- new object[] { "AA ", StringSplitOptions.TrimEntries },
- new object[] { "AA BB", StringSplitOptions.TrimEntries },
-#endif
- };
-
public static IEnumerable StringData => new List
{
new object[] { string.Empty, StringSplitOptions.None },
@@ -177,7 +37,6 @@ public static class SplitExtensionsTests
new object[] { "AAXX", StringSplitOptions.RemoveEmptyEntries },
new object[] { "AAYYBB", StringSplitOptions.RemoveEmptyEntries },
-#if NET5_0_OR_GREATER
new object[] { string.Empty, StringSplitOptions.TrimEntries },
new object[] { "XX", StringSplitOptions.TrimEntries },
new object[] { "YYA", StringSplitOptions.TrimEntries },
@@ -195,82 +54,8 @@ public static class SplitExtensionsTests
new object[] { "AAXXYY", StringSplitOptions.TrimEntries },
new object[] { "AAYYXXBB", StringSplitOptions.TrimEntries },
new object[] { " abcde XXfghijk YYlmn", StringSplitOptions.TrimEntries },
-#endif
};
- [Theory]
- [MemberData(nameof(SingleCharData))]
- public static void SingleChar(string input, StringSplitOptions options)
- {
- var expected = input.Split(new[] { '/' }, options);
-
- var actual = new StringRange[20];
- Assert.True(input.TrySplit('/', actual, out int numActuals, options));
-
- Assert.Equal(expected.Length, numActuals);
- Assert.Equal(expected.Length == 0, input.TrySplit('/', Array.Empty(), out _, options));
-
- for (int i = 0; i < expected.Length; i++)
- {
- Assert.Equal(expected[i], input.Substring(actual[i].Index, actual[i].Count));
- }
- }
-
- [Theory]
- [MemberData(nameof(MultiCharData))]
- public static void MultiChar(string input, StringSplitOptions options)
- {
- var expected = input.Split(new[] { '/', '\\' }, options);
-
- var actual = new StringRange[20];
- Assert.True(input.TrySplit(new[] { '/', '\\' }, actual, out int numActuals, options));
-
- Assert.Equal(expected.Length, numActuals);
- Assert.Equal(expected.Length == 0, input.TrySplit(new[] { '/', '\\' }, Array.Empty(), out _, options));
-
- for (int i = 0; i < expected.Length; i++)
- {
- Assert.Equal(expected[i], input.Substring(actual[i].Index, actual[i].Count));
- }
- }
-
- [Theory]
- [MemberData(nameof(WhitespaceData))]
- public static void Whitespace(string input, StringSplitOptions options)
- {
- var expected = input.Split((string[]?)null, options);
-
- var actual = new StringRange[20];
- Assert.True(input.TrySplit(actual, out int numActuals, options));
-
- Assert.Equal(expected.Length, numActuals);
- Assert.Equal(expected.Length == 0, input.TrySplit(Array.Empty(), out _, options));
-
- for (int i = 0; i < expected.Length; i++)
- {
- Assert.Equal(expected[i], input.Substring(actual[i].Index, actual[i].Count));
- }
- }
-
- [Theory]
- [MemberData(nameof(StringData))]
- public static void StringArray(string input, StringSplitOptions options)
- {
- var expected = input.Split(new[] { "XX", "YY" }, options);
-
- var actual = new StringRange[20];
- Assert.True(input.TrySplit(new[] { "XX", "YY" }, actual, out int numActuals, StringComparison.Ordinal, options));
-
- Assert.Equal(expected.Length, numActuals);
- Assert.Equal(expected.Length == 0, input.TrySplit(new[] { "XX", "YY" }, Array.Empty(), out _, StringComparison.Ordinal, options));
-
- for (int i = 0; i < expected.Length; i++)
- {
- Assert.Equal(expected[i], input.Substring(actual[i].Index, actual[i].Count));
- }
- }
-
-#if NETCOREAPP3_1_OR_GREATER
[Theory]
[MemberData(nameof(StringData))]
public static void Strings(string input, StringSplitOptions options)
@@ -282,10 +67,10 @@ public static void Strings(string input, StringSplitOptions options)
var expected = input.Split(separator, options);
var actual = new StringRange[20];
- Assert.True(input.TrySplit(separator, actual, out int numActuals, StringComparison.Ordinal, options));
+ Assert.True(input.AsSpan().TrySplit(separator, actual, out int numActuals, StringComparison.Ordinal, options));
Assert.Equal(expected.Length, numActuals);
- Assert.Equal(expected.Length == 0, input.TrySplit(separator, Array.Empty(), out _, StringComparison.Ordinal, options));
+ Assert.Equal(expected.Length == 0, input.AsSpan().TrySplit(separator, Array.Empty(), out _, StringComparison.Ordinal, options));
for (int j = 0; j < expected.Length; j++)
{
@@ -293,134 +78,6 @@ public static void Strings(string input, StringSplitOptions options)
}
}
}
-#endif
-
- [Theory]
- [MemberData(nameof(SingleCharData))]
- public static void VisitSingleChar(string input, StringSplitOptions options)
- {
- var expected = input.Split(new[] { '/' }, options);
-
- var actual = new string[20];
- int numActuals = 0;
- input.VisitSplits('/', actual, (s, c, a) =>
- {
- a[c] = s.ToString();
- numActuals++;
- }, options);
-
- Assert.Equal(expected.Length, numActuals);
-
- for (int i = 0; i < expected.Length; i++)
- {
- Assert.Equal(expected[i], actual[i]);
- }
- }
-
- [Theory]
- [MemberData(nameof(MultiCharData))]
- public static void VisitMultiChar(string input, StringSplitOptions options)
- {
- var expected = input.Split(new[] { '/', '\\' }, options);
-
- var actual = new string[20];
- int numActuals = 0;
- input.VisitSplits(new[] { '/', '\\' }, actual, (s, c, a) =>
- {
- a[c] = s.ToString();
- numActuals++;
- }, options);
-
- Assert.Equal(expected.Length, numActuals);
-
- for (int i = 0; i < expected.Length; i++)
- {
- Assert.Equal(expected[i], actual[i]);
- }
- }
-
- [Theory]
- [MemberData(nameof(WhitespaceData))]
- public static void VisitWhitespace(string input, StringSplitOptions options)
- {
- var expected = input.Split((string[]?)null, options);
-
- var actual = new string[20];
- int numActuals = 0;
- input.VisitSplits(actual, (s, c, a) =>
- {
- a[c] = s.ToString();
- numActuals++;
- }, options);
-
- Assert.Equal(expected.Length, numActuals);
-
- for (int i = 0; i < expected.Length; i++)
- {
- Assert.Equal(expected[i], actual[i]);
- }
- }
-
- [Theory]
- [MemberData(nameof(StringData))]
- public static void VisitStringArray(string input, StringSplitOptions options)
- {
- var expected = input.Split(new[] { "XX", "YY" }, options);
-
- var actual = new string[20];
- int numActuals = 0;
- input.VisitSplits(new[] { "XX", "YY" }, actual, (s, c, a) =>
- {
- a[c] = s.ToString();
- numActuals++;
- }, StringComparison.Ordinal, options);
-
- Assert.Equal(expected.Length, numActuals);
-
- for (int i = 0; i < expected.Length; i++)
- {
- Assert.Equal(expected[i], actual[i]);
- }
- }
-
-#if NETCOREAPP3_1_OR_GREATER
- [Theory]
- [MemberData(nameof(StringData))]
- public static void VisitStrings(string input, StringSplitOptions options)
- {
- for (int i = 0; i < 2; i++)
- {
- var separator = i == 0 ? "XX" : "YY";
-
- var expected = input.Split(separator, options);
-
- var actual = new string[20];
- int numActuals = 0;
- input.VisitSplits(separator, actual, (s, c, a) =>
- {
- a[c] = s.ToString();
- numActuals++;
- }, StringComparison.Ordinal, options);
-
- Assert.Equal(expected.Length, numActuals);
-
- for (int j = 0; j < expected.Length; j++)
- {
- Assert.Equal(expected[j], actual[j]);
- }
- }
- }
-#endif
-
- [Fact]
- public static void CheckOpts()
- {
- var ss = new StringRange[1];
- Assert.Throws(() => "ABC".TrySplit(new[] { '/', '\\' }, ss, out _, (StringSplitOptions)(-1)));
- Assert.Throws(() => "ABC".TrySplit(new[] { "XX", "YY" }, ss, out _, StringComparison.Ordinal, (StringSplitOptions)(-1)));
- Assert.Throws(() => "ABC".TrySplit('/', ss, out _, (StringSplitOptions)(-1)));
- Assert.Throws(() => "ABC".TrySplit(ss, out _, (StringSplitOptions)(-1)));
- }
}
#endif