Skip to content

Commit ebb32d9

Browse files
authored
Add BuildEngine implementation (#18)
1 parent 88ec2c2 commit ebb32d9

File tree

7 files changed

+368
-158
lines changed

7 files changed

+368
-158
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Copyright (c) Jeff Kluge. All rights reserved.
2+
//
3+
// Licensed under the MIT license.
4+
5+
using Microsoft.Build.Framework;
6+
using Shouldly;
7+
using System;
8+
using Xunit;
9+
10+
namespace Microsoft.Build.Utilities.ProjectCreation.UnitTests
11+
{
12+
public class BuildEngineTests : MSBuildTestBase
13+
{
14+
[Fact]
15+
public void ConsoleLog()
16+
{
17+
BuildEngine buildEngine = GetBuildEngineWithEvents(i =>
18+
{
19+
i.LogErrorEvent(new BuildErrorEventArgs(null, "A6DAB901460D483FBDF3A0980B14C46F", "48F7F352E2914991827100BCEB69331F", 3, 4, 0, 0, "D988473FF8634A16A0CD8FE94FF20D53", null, null));
20+
i.LogWarningEvent(new BuildWarningEventArgs(null, "AE1B25881A694A70B2EA299C04625596", "07006F38A63E420AAB4124EBE58081BC", 1, 2, 0, 0, "3A3DD4A40DA44BA5BBB123E105EE1F71", null, null));
21+
i.LogMessageEvent(new BuildMessageEventArgs("61BD637C7D704D4B98C25805E3111152", null, null, MessageImportance.High));
22+
i.LogMessageEvent(new BuildMessageEventArgs("B02496FA4D3348A6997DC918EBF7455B", null, null, MessageImportance.Normal));
23+
i.LogMessageEvent(new BuildMessageEventArgs("2C254C4346A347AE94AE5E7FB6C03B0C", null, null, MessageImportance.Low));
24+
});
25+
26+
buildEngine.GetConsoleLog()
27+
.ShouldBe(
28+
@"48F7F352E2914991827100BCEB69331F(3,4): error A6DAB901460D483FBDF3A0980B14C46F: D988473FF8634A16A0CD8FE94FF20D53
29+
07006F38A63E420AAB4124EBE58081BC(1,2): warning AE1B25881A694A70B2EA299C04625596: 3A3DD4A40DA44BA5BBB123E105EE1F71
30+
61BD637C7D704D4B98C25805E3111152
31+
B02496FA4D3348A6997DC918EBF7455B
32+
",
33+
StringCompareShould.IgnoreLineEndings);
34+
}
35+
36+
[Theory]
37+
[InlineData("6E1BF8E271E345BC892FA348132C02A9", "28C465226FCF47498C3C728FA8DEC0AB")]
38+
public void Errors(string expectedMessage, string expectedCode)
39+
{
40+
BuildErrorEventArgs args = GetBuildEngineWithEvents(buildEngine => buildEngine.LogErrorEvent(new BuildErrorEventArgs(null, expectedCode, null, 0, 0, 0, 0, expectedMessage, null, null)))
41+
.ErrorEvents
42+
.ShouldHaveSingleItem();
43+
44+
args.Message.ShouldBe(expectedMessage);
45+
46+
args.Code.ShouldBe(expectedCode);
47+
}
48+
49+
[Theory]
50+
[InlineData("High")]
51+
[InlineData("Normal")]
52+
[InlineData("Low")]
53+
public void MessagesByImportance(string value)
54+
{
55+
const string expectedMessage = "D2C36BFAE11847CE95B4135775E0156F";
56+
57+
MessageImportance importance = (MessageImportance)Enum.Parse(typeof(MessageImportance), value);
58+
59+
BuildEngine buildEngine = GetBuildEngineWithEvents(i => i.LogMessageEvent(new BuildMessageEventArgs(expectedMessage, null, null, importance)));
60+
61+
string actualItem = buildEngine.Messages.ShouldHaveSingleItem();
62+
63+
actualItem.ShouldBe(expectedMessage);
64+
65+
switch (importance)
66+
{
67+
case MessageImportance.High:
68+
buildEngine.Messages.High.ShouldHaveSingleItem().ShouldBe(actualItem);
69+
break;
70+
71+
case MessageImportance.Normal:
72+
buildEngine.Messages.Normal.ShouldHaveSingleItem().ShouldBe(actualItem);
73+
break;
74+
75+
case MessageImportance.Low:
76+
buildEngine.Messages.Low.ShouldHaveSingleItem().ShouldBe(actualItem);
77+
break;
78+
}
79+
}
80+
81+
[Theory]
82+
[InlineData("87647890B61644A1B8CCE28EA7F2CD67", "1684AC30BB494C86AAFE67CD081547F9")]
83+
public void Warnings(string expectedMessage, string expectedCode)
84+
{
85+
BuildWarningEventArgs args = GetBuildEngineWithEvents(buildEngine => buildEngine.LogWarningEvent(new BuildWarningEventArgs(null, expectedCode, null, 0, 0, 0, 0, expectedMessage, null, null)))
86+
.WarningEvents
87+
.ShouldHaveSingleItem();
88+
89+
args.Message.ShouldBe(expectedMessage);
90+
91+
args.Code.ShouldBe(expectedCode);
92+
}
93+
94+
private BuildEngine GetBuildEngineWithEvents(Action<IBuildEngine> action)
95+
{
96+
BuildEngine buildEngine = BuildEngine.Create();
97+
98+
action(buildEngine);
99+
100+
return buildEngine;
101+
}
102+
}
103+
}

src/MSBuildProjectCreator.UnitTests/BuildOutputTests.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,18 @@ public void Errors(string expectedMessage, string expectedCode)
5050
}
5151

