From 2bad49e8c4b55da1a44403b4c6815adc0e698c44 Mon Sep 17 00:00:00 2001
From: NoviProg <80051122+NoviProg@users.noreply.github.com>
Date: Thu, 1 Sep 2022 22:47:12 +0300
Subject: [PATCH 1/4] Add extension method ReadGroups This method make it easy
to work groups. By use enumerator
---
QuickFIXn/Extensions/GroupExtension.cs | 35 +++++++++++++
UnitTests/Extensions/GroupExtensionTest.cs | 61 ++++++++++++++++++++++
2 files changed, 96 insertions(+)
create mode 100644 QuickFIXn/Extensions/GroupExtension.cs
create mode 100644 UnitTests/Extensions/GroupExtensionTest.cs
diff --git a/QuickFIXn/Extensions/GroupExtension.cs b/QuickFIXn/Extensions/GroupExtension.cs
new file mode 100644
index 000000000..0199a1c25
--- /dev/null
+++ b/QuickFIXn/Extensions/GroupExtension.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace QuickFix.Extensions
+{
+ public static class GroupExtension
+ {
+ ///
+ /// Gets an instance of enumerable groups
+ ///
+ /// A repeating field group within a message
+ /// Field container used by messages, groups, and composites
+ /// The FIX group tag
+ /// retrieved enumerable groups
+ ///
+ public static IEnumerable ReadGroups(this FieldMap message, int noGroupTag) where TGroup : Group //, new()
+ {
+ if (message != null && message.IsSetField(noGroupTag))
+ {
+ int grpCount = message.GetInt(noGroupTag);
+ for (int grpIndex = 1; grpIndex <= grpCount; grpIndex += 1)
+ {
+ //var group = new TGroup();
+ //message.GetGroup(grpIndex, group);
+ var group = message.GetGroup(grpIndex, noGroupTag) as TGroup;
+ //if (group == null)
+ // throw new InvalidCastException($"Can't cast {message.GetGroup(grpIndex, noGroupTag).GetType()} to {typeof(TGroup)}");
+ yield return group;
+ }
+ }
+ }
+ }
+}
diff --git a/UnitTests/Extensions/GroupExtensionTest.cs b/UnitTests/Extensions/GroupExtensionTest.cs
new file mode 100644
index 000000000..9729d1664
--- /dev/null
+++ b/UnitTests/Extensions/GroupExtensionTest.cs
@@ -0,0 +1,61 @@
+using NUnit.Framework;
+using QuickFix;
+using QuickFix.Fields;
+using QuickFix.Extensions;
+using System;
+using UnitTests.TestHelpers;
+using System.Linq;
+
+namespace UnitTests.Extensions
+{
+ [TestFixture]
+ public class GroupExtensionTest
+ {
+ private IMessageFactory _defaultMsgFactory = new DefaultMessageFactory();
+
+ [Test]
+ public void EnumerableGroupsTest()
+ {
+ var nos = new QuickFix.FIX42.NewOrderSingle();
+ for (int i = 0; i < 100; i++)
+ {
+ var noTradingSessions = new QuickFix.FIX42.NewOrderSingle.NoTradingSessionsGroup();
+ noTradingSessions.SetField(new StringField(336, $"OHHAI_{i}"));
+ nos.AddGroup(noTradingSessions);
+ }
+ int iterator = 0;
+ foreach (var item in nos.ReadGroups(Tags.NoTradingSessions))
+ {
+ Assert.That(item.GetString(336), Is.EqualTo($"OHHAI_{iterator++}"));
+ }
+ Assert.True(iterator == 100);
+
+ // Wrong tag
+ Assert.IsEmpty(nos.ReadGroups(1));
+
+ // Wrong group
+ foreach (var item in nos.ReadGroups(Tags.NoTradingSessions))
+ {
+ Assert.IsNull(item);
+ }
+
+ //--
+ String data = "8=FIX.4.4\x01" + "9=309\x01" + "35=8\x01" + "49=ASX\x01" + "56=CL1_FIX44\x01" + "34=4\x01" + "52=20060324-01:05:58\x01" + ""
+ + "17=X-B-WOW-1494E9A0:58BD3F9D-1109\x01" + "150=D\x01" + "39=0\x01" + "11=184271\x01" + "38=200\x01" + "198=1494E9A0:58BD3F9D\x01" + ""
+ + "526=4324\x01" + "37=B-WOW-1494E9A0:58BD3F9D\x01" + "55=WOW\x01" + "54=1\x01" + "151=200\x01" + "14=0\x01" + "40=2\x01" + "44=15\x01" + "59=1\x01" + "6=0\x01" + ""
+ + "453=3\x01"
+ + "448=AAA35791\x01" + "447=D\x01" + "452=3\x01" + "802=1\x01" + "523=OHAI123\x01"
+ + "448=8\x01" + "447=D\x01" + "452=4\x01"
+ + "448=FIX11\x01" + "447=D\x01" + "452=36\x01" + ""
+ + "60=20060320-03:34:29\x01" + "10=169\x01" + "";
+ var msg = new QuickFix.FIX44.ExecutionReport();
+ var dd = new QuickFix.DataDictionary.DataDictionary();
+ dd.LoadFIXSpec("FIX44");
+ msg.FromString(data, false, dd, dd, _defaultMsgFactory);
+
+ var eRNoPartyIDsGroup = msg.ReadGroups(Tags.NoPartyIDs).ToList();
+ Assert.That(eRNoPartyIDsGroup[0].PartyID?.Obj, Is.EqualTo("AAA35791"));
+ Assert.That(eRNoPartyIDsGroup[2].PartyID?.Obj, Is.EqualTo("FIX11"));
+ }
+ }
+}
From 512a06ac92c46162a744cd9a6c826d211167d012 Mon Sep 17 00:00:00 2001
From: Grant Birchmeier
Date: Fri, 20 Dec 2024 11:35:03 -0600
Subject: [PATCH 2/4] use NUnit 4 asserts
---
UnitTests/Extensions/GroupExtensionTest.cs | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/UnitTests/Extensions/GroupExtensionTest.cs b/UnitTests/Extensions/GroupExtensionTest.cs
index 9729d1664..a5a96d066 100644
--- a/UnitTests/Extensions/GroupExtensionTest.cs
+++ b/UnitTests/Extensions/GroupExtensionTest.cs
@@ -28,15 +28,15 @@ public void EnumerableGroupsTest()
{
Assert.That(item.GetString(336), Is.EqualTo($"OHHAI_{iterator++}"));
}
- Assert.True(iterator == 100);
+ Assert.That(iterator == 100);
// Wrong tag
- Assert.IsEmpty(nos.ReadGroups(1));
+ Assert.That(nos.ReadGroups(1), Is.Empty);
// Wrong group
foreach (var item in nos.ReadGroups(Tags.NoTradingSessions))
{
- Assert.IsNull(item);
+ Assert.That(item, Is.Null);
}
//--
@@ -54,8 +54,8 @@ public void EnumerableGroupsTest()
msg.FromString(data, false, dd, dd, _defaultMsgFactory);
var eRNoPartyIDsGroup = msg.ReadGroups(Tags.NoPartyIDs).ToList();
- Assert.That(eRNoPartyIDsGroup[0].PartyID?.Obj, Is.EqualTo("AAA35791"));
- Assert.That(eRNoPartyIDsGroup[2].PartyID?.Obj, Is.EqualTo("FIX11"));
+ Assert.That(eRNoPartyIDsGroup[0].PartyID?.Value, Is.EqualTo("AAA35791"));
+ Assert.That(eRNoPartyIDsGroup[2].PartyID?.Value, Is.EqualTo("FIX11"));
}
}
}
From 2e2cd8200064ad1e186388a9ce5ded36b52d813e Mon Sep 17 00:00:00 2001
From: Grant Birchmeier
Date: Fri, 20 Dec 2024 11:51:07 -0600
Subject: [PATCH 3/4] Improve ReadGroups and its tests
---
QuickFIXn/Extensions/GroupExtension.cs | 15 ++-
UnitTests/Extensions/GroupExtensionTest.cs | 102 ++++++++++++---------
2 files changed, 65 insertions(+), 52 deletions(-)
diff --git a/QuickFIXn/Extensions/GroupExtension.cs b/QuickFIXn/Extensions/GroupExtension.cs
index 0199a1c25..a3acbfacc 100644
--- a/QuickFIXn/Extensions/GroupExtension.cs
+++ b/QuickFIXn/Extensions/GroupExtension.cs
@@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Text;
-using System.Text.RegularExpressions;
namespace QuickFix.Extensions
{
@@ -17,17 +15,16 @@ public static class GroupExtension
///
public static IEnumerable ReadGroups(this FieldMap message, int noGroupTag) where TGroup : Group //, new()
{
- if (message != null && message.IsSetField(noGroupTag))
+ if (message.IsSetField(noGroupTag) && message.GetGroupTags().Contains(noGroupTag))
{
int grpCount = message.GetInt(noGroupTag);
+
for (int grpIndex = 1; grpIndex <= grpCount; grpIndex += 1)
{
- //var group = new TGroup();
- //message.GetGroup(grpIndex, group);
- var group = message.GetGroup(grpIndex, noGroupTag) as TGroup;
- //if (group == null)
- // throw new InvalidCastException($"Can't cast {message.GetGroup(grpIndex, noGroupTag).GetType()} to {typeof(TGroup)}");
- yield return group;
+ Group group = message.GetGroup(grpIndex, noGroupTag);
+ if(group is not TGroup tgroup)
+ throw new InvalidCastException($"Can't cast {group.GetType()} to {typeof(TGroup)}");
+ yield return tgroup;
}
}
}
diff --git a/UnitTests/Extensions/GroupExtensionTest.cs b/UnitTests/Extensions/GroupExtensionTest.cs
index a5a96d066..99944c943 100644
--- a/UnitTests/Extensions/GroupExtensionTest.cs
+++ b/UnitTests/Extensions/GroupExtensionTest.cs
@@ -1,61 +1,77 @@
-using NUnit.Framework;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using NUnit.Framework;
using QuickFix;
+using QuickFix.FIX42;
using QuickFix.Fields;
using QuickFix.Extensions;
-using System;
using UnitTests.TestHelpers;
-using System.Linq;
+using Message = QuickFix.Message;
namespace UnitTests.Extensions
{
[TestFixture]
public class GroupExtensionTest
{
- private IMessageFactory _defaultMsgFactory = new DefaultMessageFactory();
+ private readonly IMessageFactory _defaultMsgFactory = new DefaultMessageFactory();
- [Test]
- public void EnumerableGroupsTest()
- {
- var nos = new QuickFix.FIX42.NewOrderSingle();
- for (int i = 0; i < 100; i++)
- {
- var noTradingSessions = new QuickFix.FIX42.NewOrderSingle.NoTradingSessionsGroup();
- noTradingSessions.SetField(new StringField(336, $"OHHAI_{i}"));
- nos.AddGroup(noTradingSessions);
+ private static News CreateNewsForTest(int numberOfLines) {
+ News msg = new();
+ msg.Headline = new Headline("Blah headline");
+ for (int i = 0; i < numberOfLines; i++) {
+ News.LinesOfTextGroup grp = new();
+ grp.SetField(new Text($"OHHAI_{i}"));
+ msg.AddGroup(grp);
}
- int iterator = 0;
- foreach (var item in nos.ReadGroups(Tags.NoTradingSessions))
- {
- Assert.That(item.GetString(336), Is.EqualTo($"OHHAI_{iterator++}"));
- }
- Assert.That(iterator == 100);
+ return msg;
+ }
- // Wrong tag
- Assert.That(nos.ReadGroups(1), Is.Empty);
+ [Test]
+ public void ReadGroups_NormalScenario() {
+ News msg = CreateNewsForTest(10);
+ int iterCount = 0;
+ foreach (News.LinesOfTextGroup item in msg.ReadGroups(Tags.NoLinesOfText))
+ Assert.That(item.Text.Value, Is.EqualTo($"OHHAI_{iterCount++}"));
+ Assert.That(iterCount == 10);
+ }
- // Wrong group
- foreach (var item in nos.ReadGroups(Tags.NoTradingSessions))
- {
- Assert.That(item, Is.Null);
- }
+ [Test]
+ public void ReadGroups_ZeroCount() {
+ News msg = new();
+ Assert.That(msg.ReadGroups(Tags.NoLinesOfText).ToList(), Is.Empty);
+ }
+
+ [Test]
+ public void ReadGroups_WrongTag() {
+ // Tag doesn't match the templated group
+ // case 1: incorrect tag is not present in message
+ News msg = CreateNewsForTest(3);
+ Assert.That(msg.IsSetField(Tags.Account), Is.False);
+ Assert.That(msg.ReadGroups(Tags.Account).ToList(), Is.Empty);
+
+ // case 2: incorrect tag present but value isn't an int (like a group counter would be)
+ Assert.That(msg.IsSetHeadline());
+ Assert.That(msg.ReadGroups(Tags.Headline).ToList(), Is.Empty);
+
+ // case 3: incorrect tag present and an int, but isn't group counter at all
+ msg.SetField(new RawDataLength(100));
+ Assert.That(msg.ReadGroups(Tags.RawDataLength).ToList(), Is.Empty);
+ }
+
+ [Test]
+ public void ReadGroups_WrongGroup() {
+ // The tag is good, but the template type is incorrect
+ // Case 1: The tag's group is empty, so the templated type doesn't matter.
+ News msg = CreateNewsForTest(0);
+ Assert.That(msg.ReadGroups(Tags.NoLinesOfText).ToList(),
+ Is.Empty);
- //--
- String data = "8=FIX.4.4\x01" + "9=309\x01" + "35=8\x01" + "49=ASX\x01" + "56=CL1_FIX44\x01" + "34=4\x01" + "52=20060324-01:05:58\x01" + ""
- + "17=X-B-WOW-1494E9A0:58BD3F9D-1109\x01" + "150=D\x01" + "39=0\x01" + "11=184271\x01" + "38=200\x01" + "198=1494E9A0:58BD3F9D\x01" + ""
- + "526=4324\x01" + "37=B-WOW-1494E9A0:58BD3F9D\x01" + "55=WOW\x01" + "54=1\x01" + "151=200\x01" + "14=0\x01" + "40=2\x01" + "44=15\x01" + "59=1\x01" + "6=0\x01" + ""
- + "453=3\x01"
- + "448=AAA35791\x01" + "447=D\x01" + "452=3\x01" + "802=1\x01" + "523=OHAI123\x01"
- + "448=8\x01" + "447=D\x01" + "452=4\x01"
- + "448=FIX11\x01" + "447=D\x01" + "452=36\x01" + ""
- + "60=20060320-03:34:29\x01" + "10=169\x01" + "";
- var msg = new QuickFix.FIX44.ExecutionReport();
- var dd = new QuickFix.DataDictionary.DataDictionary();
- dd.LoadFIXSpec("FIX44");
- msg.FromString(data, false, dd, dd, _defaultMsgFactory);
-
- var eRNoPartyIDsGroup = msg.ReadGroups(Tags.NoPartyIDs).ToList();
- Assert.That(eRNoPartyIDsGroup[0].PartyID?.Value, Is.EqualTo("AAA35791"));
- Assert.That(eRNoPartyIDsGroup[2].PartyID?.Value, Is.EqualTo("FIX11"));
+ // Case 2: Group is not empty, so InvalidCastException will occur
+ msg = CreateNewsForTest(1);
+ var ex = Assert.Throws(
+ () => msg.ReadGroups(Tags.NoLinesOfText).ToList());
+ Assert.That(ex!.Message, Is.EqualTo("Can't cast QuickFix.FIX42.News+LinesOfTextGroup to QuickFix.FIX44.ExecutionReport+NoLegsGroup"));
}
}
}
From 10c4c4400cb68f74a44f0733f65f5285e2f78377 Mon Sep 17 00:00:00 2001
From: Grant Birchmeier
Date: Fri, 20 Dec 2024 12:17:19 -0600
Subject: [PATCH 4/4] promote ReadGroups to a FieldMap method
no reason for it to be an extension
---
QuickFIXn/Extensions/GroupExtension.cs | 32 -------------------
QuickFIXn/Message/FieldMap.cs | 31 ++++++++++++++----
RELEASE_NOTES.md | 1 +
...ionTest.cs => FieldMap_ReadGroupsTests.cs} | 16 +++-------
4 files changed, 30 insertions(+), 50 deletions(-)
delete mode 100644 QuickFIXn/Extensions/GroupExtension.cs
rename UnitTests/{Extensions/GroupExtensionTest.cs => FieldMap_ReadGroupsTests.cs} (90%)
diff --git a/QuickFIXn/Extensions/GroupExtension.cs b/QuickFIXn/Extensions/GroupExtension.cs
deleted file mode 100644
index a3acbfacc..000000000
--- a/QuickFIXn/Extensions/GroupExtension.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace QuickFix.Extensions
-{
- public static class GroupExtension
- {
- ///
- /// Gets an instance of enumerable groups
- ///
- /// A repeating field group within a message
- /// Field container used by messages, groups, and composites
- /// The FIX group tag
- /// retrieved enumerable groups
- ///
- public static IEnumerable ReadGroups(this FieldMap message, int noGroupTag) where TGroup : Group //, new()
- {
- if (message.IsSetField(noGroupTag) && message.GetGroupTags().Contains(noGroupTag))
- {
- int grpCount = message.GetInt(noGroupTag);
-
- for (int grpIndex = 1; grpIndex <= grpCount; grpIndex += 1)
- {
- Group group = message.GetGroup(grpIndex, noGroupTag);
- if(group is not TGroup tgroup)
- throw new InvalidCastException($"Can't cast {group.GetType()} to {typeof(TGroup)}");
- yield return tgroup;
- }
- }
- }
- }
-}
diff --git a/QuickFIXn/Message/FieldMap.cs b/QuickFIXn/Message/FieldMap.cs
index c544affd8..904eb7850 100644
--- a/QuickFIXn/Message/FieldMap.cs
+++ b/QuickFIXn/Message/FieldMap.cs
@@ -657,22 +657,39 @@ public List GetGroupTags()
return new List(_groups.Keys);
}
- #region IEnumerable> Members
+ ///
+ /// Read groups via IEnumerable.
+ ///
+ /// The group class type retrieved by groupCountTag
+ /// The FIX group tag
+ /// retrieved enumerable groups
+ /// If groupCountTag retreives a group that can't be cast to TGroup
+ public IEnumerable ReadGroups(int groupCountTag) where TGroup : Group
+ {
+ if (IsSetField(groupCountTag) && GetGroupTags().Contains(groupCountTag))
+ {
+ int grpCount = GetInt(groupCountTag);
+
+ for (int grpIndex = 1; grpIndex <= grpCount; grpIndex += 1)
+ {
+ Group group = GetGroup(grpIndex, groupCountTag);
+ if(group is not TGroup tgroup)
+ throw new InvalidCastException($"Can't cast {group.GetType()} to {typeof(TGroup)}");
+ yield return tgroup;
+ }
+ }
+ }
+ // IEnumerable> Member
public IEnumerator> GetEnumerator()
{
return _fields.GetEnumerator();
}
- #endregion
-
- #region IEnumerable Members
-
+ // IEnumerable Member
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _fields.GetEnumerator();
}
-
- #endregion
}
}
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 025268447..7a188562c 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -48,6 +48,7 @@ What's New
* #907 - A fix to make body-less messages work, specifically 35=n aka XMLnonFIX (gbirchmeier)
* #910 - faster Message.GetMsgType that doesn't use Regex (jkulubya)
* #516 - remove ability to toggle Session-enable via HttpServer because it never really worked (gbirchmeier)
+* #913/#741 - new FieldMap.ReadGroups for iterating on groups (NoviProg/gbirchmeier)
### v1.12.0
diff --git a/UnitTests/Extensions/GroupExtensionTest.cs b/UnitTests/FieldMap_ReadGroupsTests.cs
similarity index 90%
rename from UnitTests/Extensions/GroupExtensionTest.cs
rename to UnitTests/FieldMap_ReadGroupsTests.cs
index 99944c943..a3c8a8ef6 100644
--- a/UnitTests/Extensions/GroupExtensionTest.cs
+++ b/UnitTests/FieldMap_ReadGroupsTests.cs
@@ -1,21 +1,15 @@
-using System;
-using System.Collections.Generic;
+using System;
using System.Linq;
using NUnit.Framework;
-using QuickFix;
using QuickFix.FIX42;
using QuickFix.Fields;
-using QuickFix.Extensions;
-using UnitTests.TestHelpers;
-using Message = QuickFix.Message;
-namespace UnitTests.Extensions
-{
- [TestFixture]
+namespace UnitTests;
+
+[TestFixture]
+public class FieldMap_ReadGroupsTests {
public class GroupExtensionTest
{
- private readonly IMessageFactory _defaultMsgFactory = new DefaultMessageFactory();
-
private static News CreateNewsForTest(int numberOfLines) {
News msg = new();
msg.Headline = new Headline("Blah headline");