/*
 * Copyright 2015-2023 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.opentest4j;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

/**
 * Unit tests for {@link MultipleFailuresError}.
 *
 * @author Marc Philipp
 * @author Sam Brannen
 * @since 1.0
 */
public class MultipleFailuresErrorTests {

	private static final String DEFAULT_HEADING = "Multiple Failures";
	private static final String HEADING = "Custom Heading";

	@Test
	public void mfeWithNoFailuresWithNullHeading() {
		assertExceptionWithNoFailures(null, DEFAULT_HEADING);
	}

	@Test
	public void mfeWithNoFailuresWithEmptyHeading() {
		assertExceptionWithNoFailures("    ", DEFAULT_HEADING);
	}

	@Test
	public void mfeWithNoFailuresWithCustomHeading() {
		assertExceptionWithNoFailures(HEADING, HEADING);
	}

	@Test
	public void mfeWithSingleFailureWithNullHeading() {
		assertExceptionWithSingleFailure(null, DEFAULT_HEADING);
	}

	@Test
	public void mfeWithSingleFailureWithEmptyHeading() {
		assertExceptionWithSingleFailure("   ", DEFAULT_HEADING);
	}

	@Test
	public void mfeWithSingleFailureWithCustomHeading() {
		assertExceptionWithSingleFailure(HEADING, HEADING);
	}

	@Test
	public void mfeWithFailuresWithNullHeading() {
		assertExceptionWithFailures(null, DEFAULT_HEADING);
	}

	@Test
	public void mfeWithFailuresWithEmptyHeading() {
		assertExceptionWithFailures("   ", DEFAULT_HEADING);
	}

	@Test
	public void mfeWithFailuresWithCustomHeading() {
		assertExceptionWithFailures(HEADING, HEADING);
	}

	@Test
	public void mfeWithNullMessageFailures() throws Exception {
		List<Throwable> failures = new ArrayList<Throwable>();
		failures.add(new AssertionError());
		failures.add(new AssertionFailedError());

		MultipleFailuresError mfe = new MultipleFailuresError(HEADING, failures);

		assertEquals(String.format("%s %s%n\t%s%n\t%s", HEADING, "(2 failures)", //
			"java.lang.AssertionError: <no message>", //
			"org.opentest4j.AssertionFailedError: <no message>"), //
			mfe.getMessage());
	}

	@Test(expected = NullPointerException.class)
	public void mfeThrowsNPEForNullFailureElements() {
		List<Throwable> failures = new ArrayList<Throwable>();
		failures.add(new AssertionError());
		failures.add(null);

		new MultipleFailuresError("", failures);
	}

	private void assertExceptionWithNoFailures(String inputHeading, String outputHeading) {
		MultipleFailuresError mfe = new MultipleFailuresError(inputHeading, new ArrayList<Throwable>());

		assertTrue(mfe.getFailures().isEmpty());
		assertFalse(mfe.hasFailures());
		assertEquals(outputHeading, mfe.getMessage());
	}

	private void assertExceptionWithSingleFailure(String inputHeading, String outputHeading) {
		List<Throwable> failures = new ArrayList<Throwable>();
		failures.add(new AssertionError("failure 1"));

		MultipleFailuresError mfe = new MultipleFailuresError(inputHeading, failures);

		assertEquals(1, mfe.getFailures().size());
		assertTrue(mfe.hasFailures());
		assertEquals(
			String.format("%s %s%n\t%s: %s", outputHeading, "(1 failure)", AssertionError.class.getName(), "failure 1"),
			mfe.getMessage());
	}

	private void assertExceptionWithFailures(String inputHeading, String outputHeading) {
		List<Throwable> failures = new ArrayList<Throwable>();
		failures.add(new AssertionError("failure 1"));
		failures.add(new AssertionError("failure 2"));
		String assertErrorClassName = AssertionError.class.getName();

		MultipleFailuresError mfe = new MultipleFailuresError(inputHeading, failures);

		assertEquals(2, mfe.getFailures().size());
		assertTrue(mfe.hasFailures());
		assertEquals(String.format("%s %s%n\t%s: %s%n\t%s: %s", outputHeading, "(2 failures)", assertErrorClassName,
			"failure 1", assertErrorClassName, "failure 2"), mfe.getMessage());
	}

}
