Breaking Build Postmortem: Biting the line that feeds you

4 minute read Published:

It was early one weekend morning and I was trying to integrate AppVeyor with my GitHub project. But there was one problem: my build was failing miserably on AppVeyor. Strangely, it built just fine on my machine; but on AppVeyor the test ToStringShouldReturnResourceKey was failing a string comparison.

Breaking Build: Analysis of a build failure

Investigating the failure

Fortunately I was using Shouldly, which produces very readable test output when your tests fail. See what you notice in the snippet below.

            resource.ToString()
        should be
    "<data name="My_resource" xml:space="preserve">
  <value>Banana's are quite good</value>
</data>"
        but was
    "<data name="My_resource" xml:space="preserve">
  <value>Banana's are quite good</value>
</data>"
        difference
    Case Sensitive Comparison
Difference     |       |    |         |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |        
               |      \|/  \|/       \|/  \|/  \|/  \|/  \|/  \|/  \|/  \|/  \|/  \|/  \|/  \|/  \|/  \|/  \|/  \|/  \|/  \|/       
Index          | ...  46   47   48   49   50   51   52   53   54   55   56   57   58   59   60   61   62   63   64   65   66   ...  
Expected Value | ...  \n   \s   \s   <    v    a    l    u    e    >    B    a    n    a    n    a    '    s    \s   a    r    ...  
Actual Value   | ...  \r   \n   \s   \s   <    v    a    l    u    e    >    B    a    n    a    n    a    '    s    \s   a    ...  
Expected Code  | ...  10   32   32   60   118  97   108  117  101  62   66   97   110  97   110  97   39   115  32   97   114  ...  
Actual Code    | ...  13   10   32   32   60   118  97   108  117  101  62   66   97   110  97   110  97   39   115  32   97 

Yes, you spotted it. Shouldly is expecting a string that starts with ‘\n’, a line feed (LF, 10), whereas the actual value string starts with ‘\r\n’, a carriage return (CR, 13) followed by a line feed (CRLF). Typically, CRLF is the character sequence used for line endings in Windows, and LF is the end of line character used in Linux. So why would this test pass on my system, and fail on Appveyor?

The expected string is a multi-line C# verbatim string, while the actual string is created by the ToString() method of XElement. Buried as it is deep in the Windows and .NET ecosystem, XElement.ToString() uses CRLF for its line breaks. Verbatim strings, on the other hand, use the line endings setting of the file they live in.

Making the test more environment agnostic

My first thought was that this was a really lousy reason for a test to fail. The purpose of this unit test was not to test Windows-vs-Linux line endings. To make this test less brittle (more pliable?), I introduced a string extension method to strip all of the line endings from the string:

public static string StripNewLines(this string str)
{
    return Regex.Replace(str, @"\r\n?|\n", string.Empty);
}

Case closed

While that solved the immediate issue, I still wanted to solve the mystery at hand.: Why would the line endings be different on my machine and the build machine? Being on GitHub, the project is stored in the Git source code management system. Git originated on Linux, so it favors LF line endings. Git does play nice with Windows however, and offers the autocrlf configuration option. When this option is set to true, source files are checked out with CRLF line endings, but committed to Git with LF line endings. But what happens when someone clones your repository and they haven’t set the autocrlf option? You guessed it - the files are checked out with LF line endings. And that’s exactly what broke baby bird’s balloon on the build box.

git config core.autocrlf true

Configure Git on AppVeyor

If you’re using AppVeyor and you run into this issue, how do you fix it? In the “Environment” section of your project’s settings, find the “Init script” text box and enter the following:

git config --global core.autocrlf true

That’s all folks! Case closed. :)