From af269ef87e0cd5f1b7ca59faa90bfeb4ed51358e Mon Sep 17 00:00:00 2001 From: David Noble Date: Thu, 4 Aug 2016 22:06:29 -0700 Subject: [PATCH 1/6] Resolved issue GitHub issue #54, hangs when enumerating stream, and added unit test coverage. --- .../Splunk/Client/SearchResultMetadata.cs | 5 +++++ .../Splunk/Client/SearchResultStream.cs | 11 +++++++++- test/unit-tests/Data/Client/DVPL-6838.xml | 2 ++ test/unit-tests/TestSearchResultStream.cs | 22 +++++++++++++++++++ 4 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 test/unit-tests/Data/Client/DVPL-6838.xml diff --git a/src/Splunk.Client/Splunk/Client/SearchResultMetadata.cs b/src/Splunk.Client/Splunk/Client/SearchResultMetadata.cs index 99278950..986d6078 100644 --- a/src/Splunk.Client/Splunk/Client/SearchResultMetadata.cs +++ b/src/Splunk.Client/Splunk/Client/SearchResultMetadata.cs @@ -92,6 +92,11 @@ public async Task ReadXmlAsync(XmlReader reader) string preview = reader.GetRequiredAttribute("preview"); this.IsFinal = !BooleanConverter.Instance.Convert(preview); + if (reader.IsEmptyElement) + { + return; + } + if (!await reader.ReadAsync().ConfigureAwait(false)) { return; diff --git a/src/Splunk.Client/Splunk/Client/SearchResultStream.cs b/src/Splunk.Client/Splunk/Client/SearchResultStream.cs index e99dbb0d..f240ea0a 100644 --- a/src/Splunk.Client/Splunk/Client/SearchResultStream.cs +++ b/src/Splunk.Client/Splunk/Client/SearchResultStream.cs @@ -260,8 +260,17 @@ async Task ReadMetadataAsync() break; } - Debug.Assert(reader.NodeType == XmlNodeType.EndElement && reader.Name == "results", "Expected: "); + Debug.Assert(reader.Name == "results" && (reader.IsEmptyElement || + reader.NodeType == XmlNodeType.EndElement), + "Expected: or "); + + var isEmptyElement = reader.IsEmptyElement; await reader.ReadAsync().ConfigureAwait(false); + + if (isEmptyElement) + { + break; + } } return metadata; diff --git a/test/unit-tests/Data/Client/DVPL-6838.xml b/test/unit-tests/Data/Client/DVPL-6838.xml new file mode 100644 index 00000000..62db9723 --- /dev/null +++ b/test/unit-tests/Data/Client/DVPL-6838.xml @@ -0,0 +1,2 @@ + + diff --git a/test/unit-tests/TestSearchResultStream.cs b/test/unit-tests/TestSearchResultStream.cs index dfe18dc0..8f6fc813 100644 --- a/test/unit-tests/TestSearchResultStream.cs +++ b/test/unit-tests/TestSearchResultStream.cs @@ -149,6 +149,28 @@ async Task CanHandleInFlightErrorsReportedBySplunk() } } + [Trait("unit-test", "Splunk.Client.SearchResultStream")] + [Fact] + async Task CanHandleNoResults() + { + var baseFileName = Path.Combine(TestAtomFeed.Directory, "DVPL-6838"); + var message = new HttpResponseMessage(HttpStatusCode.OK); + + message.Content = new StreamContent(new FileStream(baseFileName + ".xml", FileMode.Open, FileAccess.Read)); + + using (var stream = await SearchResultStream.CreateAsync(message)) + { + int count = 0; + + foreach (var observedResult in stream) + { + ++count; + } + + Assert.Equal(count, stream.ReadCount); + } + } + [Trait("unit-test", "Splunk.Client.SearchResultStream")] [Fact] async Task CanSkipEmptyResults() From ae7cdc59a43f4e2c46c256506f9c459ff6e711c2 Mon Sep 17 00:00:00 2001 From: Shakeel Mohamed Date: Thu, 13 Oct 2016 14:39:46 -0700 Subject: [PATCH 2/6] Run appveyor tests as release --- appveyor.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 1dcbef8a..90c35eb2 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -43,7 +43,11 @@ before_build: build: verbosity: minimal +# Avoid test failures from debug assertions by building in release mode +configuration: +- Release + test_script: # Run the unit tests and acceptance tests -- ps: xunit.console.clr4.x86 test\unit-tests\bin\Debug\unit-tests.dll /appveyor -- ps: xunit.console.clr4.x86 test\acceptance-tests\bin\Debug\acceptance-tests.dll /appveyor +- ps: xunit.console.clr4.x86 test\unit-tests\bin\Release\unit-tests.dll /appveyor +- ps: xunit.console.clr4.x86 test\acceptance-tests\bin\Release\acceptance-tests.dll /appveyor From 974762f4432d08a27489b3389bb73affa59c209c Mon Sep 17 00:00:00 2001 From: Shakeel Mohamed Date: Thu, 13 Oct 2016 18:58:19 -0700 Subject: [PATCH 3/6] Fix infinite loop when expecting for more search results than available --- src/Splunk.Client/Splunk/Client/SearchResultStream.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Splunk.Client/Splunk/Client/SearchResultStream.cs b/src/Splunk.Client/Splunk/Client/SearchResultStream.cs index f240ea0a..8946ddbb 100644 --- a/src/Splunk.Client/Splunk/Client/SearchResultStream.cs +++ b/src/Splunk.Client/Splunk/Client/SearchResultStream.cs @@ -263,11 +263,12 @@ async Task ReadMetadataAsync() Debug.Assert(reader.Name == "results" && (reader.IsEmptyElement || reader.NodeType == XmlNodeType.EndElement), "Expected: or "); - + var isEmptyElement = reader.IsEmptyElement; + var readerNodeType = reader.NodeType; await reader.ReadAsync().ConfigureAwait(false); - if (isEmptyElement) + if (isEmptyElement || readerNodeType == XmlNodeType.EndElement) { break; } From 10ea83fb26b140ec6fa28caa644d5cf571ac9a2b Mon Sep 17 00:00:00 2001 From: Shakeel Mohamed Date: Thu, 13 Oct 2016 18:58:56 -0700 Subject: [PATCH 4/6] Avoid false test failures when running on againt fresh Splunk instances ie: on Appveyor for CI --- test/acceptance-tests/TestService.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/acceptance-tests/TestService.cs b/test/acceptance-tests/TestService.cs index dc4e30ab..377c7564 100644 --- a/test/acceptance-tests/TestService.cs +++ b/test/acceptance-tests/TestService.cs @@ -1749,7 +1749,8 @@ public async Task CanExportSearchResultsToEnumerable() { using (var service = await SdkHelper.CreateService()) { - const string search = "search index=_internal | head 100"; + // Changed to 10, we aren't guaranteed to have 100 events yet, especially when running in CI + const string search = "search index=_internal | head 10"; var args = new SearchExportArgs { Count = 0 }; using (SearchResultStream stream = await service.ExportSearchResultsAsync(search, args)) From 8d4754251c6c2f13e17226085c51e7e2a9ced02c Mon Sep 17 00:00:00 2001 From: Shakeel Mohamed Date: Thu, 13 Oct 2016 18:59:51 -0700 Subject: [PATCH 5/6] Add a test to verity changes for PR #57 --- .../AtomFeed.EmptyDictionaryKeyName.xml | 26 +++++++++++++++++++ test/unit-tests/TestAtomFeed.cs | 25 ++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 test/unit-tests/Data/Client/AtomFeed.EmptyDictionaryKeyName.xml diff --git a/test/unit-tests/Data/Client/AtomFeed.EmptyDictionaryKeyName.xml b/test/unit-tests/Data/Client/AtomFeed.EmptyDictionaryKeyName.xml new file mode 100644 index 00000000..85c1d52c --- /dev/null +++ b/test/unit-tests/Data/Client/AtomFeed.EmptyDictionaryKeyName.xml @@ -0,0 +1,26 @@ + + + + + foo + foo + 2014-05-29T12:13:01-07:00 + + Splunk + + + + + + + + + num + + + + + + + + diff --git a/test/unit-tests/TestAtomFeed.cs b/test/unit-tests/TestAtomFeed.cs index 3e2376d4..46df51c5 100644 --- a/test/unit-tests/TestAtomFeed.cs +++ b/test/unit-tests/TestAtomFeed.cs @@ -91,6 +91,31 @@ public static async Task DefaultCollections() } } + [Trait("unit-test", "Splunk.Client.AtomFeed")] + [Fact] + public static async Task ParseDictionaryAsync() + { + // Test to verify GitHub PR #57 - handling empty key name to "Empty" for + var path = Path.Combine(Directory, "AtomFeed.EmptyDictionaryKeyName.xml"); + using(var stream = new FileStream(path, FileMode.Open, FileAccess.Read)) + { + var reader = XmlReader.Create(stream, TestAtomFeed.XmlReaderSettings); + var feed = new AtomFeed(); + await feed.ReadXmlAsync(reader); + + Assert.Equal(1, feed.Entries.Count); + AtomEntry entry = feed.Entries[0]; + dynamic content = entry.Content; + Assert.NotNull(content); + Assert.NotNull(content.Key); + Assert.NotNull(content.Key.Empty); + Assert.Equal(1, ((IDictionary)content.Key.Empty).Count); + Assert.Equal("num", content.Key.Empty.Type); + Assert.Equal(new ReadOnlyDictionary(new Dictionary()), feed.Links); + Assert.Equal(new ReadOnlyCollection(new List()), feed.Messages); + } + } + #region Privates/internals static readonly XmlReaderSettings XmlReaderSettings = new XmlReaderSettings() From 2c5d49b5a80d97b92cc057ec4e63b7004c3f7eaa Mon Sep 17 00:00:00 2001 From: Shakeel Mohamed Date: Wed, 9 Nov 2016 11:53:45 -0800 Subject: [PATCH 6/6] Add additional test coverage --- ...omFeed.EmptyDictionaryKeyNameAtomEntry.xml | 295 ++++++++++++++++++ ...omFeed.EmptyDictionaryKeyNameAtomFeed.xml} | 0 test/unit-tests/TestAtomFeed.cs | 26 +- 3 files changed, 319 insertions(+), 2 deletions(-) create mode 100644 test/unit-tests/Data/Client/AtomFeed.EmptyDictionaryKeyNameAtomEntry.xml rename test/unit-tests/Data/Client/{AtomFeed.EmptyDictionaryKeyName.xml => AtomFeed.EmptyDictionaryKeyNameAtomFeed.xml} (100%) diff --git a/test/unit-tests/Data/Client/AtomFeed.EmptyDictionaryKeyNameAtomEntry.xml b/test/unit-tests/Data/Client/AtomFeed.EmptyDictionaryKeyNameAtomEntry.xml new file mode 100644 index 00000000..15ee25ab --- /dev/null +++ b/test/unit-tests/Data/Client/AtomFeed.EmptyDictionaryKeyNameAtomEntry.xml @@ -0,0 +1,295 @@ + + + + + search index=_internal | head 10 + http://localhost:8089/servicesNS/nobody/search/search/jobs/1474403860.123 + 2016-09-20T13:37:40.375-07:00 + + 2016-09-20T13:37:40.000-07:00 + + + + + + + + + admin + + + + 0 + 1969-12-31T16:00:00.000-08:00 + 604800 + 600 + + 196608 + DONE + 1.00000 + 0 + 2000-11-17T00:36:11.000-08:00 + 0 + 0 + 0 + 1 + 1 + search index=_internal | head 10 + desc + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + eventid::123 source::wineventlog:security username::admin + + search index=_internal | head 0 + 0 + 32111 + 5 + search index=_internal | head 10 + table _time,summary,HostName,clientip,location + 0 + 0 + 0 + 0.291000 + 1 + 0 + 0 + 0 + 6 + 5 + 1474403860.123 + 0 + 600 + + + + + 0.008000 + 8 + 0 + 0 + + + + + 0.008000 + 8 + 0 + 0 + + + + + 0.005000 + 7 + 0 + 0 + + + + + 0.008000 + 8 + 0 + 0 + + + + + 0.007000 + 7 + 0 + 0 + + + + + 0.007000 + 7 + 0 + 0 + + + + + 0.009000 + 9 + + + + + 0.004000 + 7 + + + + + 0.001000 + 1 + 0 + 0 + + + + + 0.001000 + 1 + + + + + 0.001000 + 1 + + + + + 0.127000 + 1 + + + + + 0.001000 + 1 + + + + + 0.001000 + 1 + + + + + 0.003000 + 1 + + + + + 0.124000 + 1 + + + + + 0.001000 + 1 + + + + + 0.121000 + 8 + + + + + 0.002000 + 1 + + + + + 0.008000 + 8 + + + + + 0.007000 + 7 + + + + + 0.009000 + 5 + + + + + 0.022000 + 1 + + + + + 0.051000 + 1 + + + + + + + + + str + + + + + + + + + No matching fields exist + + + + + + + 0 + search index=_internal | head 10 + + + + + 0 + 0 + + + + + + + + + admin + + + + + admin + + + + + admin + 1 + global + search + 1 + 600 + + + + + localhost + + + + + diff --git a/test/unit-tests/Data/Client/AtomFeed.EmptyDictionaryKeyName.xml b/test/unit-tests/Data/Client/AtomFeed.EmptyDictionaryKeyNameAtomFeed.xml similarity index 100% rename from test/unit-tests/Data/Client/AtomFeed.EmptyDictionaryKeyName.xml rename to test/unit-tests/Data/Client/AtomFeed.EmptyDictionaryKeyNameAtomFeed.xml diff --git a/test/unit-tests/TestAtomFeed.cs b/test/unit-tests/TestAtomFeed.cs index 46df51c5..0844a9bb 100644 --- a/test/unit-tests/TestAtomFeed.cs +++ b/test/unit-tests/TestAtomFeed.cs @@ -93,10 +93,10 @@ public static async Task DefaultCollections() [Trait("unit-test", "Splunk.Client.AtomFeed")] [Fact] - public static async Task ParseDictionaryAsync() + public static async Task ParseDictionaryAsyncAtomFeed() { // Test to verify GitHub PR #57 - handling empty key name to "Empty" for - var path = Path.Combine(Directory, "AtomFeed.EmptyDictionaryKeyName.xml"); + var path = Path.Combine(Directory, "AtomFeed.EmptyDictionaryKeyNameAtomFeed.xml"); using(var stream = new FileStream(path, FileMode.Open, FileAccess.Read)) { var reader = XmlReader.Create(stream, TestAtomFeed.XmlReaderSettings); @@ -116,6 +116,28 @@ public static async Task ParseDictionaryAsync() } } + [Trait("unit-test", "Splunk.Client.AtomFeed")] + [Fact] + public static async Task ParseDictionaryAsyncAtomEntry() + { + // Another Test to verify GitHub PR #57 - handling empty key name to "Empty" for + var path = Path.Combine(Directory, "AtomFeed.EmptyDictionaryKeyNameAtomEntry.xml"); + using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read)) + { + var reader = XmlReader.Create(stream, TestAtomFeed.XmlReaderSettings); + var entry = new AtomEntry(); + await entry.ReadXmlAsync(reader); + + dynamic content = entry.Content; + Assert.NotNull(content); + Assert.NotNull(content.FieldMetadataEvents); + Assert.NotNull(content.FieldMetadataEvents.Empty); + Assert.Equal(1, ((IDictionary)content.FieldMetadataEvents.Empty).Count); + Assert.Equal("str", content.FieldMetadataEvents.Empty.Type); + Assert.NotEqual(new ReadOnlyDictionary(new Dictionary()), entry.Links); + } + } + #region Privates/internals static readonly XmlReaderSettings XmlReaderSettings = new XmlReaderSettings()