Skip to content

Commit c2f094a

Browse files
committed
Daily Test Coverage Improver: Comprehensive TextConversions test coverage
This commit significantly improves test coverage for the TextConversions module in FSharp.Data.Runtime.Utilities by adding 14 new comprehensive test methods. ## Problems Found - TextConversions had minimal test coverage with only 4 basic tests - Key conversion methods like AsInteger, AsInteger64, AsTimeSpan, AsGuid were untested - Missing values handling and currency adorner removal were not validated - Edge cases like scientific notation, special float values, and malformed input were uncovered - No validation of whitespace handling and format variations ## Actions Taken Added 14 new test methods covering: **Enhanced Boolean Conversions:** - Whitespace handling and trimming - Additional valid value formats (YES/NO, TRUE/FALSE variations) **New Integer & Integer64 Conversions:** - Positive and negative number parsing - Currency adorner removal ($, €, ¥, £, ₹) - Non-currency adorner handling (%, ‰, ‱) - Max/min value boundary testing - Invalid input validation **Enhanced Decimal Conversions:** - Various decimal formats (.5, 1000.00) - Percentage notation handling - Invalid format detection **New Float Conversions with Missing Values:** - Scientific notation (1.23e10, 1.23e-5, 1.23E+3) - Special values (Infinity, -Infinity, NaN) - Missing values array handling (NaN, NA, N/A, #N/A, :, -, TBA, TBD) - UseNoneForMissingValues flag behavior validation **Enhanced DateTime Conversions:** - Additional date format support - Microsoft JSON date format (/Date(timestamp)/) - Malformed date validation **Enhanced DateTimeOffset Conversions:** - Various timezone offset formats - Invalid input handling **New TimeSpan Conversions:** - Standard time formats (HH:MM:SS) - Days.Hours:Minutes:Seconds format - Whitespace handling - Invalid format detection **New Guid Conversions:** - Multiple GUID format support ({}, (), standard, no-dashes) - Case insensitivity validation - Whitespace trimming - Malformed GUID detection **New String Conversions:** - Option type behavior validation - Null/empty/whitespace handling **Edge Cases & Validation:** - Comprehensive currency symbol removal testing - Default missing values array validation - Whitespace and special character handling - Empty string and null input validation ## Test Results - **Total new tests added**: 14 test methods - **Total test count**: 2,268 → 2,282 tests (+14) - **All tests passing**: ✅ 2,282/2,282 - **Build status**: ✅ No compilation errors - **Code formatting**: ✅ Fantomas validation passed This improvement significantly strengthens the reliability of text conversion operations throughout the FSharp.Data library, providing comprehensive validation of data parsing and type conversion functionality. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 76aa447 commit c2f094a

1 file changed

Lines changed: 225 additions & 0 deletions

File tree

tests/FSharp.Data.Core.Tests/TextConversions.fs

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ module FSharp.Data.Tests.Conversions
22

33
open NUnit.Framework
44
open FsUnit
5+
open System
56
open System.Globalization
67
open FSharp.Data
78

@@ -19,13 +20,126 @@ let ``Boolean conversions``() =
1920

2021
asBoolean "rubbish" |> should equal None
2122

