com.github.sevntu.checkstyle.domain.BaseCheckTestSupport.java Source code

Java tutorial

Introduction

Here is the source code for com.github.sevntu.checkstyle.domain.BaseCheckTestSupport.java

Source

////////////////////////////////////////////////////////////////////////////////
// checkstyle: Checks Java source code for adherence to a set of rules.
// Copyright (C) 2001-2018 the original author or authors.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
////////////////////////////////////////////////////////////////////////////////

package com.github.sevntu.checkstyle.domain;

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

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;

import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import com.google.common.collect.MapDifference;
import com.google.common.collect.MapDifference.ValueDifference;
import com.google.common.collect.Maps;
import com.puppycrawl.tools.checkstyle.Checker;
import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
import com.puppycrawl.tools.checkstyle.DefaultLogger;
import com.puppycrawl.tools.checkstyle.TreeWalker;
import com.puppycrawl.tools.checkstyle.api.AuditEvent;
import com.puppycrawl.tools.checkstyle.api.Configuration;

public class BaseCheckTestSupport {
    private final ByteArrayOutputStream stream = new ByteArrayOutputStream();

    protected static DefaultConfiguration createCheckConfig(Class<?> clazz) {
        return new DefaultConfiguration(clazz.getName());
    }

    protected Checker createChecker(Configuration checkConfig) throws Exception {
        final DefaultConfiguration dc = createCheckerConfig(checkConfig);
        final Checker checker = new Checker();
        // make sure the tests always run with default error messages (language-invariant)
        // so the tests don't fail in supported locales like German
        final Locale locale = Locale.ROOT;
        checker.setLocaleCountry(locale.getCountry());
        checker.setLocaleLanguage(locale.getLanguage());
        checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
        checker.configure(dc);
        checker.addListener(new BriefLogger(stream));
        return checker;
    }

    protected Checker createChecker(Configuration checkConfig, boolean printSeverity) throws Exception {

        final DefaultConfiguration dc = createCheckerConfig(checkConfig);
        final Checker checker = new Checker();
        // make sure the tests always run with english error messages
        // so the tests don't fail in supported locales like german
        final Locale locale = Locale.ENGLISH;
        checker.setLocaleCountry(locale.getCountry());
        checker.setLocaleLanguage(locale.getLanguage());
        checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
        checker.configure(dc);
        checker.addListener(new BriefLogger(stream, printSeverity));
        return checker;
    }

    protected DefaultConfiguration createCheckerConfig(Configuration config) {
        final DefaultConfiguration dc = new DefaultConfiguration("configuration");
        final DefaultConfiguration twConf = createCheckConfig(TreeWalker.class);
        // make sure that the tests always run with this charset
        dc.addAttribute("charset", "UTF-8");
        dc.addChild(twConf);
        twConf.addChild(config);
        return dc;
    }

    protected String getPath(String filename) throws IOException {
        return new File("src/test/resources/com/puppycrawl/tools/checkstyle/" + filename).getCanonicalPath();
    }

    protected String getUriString(String filename) {
        return new File("src/test/resources/com/puppycrawl/tools/checkstyle/" + filename).toURI().toString();
    }

    protected String getSrcPath(String filename) throws IOException {
        return new File("src/test/java/com/puppycrawl/tools/checkstyle/" + filename).getCanonicalPath();
    }

    protected String getNonCompilablePath(String filename) throws IOException {
        return new File("src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/" + filename)
                .getCanonicalPath();
    }

    protected void verify(Configuration aConfig, String fileName, String... expected) throws Exception {
        verify(createChecker(aConfig), fileName, fileName, expected);
    }

    protected void verify(Configuration aConfig, boolean printSeverity, String filename, String... expected)
            throws Exception {
        verify(createChecker(aConfig, printSeverity), filename, filename, expected);
    }

    protected void verify(Checker checker, String fileName, String... expected) throws Exception {
        verify(checker, fileName, fileName, expected);
    }

    protected void verify(Checker checker, String processedFilename, String messageFileName, String... expected)
            throws Exception {
        verify(checker, new File[] { new File(processedFilename) }, messageFileName, expected);
    }

