Skip to content

Commit 3e61d1b

Browse files
authored
Merge branch 'develop' into 1008-fix-qicore-2025-bugs-revisit-aliasrefresulttypespecifier-resolver-logic
2 parents 96ab186 + 08c1861 commit 3e61d1b

File tree

2 files changed

+247
-18
lines changed

2 files changed

+247
-18
lines changed

Cql/CoreTests/PrimitiveTests.cs

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3724,6 +3724,234 @@ public void Subtract_Decimal_To_MinDecimal()
37243724
var subtractedValue = fcq.Subtract(decimal.MinValue, 1m);
37253725
Assert.IsNull(subtractedValue);
37263726
}
3727+
3728+
#region Slice tests
3729+
3730+
/* Refer http://cql.hl7.org/09-b-cqlreference.html for operation details on Skip, Tail and Take cql operators
3731+
* These CQL operators uses Slice semantics from http://cql.hl7.org/04-logicalspecification.html#slice
3732+
*/
3733+
3734+
[TestMethod]
3735+
public void SliceSkip2()
3736+
{
3737+
//The Skip operator returns the elements in the list, skipping the first number elements.
3738+
//define "Skip2": Skip({ 1, 2, 3, 4, 5 }, 2) // { 3, 4, 5 }
3739+
var rtx = GetNewContext();
3740+
var inputList = new List<int> { 1, 2, 3, 4, 5 };
3741+
var expectedList = new List<int> { 3, 4, 5 };
3742+
var slicedList = rtx.Operators.Slice(inputList, 2, null);
3743+
Assert.IsNotNull(slicedList);
3744+
CollectionAssert.AreEqual(expectedList, slicedList.ToList());
3745+
}
3746+
3747+
[TestMethod]
3748+
public void SliceSkipNull()
3749+
{
3750+
//If the number of elements is null, the result is the entire list, no elements are skipped.
3751+
//define "SkipNull": Skip({ 1, 3, 5 }, null) // { 1, 3, 5 }
3752+
var rtx = GetNewContext();
3753+
var inputList = new List<int> { 1, 3, 5 };
3754+
var expectedList = new List<int> { 1, 3, 5 };
3755+
var slicedList = rtx.Operators.Slice(inputList, null, null);
3756+
Assert.IsNotNull(slicedList);
3757+
CollectionAssert.AreEqual(expectedList, slicedList.ToList());
3758+
}
3759+
3760+
[TestMethod]
3761+
public void SliceSkipEmpty()
3762+
{
3763+
//If the number of elements is less than zero, the result is an empty list.
3764+
//define "SkipEmpty": Skip({ 1, 3, 5 }, -1) // { }
3765+
var rtx = GetNewContext();
3766+
var inputList = new List<int> { 1, 3, 5 };
3767+
var expectedList = new List<int> { };
3768+
var slicedList = rtx.Operators.Slice(inputList, -1, null);
3769+
Assert.IsNotNull(slicedList);
3770+
CollectionAssert.AreEqual(expectedList, slicedList.ToList());
3771+
}
3772+
3773+
[TestMethod]
3774+
public void SliceSkipIsNull()
3775+
{
3776+
//If the source list is null, the result is null.
3777+
//define "SkipIsNull": Skip(null, 2)
3778+
var rtx = GetNewContext();
3779+
var inputList = null as List<int>;
3780+
var expectedList = null as List<int>;
3781+
var slicedList = rtx.Operators.Slice(inputList, 2, null);
3782+
Assert.IsNull(slicedList);
3783+
Assert.AreEqual(expectedList, slicedList);
3784+
}
3785+
3786+
[TestMethod]
3787+
public void SliceSkipZero()
3788+
{
3789+
var rtx = GetNewContext();
3790+
var inputList = new List<int> { 1, 2, 3, 4, 5 };
3791+
var expectedList = new List<int> { 1, 2, 3, 4, 5 };
3792+
var slicedList = rtx.Operators.Slice(inputList, 0, null);
3793+
Assert.IsNotNull(slicedList);
3794+
CollectionAssert.AreEqual(expectedList, slicedList.ToList());
3795+
}
3796+
3797+
[TestMethod]
3798+
public void SliceTail234()
3799+
{
3800+
//The Tail operator returns all but the first element from the given list.
3801+
//define "Tail234": Tail({ 1, 2, 3, 4 }) // { 2, 3, 4 }
3802+
var rtx = GetNewContext();
3803+
var inputList = new List<int> { 1, 2, 3, 4 };
3804+
var expectedList = new List<int> { 2, 3, 4 };
3805+
var slicedList = rtx.Operators.Slice(inputList, 1, null);
3806+
Assert.IsNotNull(slicedList);
3807+
CollectionAssert.AreEqual(expectedList, slicedList.ToList());
3808+
}
3809+
3810+
[TestMethod]
3811+
public void SliceTailEmpty()
3812+
{
3813+
//If the list is empty, the result is empty.
3814+
//define "TailEmpty": Tail({ }) // { }
3815+
var rtx = GetNewContext();
3816+
var inputList = new List<int> { };
3817+
var expectedList = new List<int> { };
3818+
var slicedList = rtx.Operators.Slice(inputList, 1, null);
3819+
Assert.IsNotNull(slicedList);
3820+
CollectionAssert.AreEqual(expectedList, slicedList.ToList());
3821+
}
3822+
3823+
[TestMethod]
3824+
public void SliceTailIsNull()
3825+
{
3826+
//If the source list is null, the result is null.
3827+
//define "TailIsNull": Tail(null)
3828+
var rtx = GetNewContext();
3829+
var inputList = null as List<int>;
3830+
var expectedList = null as List<int>;
3831+
var slicedList = rtx.Operators.Slice(inputList, 1, null);
3832+
Assert.IsNull(slicedList);
3833+
Assert.AreEqual(expectedList, slicedList);
3834+
}
3835+
3836+
[TestMethod]
3837+
public void SliceTake2()
3838+
{
3839+
//The Take operator returns the first number elements from the given list.
3840+
//define "Take2": Take({ 1, 2, 3, 4 }, 2) // { 1, 2 }
3841+
var rtx = GetNewContext();
3842+
var inputList = new List<int> { 1, 2, 3, 4 };
3843+
var expectedList = new List<int> { 1, 2 };
3844+
var slicedList = rtx.Operators.Slice(inputList, 0, 2);
3845+
Assert.IsNotNull(slicedList);
3846+
CollectionAssert.AreEqual(expectedList, slicedList.ToList());
3847+
}
3848+
3849+
[TestMethod]
3850+
public void SliceTakeTooMany()
3851+
{
3852+
//If the list has less than number elements, the result only contains the elements in the list.
3853+
//define "TakeTooMany": Take({ 1, 2 }, 3) // { 1, 2 }
3854+
var rtx = GetNewContext();
3855+
var inputList = new List<int> { 1, 2 };
3856+
var expectedList = new List<int> { 1, 2 };
3857+
var slicedList = rtx.Operators.Slice(inputList, 0, 3);
3858+
Assert.IsNotNull(slicedList);
3859+
CollectionAssert.AreEqual(expectedList, slicedList.ToList());
3860+
}
3861+
3862+
[TestMethod]
3863+
public void SliceTakeEmpty()
3864+
{
3865+
//If number is null, or 0 or less, the result is an empty list.
3866+
//define "TakeEmpty": Take({ 1, 2, 3, 4 }, null) // { }
3867+
var rtx = GetNewContext();
3868+
var inputList = new List<int> { 1, 2, 3, 4 };
3869+
var expectedList = new List<int> { };
3870+
var slicedList = rtx.Operators.Slice(inputList, 0, 0);
3871+
Assert.IsNotNull(slicedList);
3872+
CollectionAssert.AreEqual(expectedList, slicedList.ToList());
3873+
}
3874+
3875+
[TestMethod]
3876+
public void SliceTakeIsNull()
3877+
{
3878+
//If the source list is null, the result is null.
3879+
//define "TakeIsNull": Take(null, 2)
3880+
var rtx = GetNewContext();
3881+
var inputList = null as List<int>;
3882+
var expectedList = null as List<int>;
3883+
var slicedList = rtx.Operators.Slice(inputList, 0, 2);
3884+
Assert.IsNull(slicedList);
3885+
Assert.AreEqual(expectedList, slicedList);
3886+
}
3887+
3888+
#endregion
3889+
3890+
#region ListSkip and ListTake tests
3891+
3892+
[TestMethod]
3893+
public void ListSkipNull()
3894+
{
3895+
var rtx = GetNewContext();
3896+
var inputList = new List<int> { 1, 2, 3, 4, 5 };
3897+
var expectedList = new List<int> { 1, 2, 3, 4, 5 };
3898+
var skippedList = rtx.Operators.ListSkip(inputList, null);
3899+
Assert.IsNotNull(skippedList);
3900+
CollectionAssert.AreEqual(expectedList, skippedList.ToList());
3901+
}
3902+
3903+
[TestMethod]
3904+
public void ListSkipNullInput()
3905+
{
3906+
var rtx = GetNewContext();
3907+
var inputList = null as List<int>;
3908+
var skippedList = rtx.Operators.ListSkip(inputList, 2);
3909+
Assert.IsNull(skippedList);
3910+
}
3911+
3912+
[TestMethod]
3913+
public void ListSkip()
3914+
{
3915+
var rtx = GetNewContext();
3916+
var inputList = new List<int> { 1, 2, 3, 4, 5 };
3917+
var expectedList = new List<int> { 3, 4, 5 };
3918+
var skippedList = rtx.Operators.ListSkip(inputList, 2);
3919+
Assert.IsNotNull(skippedList);
3920+
CollectionAssert.AreEqual(expectedList, skippedList.ToList());
3921+
}
3922+
3923+
[TestMethod]
3924+
public void ListTakeNull()
3925+
{
3926+
var rtx = GetNewContext();
3927+
var inputList = new List<int> { 1, 2, 3, 4, 5 };
3928+
var expectedList = new List<int> { };
3929+
var takenList = rtx.Operators.ListTake(inputList, null);
3930+
Assert.IsNotNull(takenList);
3931+
CollectionAssert.AreEqual(expectedList, takenList.ToList());
3932+
}
3933+
3934+
[TestMethod]
3935+
public void ListTakeNullInput()
3936+
{
3937+
var rtx = GetNewContext();
3938+
var inputList = null as List<int>;
3939+
var takenList = rtx.Operators.ListTake(inputList, 2);
3940+
Assert.IsNull(takenList);
3941+
}
3942+
3943+
[TestMethod]
3944+
public void ListTake()
3945+
{
3946+
var rtx = GetNewContext();
3947+
var inputList = new List<int> { 1, 2, 3, 4, 5 };
3948+
var expectedList = new List<int> { 1, 2 };
3949+
var takenList = rtx.Operators.ListTake(inputList, 2);
3950+
Assert.IsNotNull(takenList);
3951+
CollectionAssert.AreEqual(expectedList, takenList.ToList());
3952+
}
3953+
3954+
#endregion
37273955