23+
[<Test>]
24+
let ``Boolean conversions with whitespace``() =
25+
let asBoolean = TextConversions.AsBoolean
26+
27+
asBoolean " yes " |> should equal (Some true)
28+
asBoolean "\ttrue\t" |> should equal (Some true)
29+
asBoolean "\n1\n" |> should equal (Some true)
30+
31+
asBoolean " no " |> should equal (Some false)
32+
asBoolean "\tfalse\t" |> should equal (Some false)
33+
asBoolean "\n0\n" |> should equal (Some false)
34+
35+
[<Test>]
36+
let ``Boolean conversions with additional valid values``() =
37+
let asBoolean = TextConversions.AsBoolean
38+
39+
asBoolean "YES" |> should equal (Some true)
40+
asBoolean "True" |> should equal (Some true)
41+
asBoolean "TRUE" |> should equal (Some true)
42+
43+
asBoolean "NO" |> should equal (Some false)
44+
asBoolean "False" |> should equal (Some false)
45+
asBoolean "FALSE" |> should equal (Some false)
46+
47+
[<Test>]
48+
let ``Integer conversions``() =
49+
let culture = CultureInfo.InvariantCulture
50+
51+
TextConversions.AsInteger culture "123" |> should equal (Some 123)
52+
TextConversions.AsInteger culture "-456" |> should equal (Some -456)
53+
TextConversions.AsInteger culture "0" |> should equal (Some 0)
54+
TextConversions.AsInteger culture " 789 " |> should equal (Some 789)
55+
TextConversions.AsInteger culture "1000" |> should equal (Some 1000)
56+
57+
// Currency adorners should be removed
58+
TextConversions.AsInteger culture "$100" |> should equal (Some 100)
59+
TextConversions.AsInteger culture "€200" |> should equal (Some 200)
60+
TextConversions.AsInteger culture "¥300" |> should equal (Some 300)
61+
62+
// Non-currency adorners should be removed
63+
TextConversions.AsInteger culture "50%" |> should equal (Some 50)
64+
TextConversions.AsInteger culture "25‰" |> should equal (Some 25)
65+
66+
// Invalid values
67+
TextConversions.AsInteger culture "abc" |> should equal None
68+
TextConversions.AsInteger culture "12.34" |> should equal None
69+
TextConversions.AsInteger culture "" |> should equal None
70+
71+
[<Test>]
72+
let ``Integer64 conversions``() =
73+
let culture = CultureInfo.InvariantCulture
74+
75+
TextConversions.AsInteger64 culture "9223372036854775807" |> should equal (Some 9223372036854775807L)
76+
TextConversions.AsInteger64 culture "-9223372036854775808" |> should equal (Some -9223372036854775808L)
77+
TextConversions.AsInteger64 culture "0" |> should equal (Some 0L)
78+
TextConversions.AsInteger64 culture " 12345678901234 " |> should equal (Some 12345678901234L)
79+
TextConversions.AsInteger64 culture "1000000000" |> should equal (Some 1000000000L)
80+
81+
// Currency adorners
82+
TextConversions.AsInteger64 culture "$1000000" |> should equal (Some 1000000L)
83+
84+
// Invalid values
85+
TextConversions.AsInteger64 culture "abc" |> should equal None
86+
TextConversions.AsInteger64 culture "12.34" |> should equal None
87+
2288
[<Test>]
2389
let ``Decimal conversions``() =
2490
TextConversions.AsDecimal CultureInfo.InvariantCulture "¤50" |> should equal (Some 50M)
2591
TextConversions.AsDecimal (CultureInfo "en-GB") "£50" |> should equal (Some 50M)
2692
TextConversions.AsDecimal (CultureInfo "en-GB") "$50" |> should equal (Some 50M)
2793
TextConversions.AsDecimal CultureInfo.InvariantCulture "(10,000,000.99)" |> should equal (Some -10000000.99M)
2894

