tools/cts: Handle CRLF in expectations.txt

Change-Id: Iccc9f0b4c488a93a3014bd1d2415b42b05c01553
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/114000
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
diff --git a/tools/src/cmd/cts/roll/roll.go b/tools/src/cmd/cts/roll/roll.go
index 98be378..929dc77 100644
--- a/tools/src/cmd/cts/roll/roll.go
+++ b/tools/src/cmd/cts/roll/roll.go
@@ -222,7 +222,7 @@
 	if err != nil {
 		return err
 	}
-	ex, err := expectations.Parse(expectationsFile)
+	ex, err := expectations.Parse(common.RelativeExpectationsPath, expectationsFile)
 	if err != nil {
 		return fmt.Errorf("failed to load expectations: %v", err)
 	}
diff --git a/tools/src/cts/expectations/expectations.go b/tools/src/cts/expectations/expectations.go
index b47dd67..93edccf 100644
--- a/tools/src/cts/expectations/expectations.go
+++ b/tools/src/cts/expectations/expectations.go
@@ -90,7 +90,7 @@
 	if err != nil {
 		return Content{}, err
 	}
-	ex, err := Parse(string(content))
+	ex, err := Parse(path, string(content))
 	if err != nil {
 		return Content{}, err
 	}
diff --git a/tools/src/cts/expectations/parse.go b/tools/src/cts/expectations/parse.go
index aafb800..579b26d 100644
--- a/tools/src/cts/expectations/parse.go
+++ b/tools/src/cts/expectations/parse.go
@@ -15,6 +15,7 @@
 package expectations
 
 import (
+	"fmt"
 	"strings"
 
 	"dawn.googlesource.com/dawn/tools/src/cts/result"
@@ -26,7 +27,10 @@
 )
 
 // Parse parses an expectations file, returning the Content
-func Parse(body string) (Content, error) {
+func Parse(path, body string) (Content, error) {
+	// Normalize CRLF -> LF
+	body = strings.ReplaceAll(body, "\r\n", "\n")
+
 	// LineType is an enumerator classifying the 'type' of the line.
 	type LineType int
 	const (
@@ -120,7 +124,7 @@
 			if columnIdx == 1 {
 				columnIdx = len(l) + 1
 			}
-			return Diagnostic{Error, lineIdx, columnIdx, msg}
+			return fmt.Errorf("%v:%v:%v error: %v", path, lineIdx, columnIdx, msg)
 		}
 
 		// peek returns the next token without consuming it.
diff --git a/tools/src/cts/expectations/parse_test.go b/tools/src/cts/expectations/parse_test.go
index 041080b..e206bcc 100644
--- a/tools/src/cts/expectations/parse_test.go
+++ b/tools/src/cts/expectations/parse_test.go
@@ -66,6 +66,16 @@
 			},
 		}, /////////////////////////////////////////////////////////////////////
 		{
+			name: "carriage-return line-feed, followed by single line comment",
+			in:   "\r\n# a comment",
+			expect: expectations.Content{
+				Chunks: []expectations.Chunk{
+					{},
+					{Comments: []string{`# a comment`}},
+				},
+			},
+		}, /////////////////////////////////////////////////////////////////////
+		{
 			name: "comments separated by single newline",
 			in: `# comment 1
 # comment 2`,
@@ -127,6 +137,30 @@
 			},
 		}, /////////////////////////////////////////////////////////////////////
 		{
+			name: "two expectations, separated with carriage-return line-feed",
+			in:   "abc,def [ FAIL ]\r\nghi,jkl [ PASS ]",
+			expect: expectations.Content{
+				Chunks: []expectations.Chunk{
+					{
+						Expectations: []expectations.Expectation{
+							{
+								Line:   1,
+								Tags:   result.NewTags(),
+								Query:  "abc,def",
+								Status: []string{"FAIL"},
+							},
+							{
+								Line:   2,
+								Tags:   result.NewTags(),
+								Query:  "ghi,jkl",
+								Status: []string{"PASS"},
+							},
+						},
+					},
+				},
+			},
+		}, /////////////////////////////////////////////////////////////////////
+		{
 			name: "expectation, with comment",
 			in:   `abc,def [ FAIL ] # this is a comment`,
 			expect: expectations.Content{
@@ -432,31 +466,31 @@
 		{
 			name:      "err missing tag ']'",
 			in:        `[`,
-			expectErr: "1:2 error: expected ']' for tags",
+			expectErr: "expectations.txt:1:2 error: expected ']' for tags",
 		}, /////////////////////////////////////////////////////////////////////
 		{
 			name:      "err missing test query",
 			in:        `[ a ]`,
-			expectErr: "1:6 error: expected test query",
+			expectErr: "expectations.txt:1:6 error: expected test query",
 		}, /////////////////////////////////////////////////////////////////////
 		{
 			name:      "err missing status EOL",
 			in:        `[ a ] b`,
-			expectErr: "1:8 error: expected status",
+			expectErr: "expectations.txt:1:8 error: expected status",
 		}, /////////////////////////////////////////////////////////////////////
 		{
 			name:      "err missing status comment",
 			in:        `[ a ] b # c`,
-			expectErr: "1:9 error: expected status",
+			expectErr: "expectations.txt:1:9 error: expected status",
 		}, /////////////////////////////////////////////////////////////////////
 		{
 			name:      "err missing status ']'",
 			in:        `[ a ] b [ c`,
-			expectErr: "1:12 error: expected ']' for status",
+			expectErr: "expectations.txt:1:12 error: expected ']' for status",
 		},
 	} {
 
-		got, err := expectations.Parse(test.in)
+		got, err := expectations.Parse("expectations.txt", test.in)
 		errMsg := ""
 		if err != nil {
 			errMsg = err.Error()
diff --git a/tools/src/cts/expectations/update_test.go b/tools/src/cts/expectations/update_test.go
index 6b4da10..b291fdc 100644
--- a/tools/src/cts/expectations/update_test.go
+++ b/tools/src/cts/expectations/update_test.go
@@ -507,7 +507,7 @@
 `,
 		},
 	} {
-		ex, err := expectations.Parse(header + test.expectations)
+		ex, err := expectations.Parse("expectations.txt", header+test.expectations)
 		if err != nil {
 			t.Fatalf("'%v': expectations.Parse():\n%v", test.name, err)
 		}
diff --git a/tools/src/cts/expectations/validate_test.go b/tools/src/cts/expectations/validate_test.go
index 1be5082..d9b15b9 100644
--- a/tools/src/cts/expectations/validate_test.go
+++ b/tools/src/cts/expectations/validate_test.go
@@ -117,7 +117,7 @@
 			},
 		},
 	} {
-		ex, err := expectations.Parse(header + test.expectations)
+		ex, err := expectations.Parse("expectations.txt", header+test.expectations)
 		if err != nil {
 			t.Fatalf("'%v': expectations.Parse():\n%v", test.name, err)
 		}