public void CheckLerping <Q> (Func <Q, Q, float, Q> lerpFunc) where Q : struct, IQuat <Q, float> { (from quat1 in Prop.ForAll <Q> () from quat2 in Prop.ForAll <Q> () from alpha in Prop.Any(Gen.ChooseDouble(0.0, 1.0).ToFloat()) let lerp = lerpFunc(quat1, quat2, alpha) let len = lerp.Length select new { quat1, quat2, alpha, lerp, len }) .Check(p => p.lerp.IsNormalized, label: $"{typeof (Q).Name}: | lerp (quat1, quat2) | = 1"); }
/* * You should now see your checks pass and the names are now a bit easier * to decipher. * ``` * 'Count increased by one.' passed 100 tests. Discarded: 0 * 'First item is the added item.' passed 100 tests. Discarded: 0 * 'Rest of the sequence is same as the original.' passed 100 tests. Discarded: 0 * 'Count increased by one.' passed 100 tests. Discarded: 0 * 'First item is the added item.' passed 100 tests. Discarded: 0 * 'Rest of the sequence is same as the original.' passed 100 tests. Discarded: 0 * 00:00:00.0283599 - TestAddition * ``` * ## Testing Removal ##The last feature we test is removing an item. */ public void CheckRemoval <T> () { /* * Let's again generate an arbitrary sequence and make sure it is not * empty. This time we use the `SuchThat` combinator defined for the * `IArbitrary` interface, which filters out all the randomly generated * values that do not match a given predicate. This has the benefit * that no test cases are discarded, but it also makes the test run * a bit longer. Also, if the predicate is too strict, `SuchThat` * might not find a suitable value. In this case the generator will * fail and throw an exception. */ (from seq in Prop.ForAll(ArbitrarySeq <T> ().SuchThat( s => !s.IsEmpty())) /* * Next we need to select from the sequence an arbitrary item that we * can remove. We do this by calling `Prop.Any`. It differs from * `Prop.ForAll` in that it takes `Gen<T>` as an argument instead * of `IArbitrary<T>`. This means that the random values generated * by `Any` are not shrunk, if the test fails. * * Also, the values produced by `Any` might depend on the other * generated values. As in this case, the chosen element must be * inside the sequence that we generated previously. It would not * make sense to shrink this value, because then we would probably * loose the failing test case. * * In general, we need to make sure that the same input data * provides always the same result, and that our test case is * deterministic. Given the same parameters, `Any` produces always * the same result, whereas `ForAll` will produce a different value * every time it is called. */ from item in Prop.Any(Gen.ElementOf(seq)) /* * Now we can remove the chosen item. */ let newSeq = seq.Remove(item) /* ### Classifying Test Cases ###As a last step we return the test case. This time, however, ###we use the `orderby` clause to classify our test cases by the ###length of the sequence. By adding this clause we get a report ###with the results of how many test cases we have with the specified ###property. The report helps us determine, for example, if we have ###enough test coverage for longer sequences. */ orderby seq.Count() select new { seq, item, newSeq }) /* * Next we define some properties related to the removal operation. * We check that the new sequence should be one item shorter than the * original one. */ .Check(t => t.newSeq.IsEmpty().Implies(t.seq.Count() == 1) || t.newSeq.Count() == t.seq.Count() - 1) /* ### Changing the Size of the Generated Data ###Before the second check let's use the `Restrict` combinator to make ###the test range a bit bigger. The combinator either widens our ###narrows our test set. The default test "size" is 10, which means ###that we don't get sequences longer than 9 items. Effectively, we ###double our test range by setting the size to 20. ### ###The second property says that if we find the same item that was ###removed in the new sequence, it must be a duplicate, and thus ###appear at least twice. */ .Restrict(20) .Check(t => !t.newSeq.Contains(t.item) || t.seq.Count(i => i.Equals(t.item)) > 1); }