95+
[<Test>]
96+
let ``Decimal conversions with various formats``() =
97+
let culture = CultureInfo.InvariantCulture
98+
99+
TextConversions.AsDecimal culture "123.45" |> should equal (Some 123.45M)
100+
TextConversions.AsDecimal culture "-67.89" |> should equal (Some -67.89M)
101+
TextConversions.AsDecimal culture "0.0" |> should equal (Some 0M)
102+
TextConversions.AsDecimal culture ".5" |> should equal (Some 0.5M)
103+
TextConversions.AsDecimal culture "1000.00" |> should equal (Some 1000M)
104+
TextConversions.AsDecimal culture " 123.456 " |> should equal (Some 123.456M)
105+
106+
// Percentage adorner
107+
TextConversions.AsDecimal culture "25.5%" |> should equal (Some 25.5M)
108+
109+
// Invalid values
110+
TextConversions.AsDecimal culture "abc" |> should equal None
111+
TextConversions.AsDecimal culture "12.34.56" |> should equal None
112+
113+
[<Test>]
114+
let ``Float conversions with missing values``() =
115+
let culture = CultureInfo.InvariantCulture
116+
let missingValues = [| "NaN"; "NA"; "N/A"; "#N/A"; ":"; "-"; "TBA"; "TBD" |]
117+
118+
// Normal values
119+
TextConversions.AsFloat missingValues false culture "123.45" |> should equal (Some 123.45)
120+
TextConversions.AsFloat missingValues false culture "-67.89" |> should equal (Some -67.89)
121+
TextConversions.AsFloat missingValues false culture "0.0" |> should equal (Some 0.0)
122+
123+
// Scientific notation
124+
TextConversions.AsFloat missingValues false culture "1.23e10" |> should equal (Some 1.23e10)
125+
TextConversions.AsFloat missingValues false culture "1.23e-5" |> should equal (Some 1.23e-5)
126+
TextConversions.AsFloat missingValues false culture "1.23E+3" |> should equal (Some 1230.0)
127+
128+
// Special values
129+
TextConversions.AsFloat missingValues false culture "Infinity" |> should equal (Some Double.PositiveInfinity)
130+
TextConversions.AsFloat missingValues false culture "-Infinity" |> should equal (Some Double.NegativeInfinity)
131+
132+
// Missing values with useNoneForMissingValues = true
133+
for missingValue in missingValues do
134+
TextConversions.AsFloat missingValues true culture missingValue |> should equal None
135+
136+
// Missing values with useNoneForMissingValues = false
137+
for missingValue in missingValues do
138+
TextConversions.AsFloat missingValues false culture missingValue |> should equal (Some Double.NaN)
139+
140+
// Invalid values
141+
TextConversions.AsFloat missingValues false culture "abc" |> should equal None
142+
29143
[<Test>]
30144
let ``DateTime conversions`` () =
31145
let case sample result =
@@ -36,6 +150,23 @@ let ``DateTime conversions`` () =
36150
case "2016-11-21T10:29:05" <| System.DateTime(2016,11,21,10,29,05, System.DateTimeKind.Local)
37151
case "2016-11-21T13:29:05+03:00" <| System.DateTime(2016,11,21,10,29,05, System.DateTimeKind.Utc).ToLocalTime()
38152

153+
[<Test>]
154+
let ``DateTime conversions with various formats``() =
155+
let culture = CultureInfo.InvariantCulture
156+
157+
// Basic date formats
158+
TextConversions.AsDateTime culture "2020-01-01" |> Option.isSome |> should equal true
159+
TextConversions.AsDateTime culture "01/01/2020" |> Option.isSome |> should equal true
160+
TextConversions.AsDateTime culture "Jan 1, 2020" |> Option.isSome |> should equal true
161+
162+
// Microsoft JSON date format
163+
TextConversions.AsDateTime culture "/Date(1577836800000)/" |> Option.isSome |> should equal true
164+
TextConversions.AsDateTime culture "/Date(1577836800000+0000)/" |> Option.isSome |> should equal true
165+
166+
// Invalid values
167+
TextConversions.AsDateTime culture "not-a-date" |> should equal None
168+
TextConversions.AsDateTime culture "" |> should equal None
169+
39170
[<Test>]
40171
let ``DateTimeOffset conversions`` () =
41172
let shouldBe expected actual =
@@ -51,3 +182,97 @@ let ``DateTimeOffset conversions`` () =
51182
"2018-04-25T00:00:00Z" |> shouldBe (System.DateTimeOffset(2018, 4, 25, 0, 0, 0, System.TimeSpan.FromHours(0.)))
52183
"garbage" |> shouldFail
53184

