Skip to content

Commit a60334e

Browse files
authored
Merge pull request #1592 from fsprojects/daily-test-improver-xml-inference-coverage
Daily Test Coverage Improver: Add comprehensive XmlInference module tests
2 parents bca3ba9 + 436f719 commit a60334e

File tree

2 files changed

+329
-0
lines changed

2 files changed

+329
-0
lines changed

tests/FSharp.Data.Core.Tests/FSharp.Data.Core.Tests.fsproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
<Compile Include="BaseTypesHtmlDocument.fs" />
4747
<Compile Include="HtmlRuntimeTypes.fs" />
4848
<Compile Include="XmlExtensions.fs" />
49+
<Compile Include="XmlInference.fs" />
4950
<Compile Include="XmlRuntime.fs" />
5051
<Compile Include="XmlSchema.fs" />
5152
<Compile Include="WorldBankRuntime.fs" />
@@ -62,6 +63,7 @@
6263
<ProjectReference Include="..\..\src\FSharp.Data.Csv.Core\FSharp.Data.Csv.Core.fsproj" />
6364
<ProjectReference Include="..\..\src\FSharp.Data.Html.Core\FSharp.Data.Html.Core.fsproj" />
6465
<ProjectReference Include="..\..\src\FSharp.Data.WorldBank.Core\FSharp.Data.WorldBank.Core.fsproj" />
66+
<ProjectReference Include="..\..\src\FSharp.Data.DesignTime\FSharp.Data.DesignTime.fsproj" />
6567
</ItemGroup>
6668
<ItemGroup>
6769
<PackageReference Update="FSharp.Core" Version="6.0.1" />
Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
module FSharp.Data.Core.Tests.XmlInference
2+
3+
open FsUnit
4+
open NUnit.Framework
5+
open System
6+
open System.Xml.Linq
7+
open FSharp.Data
8+
open FSharp.Data.Runtime
9+
open FSharp.Data.Runtime.StructuralTypes
10+
open FSharp.Data.Runtime.StructuralInference
11+
open ProviderImplementation
12+
13+
// Test infrastructure similar to InferenceTests.fs
14+
let internal culture = TextRuntime.GetCulture ""
15+
let internal inferenceMode = InferenceMode'.ValuesOnly
16+
let internal unitsOfMeasureProvider =
17+
{ new StructuralInference.IUnitsOfMeasureProvider with
18+
member x.SI(_) : System.Type = null
19+
member x.Product(_, _) = failwith "Not implemented yet"
20+
member x.Inverse(_) = failwith "Not implemented yet" }
21+
let internal allowEmptyValues = true
22+
23+
// Helper function to create XElement from string
24+
let createElement xmlString =
25+
XElement.Parse(xmlString)
26+
27+
// Helper function to create XElement array
28+
let createElements xmlStrings =
29+
xmlStrings |> Array.map createElement
30+
31+
[<Test>]
32+
let ``getInferedTypeFromValue handles simple string value`` () =
33+
let element = createElement """<name>John</name>"""
34+
let result = XmlInference.getInferedTypeFromValue unitsOfMeasureProvider inferenceMode culture element
35+
36+
match result with
37+
| InferedType.Primitive(t, _, _, _) -> t |> should equal typeof<string>
38+
| _ -> failwith "Expected primitive string type"
39+
40+
[<Test>]
41+
let ``getInferedTypeFromValue handles numeric value`` () =
42+
let element = createElement """<age>30</age>"""
43+
let result = XmlInference.getInferedTypeFromValue unitsOfMeasureProvider inferenceMode culture element
44+
45+
match result with
46+
| InferedType.Primitive(t, _, _, _) -> t |> should equal typeof<int>
47+
| _ -> failwith "Expected primitive int type"
48+
49+
[<Test>]
50+
let ``getInferedTypeFromValue handles boolean value`` () =
51+
let element = createElement """<active>true</active>"""
52+
let result = XmlInference.getInferedTypeFromValue unitsOfMeasureProvider inferenceMode culture element
53+
54+
match result with
55+
| InferedType.Primitive(t, _, _, _) -> t |> should equal typeof<bool>
56+
| _ -> failwith "Expected primitive bool type"
57+
58+
[<Test>]
59+
let ``getInferedTypeFromValue handles decimal value`` () =
60+
let element = createElement """<price>19.99</price>"""
61+
let result = XmlInference.getInferedTypeFromValue unitsOfMeasureProvider inferenceMode culture element
62+
63+
match result with
64+
| InferedType.Primitive(t, _, _, _) -> t |> should equal typeof<decimal>
65+
| _ -> failwith "Expected primitive decimal type"
66+
67+
[<Test>]
68+
let ``getInferedTypeFromValue handles embedded JSON object`` () =
69+
let element = createElement """<data>{"name": "John", "age": 30}</data>"""
70+
let result = XmlInference.getInferedTypeFromValue unitsOfMeasureProvider inferenceMode culture element
71+
72+
match result with
73+
| InferedType.Json(_, _) -> () // Success - embedded JSON detected
74+
| _ -> failwith "Expected JSON type for embedded JSON content"
75+
76+
[<Test>]
77+
let ``getInferedTypeFromValue handles embedded JSON array`` () =
78+
let element = createElement """<items>[1, 2, 3]</items>"""
79+
let result = XmlInference.getInferedTypeFromValue unitsOfMeasureProvider inferenceMode culture element
80+
81+
match result with
82+
| InferedType.Json(_, _) -> () // Success - embedded JSON detected
83+
| _ -> failwith "Expected JSON type for embedded JSON array"
84+
85+
[<Test>]
86+
let ``getInferedTypeFromValue with NoInference mode skips JSON parsing`` () =
87+
let element = createElement """<data>{"name": "John"}</data>"""
88+
let result = XmlInference.getInferedTypeFromValue unitsOfMeasureProvider InferenceMode'.NoInference culture element
89+
90+
match result with
91+
| InferedType.Primitive(t, _, _, _) -> t |> should equal typeof<string>
92+
| _ -> failwith "Expected string type with NoInference mode"
93+
94+
[<Test>]
95+
let ``getInferedTypeFromValue handles malformed JSON as string`` () =
96+
let element = createElement """<data>{"name": invalid}</data>"""
97+
let result = XmlInference.getInferedTypeFromValue unitsOfMeasureProvider inferenceMode culture element
98+
99+
match result with
100+
| InferedType.Primitive(t, _, _, _) -> t |> should equal typeof<string>
101+
| _ -> failwith "Expected string type for malformed JSON"
102+
103+
[<Test>]
104+
let ``getInferedTypeFromValue handles empty element`` () =
105+
let element = createElement """<empty></empty>"""
106+
let result = XmlInference.getInferedTypeFromValue unitsOfMeasureProvider inferenceMode culture element
107+
108+
// For empty elements, the inference returns Null type
109+
match result with
110+
| InferedType.Null -> () // Success - empty value gives Null type
111+
| _ -> failwithf "Expected Null type for empty value, got %A" result
112+
113+
[<Test>]
114+
let ``inferLocalType handles simple element with content`` () =
115+
let element = createElement """<name>John</name>"""
116+
let result = XmlInference.inferLocalType unitsOfMeasureProvider inferenceMode culture allowEmptyValues element
117+
118+
match result with
119+
| InferedType.Record(Some name, properties, false) ->
120+
name |> should equal "name"
121+
properties.Length |> should equal 1
122+
properties.[0].Name |> should equal "" // Body content
123+
| _ -> failwith "Expected record type with body content"
124+
125+
[<Test>]
126+
let ``inferLocalType handles element with attributes`` () =
127+
let element = createElement """<person name="John" age="30">Developer</person>"""
128+
let result = XmlInference.inferLocalType unitsOfMeasureProvider inferenceMode culture allowEmptyValues element
129+
130+
match result with
131+
| InferedType.Record(Some name, properties, false) ->
132+
name |> should equal "person"
133+
properties.Length |> should equal 3 // body content + 2 attributes
134+
properties |> List.exists (fun p -> p.Name = "name") |> should equal true
135+
properties |> List.exists (fun p -> p.Name = "age") |> should equal true
136+
properties |> List.exists (fun p -> p.Name = "") |> should equal true // body
137+
| _ -> failwith "Expected record type with attributes and body"
138+
139+
[<Test>]
140+
let ``inferLocalType handles element with child elements`` () =
141+
let element = createElement """<person><name>John</name><age>30</age></person>"""
142+
let result = XmlInference.inferLocalType unitsOfMeasureProvider inferenceMode culture allowEmptyValues element
143+
144+
match result with
145+
| InferedType.Record(Some name, properties, false) ->
146+
name |> should equal "person"
147+
properties.Length |> should equal 1 // Collection of children
148+
properties.[0].Name |> should equal "" // Body content (collection)
149+
match properties.[0].Type with
150+
| InferedType.Collection(_, _) -> () // Success - collection of children
151+
| _ -> failwith "Expected collection type for child elements"
152+
| _ -> failwith "Expected record type with child collection"
153+
154+
[<Test>]
155+
let ``inferLocalType handles empty element`` () =
156+
let element = createElement """<empty />"""
157+
let result = XmlInference.inferLocalType unitsOfMeasureProvider inferenceMode culture allowEmptyValues element
158+
159+
match result with
160+
| InferedType.Record(Some name, properties, false) ->
161+
name |> should equal "empty"
162+
properties.Length |> should equal 0 // No content or attributes
163+
| _ -> failwith "Expected empty record type"
164+
165+
[<Test>]
166+
let ``inferLocalType handles element with only attributes`` () =
167+
let element = createElement """<config debug="true" timeout="30" />"""
168+
let result = XmlInference.inferLocalType unitsOfMeasureProvider inferenceMode culture allowEmptyValues element
169+
170+
match result with
171+
| InferedType.Record(Some name, properties, false) ->
172+
name |> should equal "config"
173+
properties.Length |> should equal 2 // 2 attributes, no body
174+
properties |> List.exists (fun p -> p.Name = "debug") |> should equal true
175+
properties |> List.exists (fun p -> p.Name = "timeout") |> should equal true
176+
| _ -> failwith "Expected record type with attributes only"
177+
178+
[<Test>]
179+
let ``inferLocalType handles nested structure`` () =
180+
let element = createElement """<root><item id="1"><value>test</value></item><item id="2"><value>test2</value></item></root>"""
181+
let result = XmlInference.inferLocalType unitsOfMeasureProvider inferenceMode culture allowEmptyValues element
182+
183+
match result with
184+
| InferedType.Record(Some name, properties, false) ->
185+
name |> should equal "root"
186+
properties.Length |> should equal 1
187+
match properties.[0].Type with
188+
| InferedType.Collection(_, _) -> () // Collection of items
189+
| _ -> failwith "Expected collection of items"
190+
| _ -> failwith "Expected root record with item collection"
191+
192+
[<Test>]
193+
let ``inferGlobalType handles single element`` () =
194+
let doc = XDocument.Parse("""<root><person name="John">Developer</person></root>""")
195+
let elements = [| doc.Root |]
196+
let result = XmlInference.inferGlobalType unitsOfMeasureProvider inferenceMode culture allowEmptyValues elements
197+
198+
result.Length |> should equal 1
199+
match result.[0] with
200+
| InferedType.Record(Some name, _, false) ->
201+
name |> should equal "root"
202+
| _ -> failwith "Expected root record type"
203+
204+
[<Test>]
205+
let ``inferGlobalType handles multiple elements of same type`` () =
206+
let xml = """<doc><person name="John" age="30" /><person name="Jane" city="NYC" /></doc>"""
207+
let doc = XDocument.Parse(xml)
208+
let elements = [| doc.Root |]
209+
let result = XmlInference.inferGlobalType unitsOfMeasureProvider inferenceMode culture allowEmptyValues elements
210+
211+
result.Length |> should equal 1
212+
213+
[<Test>]
214+
let ``inferType with globalInference=true uses global inference`` () =
215+
let doc = XDocument.Parse("""<root><person name="John">Developer</person></root>""")
216+
let elements = [| doc.Root |]
217+
let result = XmlInference.inferType unitsOfMeasureProvider inferenceMode culture allowEmptyValues true elements
218+
219+
result.Length |> should equal 1
220+
match result.[0] with
221+
| InferedType.Record(Some name, _, false) ->
222+
name |> should equal "root"
223+
| _ -> failwith "Expected root record type from global inference"
224+
225+
[<Test>]
226+
let ``inferType with globalInference=false uses local inference`` () =
227+
let elements = [| createElement """<person name="John">Developer</person>""" |]
228+
let result = XmlInference.inferType unitsOfMeasureProvider inferenceMode culture allowEmptyValues false elements
229+
230+
result.Length |> should equal 1
231+
match result.[0] with
232+
| InferedType.Record(Some name, _, false) ->
233+
name |> should equal "person"
234+
| _ -> failwith "Expected person record type from local inference"
235+
236+
[<Test>]
237+
let ``inferType handles multiple root elements`` () =
238+
let elements = [|
239+
createElement """<person name="John">Developer</person>"""
240+
createElement """<person name="Jane">Designer</person>"""
241+
|]
242+
let result = XmlInference.inferType unitsOfMeasureProvider inferenceMode culture allowEmptyValues false elements
243+
244+
result.Length |> should equal 2
245+
// Both should be person records
246+
result |> Array.forall (function
247+
| InferedType.Record(Some "person", _, false) -> true
248+
| _ -> false) |> should equal true
249+
250+
[<Test>]
251+
let ``XML with complex nested structure infers correctly`` () =
252+
let element = createElement """
253+
<library name="City Library">
254+
<book id="1" category="fiction">
255+
<title>The Great Gatsby</title>
256+
<author>F. Scott Fitzgerald</author>
257+
<year>1925</year>
258+
<available>true</available>
259+
</book>
260+
<book id="2" category="science">
261+
<title>A Brief History of Time</title>
262+
<author>Stephen Hawking</author>
263+
<year>1988</year>
264+
<available>false</available>
265+
</book>
266+
</library>
267+
"""
268+
269+
let result = XmlInference.inferLocalType unitsOfMeasureProvider inferenceMode culture allowEmptyValues element
270+
271+
match result with
272+
| InferedType.Record(Some name, properties, false) ->
273+
name |> should equal "library"
274+
properties |> List.exists (fun p -> p.Name = "name") |> should equal true
275+
properties |> List.exists (fun p -> p.Name = "") |> should equal true // Collection of books
276+
| _ -> failwith "Expected library record with books collection"
277+
278+
[<Test>]
279+
let ``XML with mixed content types infers correctly`` () =
280+
let element = createElement """
281+
<data>
282+
<number>42</number>
283+
<text>Hello</text>
284+
<flag>true</flag>
285+
<price>19.99</price>
286+
</data>
287+
"""
288+
289+
let result = XmlInference.inferLocalType unitsOfMeasureProvider inferenceMode culture allowEmptyValues element
290+
291+
match result with
292+
| InferedType.Record(Some name, properties, false) ->
293+
name |> should equal "data"
294+
properties.Length |> should equal 1 // Collection of mixed elements
295+
match properties.[0].Type with
296+
| InferedType.Collection(_, _) -> () // Success
297+
| _ -> failwith "Expected collection of mixed elements"
298+
| _ -> failwith "Expected data record with mixed content"
299+
300+
[<Test>]
301+
let ``XML with namespaced elements handles correctly`` () =
302+
let element = createElement """<root xmlns:ns="http://example.com"><ns:item ns:value="test">content</ns:item></root>"""
303+
304+
let result = XmlInference.inferLocalType unitsOfMeasureProvider inferenceMode culture allowEmptyValues element
305+
306+
match result with
307+
| InferedType.Record(Some name, _, false) ->
308+
name |> should equal "root"
309+
| _ -> failwith "Expected root record type for namespaced XML"
310+
311+
[<Test>]
312+
let ``XML inference handles large values correctly`` () =
313+
let element = createElement """<data>9223372036854775807</data>""" // Int64.MaxValue
314+
let result = XmlInference.getInferedTypeFromValue unitsOfMeasureProvider inferenceMode culture element
315+
316+
match result with
317+
| InferedType.Primitive(t, _, _, _) -> t |> should equal typeof<int64>
318+
| _ -> failwith "Expected int64 type for large integer"
319+
320+
[<Test>]
321+
let ``XML inference handles floating point values`` () =
322+
let element = createElement """<value>123.456789</value>"""
323+
let result = XmlInference.getInferedTypeFromValue unitsOfMeasureProvider inferenceMode culture element
324+
325+
match result with
326+
| InferedType.Primitive(t, _, _, _) -> t |> should equal typeof<decimal>
327+
| _ -> failwith "Expected decimal type for floating point"

0 commit comments

Comments
 (0)