-
Notifications
You must be signed in to change notification settings - Fork 748
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
first version of generator tool + msbuild task
- Loading branch information
1 parent
9be167e
commit 8a83666
Showing
14 changed files
with
521 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
using System; | ||
using System.Diagnostics; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Text; | ||
using TechTalk.SpecFlow.Generator.Configuration; | ||
|
||
namespace TechTalk.SpecFlow.Generator | ||
{ | ||
public class BatchGenerator | ||
{ | ||
private readonly TextWriter traceWriter; | ||
private readonly bool verboseOutput; | ||
|
||
public BatchGenerator(TextWriter traceWriter, bool verboseOutput) | ||
{ | ||
this.traceWriter = traceWriter; | ||
this.verboseOutput = verboseOutput; | ||
} | ||
|
||
public void ProcessProject(SpecFlowProject specFlowProject, bool forceGenerate) | ||
{ | ||
if (verboseOutput) | ||
traceWriter.WriteLine("Processing project: " + specFlowProject); | ||
|
||
SpecFlowGenerator generator = new SpecFlowGenerator(specFlowProject); | ||
|
||
foreach (var featureFile in specFlowProject.FeatureFiles) | ||
{ | ||
string featureFileFullPath = featureFile.GetFullPath(specFlowProject); | ||
|
||
string codeFileFullPath = featureFileFullPath + ".cs"; | ||
|
||
bool needsProcessing = true; | ||
|
||
if (!forceGenerate && IsUpToDate(generator, featureFileFullPath, codeFileFullPath)) | ||
{ | ||
needsProcessing = false; | ||
} | ||
|
||
if (verboseOutput || needsProcessing) | ||
{ | ||
traceWriter.WriteLine("Processing file: {0}", featureFileFullPath); | ||
if (!needsProcessing) | ||
traceWriter.WriteLine(" up-to-date"); | ||
} | ||
|
||
if (needsProcessing) | ||
{ | ||
GenerateFile(generator, featureFile, codeFileFullPath); | ||
} | ||
} | ||
} | ||
|
||
protected virtual void GenerateFile(SpecFlowGenerator generator, SpecFlowFeatureFile featureFile, string codeFileFullPath) | ||
{ | ||
using (var writer = GetWriter(codeFileFullPath)) | ||
{ | ||
generator.GenerateCSharpTestFile(featureFile, writer); | ||
} | ||
} | ||
|
||
protected virtual StreamWriter GetWriter(string codeFileFullPath) | ||
{ | ||
return new StreamWriter(codeFileFullPath, false, Encoding.UTF8); | ||
} | ||
|
||
private static bool IsUpToDate(SpecFlowGenerator generator, string featureFileFullPath, string codeFileFullPath) | ||
{ | ||
// check existance of the target file | ||
if (!File.Exists(codeFileFullPath)) | ||
return false; | ||
|
||
// check modification time of the target file | ||
try | ||
{ | ||
var featureFileModificationTime = File.GetLastWriteTime(featureFileFullPath); | ||
var codeFileModificationTime = File.GetLastWriteTime(codeFileFullPath); | ||
|
||
if (featureFileModificationTime > codeFileModificationTime) | ||
return false; | ||
} | ||
catch (Exception ex) | ||
{ | ||
Debug.WriteLine(ex); | ||
return false; | ||
} | ||
|
||
// check tools version | ||
var codeFileVersion = generator.GetGeneratedFileSpecFlowVersion(codeFileFullPath); | ||
if (codeFileVersion == null || codeFileVersion != generator.GetCurrentSpecFlowVersion()) | ||
return false; | ||
|
||
return true; | ||
} | ||
} | ||
} |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
using System; | ||
using System.CodeDom.Compiler; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Text; | ||
using Microsoft.Build.Framework; | ||
using TechTalk.SpecFlow.Generator; | ||
using TechTalk.SpecFlow.Generator.Configuration; | ||
|
||
namespace TechTalk.SpecFlow.Tools.MsBuild | ||
{ | ||
internal class MsBuildBatchGenerator : BatchGenerator | ||
{ | ||
private readonly GeneratorTaskBase task; | ||
|
||
public MsBuildBatchGenerator(TextWriter traceWriter, bool verboseOutput, GeneratorTaskBase task) : base(traceWriter, verboseOutput) | ||
{ | ||
this.task = task; | ||
} | ||
|
||
private GeneratorTaskBase.OutputFile outputFile = null; | ||
|
||
protected override StreamWriter GetWriter(string codeFileFullPath) | ||
{ | ||
outputFile = task.PrepareOutputFile(codeFileFullPath); | ||
|
||
return base.GetWriter(outputFile.FilePathForWriting); | ||
} | ||
|
||
protected override void GenerateFile(SpecFlowGenerator generator, SpecFlowFeatureFile featureFile, string codeFileFullPath) | ||
{ | ||
try | ||
{ | ||
base.GenerateFile(generator, featureFile, codeFileFullPath); | ||
outputFile.Done(task.Errors); | ||
} | ||
catch(Exception ex) | ||
{ | ||
task.RecordException(ex); | ||
} | ||
finally | ||
{ | ||
outputFile = null; | ||
} | ||
} | ||
} | ||
|
||
public class GeneratorTask : GeneratorTaskBase | ||
{ | ||
public bool VerboseOutput { get; set; } | ||
public bool ForceGeneration { get; set; } | ||
|
||
[Required] | ||
public string ProjectPath { get; set; } | ||
|
||
protected override void DoExecute() | ||
{ | ||
SpecFlowProject specFlowProject = MsBuildProjectReader.LoadSpecFlowProjectFromMsBuild(ProjectPath); | ||
|
||
BatchGenerator batchGenerator = new MsBuildBatchGenerator(GetMessageWriter(), VerboseOutput, this); | ||
batchGenerator.ProcessProject(specFlowProject, ForceGeneration); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
using System; | ||
using System.CodeDom.Compiler; | ||
using System.Diagnostics; | ||
using System.IO; | ||
using System.Linq; | ||
|
||
namespace TechTalk.SpecFlow.Tools.MsBuild | ||
{ | ||
public abstract class GeneratorTaskBase : TaskBase | ||
{ | ||
public bool BuildServerMode { get; set; } | ||
public bool OverwriteReadOnlyFiles { get; set; } | ||
|
||
public class OutputFile | ||
{ | ||
public string FilePath { get; private set; } | ||
|
||
public virtual string FilePathForWriting | ||
{ | ||
get { return FilePath; } | ||
} | ||
|
||
public OutputFile(string filePath) | ||
{ | ||
FilePath = filePath; | ||
} | ||
|
||
public virtual void Done(CompilerErrorCollection result) | ||
{ | ||
// nop; | ||
} | ||
} | ||
|
||
public class VerifyDifferenceOutputFile : OutputFile | ||
{ | ||
public string TempFilePath { get; private set; } | ||
|
||
public override string FilePathForWriting | ||
{ | ||
get { return TempFilePath; } | ||
} | ||
|
||
public VerifyDifferenceOutputFile(string filePath) : base(filePath) | ||
{ | ||
TempFilePath = Path.Combine(Path.GetTempPath(), "tmp" + Path.GetFileName(filePath)); | ||
} | ||
|
||
public override void Done(CompilerErrorCollection result) | ||
{ | ||
Compare(TempFilePath, FilePath, result); | ||
} | ||
|
||
private void Compare(string path1, string path2, CompilerErrorCollection result) | ||
{ | ||
if (FileCompare(path1, path2)) | ||
return; | ||
|
||
string message = String.Format("Error during file generation. The target file '{0}' is read-only, but different from the transformation result. This problem can be a sign of an inconsistent source code package. Compile and check-in the current version of the file from the development environment or remove the read-only flag from the generation result. To compile a solution that contains messaging project on a build server, you can also exclude the messaging project from the build-server solution or set the <OverwriteReadOnlyFiles> msbuild project parameter to 'true' in the messaging project file.", Path.GetFullPath(path2)); | ||
result.Add(new CompilerError(String.Empty, 0, 0, null, message)); | ||
} | ||
} | ||
|
||
public OutputFile PrepareOutputFile(string outputFilePath) | ||
{ | ||
if (OverwriteReadOnlyFiles) | ||
{ | ||
RemoveReadOnly(outputFilePath); | ||
} | ||
|
||
bool isReadOnly = IsReadOnly(outputFilePath); | ||
if (isReadOnly && BuildServerMode) | ||
return new VerifyDifferenceOutputFile(outputFilePath); | ||
|
||
return new OutputFile(outputFilePath); | ||
} | ||
|
||
// This method accepts two strings the represent two files to | ||
// compare. A return value of true indicates that the contents of the files | ||
// are the same. A return value of any other value indicates that the | ||
// files are not the same. | ||
private static bool FileCompare(string filePath1, string filePath2) | ||
{ | ||
int file1byte; | ||
int file2byte; | ||
|
||
// Determine if the same file was referenced two times. | ||
if (string.Equals(filePath1, filePath2, StringComparison.CurrentCultureIgnoreCase)) | ||
{ | ||
// Return true to indicate that the files are the same. | ||
return true; | ||
} | ||
|
||
// Open the two files. | ||
using (FileStream fs1 = new FileStream(filePath1, FileMode.Open, FileAccess.Read)) | ||
using (FileStream fs2 = new FileStream(filePath2, FileMode.Open, FileAccess.Read)) | ||
{ | ||
// Check the file sizes. If they are not the same, the files | ||
// are not the same. | ||
if (fs1.Length != fs2.Length) | ||
{ | ||
// Return false to indicate files are different | ||
return false; | ||
} | ||
|
||
// Read and compare a byte from each file until either a | ||
// non-matching set of bytes is found or until the end of | ||
// file1 is reached. | ||
do | ||
{ | ||
// Read one byte from each file. | ||
file1byte = fs1.ReadByte(); | ||
file2byte = fs2.ReadByte(); | ||
} while ((file1byte == file2byte) && (file1byte != -1)); | ||
} | ||
|
||
// Return the success of the comparison. "file1byte" is | ||
// equal to "file2byte" at this point only if the files are | ||
// the same. | ||
return ((file1byte - file2byte) == 0); | ||
} | ||
|
||
private bool IsReadOnly(string path) | ||
{ | ||
try | ||
{ | ||
FileInfo fileInfo = new FileInfo(path); | ||
if (!fileInfo.Exists) | ||
return false; | ||
return fileInfo.IsReadOnly; | ||
|
||
} | ||
catch (Exception ex) | ||
{ | ||
Debug.WriteLine(ex, "IsReadOnly"); | ||
|
||
// if there is an exception, we let the generation to discover the real problem | ||
return false; | ||
} | ||
} | ||
|
||
private void RemoveReadOnly(string path) | ||
{ | ||
FileInfo fileInfo = new FileInfo(path); | ||
if (fileInfo.Exists && fileInfo.IsReadOnly) | ||
fileInfo.IsReadOnly = false; | ||
} | ||
} | ||
} |
Oops, something went wrong.