5252
[Theory]
53-
[InlineData(MessageImportance.High)]
54-
[InlineData(MessageImportance.Normal)]
55-
[InlineData(MessageImportance.Low)]
56-
public void MessagesByImportance(MessageImportance importance)
53+
[InlineData("High")]
54+
[InlineData("Normal")]
55+
[InlineData("Low")]
56+
public void MessagesByImportance(string value)
5757
{
5858
const string expectedMessage = "A7E9F67E46A64181B25DC136A786F480";
5959

60+
MessageImportance importance = (MessageImportance)Enum.Parse(typeof(MessageImportance), value);
61+
6062
BuildOutput buildOutput = GetProjectLoggerWithEvents(eventSource => { eventSource.OnMessageRaised(expectedMessage, importance); });
6163

62-
var actualItem = buildOutput.Messages.ShouldHaveSingleItem();
64+
string actualItem = buildOutput.Messages.ShouldHaveSingleItem();
6365

6466
actualItem.ShouldBe(expectedMessage);
6567

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright (c) Jeff Kluge. All rights reserved.
2+
//
3+
// Licensed under the MIT license.
4+
5+
using Microsoft.Build.Framework;
6+
using System;
7+
using System.Collections;
8+
9+
namespace Microsoft.Build.Utilities.ProjectCreation
10+
{
11+
/// <summary>
12+
/// Represents an implementation of <see cref="IBuildEngine"/> that allows for capturing logged events in tasks.
13+
/// </summary>
14+
public sealed class BuildEngine : BuildEventArgsCollection, IBuildEngine
15+
{
16+
private BuildEngine()
17+
{
18+
}
19+
20+
/// <inheritdoc cref="IBuildEngine.ColumnNumberOfTaskNode"/>
21+
public int ColumnNumberOfTaskNode => 0;
22+
23+
/// <inheritdoc cref="IBuildEngine.ContinueOnError"/>
24+
public bool ContinueOnError => false;
25+
26+
/// <inheritdoc cref="IBuildEngine.LineNumberOfTaskNode"/>
27+
public int LineNumberOfTaskNode => 0;
28+
29+
/// <inheritdoc cref="IBuildEngine.ProjectFileOfTaskNode"/>
30+
public string ProjectFileOfTaskNode => null;
31+
32+
/// <summary>
33+
/// Creates an instance of the <see cref="BuildEngine"/> class.
34+
/// </summary>
35+
/// <returns>A <see cref="BuildEngine"/> instance.</returns>
36+
public static BuildEngine Create()
37+
{
38+
return new BuildEngine();
39+
}
40+
41+
/// <inheritdoc cref="IBuildEngine.BuildProjectFile"/>
42+
public bool BuildProjectFile(string projectFileName, string[] targetNames, IDictionary globalProperties, IDictionary targetOutputs)
43+
{
44+
throw new NotSupportedException();
45+
}
46+
47+
/// <inheritdoc cref="IBuildEngine.LogCustomEvent"/>
48+
public void LogCustomEvent(CustomBuildEventArgs e) => Add(e);
49+
50+
/// <inheritdoc cref="IBuildEngine.LogErrorEvent"/>
51+
public void LogErrorEvent(BuildErrorEventArgs e) => Add(e);
52+
53+
/// <inheritdoc cref="IBuildEngine.LogMessageEvent"/>
54+
public void LogMessageEvent(BuildMessageEventArgs e) => Add(e);
55+
56+
/// <inheritdoc cref="IBuildEngine.LogWarningEvent"/>
57+
public void LogWarningEvent(BuildWarningEventArgs e) => Add(e);
58+
}
59+
}
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
// Copyright (c) Jeff Kluge. All rights reserved.
2+
//
3+
// Licensed under the MIT license.
4+
5+
using Microsoft.Build.Framework;
6+
using Microsoft.Build.Logging;
7+
using System;
8+
using System.Collections.Concurrent;
9+
using System.Collections.Generic;
10+
using System.Linq;
11+
using System.Text;
12+
13+
namespace Microsoft.Build.Utilities.ProjectCreation
14+
{
15+
/// <summary>
16+
/// Represents a collection of <see cref="BuildEventArgs"/> objects.
17+
/// </summary>
18+
public abstract class BuildEventArgsCollection : IDisposable
19+
{
20+
/// <summary>
21+
/// Stores the errors that were logged.
22+
/// </summary>
23+
private readonly List<BuildErrorEventArgs> _errorEvents = new List<BuildErrorEventArgs>();
24+
25+
/// <summary>
26+
/// Stores the messages that were logged.
27+
/// </summary>
28+
private readonly List<BuildMessageEventArgs> _messageEvents = new List<BuildMessageEventArgs>(50);
29+
30+
/// <summary>
31+
/// Stores the warnings that were logged.
32+
/// </summary>
33+
private readonly List<BuildWarningEventArgs> _warningEvents = new List<BuildWarningEventArgs>();
34+
35+
/// <summary>
36+
/// Stores all build events.
37+
/// </summary>
38+
private ConcurrentQueue<BuildEventArgs> _allEvents = new ConcurrentQueue<BuildEventArgs>();
39+
40+
/// <summary>
41+
/// Initializes a new instance of the <see cref="BuildEventArgsCollection"/> class.
42+
/// </summary>
43+
protected BuildEventArgsCollection()
44+
{
45+
MessageEvents = new BuildMessageEventArgsCollection(_messageEvents);
46+
Messages = new BuildMessageCollection(this);
47+
}
48+
49+
/// <summary>
50+
/// Gets all events that were logged.
51+
/// </summary>
52+
public IReadOnlyCollection<BuildEventArgs> AllEvents => _allEvents;
53+
54+
/// <summary>
55+
/// Gets the error events that were logged.
56+
/// </summary>
57+
public IReadOnlyCollection<BuildErrorEventArgs> ErrorEvents => _errorEvents;
58+
59+
/// <summary>
60+
/// Gets the error messages that were logged.
61+
/// </summary>
62+
public IReadOnlyCollection<string> Errors => _errorEvents.Select(i => i.Message).ToList();
63+
64+
/// <summary>
65+
/// Gets the messages that were logged.
66+
/// </summary>
67+
public BuildMessageEventArgsCollection MessageEvents { get; }
68+
69+
/// <summary>
70+
/// Gets a <see cref="BuildMessageCollection"/> object that gets the messages from the build.
71+
/// </summary>
72+
public BuildMessageCollection Messages { get; }
73+
74+
/// <summary>
75+
/// Gets the warning events that were logged.
76+
/// </summary>
77+
public IReadOnlyCollection<BuildWarningEventArgs> WarningEvents => _warningEvents;
78+
79+
/// <summary>
80+
/// Gets the warning messages that were logged.
81+
/// </summary>
82+
public IReadOnlyCollection<string> Warnings => _warningEvents.Select(i => i.Message).ToList();
83+
84+
/// <inheritdoc cref="IDisposable.Dispose"/>
85+
public virtual void Dispose()
86+
{
87+
_errorEvents.Clear();
88+
_messageEvents.Clear();
89+
_warningEvents.Clear();
90+
_allEvents = null;
91+
}
92+
93+
/// <summary>
94+
/// Gets the current build output in the format of a console log.
95+
/// </summary>
96+
/// <param name="verbosity">The logger verbosity to use.</param>
97+
/// <returns>The build output in the format of a console log.</returns>
98+
public string GetConsoleLog(LoggerVerbosity verbosity = LoggerVerbosity.Normal)
99+
{
100+
StringBuilder sb = new StringBuilder(AllEvents.Count * 300);
101+
102+
ConsoleLogger logger = new ConsoleLogger(verbosity, message => sb.Append(message), color => { }, () => { });
103+
104+
foreach (BuildEventArgs buildEventArgs in AllEvents)
105+
{
106+
switch (buildEventArgs)
107+
{
108+
case BuildMessageEventArgs buildMessageEventArgs:
109+
logger.MessageHandler(logger, buildMessageEventArgs);
110+
break;
111+
112+
case BuildErrorEventArgs buildErrorEventArgs:
113+
logger.ErrorHandler(logger, buildErrorEventArgs);
114+
break;
115+
116+
case BuildWarningEventArgs buildWarningEventArgs:
117+
logger.WarningHandler(logger, buildWarningEventArgs);
118+
break;
119+
120+
case BuildStartedEventArgs buildStartedEventArgs:
121+
logger.BuildStartedHandler(logger, buildStartedEventArgs);
122+
break;
123+
124+
case BuildFinishedEventArgs buildFinishedEventArgs:
125+
logger.BuildFinishedHandler(logger, buildFinishedEventArgs);
126+
break;
127+
128+
case ProjectStartedEventArgs projectStartedEventArgs:
129+
logger.ProjectStartedHandler(logger, projectStartedEventArgs);
130+
break;
131+
132+
case ProjectFinishedEventArgs projectFinishedEventArgs:
133+
logger.ProjectFinishedHandler(logger, projectFinishedEventArgs);
134+
break;
135+
136+
case TargetStartedEventArgs targetStartedEventArgs:
137+
logger.TargetStartedHandler(logger, targetStartedEventArgs);
138+
break;
139+
140+
case TargetFinishedEventArgs targetFinishedEventArgs:
141+
logger.TargetFinishedHandler(logger, targetFinishedEventArgs);
142+
break;
143+
144+
case TaskStartedEventArgs taskStartedEventArgs:
145+
logger.TaskStartedHandler(logger, taskStartedEventArgs);
146+
break;
147+
148+
case TaskFinishedEventArgs taskFinishedEventArgs:
149+
logger.TaskFinishedHandler(logger, taskFinishedEventArgs);
150+
break;
151+
152+
case CustomBuildEventArgs customBuildEventArgs:
153+
logger.CustomEventHandler(logger, customBuildEventArgs);
154+
break;
155+
}
156+
}
157+
158+
return sb.ToString();
159+
}
160+
161+
/// <summary>
162+
/// Adds a build event.
163+
/// </summary>
164+
/// <param name="buildEventArgs">A <see cref="BuildEventArgs"/> object to add.</param>
165+
protected void Add(BuildEventArgs buildEventArgs)
166+
{
167+
_allEvents.Enqueue(buildEventArgs);
168+
169+
switch (buildEventArgs)
170+
{
171+
case BuildMessageEventArgs buildMessageEventArgs:
172+
_messageEvents.Add(buildMessageEventArgs);
173+
break;
174+
175+
case BuildWarningEventArgs buildWarningEventArgs:
176+
_warningEvents.Add(buildWarningEventArgs);
177+
break;
178+
179+
case BuildErrorEventArgs buildErrorEventArgs:
180+
_errorEvents.Add(buildErrorEventArgs);
181+
break;
182+
}
183+
}
184+
}
185+
}

0 commit comments

Comments
 (0)