public ILCompilerTestCaseResult(TestCase testCase, NPath inputAssemblyPath, NPath expectationsAssemblyPath, TestCaseSandbox sandbox, TestCaseMetadataProvider metadataProvider, ManagedCompilationResult compilationResult, TestLogWriter logWriter) { TestCase = testCase; InputAssemblyPath = inputAssemblyPath; ExpectationsAssemblyPath = expectationsAssemblyPath; Sandbox = sandbox; MetadataProvider = metadataProvider; CompilationResult = compilationResult; LogWriter = logWriter; }
private ILCompilerTestCaseResult Link(TestCase testCase, TestCaseSandbox sandbox, ManagedCompilationResult compilationResult, TestCaseMetadataProvider metadataProvider) { var trimmer = _factory.CreateTrimmer(); var builder = _factory.CreateTrimmerOptionsBuilder(metadataProvider); AddLinkOptions(sandbox, compilationResult, builder, metadataProvider); var logWriter = new TestLogWriter(); trimmer.Trim(builder.Options, logWriter); return(new ILCompilerTestCaseResult(testCase, compilationResult.InputAssemblyPath, compilationResult.ExpectationsAssemblyPath, sandbox, metadataProvider, compilationResult, logWriter)); }
void VerifyLoggedMessages(AssemblyDefinition original, TestLogWriter logger, bool checkRemainingErrors) { List <MessageContainer> loggedMessages = logger.GetLoggedMessages(); List <(IMemberDefinition, CustomAttribute)> expectedNoWarningsAttributes = new List <(IMemberDefinition, CustomAttribute)> (); foreach (var attrProvider in GetAttributeProviders(original)) { if (attrProvider.ToString() is String mystring && mystring.Contains("RequiresInCompilerGeneratedCode/SuppressInLambda")) { Debug.WriteLine("Print"); } foreach (var attr in attrProvider.CustomAttributes) { if (!IsProducedByNativeAOT(attr)) { continue; } switch (attr.AttributeType.Name) { case nameof(LogContainsAttribute): { var expectedMessage = (string)attr.ConstructorArguments[0].Value; List <MessageContainer> matchedMessages; if ((bool)attr.ConstructorArguments[1].Value) { matchedMessages = loggedMessages.Where(m => Regex.IsMatch(m.ToString(), expectedMessage)).ToList(); } else { matchedMessages = loggedMessages.Where(m => MessageTextContains(m.ToString(), expectedMessage)).ToList(); }; Assert.True( matchedMessages.Count > 0, $"Expected to find logged message matching `{expectedMessage}`, but no such message was found.{Environment.NewLine}Logged messages:{Environment.NewLine}{string.Join (Environment.NewLine, loggedMessages)}"); foreach (var matchedMessage in matchedMessages) { loggedMessages.Remove(matchedMessage); } } break; case nameof(LogDoesNotContainAttribute): { var unexpectedMessage = (string)attr.ConstructorArguments[0].Value; foreach (var loggedMessage in loggedMessages) { var isLogged = () => { if ((bool)attr.ConstructorArguments[1].Value) { return(!Regex.IsMatch(loggedMessage.ToString(), unexpectedMessage)); } return(!MessageTextContains(loggedMessage.ToString(), unexpectedMessage)); }; Assert.True( isLogged(), $"Expected to not find logged message matching `{unexpectedMessage}`, but found:{Environment.NewLine}{loggedMessage.ToString ()}{Environment.NewLine}Logged messages:{Environment.NewLine}{string.Join (Environment.NewLine, loggedMessages)}"); } } break; case nameof(ExpectedWarningAttribute): { var expectedWarningCode = (string)attr.GetConstructorArgumentValue(0); if (!expectedWarningCode.StartsWith("IL")) { Assert.Fail($"The warning code specified in {nameof (ExpectedWarningAttribute)} must start with the 'IL' prefix. Specified value: '{expectedWarningCode}'."); } var expectedMessageContains = ((CustomAttributeArgument[])attr.GetConstructorArgumentValue(1)).Select(a => (string)a.Value).ToArray(); string fileName = (string)attr.GetPropertyValue("FileName") !; int? sourceLine = (int?)attr.GetPropertyValue("SourceLine"); int? sourceColumn = (int?)attr.GetPropertyValue("SourceColumn"); bool? isCompilerGeneratedCode = (bool?)attr.GetPropertyValue("CompilerGeneratedCode"); int expectedWarningCodeNumber = int.Parse(expectedWarningCode.Substring(2)); string?expectedOrigin = null; bool expectedWarningFound = false; foreach (var loggedMessage in loggedMessages) { if (loggedMessage.ToString().Contains("RequiresInCompilerGeneratedCode.SuppressInLambda")) { Debug.WriteLine("Print 2"); } if (loggedMessage.Category != MessageCategory.Warning || loggedMessage.Code != expectedWarningCodeNumber) { continue; } bool messageNotFound = false; foreach (var expectedMessage in expectedMessageContains) { if (!MessageTextContains(loggedMessage.Text, expectedMessage)) { messageNotFound = true; break; } } if (messageNotFound) { continue; } if (fileName != null) { if (loggedMessage.Origin == null) { continue; } var actualOrigin = loggedMessage.Origin.Value; if (actualOrigin.FileName != null) { // Note: string.Compare(string, StringComparison) doesn't exist in .NET Framework API set if (actualOrigin.FileName.IndexOf(fileName, StringComparison.OrdinalIgnoreCase) < 0) { continue; } if (sourceLine != null && loggedMessage.Origin?.SourceLine != sourceLine.Value) { continue; } if (sourceColumn != null && loggedMessage.Origin?.SourceColumn != sourceColumn.Value) { continue; } } else { // The warning was logged with member/ILoffset, so it didn't have line/column info filled // but it will be computed from PDBs, so instead compare it in a string representation if (expectedOrigin == null) { expectedOrigin = fileName; if (sourceLine.HasValue) { expectedOrigin += "(" + sourceLine.Value; if (sourceColumn.HasValue) { expectedOrigin += "," + sourceColumn.Value; } expectedOrigin += ")"; } } string actualOriginString = actualOrigin.ToString() ?? ""; if (!actualOriginString.EndsWith(expectedOrigin, StringComparison.OrdinalIgnoreCase)) { continue; } } } else if (isCompilerGeneratedCode == true) { if (loggedMessage.Origin?.MemberDefinition is MethodDesc methodDesc) { if (attrProvider is not IMemberDefinition expectedMember) { continue; } string actualName = methodDesc.OwningType.ToString().Replace("+", ".") + "." + methodDesc.Name; if (actualName.Contains(expectedMember.DeclaringType.FullName.Replace("/", ".")) && actualName.Contains("<" + expectedMember.Name + ">")) { expectedWarningFound = true; loggedMessages.Remove(loggedMessage); break; } if (actualName.StartsWith(expectedMember.DeclaringType.FullName) && actualName.Contains(".cctor") && (expectedMember is FieldDefinition || expectedMember is PropertyDefinition)) { expectedWarningFound = true; loggedMessages.Remove(loggedMessage); break; } if (methodDesc.Name == ".ctor" && methodDesc.OwningType.ToString() == expectedMember.FullName) { expectedWarningFound = true; loggedMessages.Remove(loggedMessage); break; } } continue; } else { if (LogMessageHasSameOriginMember(loggedMessage, attrProvider)) { expectedWarningFound = true; loggedMessages.Remove(loggedMessage); break; } continue; } expectedWarningFound = true; loggedMessages.Remove(loggedMessage); break; } var expectedOriginString = fileName == null ? GetExpectedOriginDisplayName(attrProvider) + ": " : ""; Assert.True(expectedWarningFound, $"Expected to find warning: {(fileName != null ? fileName + (sourceLine != null ? $"({sourceLine},{sourceColumn})" : "") + ": " : "")}" + $"warning {expectedWarningCode}: {expectedOriginString}" + $"and message containing {string.Join (" ", expectedMessageContains.Select (m => "'" + m + "'"))}, " + $"but no such message was found.{Environment.NewLine}Logged messages:{Environment.NewLine}{string.Join (Environment.NewLine, loggedMessages)}"); } break; case nameof(ExpectedNoWarningsAttribute): // Postpone processing of negative checks, to make it possible to mark some warnings as expected (will be removed from the list above) // and then do the negative check on the rest. var memberDefinition = attrProvider as IMemberDefinition; Assert.NotNull(memberDefinition); expectedNoWarningsAttributes.Add((memberDefinition, attr)); break; } } } foreach ((var attrProvider, var attr) in expectedNoWarningsAttributes) { var unexpectedWarningCode = attr.ConstructorArguments.Count == 0 ? null : (string)attr.GetConstructorArgumentValue(0); if (unexpectedWarningCode != null && !unexpectedWarningCode.StartsWith("IL")) { Assert.Fail($"The warning code specified in ExpectedNoWarnings attribute must start with the 'IL' prefix. Specified value: '{unexpectedWarningCode}'."); } int?unexpectedWarningCodeNumber = unexpectedWarningCode == null ? null : int.Parse(unexpectedWarningCode.Substring(2)); MessageContainer?unexpectedWarningMessage = null; foreach (var mc in logger.GetLoggedMessages()) { if (mc.Category != MessageCategory.Warning) { continue; } if (unexpectedWarningCodeNumber != null && unexpectedWarningCodeNumber.Value != mc.Code) { continue; } // This is a hacky way to say anything in the "subtree" of the attrProvider if ((mc.Origin?.MemberDefinition is TypeSystemEntity member) && member.ToString()?.Contains(attrProvider.FullName) != true) { continue; } unexpectedWarningMessage = mc; break; } Assert.False(unexpectedWarningMessage.HasValue, $"Unexpected warning found: {unexpectedWarningMessage}"); } if (checkRemainingErrors) { var remainingErrors = loggedMessages.Where(m => Regex.IsMatch(m.ToString(), @".*(error | warning): \d{4}.*")); Assert.False(remainingErrors.Any(), $"Found unexpected errors:{Environment.NewLine}{string.Join (Environment.NewLine, remainingErrors)}"); } bool LogMessageHasSameOriginMember(MessageContainer mc, ICustomAttributeProvider expectedOriginProvider) { var origin = mc.Origin; Debug.Assert(origin != null); if (GetActualOriginDisplayName(origin?.MemberDefinition) == ConvertSignatureToIlcFormat(GetExpectedOriginDisplayName(expectedOriginProvider))) { return(true); } var actualMember = origin !.Value.MemberDefinition; // Compensate for cases where for some reason the OM doesn't preserve the declaring types // on certain things after trimming. if (actualMember != null && GetOwningType(actualMember) == null && GetMemberName(actualMember) == (expectedOriginProvider as IMemberDefinition)?.Name) { return(true); } return(false); }