Пример #1
0
        public IEnumerable<Violation> Apply(TestCase testCase)
        {
            var calledAssertingMethods = testCase.GetCalledAssertingMethods();
            var tracker = new MethodValueTracker(testCase.TestMethod);

            // For each asserting method with >= 1 parameters:
            foreach (var cm in calledAssertingMethods)
            {
                var methodRef = cm.MethodReference;
                var parameterPurposes = testCase.Framework.GetParameterPurposes(methodRef);
                if (!IsSingleTruthCheckingMethod(methodRef, parameterPurposes))
                    continue;

                foreach (var valueGraph in tracker.ValueGraphs)
                {
                    IList<MethodValueTracker.Value> consumedValues =
                        tracker.GetConsumedValues(valueGraph, cm.Instruction).ToList();
                    if (consumedValues.Count == 0)
                        continue; // not part of value graph
                    var interestingValue = consumedValues[0];
                    var producers = UltimateProducers(interestingValue);
                    if (producers.Count > 1)
                    {
                        yield return new Violation(this, testCase, cm.Instruction, string.Format("{0}.{1} performs a boolean test on a composite boolean value",
                            cm.MethodReference.DeclaringType.Name,
                            cm.MethodReference.Name));
                    }
                }
            }
        }
 public void TestThatValueConsumptionForInstructionMatchesPath()
 {
     var method = GetType().FindMethod("MethodWithBranch()");
     var tracker = new MethodValueTracker(method);
     var graph = tracker.ValueGraphs[1]; // second path
     // Find the last instruction that stores to y (first local)
     var instr = method.Body.Instructions.Where(i => i.OpCode == OpCodes.Stloc_0).Last();
     var producers = tracker.GetConsumedValues(graph, instr).Select(v => v.Producer.OpCode).ToList();
     // Expect the producer of the conssumed value to load from u (third local)
     CollectionAssert.AreEqual(new[] { OpCodes.Ldloc_2 }, producers);
 }
        public void TestThatValueConsumptionForInstructionOutsidePathIsEmpty()
        {
            var method = GetType().FindMethod("MethodWithBranch()");
            var tracker = new MethodValueTracker(method);
            var graph = tracker.ValueGraphs[0]; // first path
            // Find the last instruction that stores to y, part of second path
            var instr = method.Body.Instructions.Where(i => i.OpCode == OpCodes.Stloc_0).Last();

            var consumed = tracker.GetConsumedValues(graph, instr);
            Assert.AreEqual(0, consumed.Count());
        }
Пример #4
0
        public IEnumerable<Violation> Apply(TestCase testCase)
        {
            // Store the framework so that we don't have to pass it around everywhere.
            _framework = testCase.Framework;

            var calledAssertingMethods = testCase.GetCalledAssertingMethods();
            var tracker = new MethodValueTracker(testCase.TestMethod);

            var whitelistedFields = FindWhitelistedFields(testCase.TestMethod.DeclaringType);

            // For each asserting method with >= 1 parameters:
            foreach (var cm in calledAssertingMethods.Where(cm => cm.MethodDefinition.HasParameters))
            {
                var method = cm.MethodDefinition;
                //TODO: if the method is a helper, we need to "unfold" the helper
                // to get to the real asserting methods, and this will require us
                // to join value-generation graphs across method calls...
                var paramPurposes = _framework.GetParameterPurposes(method);
                if (paramPurposes == null) continue; // unknown method, rule does not apply

                foreach (var valueGraph in tracker.ValueGraphs)
                {
                    IList<MethodValueTracker.Value> consumedValues = tracker.GetConsumedValues(valueGraph, cm.Instruction).ToList();
                    if (consumedValues.Count == 0)
                        continue; // not part of value graph

                    // Build a list of arguments with the details we need to know if the rule applies.
                    var arguments = method.Parameters
                        .Select((p, index) => new ArgumentDetails { Method = method, Index = index, Purpose = paramPurposes[index], ConsumedValue = consumedValues[index] }).ToList();

                    // Handle cases like Assert.IsTrue(x == 5) by expanding arguments
                    ExpandIfSingleTruthCheckingMethod(method, ref arguments);

                    // We're only interested in arguments that represent expectations!
                    var interestingArguments = arguments.Where(a => IsPerhapsExpectation(a.Purpose)).ToList();

                    // This might happen with for example Assert.Fail("some reason").
                    if (interestingArguments.Count == 0)
                        continue;

                    // Add in the "forbidden producer", if any, for each argument. A forbidden producer is an
                    // instruction that generates a value externally, such as a call.
                    interestingArguments = interestingArguments.Select(
                            a => { a.ForbiddenProducer = FirstForbiddenProducer(valueGraph, a.ConsumedValue, whitelistedFields); return a; }).ToList();

                    // If there is at least one locally produced argument, the rule doesn't apply.
                    if (interestingArguments.Any(IsLocallyProduced))
                        continue;

                    if (interestingArguments.All(a => a.Purpose == ParameterPurpose.ExpectedOrActual))
                    {
                        // Since we don't know exactly which parameter that represents the expectation, we
                        // just generate a single violation.
                        yield return new Violation(this, testCase, interestingArguments[0].ConsumedValue.Consumer,
                            CreateViolationMessageForUncertainCase(interestingArguments[0]));
                        continue;
                    }

                    foreach (var a in interestingArguments.Where(IsExternallyProduced))
                    {

                        // Generate a violation at the location of the forbidden producer!
                        yield return new Violation(this, testCase, a.ForbiddenProducer, CreateViolationMessage(a));
                    }
                }
            }
        }
