[Test] public void BinaryWithContents() { RepositoryFile file = new RepositoryFile(repository, BINARY_WITH_CONTENTS_PATH, RepositoryStatus.Unchanged, RepositoryStatus.Unchanged); Stream stream = file.GetContents(); }
[Test] public void CSharpMimeSet() { RepositoryFile file = new RepositoryFile(repository, CSHARP_PATH, RepositoryStatus.Unchanged, RepositoryStatus.Unchanged); Assert.AreEqual("text/cs", file.MimeType, "RepositoryFile.MimeType does not return the svn:mime-type"); Assert.IsTrue(file.IsText, "RepositoryFile.IsText does not return true when svn:mime-type starts with \"text/\""); }
[Test] public void NoMimeTypeSet() { RepositoryFile file = new RepositoryFile(repository, NO_MIME_PATH, RepositoryStatus.Unchanged, RepositoryStatus.Unchanged); Assert.AreEqual("text/plain", file.MimeType, "RepositoryFile.MimeType does not return \"text/plain\" when svn:mime-type is not set"); Assert.IsTrue(file.IsText, "RepositoryFile.IsText does not return true when svn:mime-type is not set"); }
[Test] public void Deleted() { using(RepositoryFile file = new RepositoryFile(new MockRepository(null), "File.cs", RepositoryStatus.Deleted, RepositoryStatus.Unchanged)) { Error[] errors = hook.PreCommit(file); Assert.AreEqual(0, errors.Length); } }
[Test] public void Contents() { RepositoryFile file = new RepositoryFile(repository, CONTENTS_PATH, RepositoryStatus.Unchanged, RepositoryStatus.Unchanged); using(Stream stream = file.GetContents()) using(TextReader reader = new StreamReader(stream)) { Assert.AreEqual("This is the first line", reader.ReadLine()); Assert.AreEqual("Second Line...", reader.ReadLine()); Assert.AreEqual("And the third", reader.ReadLine()); Assert.AreEqual("", reader.ReadLine()); Assert.AreEqual("Final line!", reader.ReadLine()); Assert.AreEqual(null, reader.ReadLine()); } }
private static bool HooksAllow(RepositoryFile file, IPreCommit[] hooks) { bool hooksOK = true; foreach (IPreCommit hook in hooks) { Error[] errors = hook.PreCommit(file); if(errors != null && errors.Length > 0) { hooksOK = false; Console.Error.WriteLine("An error occured in \"{0}\"", file); foreach(Error error in errors) { Console.Error.WriteLine(error.Description); } } } return hooksOK; }
private static bool HooksAllow(RepositoryFile file, IPreCommit[] hooks) { bool hooksOK = true; foreach (IPreCommit hook in hooks) { Error[] errors = hook.PreCommit(file); if (errors != null && errors.Length > 0) { hooksOK = false; Console.Error.WriteLine("An error occured in \"{0}\"", file); foreach (Error error in errors) { Console.Error.WriteLine(error.Description); } } } return(hooksOK); }
public Error[] PreCommit(RepositoryFile file) { // Check files that have the proper extension // but skip the files that are deleted if (file.ContentsStatus == RepositoryStatus.Deleted || Array.IndexOf(extensions, file.Extension) == -1) { return Error.NoErrors; } ArrayList errors = new ArrayList(); String[] props = file.GetProperty("svn:eol-style"); if (props == null || props.Length == 0 || props[0].ToLower() != "native") { return new Error[]{new Error(file, "This file must have svn:eol-style set to native")}; } return Error.NoErrors; }
public Error[] PreCommit(RepositoryFile file) { // Check files that have the proper extension // but skip the files that are deleted if (file.ContentsStatus == RepositoryStatus.Deleted || Array.IndexOf(extensions, file.Extension) == -1) { return Error.NoErrors; } // Skip AssemblyInfo.cs, it is a special // case file and should not be necessary // to add the header to. I think? if(file.FileName == "AssemblyInfo.cs") return Error.NoErrors; ArrayList errors = new ArrayList(); using(Stream stream = file.GetContents()) using(TextReader reader = new StreamReader(stream)) { String line; int index = 0; while ((line = reader.ReadLine()) != null && index < LINES.Length) { line = Trim(line); if (line == String.Empty) { if(index != 0) { AddError(errors, file, "Blank lines are not allowed in the Apache License 2.0 header"); } continue; } if (line != LINES[index]) { if(index == 0) { AddError(errors, file, "No text or code is allowed before the Apache License 2.0 header"); continue; } AddError(errors, file, "Apache License 2.0 header has errors on line " + (index+1)); } index++; } // if index is still at 0 we havent found the // header at all, clear the errors and add // that as error. if(index == 0) { errors.Clear(); AddError(errors, file, "Apache License 2.0 header is missing or there are errors on the first line"); } } return (Error[])errors.ToArray(typeof(Error)); }
private static void AddError(IList errors, RepositoryFile file, String description) { Error error = new Error(file, description); if (!errors.Contains(error)) errors.Add(error); }
public Error(RepositoryFile file, String description) { this.file = file; this.description = description; }
[Test] public void FileNameUnderRoot() { RepositoryFile file = new RepositoryFile(repository, "/file.txt", RepositoryStatus.Unchanged, RepositoryStatus.Unchanged); Assert.AreEqual("file.txt", file.FileName, "RepositoryFile.FileName is not set correctly"); }
[Test] public void ConstructedWithDirectoryPath() { RepositoryFile file = new RepositoryFile(repository, "trunk/", RepositoryStatus.Unchanged, RepositoryStatus.Unchanged); }
[Test] public void TextSlashMimeSet() { RepositoryFile file = new RepositoryFile(repository, TEXT_SLASH_PATH, RepositoryStatus.Unchanged, RepositoryStatus.Unchanged); Assert.IsTrue(file.IsText, "RepositoryFile.IsText does not return true when svn:mime-type is set to only \"text/\""); }
[SetUp] public void SetUp() { count = 0; // this line has to be before the RepositoryFile line file = new RepositoryFile(this, "file", RepositoryStatus.Unchanged, RepositoryStatus.Unchanged); }
[Test] public void ConstructedWithEmptyPath() { RepositoryFile file = new RepositoryFile(repository, String.Empty, RepositoryStatus.Unchanged, RepositoryStatus.Unchanged); }
[Test] public void Extension() { RepositoryFile file = new RepositoryFile(repository, "file.extension", RepositoryStatus.Unchanged, RepositoryStatus.Unchanged); Assert.AreEqual("extension", file.Extension, "RepositoryFile.Extension is not set properly"); }
[Test] public void ExtensionNonExistantWithTrailingDot() { RepositoryFile file = new RepositoryFile(repository, "file.", RepositoryStatus.Unchanged, RepositoryStatus.Unchanged); Assert.AreEqual(String.Empty, file.Extension, "RepositoryFile.Extension is not String.Empty when file does not have an extension"); }
[Test] public void FileNameUnderRootWithLeadingSpaces() { RepositoryFile file = new RepositoryFile(repository, " file", RepositoryStatus.Unchanged, RepositoryStatus.Unchanged); }
[Test] public void FileNameWithTrailingSpaces() { RepositoryFile file = new RepositoryFile(repository, "trunk/file ", RepositoryStatus.Unchanged, RepositoryStatus.Unchanged); }
[Test] public void FileNameWithExtension() { RepositoryFile file = new RepositoryFile(repository, "file.ext", RepositoryStatus.Unchanged, RepositoryStatus.Unchanged); Assert.AreEqual("file.ext", file.FileName, "RepositoryFile.FileName is not set correctly"); }
static int Main(string[] args) { try { // Fetch paths and transaction from configuration and arguments String svnlookPath = ConfigurationSettings.AppSettings["svnlook.location"]; String repositoryPath = args[0]; String transactionString = args[1]; Transaction transaction = Transaction.Parse(transactionString); // Initialize a WindsorContainer with all the relevant hooks // and repository. WindsorContainer container = new WindsorContainer(new XmlInterpreter(new AppDomainConfigSource("castle"))); IRepository repository = new DefaultRepository( svnlookPath, repositoryPath, transaction); container.Kernel.AddComponentInstance("Repository", typeof(IRepository), repository); // Fetch all the hooks from the container, if we get none // we dont need to process the files in the transaction // and can simply exit the program with success at this point. IPreCommit[] hooks = FetchHooks(container.Kernel); if (hooks.Length == 0) return 0; // Start processing all the files commited and run the hooks // on them. This is done by executing a changed command on // svnlook and parsing the result for the files/directories. bool hooksOK = true; Regex lineRegex = new Regex("^(?<contents>[AUD_])(?<properties>[U ]) +(?<file>[^ ].*) *$"); SvnLook svnLook = new SvnLook(svnlookPath, repositoryPath); using(Process process = svnLook.Execute(SvnLookCommand.Changed, transaction, null)) { String line; while((line = process.StandardOutput.ReadLine()) != null) { Match m = lineRegex.Match(line); if(!m.Success) { Console.Error.WriteLine("Could not match line: " + line); return 3; } if(m.Groups["file"].Value.EndsWith("/")) { // This is a directory, not a file. // TODO: Add directory handling } else { RepositoryStatus contentsStatus; RepositoryStatus propertiesStatus; switch (m.Groups["contents"].Value) { case "A": contentsStatus = RepositoryStatus.Added; break; case "U": contentsStatus = RepositoryStatus.Updated; break; case "D": contentsStatus = RepositoryStatus.Deleted; break; case "_": contentsStatus = RepositoryStatus.Unchanged; break; default: Console.Error.WriteLine("Could not match status flags for contents on line: " + line); return 3; } switch (m.Groups["properties"].Value) { case "U": propertiesStatus = RepositoryStatus.Updated; break; case " ": propertiesStatus = RepositoryStatus.Unchanged; break; default: Console.Error.WriteLine("Could not match status flags for properties on line: " + line); return 3; } using(RepositoryFile file = new RepositoryFile(repository, m.Groups["file"].Value, contentsStatus, propertiesStatus)) { // If HooksAllow returns false we should not allow // the transaction, but rather than exiting we store // that fact in a boolean so that the user will be // told all errors his files contain rather than just // one at a time. hooksOK &= HooksAllow(file, hooks); } } } if (!hooksOK) return 1; } return 0; } catch(Exception e) { Console.Error.WriteLine("An uncaught exception was thrown in the hook implementation"); Console.Error.WriteLine(e.Message); return 2; } }
/// <summary> /// This test is to counter a bug in SetPathRelatedFields /// in RepositoryFile, it used path.IndexOf rather than /// filename when gathering the extension, which broke /// on real paths. /// </summary> [Test] public void LongPathWithExtension() { RepositoryFile file = new RepositoryFile(repository, "trunk/Castle/File.cs", RepositoryStatus.Unchanged, RepositoryStatus.Unchanged); Assert.AreEqual("cs", file.Extension, "RepositoryFile.Extension is not set properly"); }
/// <summary> /// This test ensures that a mime type beginning /// with "text" but not having a slash after text /// does not register as a text file; /// Subversion only considers a file a text /// file if it begins with "text/". /// </summary> [Test] public void TextingMimeSet() { RepositoryFile file = new RepositoryFile(repository, TEXTING_PATH, RepositoryStatus.Unchanged, RepositoryStatus.Unchanged); Assert.IsFalse(file.IsText, "RepositoryFile.IsText does not return false when svn:mime-type begins with only \"text\""); }
[Test] public void ConstructedWithNullPath() { RepositoryFile file = new RepositoryFile(repository, null, RepositoryStatus.Unchanged, RepositoryStatus.Unchanged); }
[Test] public new void ToString() { RepositoryFile file = new RepositoryFile(repository, "trunk/Castle/file.txt", RepositoryStatus.Unchanged, RepositoryStatus.Unchanged); Assert.AreEqual("trunk/Castle/file.txt", file.ToString(), "RepositoryFile.ToString does not return the path"); }
[Test] public void ConstructedWithBlankPath() { RepositoryFile file = new RepositoryFile(repository, " ", RepositoryStatus.Unchanged, RepositoryStatus.Unchanged); }
[Test] public void BinaryMimeSet() { RepositoryFile file = new RepositoryFile(repository, BINARY_PATH, RepositoryStatus.Unchanged, RepositoryStatus.Unchanged); Assert.AreEqual("application/octet-stream", file.MimeType, "RepositoryFile.MimeType does not return the svn:mime-type"); Assert.IsFalse(file.IsText, "RepositoryFile.IsText does not return false when svn:mime-type does NOT start with \"text/\""); }
static int Main(string[] args) { try { // Fetch paths and transaction from configuration and arguments String svnlookPath = ConfigurationSettings.AppSettings["svnlook.location"]; String repositoryPath = args[0]; String transactionString = args[1]; Transaction transaction = Transaction.Parse(transactionString); // Initialize a WindsorContainer with all the relevant hooks // and repository. WindsorContainer container = new WindsorContainer(new XmlInterpreter(new AppDomainConfigSource("castle"))); IRepository repository = new DefaultRepository( svnlookPath, repositoryPath, transaction); container.Kernel.AddComponentInstance("Repository", typeof(IRepository), repository); // Fetch all the hooks from the container, if we get none // we dont need to process the files in the transaction // and can simply exit the program with success at this point. IPreCommit[] hooks = FetchHooks(container.Kernel); if (hooks.Length == 0) { return(0); } // Start processing all the files commited and run the hooks // on them. This is done by executing a changed command on // svnlook and parsing the result for the files/directories. bool hooksOK = true; Regex lineRegex = new Regex("^(?<contents>[AUD_])(?<properties>[U ]) +(?<file>[^ ].*) *$"); SvnLook svnLook = new SvnLook(svnlookPath, repositoryPath); using (Process process = svnLook.Execute(SvnLookCommand.Changed, transaction, null)) { String line; while ((line = process.StandardOutput.ReadLine()) != null) { Match m = lineRegex.Match(line); if (!m.Success) { Console.Error.WriteLine("Could not match line: " + line); return(3); } if (m.Groups["file"].Value.EndsWith("/")) { // This is a directory, not a file. // TODO: Add directory handling } else { RepositoryStatus contentsStatus; RepositoryStatus propertiesStatus; switch (m.Groups["contents"].Value) { case "A": contentsStatus = RepositoryStatus.Added; break; case "U": contentsStatus = RepositoryStatus.Updated; break; case "D": contentsStatus = RepositoryStatus.Deleted; break; case "_": contentsStatus = RepositoryStatus.Unchanged; break; default: Console.Error.WriteLine("Could not match status flags for contents on line: " + line); return(3); } switch (m.Groups["properties"].Value) { case "U": propertiesStatus = RepositoryStatus.Updated; break; case " ": propertiesStatus = RepositoryStatus.Unchanged; break; default: Console.Error.WriteLine("Could not match status flags for properties on line: " + line); return(3); } using (RepositoryFile file = new RepositoryFile(repository, m.Groups["file"].Value, contentsStatus, propertiesStatus)) { // If HooksAllow returns false we should not allow // the transaction, but rather than exiting we store // that fact in a boolean so that the user will be // told all errors his files contain rather than just // one at a time. hooksOK &= HooksAllow(file, hooks); } } } if (!hooksOK) { return(1); } } return(0); } catch (Exception e) { Console.Error.WriteLine("An uncaught exception was thrown in the hook implementation"); Console.Error.WriteLine(e.Message); return(2); } }