@@ -8,6 +8,8 @@ open FsUnit
88open FsCheck.NUnit
99open FSharp.Data
1010open Generators
11+ open System.IO
12+ open System
1113
1214[<Property( MaxTest = 500 , Arbitrary = [| typeof< Generators.AdaptiveGenerators> |]); Timeout( 60000 ) >]
1315let ``[ AMap ] reference impl`` () ({ mreal = real ; mref = ref ; mexpression = str ; mchanges = changes } : VMap < int , int >) =
@@ -641,34 +643,110 @@ let ``[AMap] mapA``() =
641643 res |> AMap.force |> should equal ( HashMap.ofList [ " A" , 2 ; " B" , 4 ; " C" , 6 ])
642644
643645
644-
645- [<Test>]
646- let ``[ AMap ] deltaA`` () =
647- let map = cmap [ " A" , 1 ; " B" , 2 ; " C" , 3 ]
648- let flag = cval true
649646
647+ /// <summary >
648+ /// Calls a mapping function which creates additional dependencies to be tracked.
649+ /// </summary >
650+ let mapWithAdditionalDependenies ( mapping : 'a -> 'b * #seq < #IAdaptiveValue >) ( value : aval < 'a >) : aval < 'b > =
651+ let mutable lastDeps = HashSet.empty
652+
653+ { new AVal.AbstractVal< 'b>() with
654+ member x.Compute ( token : AdaptiveToken ) =
655+ let input = value.GetValue token
656+
657+ // re-evaluate the mapping based on the (possibly new input)
658+ let result , deps = mapping input
659+
660+ // compute the change in the additional dependencies and adjust the graph accordingly
661+ let newDeps = HashSet.ofSeq deps
662+
663+ for op in HashSet.computeDelta lastDeps newDeps do
664+ match op with
665+ | Add(_, d) ->
666+ // the new dependency needs to be evaluated with our token, s.t. we depend on it in the future
667+ d.GetValueUntyped token |> ignore
668+ | Rem(_, d) ->
669+ // we no longer need to depend on the old dependency so we can remove ourselves from its outputs
670+ lock d.Outputs ( fun () -> d.Outputs.Remove x) |> ignore
671+
672+ lastDeps <- newDeps
673+
674+ result }
675+ :> aval<_>
676+
677+ [<Test>]
678+ let ``[ AMap ] batchRecalcDirty`` () =
679+
680+ let file1 = " File1.fs"
681+ let file1Cval = cval 1
682+ let file1DepCval = cval 1
683+ let file2 = " File2.fs"
684+ let file2Cval = cval 2
685+ let file2DepCval = cval 1
686+ let file3 = " File3.fs"
687+ let file3Cval = cval 3
688+ let file3DepCval = cval 1
689+
690+ let m = Map [ file1, file1DepCval; file2, file2DepCval; file3, file3DepCval]
691+
692+ let projs =
693+ [
694+ file1, file1Cval
695+ file2, file2Cval
696+ file3, file3Cval
697+ ]
698+ |> AMap.ofList
699+ |> AMap.mapA( fun _ v -> v)
700+
701+ let mutable lastBatch = Unchecked.defaultof<_>
650702 let res =
651- map |> AMap.deltaA ( fun d ->
652- d
653- |> HashMap.map( fun _ v -> flag |> AVal.map ( function true -> v | false -> - 1 ))
703+ projs
704+ |> AMap.batchRecalcDirty( fun d ->
705+ lastBatch <- d
706+ HashMap.ofList [
707+ for k, v in d do
708+ k, ( AVal.constant <| Guid.NewGuid()) |> mapWithAdditionalDependenies( fun a -> a, [ m.[ k]])
709+ ]
654710 )
711+ let firstResult = res |> AMap.force
712+ lastBatch |> should haveCount 3
655713
656- res |> AMap.force |> should equal ( HashMap.ofList [ " A " , 1 ; " B " , 2 ; " C " , 3 ] )
714+ transact ( fun () -> file1Cval.Value <- file1Cval.Value + 1 )
657715
658- transact ( fun () ->
659- flag.Value <- false
660- )
716+ let secondResult = res |> AMap.force
717+ lastBatch |> should haveCount 1
718+
719+ firstResult.[ file1] |> should not' ( equal secondResult.[ file1])
720+ firstResult.[ file2] |> should equal secondResult.[ file2]
721+ firstResult.[ file3] |> should equal secondResult.[ file3]
661722
662- res |> AMap.force |> should equal ( HashMap.ofList [ " A" , - 1 ; " B" , - 1 ; " C" , - 1 ])
663723
664- transact ( fun () ->
665- map.Value <- map.Value |> HashMap.map ( fun _ v -> v * 2 )
666- )
724+ transact( fun () ->
725+ file1Cval.Value <- file1Cval.Value + 1
726+ file3Cval.Value <- file3Cval.Value + 1 )
727+
728+ let thirdResult = res |> AMap.force
729+ lastBatch |> should haveCount 2
667730
668- res |> AMap.force |> should equal ( HashMap.ofList [ " A" , - 1 ; " B" , - 1 ; " C" , - 1 ])
731+ secondResult.[ file1] |> should not' ( equal thirdResult.[ file1])
732+ secondResult.[ file2] |> should equal thirdResult.[ file2]
733+ secondResult.[ file3] |> should not' ( equal thirdResult.[ file3])
669734
670- transact ( fun () ->
671- flag.Value <- true
672- )
673735
674- res |> AMap.force |> should equal ( HashMap.ofList [ " A" , 2 ; " B" , 4 ; " C" , 6 ])
736+ transact( fun () -> file1DepCval.Value <- file1DepCval.Value + 1 )
737+
738+ let fourthResult = res |> AMap.force
739+ lastBatch |> should haveCount 1
740+
741+ thirdResult.[ file1] |> should not' ( equal fourthResult.[ file1])
742+
743+ transact( fun () ->
744+ file1DepCval.Value <- file1DepCval.Value + 1
745+ file1Cval.Value <- file1Cval.Value)
746+
747+ let fifthResult = res |> AMap.force
748+ lastBatch |> should haveCount 1
749+
750+ fourthResult.[ file1] |> should not' ( equal fifthResult.[ file1])
751+
752+ ()
0 commit comments