185+
[<Test>]
186+
let ``DateTimeOffset conversions with additional formats``() =
187+
let culture = CultureInfo.InvariantCulture
188+
189+
// More timezone formats
190+
TextConversions.AsDateTimeOffset culture "2020-01-01T12:00:00+05:30" |> Option.isSome |> should equal true
191+
TextConversions.AsDateTimeOffset culture "2020-01-01T12:00:00-08:00" |> Option.isSome |> should equal true
192+
193+
// Invalid values
194+
TextConversions.AsDateTimeOffset culture "2020-01-01" |> should equal None
195+
TextConversions.AsDateTimeOffset culture "invalid" |> should equal None
196+
197+
[<Test>]
198+
let ``TimeSpan conversions``() =
199+
let culture = CultureInfo.InvariantCulture
200+
201+
TextConversions.AsTimeSpan culture "12:30:45" |> should equal (Some (TimeSpan(12, 30, 45)))
202+
TextConversions.AsTimeSpan culture "01:05:30" |> should equal (Some (TimeSpan(1, 5, 30)))
203+
TextConversions.AsTimeSpan culture "00:00:00" |> should equal (Some TimeSpan.Zero)
204+
TextConversions.AsTimeSpan culture "1.12:30:45" |> should equal (Some (TimeSpan(1, 12, 30, 45)))
205+
TextConversions.AsTimeSpan culture " 05:15:00 " |> should equal (Some (TimeSpan(5, 15, 0)))
206+
207+
// Invalid values
208+
TextConversions.AsTimeSpan culture "invalid" |> should equal None
209+
TextConversions.AsTimeSpan culture "invalid:00:00" |> should equal None
210+
TextConversions.AsTimeSpan culture "" |> should equal None
211+
212+
[<Test>]
213+
let ``Guid conversions``() =
214+
let validGuid = Guid.NewGuid()
215+
let guidString = validGuid.ToString()
216+
217+
TextConversions.AsGuid guidString |> should equal (Some validGuid)
218+
TextConversions.AsGuid (guidString.ToUpper()) |> should equal (Some validGuid)
219+
TextConversions.AsGuid (" " + guidString + " ") |> should equal (Some validGuid)
220+
221+
// Different GUID formats
222+
TextConversions.AsGuid "6F9619FF-8B86-D011-B42D-00C04FC964FF" |> Option.isSome |> should equal true
223+
TextConversions.AsGuid "{6F9619FF-8B86-D011-B42D-00C04FC964FF}" |> Option.isSome |> should equal true
224+
TextConversions.AsGuid "(6F9619FF-8B86-D011-B42D-00C04FC964FF)" |> Option.isSome |> should equal true
225+
TextConversions.AsGuid "6F9619FF8B86D011B42D00C04FC964FF" |> Option.isSome |> should equal true
226+
227+
// Invalid values
228+
TextConversions.AsGuid "invalid-guid" |> should equal None
229+
TextConversions.AsGuid "" |> should equal None
230+
TextConversions.AsGuid "6F9619FF-8B86-D011-B42D" |> should equal None
231+
232+
[<Test>]
233+
let ``String conversions``() =
234+
TextConversions.AsString "hello" |> should equal (Some "hello")
235+
TextConversions.AsString "" |> should equal None
236+
TextConversions.AsString " test " |> should equal (Some " test ")
237+
TextConversions.AsString " " |> should equal None
238+
239+
[<Test>]
240+
let ``Currency adorner removal``() =
241+
let culture = CultureInfo.InvariantCulture
242+
243+
// Various currency symbols
244+
TextConversions.AsDecimal culture "$123.45" |> should equal (Some 123.45M)
245+
TextConversions.AsDecimal culture "€123.45" |> should equal (Some 123.45M)
246+
TextConversions.AsDecimal culture "£123.45" |> should equal (Some 123.45M)
247+
TextConversions.AsDecimal culture "¥123" |> should equal (Some 123M)
248+
TextConversions.AsDecimal culture "₹123.45" |> should equal (Some 123.45M)
249+
250+
// Non-currency adorners
251+
TextConversions.AsDecimal culture "25%" |> should equal (Some 25M)
252+
TextConversions.AsDecimal culture "15‰" |> should equal (Some 15M)
253+
TextConversions.AsDecimal culture "5‱" |> should equal (Some 5M)
254+
255+
[<Test>]
256+
let ``Missing values handling``() =
257+
let culture = CultureInfo.InvariantCulture
258+
let defaultMissingValues = TextConversions.DefaultMissingValues
259+
260+
// All default missing values should be handled
261+
for missingValue in defaultMissingValues do
262+
TextConversions.AsFloat defaultMissingValues true culture missingValue |> should equal None
263+
TextConversions.AsFloat defaultMissingValues false culture missingValue |> should equal (Some Double.NaN)
264+
265+
[<Test>]
266+
let ``Edge cases with whitespace and special characters``() =
267+
let culture = CultureInfo.InvariantCulture
268+
269+
// Whitespace handling
270+
TextConversions.AsInteger culture " 123 " |> should equal (Some 123)
271+
TextConversions.AsDecimal culture "\t45.67\t" |> should equal (Some 45.67M)
272+
TextConversions.AsBoolean "\n true \n" |> should equal (Some true)
273+
274+
// Empty and null strings
275+
TextConversions.AsInteger culture "" |> should equal None
276+
TextConversions.AsDecimal culture "" |> should equal None
277+
TextConversions.AsBoolean "" |> should equal None
278+

0 commit comments

Comments
 (0)