I have console application which launches some tests during build.
<exec>
<executable>Tests.exe</executable>
<baseDirectory>Q:\Software\Platform\</baseDirectory>
<buildTimeoutSeconds>100</buildTimeoutSeconds>
</exec>
And i want to redirect it's output to text file and then include it in my report file.
I tried the following approach to redirect output to NativeUtilsTestReport.txt (like in command line somefile.exe > file.txt):
<exec>
<executable>Tests.exe</executable>
<baseDirectory>Q:\Software\Platform\</baseDirectory>
<buildArgs> > NativeUtilsTestReport.txt</buildArgs>
<buildTimeoutSeconds>100</buildTimeoutSeconds>
</exec>
but it doesn't seem to work. Tests works fine, but there is no text file with report.
How can i get output from this "exec" section?
You cannot use < or > in xml. I am able to redirect output like this.
<exec>
<executable>Tests.exe</executable>
<baseDirectory>Q:\Software\Platform\</baseDirectory>
<buildArgs> > NativeUtilsTestReport.txt</buildArgs>
<buildTimeoutSeconds>100</buildTimeoutSeconds>
</exec>
Then you can include the output into your log using the merge task.
<merge>
<files>
<file>NativeUtilsTestReport.txt</file>
</files>
</merge>
I would also recommend that you save your output to the artifact directory.
$(ArtifactDirectory)\NativeUtilsTestReport.txt
I'll have to use a metaphor.
NUnit it a testing program.
So when you run
nunit-console.exe SomeDll.dll "c:\somefolder\NUnitTest_RESULTS.xml"
The console app itself will outsome some XML....with a specific name.
So even if get an output xml file, this doesn't do anything for CC.NET.
You use a CC.NET file-merge task to "suck up" the NUnitTest_RESULTS.xml into the "ThisBuildCCNet_SuperDuperXmlFile.xml". (That's not the name, that is the concept).
And that doesn't do anything really.
CC.NET has "NUnit" installed, which is a link to an xsl file.
So after NUnitTest_RESULTS.xml is created AND NUnitTest_RESULTS.xml is "sucked up" into the super-duper.xml file AND you have the NUnit-Xsl enabled, when you click on the NUnit menu item (on the webpage of your project), CC.NET will take the nunit.xsl and apply it to the xml that now exists in the super-duper.xml file.
Aka, a basic xsl-xml transformation.
So to your question, if you want your console app to run tests, it should be able to output XML to a file. If it can't do that, then you're d3ad in the water.
If you get xml, then you have to use the file-merge task to "suck it up", and you have to author or find an xsl file to transform it.
.......
EDIT
If you cannot generate xml or hmtl (via htmlReportPlugin )
then I suggest this article:
http://testquest.docs.bsquare.com/Integration_Samples_CCNet/Configuring_CCNet_dashboard_to_Display_Test_Execution_Logs.htm
I ended up with writing my own ExecutableTask for cruise control, which reads output from CCnet IIntegrationResult.TaskResults and writes it to file.
Application writes to output plain text, but CCnet transforms it to following XML:
<buildresults>
<task><buildArgs />
<buildTimeoutSeconds>100</buildTimeoutSeconds>
<baseDirectory>Q:\software\Platform\</baseDirectory>
<dynamicValues />
<environment />
<executable>NativeUtils.Tests.exe</executable>
<OutputFilePath>Q:\software\NativeUtilsTestReport.txt</OutputFilePath>
<priority>Normal</priority>
<successExitCodes />
</task>
<message>Running 50 test cases...</message>
</buildresults>
<buildresults>
<task><buildArgs />
<buildTimeoutSeconds>100</buildTimeoutSeconds>
<baseDirectory>Q:\software\Platform\</baseDirectory>
<dynamicValues />
<environment />
<executable>NativeUtils.Tests.exe</executable>
<OutputFilePath>Q:\software\NativeUtilsTestReport.txt</OutputFilePath>
<priority>Normal</priority>
<successExitCodes />
</task>
<message level="Error">*** No errors detected</message>
</buildresults>
Here is code:
[ReflectorType("execWithOutput")]
class ExecutableTestTask:ExecutableTask
{
[ReflectorProperty("OutputFilePath", Required = false)]
public string OutputFilePath { get; set; }
protected override bool Execute(ThoughtWorks.CruiseControl.Core.IIntegrationResult result)
{
bool rez = base.Execute(result);
if (!string.IsNullOrEmpty(OutputFilePath))
{
using (FileStream fs = new FileStream(OutputFilePath, FileMode.Create, FileAccess.Write, FileShare.Read))
{
using (StreamWriter sw = new StreamWriter(fs))
{
var lastIndex = result.TaskResults.Count-1;
// read output from last executed task
var output = ((ProcessTaskResult)result.TaskResults[lastIndex]).Data;
string parsedOutput = readMessagesFromXml(output);
sw.Write(parsedOutput);
}
}
}
return rez;
}
// parse xml
private string readMessagesFromXml(string xml)
{
StringBuilder sb = new StringBuilder();
try
{
// wrap output to into single root node
var xmlWithRootNode = string.Format("<report>{0}</report>", xml);
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlWithRootNode);
var nodes = doc.SelectNodes("//report/buildresults/message");
if (nodes.Count > 0)
{
foreach (XmlNode node in nodes)
{
sb.AppendLine("<div>" + node.InnerText + "</div>");
}
}
else
{
sb.AppendLine("Xml output does not contain valid data or there are no messages");
}
}
catch (Exception ex)
{
sb.AppendLine("Exception during parsing XML task output. "+ex.ToString());
}
return sb.ToString();
}
}
And then i call it during build:
<execWithOutput>
<executable>NativeUtils.Tests.exe</executable>
<baseDirectory>Q:\software\Platform\</baseDirectory>
<OutputFilePath>Q:\software\NativeUtilsTestReport.txt</OutputFilePath>
<buildTimeoutSeconds>100</buildTimeoutSeconds>
</execWithOutput>
Related
I created a CLI application which actually uses the standard config app.Config file.
In this file I put some subsections, like
<typicsTable>
<mainSettings>
<add key="sheetNumber" value="1"/>
<add key="firstDataRow" value="2"/>
</mainSettings>
</typicsTable>
I actually read these settings with
NameValueCollection TypicsConversionTableSettings = (NameValueCollection)ConfigurationManager.GetSection("typicsTable/mainSettings");
int ctSheetNumber = Int32.Parse(TypicsConversionTableSettings["sheetNumber"]);
int ctFirstDataRow = Int32.Parse(TypicsConversionTableSettings["firstDataRow"]);
Everything works fine in this way.
What I want to do now is
1) I want different config files with custom names (i.e. test1.config , test2.config) and take via CLI the right config file;
2) switch to a less ".net config file", and take data from a standard XML file.
I'm now focusing on the point 1, I tried different attempts, I used
ExeConfigurationFileMap configFileMap = new ExeConfigurationFileMap();
configFileMap.ExeConfigFilename = #"C:\folderTest\conf1.config";
Configuration config = ConfigurationManager.OpenMappedExeConfiguration(configFileMap, ConfigurationUserLevel.None);
But I absolutely don't get how to read sections AND subsections in the file. How can I do that?
The class that is going to help you, I believe, is System.Xml.Linq.
using System.Xml.Linq;
So Part 1 would be load the file into an XElement:
XElement xConfig = XElement.Load("app.simulated.config");
Here's a quick demo of how you can iterate through everything and also find a single element using a matching condition.
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Iterating the config file values and attributes...");
Console.WriteLine("==================================================");
XElement xConfig = XElement.Load("app.simulated.config");
foreach (var element in xConfig.DescendantsAndSelf())
{
Console.WriteLine(element.Name);
foreach (var attribute in element.Attributes())
{
Console.WriteLine("\t" + attribute.Name + "," + attribute.Value);
}
}
Console.WriteLine();
Console.WriteLine("Finding a value using matching conditions.");
Console.WriteLine("==========================================");
XElement xel =
xConfig
.DescendantsAndSelf()
.FirstOrDefault(match =>
(match.Attribute("key") != null) &&
(match.Attribute("key").Value == "sheetNumber"));
Console.WriteLine(
"The value of 'sheetNumber' is " +
xel.Attribute("value").Value
);
// Pause
Console.ReadKey();
}
}
Clone or Download this example from GitHub.
I'm working on an ASP.NET project that has several web.config transformations that are generated at build time (all at once, using MsBuild); one .config file for each deployment environment.
EX:
<Target Name="BeforeBuild">
<TransformXml
Source="Web.Base.config"
Transform="Web.DevServer1.config"
Destination="ConfigBuild\Web.DevServer1.config" />
<TransformXml
Source="Web.Base.config"
Transform="Web.QAServer1.config"
Destination="ConfigBuild\Web.QAServer1.config" />
<!-- ... -->
<!-- ... -->
</Target>
Each transformation has several elements whose values are substituted into the base web.config file. Management and I are concerned that a necessary element could potentially be overlooked by mistake in one of the transformation files.
Is there a way to automatically verify that none of our transformation files are missing any elements, using either MsBuild or some other VisualStudio tool?
Preferably, this check would be carried out at build time.
Is there a way to automatically verify that none of our transformation files are missing any elements, using either MsBuild or some other VisualStudio tool?
You can create a custom task, which compare the config file by using XmlDocument class, if they are different, use Log.LogMessage to output the node message. Like this:
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using System.Xml;
namespace Common
{
public class SimpleTask3 : Task
{
private string myProperty;
// The [Required] attribute indicates a required property.
// If a project file invokes this task without passing a value
// to this property, the build will fail immediately.
[Required]
public string MyProperty
{
get
{
return myProperty;
}
set
{
myProperty = value;
}
}
public override bool Execute()
{
// Log a high-importance comment
Log.LogMessage(MessageImportance.High, "The task was passed \"" + myProperty + "\"");
XmlDocument xDoc = new XmlDocument();
xDoc.Load(myProperty + "/web.base.config");
XmlDocument sDoc = new XmlDocument();
sDoc.Load(myProperty + "/ConfigBuild/Web.DevServer1.config");
//compare with them and check the different.
//if different
Log.LogMessage(MessageImportance.High, "different message");
return true;
}
}
}
Web.config:
<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v14.0\Web\Microsoft.Web.Publishing.Tasks.dll" />
<Target Name="BeforeBuild">
<!--<Message Text="BuildDependsOn: $(BuildDependsOn)" />-->
<Message Text="Inside of BeforeBuild, time: $([System.DateTime]::Now)" />
<TransformXml Source="D:\Project\Msbuild\App1\App2\Web.Base.config" Transform="D:\Project\Msbuild\App1\App2\Web.DevServer1.config" Destination="D:\Project\Msbuild\App1\App2\ConfigBuild\Web.DevServer1.config" />
<TransformXml Source="D:\Project\Msbuild\App1\App2\Web.Base.config" Transform="D:\Project\Msbuild\App1\App2\Web.QAServer1.config" Destination="D:\Project\Msbuild\App1\App2\ConfigBuild\Web.QAServer1.config" />
</Target>
<UsingTask TaskName="Common.SimpleTask3"
AssemblyFile="D:\Project\Msbuild\App1\Common\bin\Debug\Common.dll"/>
<Target Name="AfterBuild">
<SimpleTask3 MyProperty="D:\Project\Msbuild\App1\App2"/>
</Target>
I have a console program 'A' that at a given point will run program 'B' and program 'C'. However I'm having an issue with the app.config associate with each of the program. Basically program A is just a wrapper class that calls different console application, It should not have any app.config but it should use the current running program's app config. So in theory there should be only 2 app.config one for Program B and another for program C.
So if we run Program A and program B gets executed, it should use program B's app.config to get the information and after when program C gets executed it should use Program C's app.config.
Is there a way to do this? Currently i'm doing this:
var value = ConfigurationManager.OpenExeConfiguration(Assembly.GetExecutingAssembly().Location).AppSettings.Settings["ProgramBKey"].Value;
It does not seem to work. I checked the debug on Assembly.GetExecutingAssembly().Location it's variable is the \bin\Debug\ProgramB.exe and 'ConfigurationManager.OpenExeConfiguration(Assembly.GetExecutingAssembly().Location).AppSettings' has setting with Count=0 when there are key values as seen below.
sample code Program A:
static void Main(string[] args)
{
if(caseB)
B.Program.Main(args)
else if(caseC)
C.Program.Main(args)
}
sample app.config for Program B:
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="ProgramBKey" value="Works" />
</appSettings>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0" />
</startup>
</configuration>
Edit: the following answer pertains to this question from the original post, "Is it possible to compile the app.config for B and C within the exe of the program."
You can use the "Embedded Resource" feature. Here's a small example of using an XML file that's been included as an embedded resource:
public static class Config
{
static Config()
{
var doc = new XmlDocument();
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Fully.Qualified.Name.Config.xml"))
{
if (stream == null)
{
throw new EndOfStreamException("Failed to read Fully.Qualified.Name.Config.xml from the assembly's embedded resources.");
}
using (var reader = new StreamReader(stream))
{
doc.LoadXml(reader.ReadToEnd());
}
}
XmlElement aValue = null;
XmlElement anotherValue = null;
var config = doc["config"];
if (config != null)
{
aValue = config["a-value"];
anotherValue = config["another-value"];
}
if (aValue == null || anotheValue == null)
{
throw new XmlException("Failed to parse Config.xml XmlDocument.");
}
AValueProperty = aValue.InnerText;
AnotherValueProperty = anotherValue.InnerText;
}
}
You can have multiple application using the same config file. That way when you switch applications, they can both find their own parts of the config file.
The way I usually do it is... first let each application "do its own thing", then copy the relevant sections of config file A into config file B.
It will look like this:
<configSections>
<sectionGroup>
<sectionGroup name="applicationSettings"...A>
<sectionGroup name="userSettings"...A>
<sectionGroup name="applicationSettings"...B>
<sectionGroup name="userSettings"...B>
<applicationSettings>
<A.Properties.Settings>
<B.Properties.Settings>
<userSettings>
<A.Properties.Settings>
<B.Properties.Settings>
For me, the whole thing sounds like a "design issue". Why should you want to open Programm B with the config of Programm A?
Are you the author of all those Programms? You might want to use a dll-file instead. This will save you the trouble as all code runs with the config of the Programm running.
Here how you can do it:
Make App.config as "Embedded Resource" in the properties/build action
Copy to output Directory : Do not copy
Add this code to Proram.cs Main
if (!File.Exists(Application.ExecutablePath + ".config"))
{
File.WriteAllBytes(Application.ExecutablePath + ".config", ResourceReadAllBytes("App.config"));
Process.Start(Application.ExecutablePath);
return;
}
Here are the needed functions:
public static Stream GetResourceStream(string resName)
{
var currentAssembly = Assembly.GetExecutingAssembly();
return currentAssembly.GetManifestResourceStream(currentAssembly.GetName().Name + "." + resName);
}
public static byte[] ResourceReadAllBytes(string resourceName)
{
var file = GetResourceStream(resourceName);
byte[] all;
using (var reader = new BinaryReader(file))
{
all = reader.ReadBytes((int)file.Length);
}
file.Dispose();
return all;
}
How can I author a WiX custom action that
Is always called at the end of an installation, at least if there's an install error
Copies the current MSI log file from its current local to the user's APPDATA folder
I have this managed custom action code. Not sure how to author its invocation in my Wix script. Should the custom action be scheduled for after InstallFinalize? Can it be scheduled OnExit="error"?
[CustomAction]
public static void CopyLogFile(Session session)
{
const string company = "MyCompany";
const string product = "MyProduct";
try
{
session.Log("CustomAction.CopyLogFile entry");
var msiLogFilePath = session.CustomActionData["LOGFILEPATH"];
if (msiLogFilePath != null)
{
session.Log("CustomAction.CopyLogFile MSI log filename: {0}", msiLogFilePath);
var localAppDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var destDirPath = Path.Combine(localAppDataPath, company, product);
var destDir = Directory.CreateDirectory(destDirPath);
session.Log("CustomAction.CopyLogFile Destination directory: {0}", destDir.FullName);
var destFilePath = Path.Combine(destDir.FullName, Path.GetFileName(msiLogFilePath));
File.Copy(msiLogFilePath, destFilePath, true);
session.Log("CustomAction.CopyLogFile Log file copied to: {0}", destFilePath);
}
else
{
session.Log("CustomAction.CopyLogFile File path not found");
}
}
catch (Exception exception)
{
session.Log("CustomAction.CopyLogFile exception {0}", exception);
}
finally
{
if (session != null)
{
session.Log("CustomAction.CopyLogFile exit");
session.Close();
}
}
}
Yes, you can schedule it after InstallFinalize (so did I, but I copy it every time if it is not a complete removal of the package):
<InstallExecuteSequence>
<Custom Action="CopyLogfile" After="InstallFinalize">NOT (REMOVE="ALL" AND NOT UPGRADINGPRODUCTCODE)</Custom>
</InstallExecuteSequence>
Remember to also add it to the UI sequence if you have any. I added it as event to the PushButton in the SetupCompleteSuccess- and SetupCompleteError-dialogs (maybe you need to add it only to the latter one?) like in the following:
<Dialog Id="SetupCompleteSuccess" X="50" Y="50" Width="374" Height="266" Title="[ProductName]" NoMinimize="yes">
<Control Id="OK" Type="PushButton" X="230" Y="243" Width="66" Height="17" Text="&Finish" TabSkip="no" Default="yes" Cancel="yes">
<Publish Event="EndDialog" Value="Exit">1</Publish>
<!-- ### Invoking copying the logfile if the Finish-button is pressed -->
<Publish Event="DoAction" Value="CopyLogfile">MsiLogFileLocation AND NOT (REMOVE="ALL" AND NOT UPGRADINGPRODUCTCODE)</Publish>
</Control>
<!-- ... rest of the dialog ... -->
</Dialog>
Regarding showing it only in case of an error: Maybe checking for the ProductState-properrty? Searched the web for this but didn't find anything useful.
Edit: Maybe a proper way to execute it only in case of an error is the usage of Custom Action flags that execute it only during rollback. In WiX it would be the Execute="rollback"-attribute for the CustomAction-tag.
Don't mess with custom actions after InstallFinalize if you can help it. They are often skipped entirely when deploying via tools such as SCCM, Unicenter, etc...
A custom dialog with a button to open the log file at the end of the install could work ok though.
is there any tool to validate configuration file?
Well, basically your app is the validator - if the config file is not valid, you'll get an exception when starting up. Other than that, I'm not aware of any out-of-the-box validation support for app.config files.
In your directory C:\Program Files\Microsoft Visual Studio 9.0\Xml\Schemas, you'll find some files called DotNetConfig.xsd / DotNetConfig20.xsd - those are Microsoft-supplied XML schema files, which you could easily use to validate any other config files you might have for validity.
The basic structure for programmatically validating your configs would be something like this:
using(StreamReader xsdReader = new StreamReader(xsdFileName))
{
XmlSchema Schema = new XmlSchema();
Schema = XmlSchema.Read(xsdReader, new ValidationEventHandler(XSDValidationEventHandler));
XmlReaderSettings ReaderSettings = new XmlReaderSettings();
ReaderSettings.ValidationType = ValidationType.Schema;
ReaderSettings.Schemas.Add(Schema);
ReaderSettings.ValidationEventHandler += new ValidationEventHandler(XMLValidationEventHandler);
using(XmlTextReader xmlReader = new XmlTextReader(xmlFileName))
{
XmlReader objXmlReader = XmlReader.Create(xmlReader, ReaderSettings);
while (objXmlReader.Read())
{ }
}
}
Console.WriteLine("Successful validation completed!");
What you need to do now is supply event handlers for those event that get raised when something in the validation goes wrong - that's about it! :-)
Very old question but I had the same question and here is my setup (.net framework 3.5 and up):
I created a console project named 'ConfigurationValidator' :
static void Main(string[] args)
{
try
{
string xsdFileName = ConfigurationManager.AppSettings["configXsdPath"];
string xmlFileName = args[0];
XmlSchemaSet schemas = new XmlSchemaSet();
schemas.Add(null, xsdFileName);
XDocument doc = XDocument.Load(xmlFileName);
string validationMessage = string.Empty;
doc.Validate(schemas, (sender, e) => { validationMessage += e.Message + Environment.NewLine; });
if (validationMessage == string.Empty)
{
Console.WriteLine("CONFIG FILE IS VALID");
}
else
{
Console.WriteLine("CONFIG FILE IS INVALID : {0}", validationMessage);
}
}
catch(Exception ex)
{
Console.WriteLine("EXCEPTION VALIDATING CONFIG FILE : {0}", ex.Message);
}
}
and the following app.config :
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="configXsdPath" value="C:\Program Files (x86)\Microsoft Visual Studio 11.0\Xml\Schemas\DotNetConfig35.xsd"/>
</appSettings>
</configuration>
For each project of the solution add post build event command, for example :
when validation succeeds i get the following output :
when validation fails i get an output like so: