public void ConvertMemoryLeakToVSTestResult() { LogEntryMemoryLeak leak = new LogEntryMemoryLeak(); leak.LeakLineNumber = 32; leak.LeakMemoryAllocationNumber = 836; leak.LeakLeakedDataContents = " Data: <`- Leak... > 60 2D BD 00 4C 65 61 6B 2E 2E 2E 00 CD CD CD CD "; leak.LeakSourceFilePath = @"C:\boostunittestsample.cpp"; leak.LeakSourceFileName = "boostunittestsample.cpp"; BoostTestResult testCaseResult = new BoostTestResultBuilder(). For(this.TestCase). Passed(). Duration(1000). Log(leak). Build(); VSTestResult result = testCaseResult.AsVSTestResult(this.TestCase); AssertVSTestModelProperties(result); Assert.That(result.Outcome, Is.EqualTo(TestOutcome.Passed)); Assert.That(result.Duration, Is.EqualTo(Microseconds(1000))); Assert.That(result.Messages.Count, Is.EqualTo(1)); TestResultMessage message = result.Messages.First(); Assert.That(message.Category, Is.EqualTo(TestResultMessage.StandardErrorCategory)); }
/// <summary> /// Formats a LogEntryMemoryLeak to append to test result string /// </summary> /// <param name="memoryLeak">The memory leak to format</param> /// <param name="sb">The StringBuilder which will host the output</param> /// <returns>sb</returns> private static StringBuilder FormatMemoryLeak(LogEntryMemoryLeak memoryLeak, StringBuilder sb) { if ((memoryLeak.LeakSourceFilePath != null) && (memoryLeak.LeakSourceFileName != null)) { sb.Append("source file path leak detected at :"). Append(memoryLeak.LeakSourceFilePath). Append(memoryLeak.LeakSourceFileName); } if (memoryLeak.LeakLineNumber != null) { sb.Append(", "). Append("Line number: "). Append(memoryLeak.LeakLineNumber); } sb.Append(", "). Append("Memory allocation number: "). Append(memoryLeak.LeakMemoryAllocationNumber); sb.Append(", "). Append("Leak size: "). Append(memoryLeak.LeakSizeInBytes). Append(" byte"); if (memoryLeak.LeakSizeInBytes > 0) { sb.Append('s'); } sb.Append(Environment.NewLine). Append(memoryLeak.LeakLeakedDataContents); return sb; }
private void RegisterMemoryLeak(string leakInformation, TestResultCollection collection) { foreach (TestResult result in collection) { if (this.FailTestOnMemoryLeak) { result.Result = TestResultType.Failed; } Regex regexLeakInformation = new Regex(@"(?:([\\:\w\rA-z.]*?)([\w\d.]*)\((\d{1,})\)\s:\s)?\{(\d{1,})\}[\w\s\d]*,\s(\d{1,})[\s\w.]*\n(.*?)(?=$|(?:[\\\w.:]*\(\d{1,}\)\s:\s)?\{\d{1,}\d)", RegexOptions.IgnoreCase | RegexOptions.Singleline); //the old one wasRegex regexLeakInformation = new Regex(@"^(.*\\)(.*?)\((\d{1,})\).*?{(\d{1,})}.*?(\d{1,})\sbyte", RegexOptions.IgnoreCase | RegexOptions.Multiline); /* The same regex works for when the complete file path along with the line number are reported in the console output such as in the below sample output d:\hwa\dev\svn\boostunittestadapterdev\branches\tempbugfixing\sample\boostunittest\boostunittest2\adapterbugs.cpp(58) : {869} normal block at 0x00A88A58, 4 bytes long. Data: < > CD CD CD CD d:\hwa\dev\svn\boostunittestadapterdev\branches\tempbugfixing\sample\boostunittest\boostunittest2\adapterbugs.cpp(55) : {868} normal block at 0x00A88788, 4 bytes long. Data: < > F5 01 00 00 and also when this information is not reported such as in the below sample output {869} normal block at 0x005E8998, 4 bytes long. Data: < > CD CD CD CD {868} normal block at 0x005E8848, 4 bytes long. Data: < > F5 01 00 00 */ #region regexLeakInformation // (?:([\\:\w\rA-z.]*?)([\w\d.]*)\((\d{1,})\)\s:\s)?\{(\d{1,})\}[\w\s\d]*,\s(\d{1,})[\s\w.]*\n(.*?)(?=$|(?:[\\\w.:]*\(\d{1,}\)\s:\s)?\{\d{1,}\d) // // Options: Case insensitive; Exact spacing; Dot matches line breaks; ^$ don't match at line breaks; Numbered capture // // Match the regular expression below «(?:([\\:\w\rA-z.]*?)([\w\d.]*)\((\d{1,})\)\s:\s)?» // Between zero and one times, as many times as possible, giving back as needed (greedy) «?» // Match the regex below and capture its match into backreference number 1 «([\\:\w\rA-z.]*?)» // Match a single character present in the list below «[\\:\w\rA-z.]*?» // Between zero and unlimited times, as few times as possible, expanding as needed (lazy) «*?» // The backslash character «\\» // The literal character “:” «:» // A “word character” (Unicode; any letter or ideograph, digit, connector punctuation) «\w» // The carriage return character «\r» // A character in the range between “A” and “z” (case insensitive) «A-z» // The literal character “.” «.» // Match the regex below and capture its match into backreference number 2 «([\w\d.]*)» // Match a single character present in the list below «[\w\d.]*» // Between zero and unlimited times, as many times as possible, giving back as needed (greedy) «*» // A “word character” (Unicode; any letter or ideograph, digit, connector punctuation) «\w» // A “digit” (0–9 in any Unicode script) «\d» // The literal character “.” «.» // Match the character “(” literally «\(» // Match the regex below and capture its match into backreference number 3 «(\d{1,})» // Match a single character that is a “digit” (0–9 in any Unicode script) «\d{1,}» // Between one and unlimited times, as many times as possible, giving back as needed (greedy) «{1,}» // Match the character “)” literally «\)» // Match a single character that is a “whitespace character” (any Unicode separator, tab, line feed, carriage return, vertical tab, form feed, next line) «\s» // Match the character “:” literally «:» // Match a single character that is a “whitespace character” (any Unicode separator, tab, line feed, carriage return, vertical tab, form feed, next line) «\s» // Match the character “{” literally «\{» // Match the regex below and capture its match into backreference number 4 «(\d{1,})» // Match a single character that is a “digit” (0–9 in any Unicode script) «\d{1,}» // Between one and unlimited times, as many times as possible, giving back as needed (greedy) «{1,}» // Match the character “}” literally «\}» // Match a single character present in the list below «[\w\s\d]*» // Between zero and unlimited times, as many times as possible, giving back as needed (greedy) «*» // A “word character” (Unicode; any letter or ideograph, digit, connector punctuation) «\w» // A “whitespace character” (any Unicode separator, tab, line feed, carriage return, vertical tab, form feed, next line) «\s» // A “digit” (0–9 in any Unicode script) «\d» // Match the character “,” literally «,» // Match a single character that is a “whitespace character” (any Unicode separator, tab, line feed, carriage return, vertical tab, form feed, next line) «\s» // Match the regex below and capture its match into backreference number 5 «(\d{1,})» // Match a single character that is a “digit” (0–9 in any Unicode script) «\d{1,}» // Between one and unlimited times, as many times as possible, giving back as needed (greedy) «{1,}» // Match a single character present in the list below «[\s\w.]*» // Between zero and unlimited times, as many times as possible, giving back as needed (greedy) «*» // A “whitespace character” (any Unicode separator, tab, line feed, carriage return, vertical tab, form feed, next line) «\s» // A “word character” (Unicode; any letter or ideograph, digit, connector punctuation) «\w» // The literal character “.” «.» // Match the line feed character «\n» // Match the regex below and capture its match into backreference number 6 «(.*?)» // Match any single character «.*?» // Between zero and unlimited times, as few times as possible, expanding as needed (lazy) «*?» // Assert that the regex below can be matched, starting at this position (positive lookahead) «(?=$|(?:[\\\w.:]*\(\d{1,}\)\s:\s)?\{\d{1,}\d)» // Match this alternative (attempting the next alternative only if this one fails) «$» // Assert position at the end of the string, or before the line break at the end of the string, if any (line feed) «$» // Or match this alternative (the entire group fails if this one fails to match) «(?:[\\\w.:]*\(\d{1,}\)\s:\s)?\{\d{1,}\d» // Match the regular expression below «(?:[\\\w.:]*\(\d{1,}\)\s:\s)?» // Between zero and one times, as many times as possible, giving back as needed (greedy) «?» // Match a single character present in the list below «[\\\w.:]*» // Between zero and unlimited times, as many times as possible, giving back as needed (greedy) «*» // The backslash character «\\» // A “word character” (Unicode; any letter or ideograph, digit, connector punctuation) «\w» // A single character from the list “.:” «.:» // Match the character “(” literally «\(» // Match a single character that is a “digit” (0–9 in any Unicode script) «\d{1,}» // Between one and unlimited times, as many times as possible, giving back as needed (greedy) «{1,}» // Match the character “)” literally «\)» // Match a single character that is a “whitespace character” (any Unicode separator, tab, line feed, carriage return, vertical tab, form feed, next line) «\s» // Match the character “:” literally «:» // Match a single character that is a “whitespace character” (any Unicode separator, tab, line feed, carriage return, vertical tab, form feed, next line) «\s» // Match the character “{” literally «\{» // Match a single character that is a “digit” (0–9 in any Unicode script) «\d{1,}» // Between one and unlimited times, as many times as possible, giving back as needed (greedy) «{1,}» // Match a single character that is a “digit” (0–9 in any Unicode script) «\d» #endregion regexLeakInformation Match matchLeakInformation = regexLeakInformation.Match(leakInformation); while (matchLeakInformation.Success) { LogEntryMemoryLeak leak = new LogEntryMemoryLeak(); result.LogEntries.Add(leak); //Capturing group 1,2 and 3 will have the 'Success' property false in case the C++ new operator has not been replaced via the macro // Temporary variable used to try and parse unsigned integer values; uint value = 0; if (matchLeakInformation.Groups[1].Success && matchLeakInformation.Groups[2].Success && matchLeakInformation.Groups[3].Success) { leak.LeakSourceFilePath = matchLeakInformation.Groups[1].Value; leak.LeakSourceFileName = matchLeakInformation.Groups[2].Value; if (uint.TryParse(matchLeakInformation.Groups[3].Value, out value)) { leak.LeakLineNumber = value; } leak.LeakSourceFileAndLineNumberReportingActive = true; } else { leak.LeakSourceFileAndLineNumberReportingActive = false; } if (uint.TryParse(matchLeakInformation.Groups[4].Value, out value)) { leak.LeakMemoryAllocationNumber = value; } if (uint.TryParse(matchLeakInformation.Groups[5].Value, out value)) { leak.LeakSizeInBytes = value; } leak.LeakLeakedDataContents = matchLeakInformation.Groups[6].Value; matchLeakInformation = matchLeakInformation.NextMatch(); } } }
/// <summary> /// Compares 2 LogEntryMemoryLeak for equivalence. Issues an assertion failure if leak information is not equivalent. /// </summary> /// <param name="lhs">The left-hand side LogEntryMemoryLeak instance</param> /// <param name="rhs">The right-hand side LogEntryMemoryLeak instance</param> private void AssertMemoryLeakDetails(LogEntryMemoryLeak lhs, LogEntryMemoryLeak rhs) { Assert.AreEqual(lhs.LeakLineNumber, rhs.LeakLineNumber); Assert.AreEqual(lhs.LeakLeakedDataContents, rhs.LeakLeakedDataContents); Assert.AreEqual(lhs.LeakMemoryAllocationNumber, rhs.LeakMemoryAllocationNumber); Assert.AreEqual(lhs.LeakSizeInBytes, rhs.LeakSizeInBytes); Assert.AreEqual(lhs.LeakSourceFileAndLineNumberReportingActive, rhs.LeakSourceFileAndLineNumberReportingActive); Assert.AreEqual(lhs.LeakSourceFileName, rhs.LeakSourceFileName); Assert.AreEqual(lhs.LeakSourceFilePath, rhs.LeakSourceFilePath); }