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 static string GetTestResultMessageText(TestUnit unit, LogEntry entry) { Code.Require(unit, "unit"); Code.Require(entry, "entry"); if ((entry is LogEntryStandardOutputMessage) || (entry is LogEntryStandardErrorMessage)) { return(entry.Detail.TrimEnd() + Environment.NewLine); } StringBuilder sb = new StringBuilder(); if (entry.Source != null) { AppendSourceInfo(entry.Source, sb); } sb.Append(entry.ToString().ToLowerInvariant()). Append(" in \""). Append(unit.Name). Append("\""); LogEntryMemoryLeak memoryLeak = entry as LogEntryMemoryLeak; if (memoryLeak == null) { sb.Append(": ").Append(entry.Detail.TrimEnd()); } LogEntryException exception = entry as LogEntryException; if (exception != null) { FormatException(exception, sb); } LogEntryError error = entry as LogEntryError; if (error != null) { FormatError(error, sb); } if (memoryLeak != null) { FormatMemoryLeak(memoryLeak, sb); } // Append NewLine so that log entries are listed one per line return(sb.Append(Environment.NewLine).ToString()); }
/// <summary> /// Registers the provided memory leak string representation within the test collection /// </summary> /// <param name="leakInformation">The full memory leak information</param> /// <param name="collection">The test collection in which to place memory leak entries</param> private void RegisterMemoryLeak(string leakInformation, IDictionary <string, TestResult> collection) { foreach (var entry in collection) { var result = entry.Value; 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) { // Group 1 => Directory // Group 2 => Source File Name leak.Source = new SourceFileInfo(matchLeakInformation.Groups[1].Value + matchLeakInformation.Groups[2].Value); if (uint.TryParse(matchLeakInformation.Groups[3].Value, out value)) { leak.Source.LineNumber = (int)value; } } 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(); } } }