package org.jabref.logic.formatter;

import java.util.Collections;
import java.util.stream.Stream;

import org.jabref.logic.formatter.casechanger.ProtectTermsFormatter;
import org.jabref.logic.protectedterms.ProtectedTermsLoader;
import org.jabref.logic.protectedterms.ProtectedTermsPreferences;
import org.jabref.model.cleanup.Formatter;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;

public class FormatterTest {

    private static ProtectedTermsLoader protectedTermsLoader;

    @BeforeAll
    public static void setUp() {
        protectedTermsLoader = new ProtectedTermsLoader(
                new ProtectedTermsPreferences(ProtectedTermsLoader.getInternalLists(), Collections.emptyList(),
                        Collections.emptyList(), Collections.emptyList()));
    }

    @ParameterizedTest
    @MethodSource("getFormatters")
    public void getNameReturnsNotNull(Formatter formatter) {
        assertNotNull(formatter.getName());
    }

    @ParameterizedTest
    @MethodSource("getFormatters")
    public void getNameReturnsNotEmpty(Formatter formatter) {
        assertNotEquals("", formatter.getName());
    }

    @ParameterizedTest
    @MethodSource("getFormatters")
    public void getKeyReturnsNotNull(Formatter formatter) {
        assertNotNull(formatter.getKey());
    }

    @ParameterizedTest
    @MethodSource("getFormatters")
    public void getKeyReturnsNotEmpty(Formatter formatter) {
        assertNotEquals("", formatter.getKey());
    }

    @ParameterizedTest
    @MethodSource("getFormatters")
    public void formatOfNullThrowsException(Formatter formatter) {
        assertThrows(NullPointerException.class, () -> formatter.format(null));
    }

    @ParameterizedTest
    @MethodSource("getFormatters")
    public void formatOfEmptyStringReturnsEmpty(Formatter formatter) {
        assertEquals("", formatter.format(""));
    }

    @ParameterizedTest
    @MethodSource("getFormatters")
    public void formatNotReturnsNull(Formatter formatter) {
        assertNotNull(formatter.format("string"));
    }

    @ParameterizedTest
    @MethodSource("getFormatters")
    public void getDescriptionAlwaysNonEmpty(Formatter formatter) {
        assertFalse(formatter.getDescription().isEmpty());
    }

    @ParameterizedTest
    @MethodSource("getFormatters")
    public void getExampleInputAlwaysNonEmpty(Formatter formatter) {
        assertFalse(formatter.getExampleInput().isEmpty());
    }

    public static Stream<Formatter> getFormatters() {
        // all classes implementing {@link net.sf.jabref.model.cleanup.Formatter}
        // Alternative: Use reflection - https://github.com/ronmamo/reflections
        // @formatter:off
        return Stream.concat(
                Formatters.getAll().stream(),
                // following formatters are not contained in the list of all formatters, because
                // - the IdentityFormatter is not offered to the user,
                // - the ProtectTermsFormatter needs more configuration
                Stream.of(
                        new IdentityFormatter(),
                        new ProtectTermsFormatter(protectedTermsLoader)));
        // @formatter:on
    }
}

@@ -60,16 +60,52 @@
 ctions;%0A
+import java.util.stream.Collectors;%0A
 import j
@@ -128,16 +128,16 @@
 Stream;%0A
-
 %0Aimport 
@@ -405,24 +405,59 @@
 .BeforeAll;%0A
+import org.junit.jupiter.api.Test;%0A
 import org.j
@@ -1166,24 +1166,24 @@
 mptyList(),%0A
-
             
@@ -1245,32 +1245,616 @@
 ist()));%0A    %7D%0A%0A
+    /**%0A     * When a new formatter is added by copy and pasting another formatter, it may happen that the %3Ccode%3EgetKey()%3C/code%3E method is not adapted. This results in duplicate keys, which this test tests for.%0A     */%0A    @Test%0A    public void allFormatterKeysAreUnique() %7B%0A        // idea for uniqueness checking by https://stackoverflow.com/a/44032568/873282%0A       assertFalse(getFormatters().collect(Collectors.groupingBy(%0A               formatter -%3E formatter.getKey(),%0A               Collectors.counting()%0A       )).entrySet().stream().anyMatch(e -%3E e.getValue() %3E 1));%0A    %7D%0A%0A
     @Parameteriz

Add test for unique keys