37283956
}
37293957
}

Cql/Cql.Runtime/Operators/CqlOperators.ListOperators.cs

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,32 +1047,31 @@ internal partial class CqlOperators
10471047
{
10481048
if (source == null)
10491049
return null;
1050-
if ((startIndex == null && endIndex == null) || !source.Any())
1051-
{
1052-
return [];
1053-
}
1054-
var si = startIndex ?? 0;
1055-
if (source is List<T> list)
1050+
1051+
if (!source.Any())
1052+
return Enumerable.Empty<T>();
1053+
1054+
if (startIndex == null && endIndex == null)
1055+
return source;
1056+
1057+
if (startIndex < 0 || endIndex <= 0)
1058+
return Enumerable.Empty<T>();
1059+
1060+
if (endIndex == null)
10561061
{
1057-
var lcm1 = list.Count - 1;
1058-
var ei = Math.Min(endIndex ?? lcm1, lcm1);
1059-
var count = ei - si + 1;
1060-
var slice = list.GetRange(si, count);
1061-
return slice;
1062+
return source.Skip(startIndex ?? 0).ToList();
10621063
}
10631064
else
10641065
{
1065-
var skip = source.Skip(si);
1066-
var result = new List<T>();
1067-
foreach (var item in skip.Take(endIndex ?? int.MaxValue))
1068-
result.Add(item);
1069-
return result;
1066+
return source.Skip(startIndex ?? 0).Take(endIndex.Value - (startIndex ?? 0)).ToList();
10701067
}
10711068
}
10721069

10731070
public IEnumerable<T>? ListSkip<T>(IEnumerable<T> argument, int? number)
10741071
{
1075-
if (argument == null || number == null) return null;
1072+
if (number == null) return argument;
1073+
1074+
if (argument == null) return null;
10761075
else return argument
10771076
.Skip(number.Value)
10781077
.ToList();
@@ -1090,7 +1089,9 @@ internal partial class CqlOperators
10901089

10911090
public IEnumerable<T>? ListTake<T>(IEnumerable<T> argument, int? number)
10921091
{
1093-
if (argument == null || number == null) return null;
1092+
if (number == null) return new List<T>();
1093+
1094+
if (argument == null) return null;
10941095
else return argument
10951096
.Take(number.Value)
10961097
.ToList();

0 commit comments

Comments
 (0)