Integrated Random Testing
18 Mar 2020For a while I've been annoyed by the performance of testing libraries in general, but also with how random testing and performance testing are not better integrated and multithreaded. Testing libraries like Expecto do a great job of improving performance by running unit tests in parallel while also opening up useful functionality like stress testing. I want to take this further with a new prototype.
The goal is a simpler, more lightweight testing library with faster, more integrated parallel random testing with automatic parallel shrinking.
The library aims to encourage the shift from a number of unit and regression tests with hard coded input and output data to fewer more general random tests. This idea is covered well by John Hughes in Don't write tests! and the idea of One test to rule them all. Key takeaways are general random tests can provide more coverage for less test code, and larger test cases have a higher probability of finding a failure for a given execution time.
Features
- Asserts are no longer exception based and all are evaluated - More than one per test is encouraged. Simpler setup and faster for multi part testing.
test "PCG demo 1" {
let pcg = PCG.TryParse "36185706b82c2e03f8" |> Option.get
Test.equal pcg.Stream 54 "stream"
Test.equal pcg.State 0x185706b82c2e03f8UL "state"
let expectedNext = [|0xa15c02b7u;0x7b47f409u|] // ...
let expectedState = [|0x2b47fed88766bb05UL;0x8b33296d19bf5b4eUL|] // ...
for i = 0 to expectedNext.Length-1 do
Test.equal (pcg.Next()) expectedNext.[i] ("n"+string i)
Test.equal pcg.State expectedState.[i] ("s"+string i)
}
- Integrated random testing - Simpler syntax. Easier to move to more general random testing. Run in fine grained parallel.
test "gen" {
test "int" {
let! s = Gen.int
let! l = Gen.int.[0..Int32.MaxValue-s]
let! i = Gen.int.[s..s+l]
Test.between i s (s+l) "between"
}
test "int distribution" {
let freq = 10
let buckets = 1000
let! ints = Gen.seq.[freq * buckets] Gen.int.[0..buckets-1]
let actual = Array.zeroCreate buckets
Seq.iter (fun i -> actual.[i] <- actual.[i] + 1) ints
let expected = Array.create buckets freq
Test.chiSquared actual expected "chi-squared"
}
- No sizing or number of runs for random tests - Instead use distributions. More realistic large test cases.
test "list rev does nothing not" {
let! list =
let f ma mi bu = Version (int ma,int mi,int bu)
Gen.map3 f Gen.byte Gen.byte Gen.byte
|> Gen.list.[0..100]
Test.equal (List.rev list) list "rev equal"
}
- Automatic parallel random shrinking giving a reproducible seed - Smaller candidates found using a fast PCG loop. Simpler reproducible examples.
- Stress testing in parallel across unit and random tests using PCG streams - Low sync, high performance, fine grained parallel testing.
test "multithreading" {
let n = 10
let ms = MapSlim()
test "update" {
let! i = Gen.int.[..n-1]
let v = &ms.GetRef i
v <- 1-v
}
test "get" {
let! i = Gen.int.[..n-1]
let v = defaultValueArg (ms.GetOption i) 0
Test.isTrue (v=0 || v=1) "get is 0 or 1"
}
test "item" {
if ms.Count > 0 then
let! i = Gen.int.[..ms.Count-1]
let k,v = ms.Item i
Test.lessThan k n "key is ok"
Test.isTrue (v=0 || v=1) "value is 0 or 1"
}
}
- Integrated performance testing - Performance tests can be random and run in parallel with statistics collected across all threads.
test "mapslim" {
test "performance" {
test "get" {
let ms = MapSlim()
let dict = Dictionary()
let! n = Gen.int.[1..100]
let! keys =
Gen.array.[n] Gen.int.[-1000..1000]
|> Gen.map (
Array.mapi (fun i h ->
let k = KeyWithHash(uint64 i, h)
ms.Set(k,k)
dict.Add(k,k)
k
)
)
Test.faster
(fun () ->
for i = 0 to n-1 do
ms.GetOption keys.[i] |> ignore
)
(fun () ->
for i = 0 to n-1 do
dict.TryGetValue keys.[i] |> ignore
)
"get"
}
// ...
}
// ...
}
- Tests are run in parallel using continuations - Fine grained, in test asynchronous code to make each test faster.
test "reference" {
let getTest name (gen:Gen<'a>) = test name {
let! items = Gen.tuple gen Gen.int
|> Gen.list.[..100]
let! check = gen
let! expectedFork =
io {
return
List.fold (fun m (k,v) -> Map.add k v m) Map.empty items
|> Map.tryFind check
} |> IO.fork
let actual =
let ms = MapSlim()
List.iter ms.Set items
ms.GetOption check
|> function | ValueSome i -> Some i | ValueNone -> None
let! expected = expectedFork
Test.equal actual expected "check value equal"
}
getTest "get byte" Gen.byte
getTest "get char" Gen.char
getTest "get int" Gen.int.[..10]
getTest "get uint" Gen.uint.[..10u]
getTest "get string" Gen.string
}
Conclusion
The prototype currently has no dependencies and is a single file for the library, one for Gen and one for Test. They can easily be copied into a project to try them out, and new Gen and Test functions added.
The library is still in an early stage. It is missing more tests, label functionality, async io and needs a tidy and performance work.
I am keen to share it now to see if there are further ideas. Some of the functionality if successful could be added to existing projects or it could form its own. It is unclear at this point.
The code can be found here.
val uint64 : value:'T -> uint64 (requires member op_Explicit)
--------------------
type uint64 = UInt64
val int : value:'T -> int (requires member op_Explicit)
--------------------
type int = int32
--------------------
type int<'Measure> = int
type PCG =
new : stream:int -> PCG
private new : inc:uint64 * state:uint64 -> PCG
new : stream:int * seed:uint64 -> PCG
val Inc: uint64
val mutable State: uint64
member Next : unit -> uint32
member Next : maxExclusive:int -> int
member Next64 : unit -> uint64
member Next64 : maxExclusive:int64 -> int64
override ToString : unit -> string
...
--------------------
new : stream:int -> PCG
new : stream:int * seed:uint64 -> PCG
private new : inc:uint64 * state:uint64 -> PCG
type Stopwatch =
new : unit -> Stopwatch
member Elapsed : TimeSpan
member ElapsedMilliseconds : int64
member ElapsedTicks : int64
member IsRunning : bool
member Reset : unit -> unit
member Restart : unit -> unit
member Start : unit -> unit
member Stop : unit -> unit
static val Frequency : int64
...
--------------------
Stopwatch() : Stopwatch
val string : value:'T -> string
--------------------
type string = String
from Microsoft.FSharp.Core.Operators
struct
member CompareTo : value:obj -> int + 1 overload
member Equals : obj:obj -> bool + 1 overload
member GetHashCode : unit -> int
member GetTypeCode : unit -> TypeCode
member ToString : unit -> string + 3 overloads
static val MaxValue : int
static val MinValue : int
static member Parse : s:string -> int + 3 overloads
static member TryParse : s:string * result:int -> bool + 1 overload
end
Int32.TryParse(s: string, style: NumberStyles, provider: IFormatProvider, result: byref<int>) : bool
String.Substring(startIndex: int, length: int) : string
| None = 0
| AllowLeadingWhite = 1
| AllowTrailingWhite = 2
| AllowLeadingSign = 4
| AllowTrailingSign = 8
| AllowParentheses = 16
| AllowDecimalPoint = 32
| AllowThousands = 64
| AllowExponent = 128
| AllowCurrencySymbol = 256
...
struct
member CompareTo : value:obj -> int + 1 overload
member Equals : obj:obj -> bool + 1 overload
member GetHashCode : unit -> int
member GetTypeCode : unit -> TypeCode
member ToString : unit -> string + 3 overloads
static val MaxValue : uint64
static val MinValue : uint64
static member Parse : s:string -> uint64 + 3 overloads
static member TryParse : s:string * result:uint64 -> bool + 1 overload
end
UInt64.TryParse(s: string, style: NumberStyles, provider: IFormatProvider, result: byref<uint64>) : bool
val uint32 : value:'T -> uint32 (requires member op_Explicit)
--------------------
type uint32 = UInt32
member PCG.Next : maxExclusive:int -> int
val int64 : value:'T -> int64 (requires member op_Explicit)
--------------------
type int64 = Int64
--------------------
type int64<'Measure> = int64
member PCG.Next64 : maxExclusive:int64 -> int64
module Result
from Microsoft.FSharp.Core
--------------------
[<Struct>]
type Result<'T,'TError> =
| Ok of ResultValue: 'T
| Error of ErrorValue: 'TError
val list : 'a list
--------------------
type 'T list = List<'T>
module List
from Microsoft.FSharp.Collections
--------------------
type List<'T> =
| ( [] )
| ( :: ) of Head: 'T * Tail: 'T list
interface IReadOnlyList<'T>
interface IReadOnlyCollection<'T>
interface IEnumerable
interface IEnumerable<'T>
member GetSlice : startIndex:int option * endIndex:int option -> 'T list
member Head : 'T
member IsEmpty : bool
member Item : index:int -> 'T with get
member Length : int
member Tail : 'T list
...
val s : Result<'b list,'c list>
val list : Result<'a,'b> list
--------------------
type 'T list = List<'T>
union case Text.Text: struct (Text * Text) -> Text
--------------------
namespace System.Text
--------------------
type Text =
| For of string
| Grey of string
| Red of string
| BrightRed of string
| Green of string
| BrightGreen of string
| Yellow of string
| BrightYellow of string
| Blue of string
| BrightBlue of string
...
static member ( + ) : t1:Text * t2:Text -> Text
type LiteralAttribute =
inherit Attribute
new : unit -> LiteralAttribute
--------------------
new : unit -> LiteralAttribute
type String =
new : value:char -> string + 7 overloads
member Chars : int -> char
member Clone : unit -> obj
member CompareTo : value:obj -> int + 1 overload
member Contains : value:string -> bool
member CopyTo : sourceIndex:int * destination:char[] * destinationIndex:int * count:int -> unit
member EndsWith : value:string -> bool + 2 overloads
member Equals : obj:obj -> bool + 2 overloads
member GetEnumerator : unit -> CharEnumerator
member GetHashCode : unit -> int
...
--------------------
String(value: nativeptr<char>) : String
String(value: nativeptr<sbyte>) : String
String(value: char []) : String
String(c: char, count: int) : String
String(value: nativeptr<char>, startIndex: int, length: int) : String
String(value: nativeptr<sbyte>, startIndex: int, length: int) : String
String(value: char [], startIndex: int, length: int) : String
String(value: nativeptr<sbyte>, startIndex: int, length: int, enc: Text.Encoding) : String
(+0 other overloads)
String.Concat(values: Collections.Generic.IEnumerable<string>) : string
(+0 other overloads)
String.Concat<'T>(values: Collections.Generic.IEnumerable<'T>) : string
(+0 other overloads)
String.Concat([<ParamArray>] args: obj []) : string
(+0 other overloads)
String.Concat(arg0: obj) : string
(+0 other overloads)
String.Concat(str0: string, str1: string) : string
(+0 other overloads)
String.Concat(arg0: obj, arg1: obj) : string
(+0 other overloads)
String.Concat(str0: string, str1: string, str2: string) : string
(+0 other overloads)
String.Concat(arg0: obj, arg1: obj, arg2: obj) : string
(+0 other overloads)
String.Concat(str0: string, str1: string, str2: string, str3: string) : string
(+0 other overloads)
member Clone : unit -> obj
member CopyTo : array:Array * index:int -> unit + 1 overload
member GetEnumerator : unit -> IEnumerator
member GetLength : dimension:int -> int
member GetLongLength : dimension:int -> int64
member GetLowerBound : dimension:int -> int
member GetUpperBound : dimension:int -> int
member GetValue : [<ParamArray>] indices:int[] -> obj + 7 overloads
member Initialize : unit -> unit
member IsFixedSize : bool
...
Array.Copy(sourceArray: Array, destinationArray: Array, length: int) : unit
Array.Copy(sourceArray: Array, sourceIndex: int64, destinationArray: Array, destinationIndex: int64, length: int64) : unit
Array.Copy(sourceArray: Array, sourceIndex: int, destinationArray: Array, destinationIndex: int, length: int) : unit
type StructAttribute =
inherit Attribute
new : unit -> StructAttribute
--------------------
new : unit -> StructAttribute
struct
val mutable bucket: int
val mutable key: 'k
val mutable value: 'v
val mutable next: int
end
type private InitialHolder<'k,'v> =
new : unit -> InitialHolder<'k,'v>
static member Initial : Entry<'k,'v> []
--------------------
private new : unit -> InitialHolder<'k,'v>
member Equals : other:'T -> bool
type MethodImplAttribute =
inherit Attribute
new : unit -> MethodImplAttribute + 2 overloads
val MethodCodeType : MethodCodeType
member Value : MethodImplOptions
--------------------
MethodImplAttribute() : MethodImplAttribute
MethodImplAttribute(methodImplOptions: MethodImplOptions) : MethodImplAttribute
MethodImplAttribute(value: int16) : MethodImplAttribute
| Unmanaged = 4
| ForwardRef = 16
| PreserveSig = 128
| InternalCall = 4096
| Synchronized = 32
| NoInlining = 8
| AggressiveInlining = 256
| NoOptimization = 64
IEquatable.Equals(other: 'k) : bool
type 'T voption = ValueOption<'T>
type AllowNullLiteralAttribute =
inherit Attribute
new : unit -> AllowNullLiteralAttribute
new : value:bool -> AllowNullLiteralAttribute
member Value : bool
--------------------
new : unit -> AllowNullLiteralAttribute
new : value:bool -> AllowNullLiteralAttribute
type Size =
interface IComparable
interface IComparable<Size>
new : i:uint64 * l:Size list -> Size
val I: uint64
val L: Size list
override Equals : y:obj -> bool
override GetHashCode : unit -> int
static member zero : Size
--------------------
new : i:uint64 * l:Size list -> Size
type IComparable =
member CompareTo : obj:obj -> int
--------------------
type IComparable<'T> =
member CompareTo : other:'T -> int
interface
abstract member Gen : PCG -> 'a * Size
end
type PCG =
new : stream:int -> PCG
private new : inc:uint64 * state:uint64 -> PCG
new : stream:int * seed:uint64 -> PCG
val Inc: uint64
val mutable State: uint64
member Next : unit -> uint32
member Next : maxExclusive:int -> int
member Next64 : unit -> uint64
member Next64 : maxExclusive:int64 -> int64
override ToString : unit -> string
...
--------------------
new : stream:int -> PCG
new : stream:int * seed:uint64 -> PCG
interface
inherit Gen<'b>
abstract member GetSlice : 'a option * 'a option -> Gen<'b>
end
type GenBuilder =
new : unit -> GenBuilder
new : Gen:obj -> GenBuilder
--------------------
new : unit -> GenBuilder
new : Gen:obj -> GenBuilder
val Gen : obj
--------------------
type Gen<'a> =
interface
abstract member Gen : PCG -> 'a * Size
end
union case Text.Text: struct (Text * Text) -> Text
--------------------
module Text
from IRT
--------------------
namespace System.Text
--------------------
type Text =
| For of string
| Grey of string
| Red of string
| BrightRed of string
| Green of string
| BrightGreen of string
| Yellow of string
| BrightYellow of string
| Blue of string
| BrightBlue of string
...
static member ( + ) : t1:Text * t2:Text -> Text
val Failure : message:string -> exn
--------------------
active recognizer Failure: exn -> string option
type Exception =
new : unit -> Exception + 2 overloads
member Data : IDictionary
member GetBaseException : unit -> Exception
member GetObjectData : info:SerializationInfo * context:StreamingContext -> unit
member GetType : unit -> Type
member HResult : int with get, set
member HelpLink : string with get, set
member InnerException : Exception
member Message : string
member Source : string with get, set
...
--------------------
Exception() : Exception
Exception(message: string) : Exception
Exception(message: string, innerException: exn) : Exception
type ListSlim<'k> =
new : unit -> ListSlim<'k>
new : capacity:int -> ListSlim<'k>
val mutable private count: int
val mutable private entries: 'k []
member Add : key:'k -> int
member ToArray : unit -> 'k []
member ToList : unit -> 'k list
member Count : int
member Item : i:int -> 'k with get
member Item : i:int -> 'k with set
--------------------
new : unit -> ListSlim<'k>
new : capacity:int -> ListSlim<'k>
type AutoOpenAttribute =
inherit Attribute
new : unit -> AutoOpenAttribute
new : path:string -> AutoOpenAttribute
member Path : string
--------------------
new : unit -> AutoOpenAttribute
new : path:string -> AutoOpenAttribute
type MapSlim<'k,'v (requires equality and 'k :> IEquatable<'k>)> =
new : unit -> MapSlim<'k,'v>
new : capacity:int -> MapSlim<'k,'v>
val mutable private count: int
val mutable private entries: Entry<'k,'v> []
member private AddKey : key:'k * hashCode:int -> byref<'v>
member GetOption : key:'k -> 'v voption
member GetRef : key:'k -> byref<'v>
member GetRef : key:'k * added:outref<bool> -> byref<'v>
member Item : i:int -> 'k * 'v
member Key : i:int -> 'k
...
--------------------
new : unit -> MapSlim<'k,'v>
new : capacity:int -> MapSlim<'k,'v>
type CallerLineNumberAttribute =
inherit Attribute
new : unit -> CallerLineNumberAttribute
--------------------
CallerLineNumberAttribute() : CallerLineNumberAttribute
type OptionalAttribute =
inherit Attribute
new : unit -> OptionalAttribute
--------------------
OptionalAttribute() : OptionalAttribute
type DefaultParameterValueAttribute =
inherit Attribute
new : value:obj -> DefaultParameterValueAttribute
member Value : obj
--------------------
DefaultParameterValueAttribute(value: obj) : DefaultParameterValueAttribute
member Dispose : unit -> unit
val seq : sequence:seq<'T> -> seq<'T>
--------------------
type seq<'T> = Collections.Generic.IEnumerable<'T>
val float : value:'T -> float (requires member op_Explicit)
--------------------
type float = Double
--------------------
type float<'Measure> = float
module Result
from IRT
--------------------
module Result
from Microsoft.FSharp.Core
--------------------
[<Struct>]
type Result<'T,'TError> =
| Ok of ResultValue: 'T
| Error of ErrorValue: 'TError
String.Join<'T>(separator: string, values: Collections.Generic.IEnumerable<'T>) : string
String.Join(separator: string, [<ParamArray>] values: obj []) : string
String.Join(separator: string, [<ParamArray>] value: string []) : string
String.Join(separator: string, value: string [], startIndex: int, count: int) : string
member GetFormat : formatType:Type -> obj
type CultureInfo =
new : name:string -> CultureInfo + 3 overloads
member Calendar : Calendar
member ClearCachedData : unit -> unit
member Clone : unit -> obj
member CompareInfo : CompareInfo
member CultureTypes : CultureTypes
member DateTimeFormat : DateTimeFormatInfo with get, set
member DisplayName : string
member EnglishName : string
member Equals : value:obj -> bool
...
--------------------
CultureInfo(name: string) : CultureInfo
CultureInfo(culture: int) : CultureInfo
CultureInfo(name: string, useUserOverride: bool) : CultureInfo
CultureInfo(culture: int, useUserOverride: bool) : CultureInfo
type StringBuilder =
new : unit -> StringBuilder + 5 overloads
member Append : value:string -> StringBuilder + 19 overloads
member AppendFormat : format:string * arg0:obj -> StringBuilder + 7 overloads
member AppendLine : unit -> StringBuilder + 1 overload
member Capacity : int with get, set
member Chars : int -> char with get, set
member Clear : unit -> StringBuilder
member CopyTo : sourceIndex:int * destination:char[] * destinationIndex:int * count:int -> unit
member EnsureCapacity : capacity:int -> int
member Equals : sb:StringBuilder -> bool
...
--------------------
Text.StringBuilder() : Text.StringBuilder
Text.StringBuilder(capacity: int) : Text.StringBuilder
Text.StringBuilder(value: string) : Text.StringBuilder
Text.StringBuilder(value: string, capacity: int) : Text.StringBuilder
Text.StringBuilder(capacity: int, maxCapacity: int) : Text.StringBuilder
Text.StringBuilder(value: string, startIndex: int, length: int, capacity: int) : Text.StringBuilder
from Microsoft.FSharp.Collections
static member BackgroundColor : ConsoleColor with get, set
static member Beep : unit -> unit + 1 overload
static member BufferHeight : int with get, set
static member BufferWidth : int with get, set
static member CapsLock : bool
static member Clear : unit -> unit
static member CursorLeft : int with get, set
static member CursorSize : int with get, set
static member CursorTop : int with get, set
static member CursorVisible : bool with get, set
...
(+0 other overloads)
Console.WriteLine(value: string) : unit
(+0 other overloads)
Console.WriteLine(value: obj) : unit
(+0 other overloads)
Console.WriteLine(value: uint64) : unit
(+0 other overloads)
Console.WriteLine(value: int64) : unit
(+0 other overloads)
Console.WriteLine(value: uint32) : unit
(+0 other overloads)
Console.WriteLine(value: int) : unit
(+0 other overloads)
Console.WriteLine(value: float32) : unit
(+0 other overloads)
Console.WriteLine(value: float) : unit
(+0 other overloads)
Console.WriteLine(value: decimal) : unit
(+0 other overloads)
module Event
from Microsoft.FSharp.Control
--------------------
type Event<'T> =
new : unit -> Event<'T>
member Trigger : arg:'T -> unit
member Publish : IEvent<'T>
--------------------
type Event<'Delegate,'Args (requires delegate and 'Delegate :> Delegate)> =
new : unit -> Event<'Delegate,'Args>
member Trigger : sender:obj * args:'Args -> unit
member Publish : IEvent<'Delegate,'Args>
--------------------
new : unit -> Event<'T>
--------------------
new : unit -> Event<'Delegate,'Args>
(+0 other overloads)
Console.Write(value: obj) : unit
(+0 other overloads)
Console.Write(value: uint64) : unit
(+0 other overloads)
Console.Write(value: int64) : unit
(+0 other overloads)
Console.Write(value: uint32) : unit
(+0 other overloads)
Console.Write(value: int) : unit
(+0 other overloads)
Console.Write(value: float32) : unit
(+0 other overloads)
Console.Write(value: decimal) : unit
(+0 other overloads)
Console.Write(value: float) : unit
(+0 other overloads)
Console.Write(buffer: char []) : unit
(+0 other overloads)
val ref : value:'T -> 'T ref
--------------------
type 'T ref = Ref<'T>
struct
member CompareTo : value:obj -> int + 1 overload
member Equals : obj:obj -> bool + 1 overload
member GetHashCode : unit -> int
member GetTypeCode : unit -> TypeCode
member ToString : unit -> string + 3 overloads
static val MaxValue : int64
static val MinValue : int64
static member Parse : s:string -> int64 + 3 overloads
static member TryParse : s:string * result:int64 -> bool + 1 overload
end
static member Add : location1:int * value:int -> int + 1 overload
static member CompareExchange : location1:int * value:int * comparand:int -> int + 6 overloads
static member Decrement : location:int -> int + 1 overload
static member Exchange : location1:int * value:int -> int + 6 overloads
static member Increment : location:int -> int + 1 overload
static member MemoryBarrier : unit -> unit
static member Read : location:int64 -> int64
Interlocked.Increment(location: byref<int>) : int
static member BindHandle : osHandle:nativeint -> bool + 1 overload
static member GetAvailableThreads : workerThreads:int * completionPortThreads:int -> unit
static member GetMaxThreads : workerThreads:int * completionPortThreads:int -> unit
static member GetMinThreads : workerThreads:int * completionPortThreads:int -> unit
static member QueueUserWorkItem : callBack:WaitCallback -> bool + 1 overload
static member RegisterWaitForSingleObject : waitObject:WaitHandle * callBack:WaitOrTimerCallback * state:obj * millisecondsTimeOutInterval:uint32 * executeOnlyOnce:bool -> RegisteredWaitHandle + 3 overloads
static member SetMaxThreads : workerThreads:int * completionPortThreads:int -> bool
static member SetMinThreads : workerThreads:int * completionPortThreads:int -> bool
static member UnsafeQueueNativeOverlapped : overlapped:NativeOverlapped -> bool
static member UnsafeQueueUserWorkItem : callBack:WaitCallback * state:obj -> bool
...
from Microsoft.FSharp.Core
static member CommandLine : string
static member CurrentDirectory : string with get, set
static member CurrentManagedThreadId : int
static member Exit : exitCode:int -> unit
static member ExitCode : int with get, set
static member ExpandEnvironmentVariables : name:string -> string
static member FailFast : message:string -> unit + 1 overload
static member GetCommandLineArgs : unit -> string[]
static member GetEnvironmentVariable : variable:string -> string + 1 overload
static member GetEnvironmentVariables : unit -> IDictionary + 1 overload
...
nested type SpecialFolder
nested type SpecialFolderOption
Interlocked.Decrement(location: byref<int>) : int
static member AddMemoryPressure : bytesAllocated:int64 -> unit
static member CancelFullGCNotification : unit -> unit
static member Collect : unit -> unit + 4 overloads
static member CollectionCount : generation:int -> int
static member EndNoGCRegion : unit -> unit
static member GetGeneration : obj:obj -> int + 1 overload
static member GetTotalMemory : forceFullCollection:bool -> int64
static member KeepAlive : obj:obj -> unit
static member MaxGeneration : int
static member ReRegisterForFinalize : obj:obj -> unit
...
type Thread =
inherit CriticalFinalizerObject
new : start:ThreadStart -> Thread + 3 overloads
member Abort : unit -> unit + 1 overload
member ApartmentState : ApartmentState with get, set
member CurrentCulture : CultureInfo with get, set
member CurrentUICulture : CultureInfo with get, set
member DisableComObjectEagerCleanup : unit -> unit
member ExecutionContext : ExecutionContext
member GetApartmentState : unit -> ApartmentState
member GetCompressedStack : unit -> CompressedStack
member GetHashCode : unit -> int
...
--------------------
Thread(start: ThreadStart) : Thread
Thread(start: ParameterizedThreadStart) : Thread
Thread(start: ThreadStart, maxStackSize: int) : Thread
Thread(start: ParameterizedThreadStart, maxStackSize: int) : Thread
Thread.Sleep(millisecondsTimeout: int) : unit
val byte : value:'T -> byte (requires member op_Explicit)
--------------------
type byte = Byte
static val IsLittleEndian : bool
static member DoubleToInt64Bits : value:float -> int64
static member GetBytes : value:bool -> byte[] + 9 overloads
static member Int64BitsToDouble : value:int64 -> float
static member ToBoolean : value:byte[] * startIndex:int -> bool
static member ToChar : value:byte[] * startIndex:int -> char
static member ToDouble : value:byte[] * startIndex:int -> float
static member ToInt16 : value:byte[] * startIndex:int -> int16
static member ToInt32 : value:byte[] * startIndex:int -> int
static member ToInt64 : value:byte[] * startIndex:int -> int64
...
val char : value:'T -> char (requires member op_Explicit)
--------------------
type char = Char
from Microsoft.FSharp.Core
type CompilationRepresentationAttribute =
inherit Attribute
new : flags:CompilationRepresentationFlags -> CompilationRepresentationAttribute
member Flags : CompilationRepresentationFlags
--------------------
new : flags:CompilationRepresentationFlags -> CompilationRepresentationAttribute
| None = 0
| Static = 1
| Instance = 2
| ModuleSuffix = 4
| UseNullAsTrueValue = 8
| Event = 16
from Microsoft.FSharp.Core
type Version =
new : unit -> Version + 4 overloads
member Build : int
member Clone : unit -> obj
member CompareTo : version:obj -> int + 1 overload
member Equals : obj:obj -> bool + 1 overload
member GetHashCode : unit -> int
member Major : int
member MajorRevision : int16
member Minor : int
member MinorRevision : int16
...
--------------------
Version() : Version
Version(version: string) : Version
Version(major: int, minor: int) : Version
Version(major: int, minor: int, build: int) : Version
Version(major: int, minor: int, build: int, revision: int) : Version
module Map
from Microsoft.FSharp.Collections
--------------------
type Map<'Key,'Value (requires comparison)> =
interface IReadOnlyDictionary<'Key,'Value>
interface IReadOnlyCollection<KeyValuePair<'Key,'Value>>
interface IEnumerable
interface IComparable
interface IEnumerable<KeyValuePair<'Key,'Value>>
interface ICollection<KeyValuePair<'Key,'Value>>
interface IDictionary<'Key,'Value>
new : elements:seq<'Key * 'Value> -> Map<'Key,'Value>
member Add : key:'Key * value:'Value -> Map<'Key,'Value>
member ContainsKey : key:'Key -> bool
...
--------------------
new : elements:seq<'Key * 'Value> -> Map<'Key,'Value>