Пример #5
0
        public void TestThatConstantConsumedViaBoxedValueIsFound()
        {
            var method = GetType().FindMethod("BoxedConstantUseByStaticMethod()");
            var tracker = new MethodValueTracker(method);
            var consumedValue = tracker.GetConsumedValues(tracker.ValueGraphs[0], LastCall(method)).First();
            var roots = tracker.FindSourceValues(consumedValue);

            Assert.AreEqual(OpCodes.Ldc_I4_S, roots.First().Producer.OpCode);
        }
Пример #6
0
 private IEnumerable<OpCode> FindAllSourceValueOpCodes(MethodValueTracker tracker, Instruction instruction)
 {
     var consumedValues = tracker.GetConsumedValues(tracker.ValueGraphs[0], instruction);
     var sourceValues = consumedValues.SelectMany(tracker.FindSourceValues);
     return sourceValues.Select(v => v.Producer.OpCode).Distinct();
 }
Пример #7
0
 public void TestThatQueryInstructionCannotBeNull()
 {
     var method = GetType().FindMethod("DirectConstantUseByStaticMethod()");
     var tracker = new MethodValueTracker(method);
     tracker.GetConsumedValues(tracker.ValueGraphs[0], null);
 }
Пример #8
0
 public void TestThatGraphCannotBeNullWhenQueryingConsumedValues()
 {
     var method = GetType().FindMethod("DirectConstantUseByStaticMethod()");
     var tracker = new MethodValueTracker(method);
     tracker.GetConsumedValues(null, LastCall(method));
 }
Пример #9
0
        public void TestThatConsumedValuesIncludesFirstArgOfStaticCallInStatic()
        {
            var method = GetType().FindMethod("StaticMethodWithStaticCall(System.Int32)");
            var tracker = new MethodValueTracker(method);
            var consumedValues = tracker.GetConsumedValues(tracker.ValueGraphs[0], LastCall(method));
            var opCodes = consumedValues.Select(v => v.Producer.OpCode);

            // Ldarg_0 should be in the list!
            CollectionAssert.AreEqual(new[] { OpCodes.Ldarg_0 }, opCodes);
        }
Пример #10
0
        public void TestThatConsumedValuesExcludesThisObjectForVirtualCall()
        {
            var method = GetType().FindMethod("DirectConstantUseByVirtualMethod()");
            var tracker = new MethodValueTracker(method);
            var consumedValues = tracker.GetConsumedValues(tracker.ValueGraphs[0], LastCall(method));
            var opCodes = consumedValues.Select(v => v.Producer.OpCode);

            // Ldarg_0 should not be in the list!
            CollectionAssert.AreEqual(new[] { OpCodes.Ldc_I4_S }, opCodes);
        }
Пример #11
0
 public void TestThatConsumedValuesAreEmptyWhenQueryInstructionIsNotPartOfMethod()
 {
     var method = GetType().FindMethod("DirectConstantUseByStaticMethod()");
     var method2 = GetType().FindMethod("DirectConstantUseByVirtualMethod()");
     var tracker = new MethodValueTracker(method);
     var values = tracker.GetConsumedValues(tracker.ValueGraphs[0], LastCall(method2));
     Assert.AreEqual(0, values.Count());
 }
Пример #12
0
 public void TestThatConsumedValueCountCorrespondsToActualConsumption()
 {
     var method = GetType().FindMethod("DirectConstantUseByStaticMethod()");
     var tracker = new MethodValueTracker(method);
     var consumedValues = tracker.GetConsumedValues(tracker.ValueGraphs[0], LastCall(method));
     Assert.AreEqual(1, consumedValues.Count());
 }