@@ -67,4 +67,67 @@ let sleep(n : int32) =
6767async { do ! Async.Sleep( n) } |> Async.RunSynchronously
6868#else
6969 System.Threading.Thread.Sleep( n)
70- #endif
70+ #endif
71+
72+ module SurfaceArea =
73+ open System.Reflection
74+ open System
75+ open System.Text .RegularExpressions
76+
77+ // gets string form of public surface area for the currently-loaded FSharp.Core
78+ let private getActual () =
79+
80+ // get current fsharp.core
81+ let asm =
82+ #if portable7|| portable78|| portable259
83+ typeof< int list>. GetTypeInfo() .Assembly
84+ #else
85+ typeof< int list>. Assembly
86+ #endif
87+
88+ // public types only
89+ let types =
90+ #if portable7|| portable78|| portable259
91+ asm.ExportedTypes|> Seq.filter( fun ty -> let ti = ty.GetTypeInfo() in ti.IsPublic|| ti.IsNestedPublic) |> Array.ofSeq
92+ #else
93+ asm.GetExportedTypes()
94+ #endif
95+
96+ // extract canonical string form for every public member of every type
97+ let getTypeMemberStrings ( t : Type ) =
98+ // for System.Runtime-based profiles, need to do lots of manual work
99+ #if portable7|| portable78|| portable259
100+ let getMembers ( t : Type ) =
101+ let ti = t.GetTypeInfo()
102+ let cast ( info : #MemberInfo ) = ( t, info:> MemberInfo)
103+ seq {
104+ yield ! t.GetRuntimeEvents() |> Seq.filter( fun m -> m.AddMethod.IsPublic) |> Seq.map cast
105+ yield ! t.GetRuntimeProperties() |> Seq.filter( fun m -> m.GetMethod.IsPublic) |> Seq.map cast
106+ yield ! t.GetRuntimeMethods() |> Seq.filter( fun m -> m.IsPublic) |> Seq.map cast
107+ yield ! t.GetRuntimeFields() |> Seq.filter( fun m -> m.IsPublic) |> Seq.map cast
108+ yield ! ti.DeclaredConstructors|> Seq.filter( fun m -> m.IsPublic) |> Seq.map cast
109+ yield ! ti.DeclaredNestedTypes|> Seq.filter( fun ty -> ty.IsNestedPublic) |> Seq.map cast
110+ } |> Array.ofSeq
111+
112+ getMembers t
113+ |> Array.map( fun ( ty , m ) -> sprintf" %s :%s " ( ty.ToString()) ( m.ToString()))
114+ #else
115+ t.GetMembers()
116+ |> Array.map( fun v -> sprintf" %s :%s " ( v.ReflectedType.ToString()) ( v.ToString()))
117+ #endif
118+
119+ types
120+ |> Array.collect getTypeMemberStrings
121+ |> Array.sort
122+ |> String.concat" \r\n "
123+
124+ // verify public surface area matches expected
125+ let verify expected platform fileName =
126+ let logFile = sprintf" %s \\ CoreUnit_%s _Xml.xml" TestContext.CurrentContext.WorkDirectory platform
127+ let normalize ( s : string ) =
128+ Regex.Replace( s, " (\\ r\\ n|\\ n)+" , " \r\n " ) .Trim([| '\r' ; '\n' |])
129+
130+ let actual = getActual() |> normalize
131+ let expected = expected|> normalize
132+
133+ Assert.AreEqual( expected, actual, sprintf" \r\n %s \r\n\r\n Expected and actual surface area don't match. To see the delta, run\r\n windiff%s %s " actual fileName logFile)