public IEnumerable<Violation> Apply(TestCase testCase) { if (!testCase.TestMethod.DeclaringType.Module.HasSymbols) yield break; //TODO: decide what to do here! var assertingMethods = testCase.GetCalledAssertingMethods(); // Note: The Mono compiler appears to emit multiple sequence points with the same start line, // i.e. there are multiple instructions with sequence points that refer to the same line. // Therefore, let's store line numbers and associate line numbers with asserting calls. var sequencePointsStartLines = new SortedSet<int>(); var assertingSequencePointsStartLines = new SortedSet<int>(); var tm = testCase.TestMethod; foreach (var ins in tm.Body.Instructions) { var sp = ins.SequencePoint; if (sp != null && IsSignificantSequencePoint(ins, sp)) { sequencePointsStartLines.Add(sp.StartLine); } if (sequencePointsStartLines.Count > 0 && IsAssertCall(ins, assertingMethods)) { // As sequence point, use the last one added, which isn't necessarily sp, // since the asserting instruction may lack sequence point. var lastSpLineNumber = sequencePointsStartLines.Last(); assertingSequencePointsStartLines.Add(lastSpLineNumber); } } if (assertingSequencePointsStartLines.Count == 0) yield break; // this rule doesn't apply // If the X asserting sps are the X last ones, then it's ok! if (sequencePointsStartLines.EndsWith(assertingSequencePointsStartLines)) yield break; yield return new Violation(this, testCase); }