diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/SerializerFinders/SerializerFinderVisitMethodCall.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/SerializerFinders/SerializerFinderVisitMethodCall.cs index 1a704c6a71f..9aae17e4f8c 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/SerializerFinders/SerializerFinderVisitMethodCall.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/SerializerFinders/SerializerFinderVisitMethodCall.cs @@ -157,7 +157,11 @@ void DeduceMethodCallSerializers() case "ToHashedIndexKey": DeduceToHashedIndexKeySerializers(); break; case "ToList": DeduceToListSerializers(); break; case "ToString": DeduceToStringSerializers(); break; - case "Trim": DeduceTrimSerializers(); break; + case "Trim": + case "TrimStart": + case "TrimEnd": + DeduceTrimSerializers(); + break; case "Truncate": DeduceTruncateSerializers(); break; case "Union": DeduceUnionSerializers(); break; case "Week": DeduceWeekSerializers(); break; diff --git a/tests/MongoDB.Driver.Tests/Linq/Integration/StringTrimTests.cs b/tests/MongoDB.Driver.Tests/Linq/Integration/StringTrimTests.cs new file mode 100644 index 00000000000..d74e7977027 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Integration/StringTrimTests.cs @@ -0,0 +1,155 @@ +/* Copyright 2010-present MongoDB Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Collections.Generic; +using System.Linq; +using MongoDB.Driver.TestHelpers; +using FluentAssertions; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Integration; + +public class StringTrimTests : LinqIntegrationTest +{ + public StringTrimTests(ClassFixture fixture) + : base(fixture) + { + } + + [Fact] + public void Select_with_Trim_should_work() + { + var collection = Fixture.Collection; + + var queryable = collection.AsQueryable() + .Select(x => x.Str.Trim()); + + var stages = Translate(collection, queryable); + AssertStages(stages, """{ $project : { _v : { $trim : { input : "$Str" } }, _id : 0 } }"""); + + var results = queryable.ToList(); + results.Should().Equal("abcd", "abcd", "abcd", "abcd"); + } + + [Fact] + public void Select_with_Trim_with_chars_should_work() + { + var collection = Fixture.Collection; + + var queryable = collection.AsQueryable() + .Select(x => x.Str.Trim(new[] { ' ', 'a' })); + + var stages = Translate(collection, queryable); + AssertStages(stages, """{ $project : { _v : { $trim : { input : "$Str", chars : " a" } }, _id : 0 } }"""); + + var results = queryable.ToList(); + results.Should().Equal("bcd", "bcd", "bcd", "bcd"); + } + + [Fact] + public void Select_with_Trim_with_empty_chars_should_work() + { + var collection = Fixture.Collection; + + var queryable = collection.AsQueryable() + .Select(x => x.Str.Trim(new char[0])); + + var stages = Translate(collection, queryable); + AssertStages(stages, """{ $project : { _v : { $trim : { input : "$Str" } }, _id : 0 } }"""); + + var results = queryable.ToList(); + results.Should().Equal("abcd", "abcd", "abcd", "abcd"); + } + + [Fact] + public void Select_with_TrimStart_with_chars_should_work() + { + var collection = Fixture.Collection; + + var queryable = collection.AsQueryable() + .Select(x => x.Str.TrimStart(new[] { ' ', 'a' })); + + var stages = Translate(collection, queryable); + AssertStages(stages, """{ $project : { _v : { $ltrim : { input : "$Str", chars : " a" } }, _id : 0 } }"""); + + var results = queryable.ToList(); + results.Should().Equal("bcd ", "bcd ", "bcd", "bcd"); + } + + [Fact] + public void Select_with_TrimStart_with_empty_chars_should_work() + { + var collection = Fixture.Collection; + + var queryable = collection.AsQueryable() + .Select(x => x.Str.TrimStart(new char[0])); + + var stages = Translate(collection, queryable); + AssertStages(stages, """{ $project : { _v : { $ltrim : { input : "$Str" } }, _id : 0 } }"""); + + var results = queryable.ToList(); + results.Should().Equal("abcd ", "abcd ", "abcd", "abcd"); + } + + [Fact] + public void Select_with_TrimEnd_with_chars_should_work() + { + var collection = Fixture.Collection; + + var queryable = collection.AsQueryable() + .Select(x => x.Str.TrimEnd(new[] { ' ', 'd' })); + + var stages = Translate(collection, queryable); + AssertStages(stages, """{ $project : { _v : { $rtrim : { input : "$Str", chars : " d" } }, _id : 0 } }"""); + + var results = queryable.ToList(); + results.Should().Equal(" abc", "abc", " abc", "abc"); + } + + [Fact] + public void Select_with_TrimEnd_with_empty_chars_should_work() + { + var collection = Fixture.Collection; + + var queryable = collection.AsQueryable() + .Select(x => x.Str.TrimEnd(new char[0])); + + var stages = Translate(collection, queryable); + AssertStages(stages, """{ $project : { _v : { $rtrim : { input : "$Str" } }, _id : 0 } }"""); + + var results = queryable.ToList(); + results.Should().Equal(" abcd", "abcd", " abcd", "abcd"); + } + + // Add coverage for parameterless and single char overloads of Trim, TrimStart, and TrimEnd, see https://jira.mongodb.org/browse/CSHARP-5979 + // e.g. Trim(' '), TrimStart(), TrimStart(' '), TrimEnd(), TrimEnd(' ') + + public class C + { + public int Id { get; set; } + public string Str { get; set; } + } + + public sealed class ClassFixture : MongoCollectionFixture + { + protected override IEnumerable InitialData => + [ + new C { Id = 1, Str = " abcd "}, + new C { Id = 2, Str = "abcd "}, + new C { Id = 3, Str = " abcd"}, + new C { Id = 4, Str = "abcd"}, + ]; + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/SerializerFinders/StringTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/SerializerFinders/StringTests.cs index d57210ab78b..bb714afd53e 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/SerializerFinders/StringTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/SerializerFinders/StringTests.cs @@ -127,6 +127,8 @@ public void SerializerFinder_should_resolve_string_methods(LambdaExpression expr [TestHelpers.MakeLambda((MyModel model) => model.Name.ToUpperInvariant()), typeof(StringSerializer)], [TestHelpers.MakeLambda((MyModel model) => model.Name.Trim()), typeof(StringSerializer)], [TestHelpers.MakeLambda((MyModel model) => model.Name.Trim(new char[] { ' ' })), typeof(StringSerializer)], + [TestHelpers.MakeLambda((MyModel model) => model.Name.TrimStart(new char[] { ' ' })), typeof(StringSerializer)], + [TestHelpers.MakeLambda((MyModel model) => model.Name.TrimEnd(new char[] { ' ' })), typeof(StringSerializer)], ]; [Theory] @@ -153,6 +155,12 @@ public void SerializerFinder_should_set_unknowable_serializer_for_unsupported_st // StringComparison and CultureInfo overloads are not in ReplaceOverloads [TestHelpers.MakeLambda((MyModel model) => model.Name.Replace("old", "new", StringComparison.Ordinal))], [TestHelpers.MakeLambda((MyModel model) => model.Name.Replace("old", "new", false, CultureInfo.InvariantCulture))], + // Not supported yet, see https://jira.mongodb.org/browse/CSHARP-5979 + [TestHelpers.MakeLambda((MyModel model) => model.Name.Trim(' '))], + [TestHelpers.MakeLambda((MyModel model) => model.Name.TrimStart(' '))], + [TestHelpers.MakeLambda((MyModel model) => model.Name.TrimEnd())], + [TestHelpers.MakeLambda((MyModel model) => model.Name.TrimStart())], + [TestHelpers.MakeLambda((MyModel model) => model.Name.TrimEnd())], #endif ];