diff --git a/tests/FSharp.Data.Core.Tests/FSharp.Data.Core.Tests.fsproj b/tests/FSharp.Data.Core.Tests/FSharp.Data.Core.Tests.fsproj
index 347853e3c..41c5f488d 100644
--- a/tests/FSharp.Data.Core.Tests/FSharp.Data.Core.Tests.fsproj
+++ b/tests/FSharp.Data.Core.Tests/FSharp.Data.Core.Tests.fsproj
@@ -44,6 +44,7 @@
+
@@ -60,6 +61,7 @@
+
diff --git a/tests/FSharp.Data.Core.Tests/XmlInference.fs b/tests/FSharp.Data.Core.Tests/XmlInference.fs
new file mode 100644
index 000000000..7326f83a3
--- /dev/null
+++ b/tests/FSharp.Data.Core.Tests/XmlInference.fs
@@ -0,0 +1,327 @@
+module FSharp.Data.Core.Tests.XmlInference
+
+open FsUnit
+open NUnit.Framework
+open System
+open System.Xml.Linq
+open FSharp.Data
+open FSharp.Data.Runtime
+open FSharp.Data.Runtime.StructuralTypes
+open FSharp.Data.Runtime.StructuralInference
+open ProviderImplementation
+
+// Test infrastructure similar to InferenceTests.fs
+let internal culture = TextRuntime.GetCulture ""
+let internal inferenceMode = InferenceMode'.ValuesOnly
+let internal unitsOfMeasureProvider =
+ { new StructuralInference.IUnitsOfMeasureProvider with
+ member x.SI(_) : System.Type = null
+ member x.Product(_, _) = failwith "Not implemented yet"
+ member x.Inverse(_) = failwith "Not implemented yet" }
+let internal allowEmptyValues = true
+
+// Helper function to create XElement from string
+let createElement xmlString =
+ XElement.Parse(xmlString)
+
+// Helper function to create XElement array
+let createElements xmlStrings =
+ xmlStrings |> Array.map createElement
+
+[]
+let ``getInferedTypeFromValue handles simple string value`` () =
+ let element = createElement """John"""
+ let result = XmlInference.getInferedTypeFromValue unitsOfMeasureProvider inferenceMode culture element
+
+ match result with
+ | InferedType.Primitive(t, _, _, _) -> t |> should equal typeof
+ | _ -> failwith "Expected primitive string type"
+
+[]
+let ``getInferedTypeFromValue handles numeric value`` () =
+ let element = createElement """30"""
+ let result = XmlInference.getInferedTypeFromValue unitsOfMeasureProvider inferenceMode culture element
+
+ match result with
+ | InferedType.Primitive(t, _, _, _) -> t |> should equal typeof
+ | _ -> failwith "Expected primitive int type"
+
+[]
+let ``getInferedTypeFromValue handles boolean value`` () =
+ let element = createElement """true"""
+ let result = XmlInference.getInferedTypeFromValue unitsOfMeasureProvider inferenceMode culture element
+
+ match result with
+ | InferedType.Primitive(t, _, _, _) -> t |> should equal typeof
+ | _ -> failwith "Expected primitive bool type"
+
+[]
+let ``getInferedTypeFromValue handles decimal value`` () =
+ let element = createElement """19.99"""
+ let result = XmlInference.getInferedTypeFromValue unitsOfMeasureProvider inferenceMode culture element
+
+ match result with
+ | InferedType.Primitive(t, _, _, _) -> t |> should equal typeof
+ | _ -> failwith "Expected primitive decimal type"
+
+[]
+let ``getInferedTypeFromValue handles embedded JSON object`` () =
+ let element = createElement """{"name": "John", "age": 30}"""
+ let result = XmlInference.getInferedTypeFromValue unitsOfMeasureProvider inferenceMode culture element
+
+ match result with
+ | InferedType.Json(_, _) -> () // Success - embedded JSON detected
+ | _ -> failwith "Expected JSON type for embedded JSON content"
+
+[]
+let ``getInferedTypeFromValue handles embedded JSON array`` () =
+ let element = createElement """[1, 2, 3]"""
+ let result = XmlInference.getInferedTypeFromValue unitsOfMeasureProvider inferenceMode culture element
+
+ match result with
+ | InferedType.Json(_, _) -> () // Success - embedded JSON detected
+ | _ -> failwith "Expected JSON type for embedded JSON array"
+
+[]
+let ``getInferedTypeFromValue with NoInference mode skips JSON parsing`` () =
+ let element = createElement """{"name": "John"}"""
+ let result = XmlInference.getInferedTypeFromValue unitsOfMeasureProvider InferenceMode'.NoInference culture element
+
+ match result with
+ | InferedType.Primitive(t, _, _, _) -> t |> should equal typeof
+ | _ -> failwith "Expected string type with NoInference mode"
+
+[]
+let ``getInferedTypeFromValue handles malformed JSON as string`` () =
+ let element = createElement """{"name": invalid}"""
+ let result = XmlInference.getInferedTypeFromValue unitsOfMeasureProvider inferenceMode culture element
+
+ match result with
+ | InferedType.Primitive(t, _, _, _) -> t |> should equal typeof
+ | _ -> failwith "Expected string type for malformed JSON"
+
+[]
+let ``getInferedTypeFromValue handles empty element`` () =
+ let element = createElement """"""
+ let result = XmlInference.getInferedTypeFromValue unitsOfMeasureProvider inferenceMode culture element
+
+ // For empty elements, the inference returns Null type
+ match result with
+ | InferedType.Null -> () // Success - empty value gives Null type
+ | _ -> failwithf "Expected Null type for empty value, got %A" result
+
+[]
+let ``inferLocalType handles simple element with content`` () =
+ let element = createElement """John"""
+ let result = XmlInference.inferLocalType unitsOfMeasureProvider inferenceMode culture allowEmptyValues element
+
+ match result with
+ | InferedType.Record(Some name, properties, false) ->
+ name |> should equal "name"
+ properties.Length |> should equal 1
+ properties.[0].Name |> should equal "" // Body content
+ | _ -> failwith "Expected record type with body content"
+
+[]
+let ``inferLocalType handles element with attributes`` () =
+ let element = createElement """Developer"""
+ let result = XmlInference.inferLocalType unitsOfMeasureProvider inferenceMode culture allowEmptyValues element
+
+ match result with
+ | InferedType.Record(Some name, properties, false) ->
+ name |> should equal "person"
+ properties.Length |> should equal 3 // body content + 2 attributes
+ properties |> List.exists (fun p -> p.Name = "name") |> should equal true
+ properties |> List.exists (fun p -> p.Name = "age") |> should equal true
+ properties |> List.exists (fun p -> p.Name = "") |> should equal true // body
+ | _ -> failwith "Expected record type with attributes and body"
+
+[]
+let ``inferLocalType handles element with child elements`` () =
+ let element = createElement """John30"""
+ let result = XmlInference.inferLocalType unitsOfMeasureProvider inferenceMode culture allowEmptyValues element
+
+ match result with
+ | InferedType.Record(Some name, properties, false) ->
+ name |> should equal "person"
+ properties.Length |> should equal 1 // Collection of children
+ properties.[0].Name |> should equal "" // Body content (collection)
+ match properties.[0].Type with
+ | InferedType.Collection(_, _) -> () // Success - collection of children
+ | _ -> failwith "Expected collection type for child elements"
+ | _ -> failwith "Expected record type with child collection"
+
+[]
+let ``inferLocalType handles empty element`` () =
+ let element = createElement """"""
+ let result = XmlInference.inferLocalType unitsOfMeasureProvider inferenceMode culture allowEmptyValues element
+
+ match result with
+ | InferedType.Record(Some name, properties, false) ->
+ name |> should equal "empty"
+ properties.Length |> should equal 0 // No content or attributes
+ | _ -> failwith "Expected empty record type"
+
+[]
+let ``inferLocalType handles element with only attributes`` () =
+ let element = createElement """"""
+ let result = XmlInference.inferLocalType unitsOfMeasureProvider inferenceMode culture allowEmptyValues element
+
+ match result with
+ | InferedType.Record(Some name, properties, false) ->
+ name |> should equal "config"
+ properties.Length |> should equal 2 // 2 attributes, no body
+ properties |> List.exists (fun p -> p.Name = "debug") |> should equal true
+ properties |> List.exists (fun p -> p.Name = "timeout") |> should equal true
+ | _ -> failwith "Expected record type with attributes only"
+
+[]
+let ``inferLocalType handles nested structure`` () =
+ let element = createElement """- test
- test2
"""
+ let result = XmlInference.inferLocalType unitsOfMeasureProvider inferenceMode culture allowEmptyValues element
+
+ match result with
+ | InferedType.Record(Some name, properties, false) ->
+ name |> should equal "root"
+ properties.Length |> should equal 1
+ match properties.[0].Type with
+ | InferedType.Collection(_, _) -> () // Collection of items
+ | _ -> failwith "Expected collection of items"
+ | _ -> failwith "Expected root record with item collection"
+
+[]
+let ``inferGlobalType handles single element`` () =
+ let doc = XDocument.Parse("""Developer""")
+ let elements = [| doc.Root |]
+ let result = XmlInference.inferGlobalType unitsOfMeasureProvider inferenceMode culture allowEmptyValues elements
+
+ result.Length |> should equal 1
+ match result.[0] with
+ | InferedType.Record(Some name, _, false) ->
+ name |> should equal "root"
+ | _ -> failwith "Expected root record type"
+
+[]
+let ``inferGlobalType handles multiple elements of same type`` () =
+ let xml = """"""
+ let doc = XDocument.Parse(xml)
+ let elements = [| doc.Root |]
+ let result = XmlInference.inferGlobalType unitsOfMeasureProvider inferenceMode culture allowEmptyValues elements
+
+ result.Length |> should equal 1
+
+[]
+let ``inferType with globalInference=true uses global inference`` () =
+ let doc = XDocument.Parse("""Developer""")
+ let elements = [| doc.Root |]
+ let result = XmlInference.inferType unitsOfMeasureProvider inferenceMode culture allowEmptyValues true elements
+
+ result.Length |> should equal 1
+ match result.[0] with
+ | InferedType.Record(Some name, _, false) ->
+ name |> should equal "root"
+ | _ -> failwith "Expected root record type from global inference"
+
+[]
+let ``inferType with globalInference=false uses local inference`` () =
+ let elements = [| createElement """Developer""" |]
+ let result = XmlInference.inferType unitsOfMeasureProvider inferenceMode culture allowEmptyValues false elements
+
+ result.Length |> should equal 1
+ match result.[0] with
+ | InferedType.Record(Some name, _, false) ->
+ name |> should equal "person"
+ | _ -> failwith "Expected person record type from local inference"
+
+[]
+let ``inferType handles multiple root elements`` () =
+ let elements = [|
+ createElement """Developer"""
+ createElement """Designer"""
+ |]
+ let result = XmlInference.inferType unitsOfMeasureProvider inferenceMode culture allowEmptyValues false elements
+
+ result.Length |> should equal 2
+ // Both should be person records
+ result |> Array.forall (function
+ | InferedType.Record(Some "person", _, false) -> true
+ | _ -> false) |> should equal true
+
+[]
+let ``XML with complex nested structure infers correctly`` () =
+ let element = createElement """
+
+
+ The Great Gatsby
+ F. Scott Fitzgerald
+ 1925
+ true
+
+
+ A Brief History of Time
+ Stephen Hawking
+ 1988
+ false
+
+
+ """
+
+ let result = XmlInference.inferLocalType unitsOfMeasureProvider inferenceMode culture allowEmptyValues element
+
+ match result with
+ | InferedType.Record(Some name, properties, false) ->
+ name |> should equal "library"
+ properties |> List.exists (fun p -> p.Name = "name") |> should equal true
+ properties |> List.exists (fun p -> p.Name = "") |> should equal true // Collection of books
+ | _ -> failwith "Expected library record with books collection"
+
+[]
+let ``XML with mixed content types infers correctly`` () =
+ let element = createElement """
+
+ 42
+ Hello
+ true
+ 19.99
+
+ """
+
+ let result = XmlInference.inferLocalType unitsOfMeasureProvider inferenceMode culture allowEmptyValues element
+
+ match result with
+ | InferedType.Record(Some name, properties, false) ->
+ name |> should equal "data"
+ properties.Length |> should equal 1 // Collection of mixed elements
+ match properties.[0].Type with
+ | InferedType.Collection(_, _) -> () // Success
+ | _ -> failwith "Expected collection of mixed elements"
+ | _ -> failwith "Expected data record with mixed content"
+
+[]
+let ``XML with namespaced elements handles correctly`` () =
+ let element = createElement """content"""
+
+ let result = XmlInference.inferLocalType unitsOfMeasureProvider inferenceMode culture allowEmptyValues element
+
+ match result with
+ | InferedType.Record(Some name, _, false) ->
+ name |> should equal "root"
+ | _ -> failwith "Expected root record type for namespaced XML"
+
+[]
+let ``XML inference handles large values correctly`` () =
+ let element = createElement """9223372036854775807""" // Int64.MaxValue
+ let result = XmlInference.getInferedTypeFromValue unitsOfMeasureProvider inferenceMode culture element
+
+ match result with
+ | InferedType.Primitive(t, _, _, _) -> t |> should equal typeof
+ | _ -> failwith "Expected int64 type for large integer"
+
+[]
+let ``XML inference handles floating point values`` () =
+ let element = createElement """123.456789"""
+ let result = XmlInference.getInferedTypeFromValue unitsOfMeasureProvider inferenceMode culture element
+
+ match result with
+ | InferedType.Primitive(t, _, _, _) -> t |> should equal typeof
+ | _ -> failwith "Expected decimal type for floating point"
\ No newline at end of file