// Copyright Josh Komoroske. All rights reserved.
// Use of this source code is governed by the MIT license,
// a copy of which can be found in the LICENSE.txt file.

package junit

import (
	"fmt"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestExamplesInTheWild(t *testing.T) {
	tests := []struct {
		title    string
		filename string
		origin   string
		check    func(*testing.T, []Suite)
	}{
		{
			title:    "catchsoftware example",
			filename: "testdata/catchsoftware.xml",
			origin:   "https://help.catchsoftware.com/display/ET/JUnit+Format",
			check: func(t *testing.T, suites []Suite) {
				assert.Len(t, suites, 2)
				assert.Len(t, suites[0].Tests, 0)
				assert.Len(t, suites[1].Tests, 3)
				assert.EqualError(t, suites[1].Tests[0].Error, "Assertion failed")
			},
		},
		{
			title:    "cubic example",
			filename: "testdata/cubic.xml",
			origin:   "https://llg.cubic.org/docs/junit/",
			check: func(t *testing.T, suites []Suite) {
				assert.Len(t, suites, 1)
				assert.Len(t, suites[0].Tests, 1)
				assert.Equal(t, "STDOUT text", suites[0].SystemOut)
				assert.Equal(t, "STDERR text", suites[0].SystemErr)
				assert.Equal(t, "STDOUT text", suites[0].Tests[0].SystemOut)
				assert.Equal(t, "STDERR text", suites[0].Tests[0].SystemErr)
			},
		},
		{
			title:    "go-junit-report example",
			filename: "testdata/go-junit-report.xml",
			origin:   "https://github.com/jstemmer/go-junit-report/blob/master/testdata/06-report.xml",
			check: func(t *testing.T, suites []Suite) {
				assert.Len(t, suites, 2)
				assert.Len(t, suites[0].Tests, 2)
				assert.Len(t, suites[1].Tests, 2)
				assert.Equal(t, "1.0", suites[0].Properties["go.version"])
				assert.Equal(t, "1.0", suites[1].Properties["go.version"])
				assert.EqualError(t, suites[1].Tests[0].Error, "file_test.go:11: Error message\nfile_test.go:11: Longer\n\terror\n\tmessage.")
			},
		},
		{
			title:    "go-junit-report skipped example",
			filename: "testdata/go-junit-report-skipped.xml",
			origin:   "https://github.com/jstemmer/go-junit-report/blob/master/testdata/03-report.xml",
			check: func(t *testing.T, suites []Suite) {
				assert.Len(t, suites, 1)
				assert.Len(t, suites[0].Tests, 2)
				assert.Equal(t, "package/name", suites[0].Name)
				assert.Equal(t, "TestOne", suites[0].Tests[0].Name)
				assert.Equal(t, "file_test.go:11: Skip message", suites[0].Tests[0].Message)
			},
		},
		{
			title:    "ibm example",
			filename: "testdata/ibm.xml",
			origin:   "https://www.ibm.com/support/knowledgecenter/en/SSQ2R2_14.2.0/com.ibm.rsar.analysis.codereview.cobol.doc/topics/cac_useresults_junit.html",
			check: func(t *testing.T, suites []Suite) {
				assert.Len(t, suites, 1)
				assert.Len(t, suites[0].Tests, 1)
				assert.EqualError(t, suites[0].Tests[0].Error, "\nWARNING: Use a program name that matches the source file name\nCategory: COBOL Code Review – Naming Conventions\nFile: /project/PROGRAM.cbl\nLine: 2\n      ")
			},
		},
		{
			title:    "jenkinsci example",
			filename: "testdata/jenkinsci.xml",
			origin:   "https://github.com/jenkinsci/junit-plugin/blob/master/src/test/resources/hudson/tasks/junit/junit-report-1463.xml",
			check: func(t *testing.T, suites []Suite) {
				assert.Len(t, suites, 1)
				assert.Len(t, suites[0].Tests, 6)
				assert.Equal(t, "\n", suites[0].Properties["line.separator"])
				assert.Equal(t, `\`, suites[0].Properties["file.separator"])
			},
		},
		{
			title:    "nose2 example",
			filename: "testdata/nose2.xml",
			origin:   "https://nose2.readthedocs.io/en/latest/plugins/junitxml.html",
			check: func(t *testing.T, suites []Suite) {
				assert.Len(t, suites, 1)
				assert.Len(t, suites[0].Tests, 25)
				assert.EqualError(t, suites[0].Tests[22].Error, "Traceback (most recent call last):\n  File \"nose2/tests/functional/support/scenario/tests_in_package/pkg1/test/test_things.py\", line 13, in test_typeerr\n    raise TypeError(\"oops\")\nTypeError: oops\n")
			},
		},
		{
			title:    "python junit-xml example",
			filename: "testdata/python-junit-xml.xml",
			origin:   "https://pypi.org/project/junit-xml/",
			check: func(t *testing.T, suites []Suite) {
				assert.Len(t, suites, 1)
				assert.Len(t, suites[0].Tests, 1)
				assert.Equal(t, "\n                I am stdout!\n            ", suites[0].Tests[0].SystemOut)
				assert.Equal(t, "\n                I am stderr!\n            ", suites[0].Tests[0].SystemErr)
			},
		},
		{
			title:    "surefire example",
			filename: "testdata/surefire.xml",
			origin:   "https://gist.github.com/rwbergstrom/6f0193b1a12dca9d358e6043ee6abba4",
			check: func(t *testing.T, suites []Suite) {
				assert.Len(t, suites, 1)
				assert.Len(t, suites[0].Tests, 1)
				assert.Equal(t, "\n", suites[0].Properties["line.separator"])
				assert.Equal(t, "Hello, World\n", suites[0].Tests[0].SystemOut)
				assert.Equal(t, "I'm an error!\n", suites[0].Tests[0].SystemErr)

				var testcase = Test{
					Name:      "testStdoutStderr",
					Classname: "com.example.FooTest",
					Duration:  1234560 * time.Millisecond,
					Status:    StatusFailed,
					Error: Error{
						Type: "java.lang.AssertionError",
						Body: "java.lang.AssertionError\n\tat com.example.FooTest.testStdoutStderr(FooTest.java:13)\n",
					},
					Properties: map[string]string{
						"classname": "com.example.FooTest",
						"name":      "testStdoutStderr",
						"time":      "1,234.56",
					},
					SystemOut: "Hello, World\n",
					SystemErr: "I'm an error!\n",
				}

				assert.Equal(t, testcase, suites[0].Tests[0])
			},
		},
		{
			title:    "fastlane example",
			filename: "testdata/fastlane-trainer.xml",
			check: func(t *testing.T, suites []Suite) {
				assert.Len(t, suites, 1)
				assert.Len(t, suites[0].Tests, 4)

				var testcase = Test{
					Name:      "testSomething()",
					Classname: "TestClassSample",
					Duration:  342 * time.Millisecond,
					Status:    StatusFailed,
					Message:   "XCTAssertTrue failed",
					Error: Error{
						Message: "XCTAssertTrue failed",
						Body:    "\n            ",
					},
					Properties: map[string]string{
						"classname": "TestClassSample",
						"name":      "testSomething()",
						"time":      "0.342",
					},
				}

				assert.Equal(t, testcase, suites[0].Tests[2])
				assert.EqualError(t, suites[0].Tests[2].Error, "XCTAssertTrue failed")
				assert.EqualError(t, suites[0].Tests[3].Error, "NullPointerException")
			},
		},
		{
			title:    "phpunit example",
			filename: "testdata/phpunit.xml",
			check: func(t *testing.T, suites []Suite) {
				assert.Len(t, suites, 1)
				assert.Len(t, suites[0].Tests, 0)
				assert.Len(t, suites[0].Suites, 1)

				suite := suites[0].Suites[0]
				assert.Len(t, suite.Tests, 1)
				assert.Len(t, suite.Suites, 2)

				assert.Equal(t, "SampleTest", suite.Name)
				assert.Equal(t, "/untitled/tests/SampleTest.php", suite.Properties["file"])

				var testcase = Test{
					Name:      "testA",
					Classname: "SampleTest",
					Duration:  5917 * time.Microsecond,
					Status:    StatusPassed,
					Properties: map[string]string{
						"assertions": "1",
						"class":      "SampleTest",
						"classname":  "SampleTest",
						"file":       "/untitled/tests/SampleTest.php",
						"line":       "7",
						"name":       "testA",
						"time":       "0.005917",
					},
				}

				assert.Equal(t, testcase, suite.Tests[0])

				assert.Len(t, suite.Suites[1].Suites, 0)
				assert.Len(t, suite.Suites[1].Tests, 3)
				assert.Equal(t, "testC with data set #0", suite.Suites[1].Tests[0].Name)

				// checking recursive aggregation
				suites[0].Aggregate()
				actualTotals := suites[0].Totals
				expectedTotals := Totals{
					Tests:    7,
					Passed:   4,
					Skipped:  0,
					Failed:   3,
					Error:    0,
					Duration: 8489 * time.Microsecond,
				}
				assert.Equal(t, expectedTotals, actualTotals)
			},
		},
	}

	for index, test := range tests {
		name := fmt.Sprintf("#%d - %s", index+1, test.title)

		t.Run(name, func(t *testing.T) {
			suites, err := IngestFile(test.filename)
			require.NoError(t, err)
			test.check(t, suites)
		})
	}
}