    /**
     *  We keep two verify methods with separate logic only for convenience of debuging
     *  We have minimum amount of multi-file test cases
     */
    protected void verify(Checker checker, File[] processedFiles, String messageFileName, String... expected)
            throws Exception {
        stream.flush();
        final List<File> theFiles = Lists.newArrayList();
        Collections.addAll(theFiles, processedFiles);
        final int errs = checker.process(theFiles);

        // process each of the lines
        final ByteArrayInputStream inputStream = new ByteArrayInputStream(stream.toByteArray());
        try (LineNumberReader lnr = new LineNumberReader(
                new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {

            for (int i = 0; i < expected.length; i++) {
                final String expectedResult = messageFileName + ":" + expected[i];
                final String actual = lnr.readLine();
                assertEquals("error message " + i, expectedResult, actual);
            }

            assertEquals("unexpected output: " + lnr.readLine(), expected.length, errs);
        }

        checker.destroy();
    }

    protected void verify(Checker checker, File[] processedFiles, Map<String, List<String>> expectedViolations)
            throws Exception {
        stream.flush();
        final List<File> theFiles = Lists.newArrayList();
        Collections.addAll(theFiles, processedFiles);
        final int errs = checker.process(theFiles);

        // process each of the lines
        final Map<String, List<String>> actualViolations = getActualViolations(errs);
        final Map<String, List<String>> realExpectedViolations = Maps.filterValues(expectedViolations,
                new Predicate<List<String>>() {
                    @Override
                    public boolean apply(List<String> input) {
                        return !input.isEmpty();
                    }
                });
        final MapDifference<String, List<String>> violationDifferences = Maps.difference(realExpectedViolations,
                actualViolations);

        final Map<String, List<String>> missingViolations = violationDifferences.entriesOnlyOnLeft();
        final Map<String, List<String>> unexpectedViolations = violationDifferences.entriesOnlyOnRight();
        final Map<String, ValueDifference<List<String>>> differingViolations = violationDifferences
                .entriesDiffering();

        final StringBuilder message = new StringBuilder();
        if (!missingViolations.isEmpty()) {
            message.append("missing violations: ").append(missingViolations);
        }
        if (!unexpectedViolations.isEmpty()) {
            if (message.length() > 0) {
                message.append('\n');
            }
            message.append("unexpected violations: ").append(unexpectedViolations);
        }
        if (!differingViolations.isEmpty()) {
            if (message.length() > 0) {
                message.append('\n');
            }
            message.append("differing violations: ").append(differingViolations);
        }

        assertTrue(message.toString(),
                missingViolations.isEmpty() && unexpectedViolations.isEmpty() && differingViolations.isEmpty());

        checker.destroy();
    }

    private Map<String, List<String>> getActualViolations(int errorCount) throws IOException {
        // process each of the lines
        final ByteArrayInputStream inputStream = new ByteArrayInputStream(stream.toByteArray());

        try (LineNumberReader lnr = new LineNumberReader(
                new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {

            final Map<String, List<String>> actualViolations = new HashMap<>();
            for (String line = lnr.readLine(); line != null
                    && lnr.getLineNumber() <= errorCount; line = lnr.readLine()) {
                // have at least 2 characters before the splitting colon,
                // to not split after the drive letter on windows
                final String[] actualViolation = line.split("(?<=.{2}):", 2);
                final String actualViolationFileName = actualViolation[0];
                final String actualViolationMessage = actualViolation[1];

                List<String> actualViolationsPerFile = actualViolations.get(actualViolationFileName);
                if (actualViolationsPerFile == null) {
                    actualViolationsPerFile = new ArrayList<>();
                    actualViolations.put(actualViolationFileName, actualViolationsPerFile);
                }
                actualViolationsPerFile.add(actualViolationMessage);
            }

            return actualViolations;
        }
    }

    /**
     * Gets the module message 'as is' from appropriate 'messages.properties'
     * file.
     *
     * @param messageKey the key of message in 'messages.properties' file.
     * @param arguments  the arguments of message in 'messages.properties' file.
     */
    protected String getCheckMessage(String messageKey, Object... arguments) {
        String result = null;
        final Properties pr = new Properties();
        try {
            pr.load(getClass().getResourceAsStream("messages.properties"));
            final MessageFormat formatter = new MessageFormat(pr.getProperty(messageKey), Locale.ROOT);
            result = formatter.format(arguments);
        } catch (IOException ex) {
            // no code needed
        }
        return result;
    }

    /**
     * A brief logger that only display info about errors.
     */
    protected static class BriefLogger extends DefaultLogger {
        public BriefLogger(OutputStream out) {
            super(out, true, out, false);
        }

        public BriefLogger(OutputStream out, boolean printSeverity) {
            super(out, true, out, false);
        }

        @Override
        public void auditStarted(AuditEvent event) {
        }
    }
}