com.googlesource.gerrit.plugins.findowners.OwnersValidatorTest.java Source code

Java tutorial

Introduction

Here is the source code for com.googlesource.gerrit.plugins.findowners.OwnersValidatorTest.java

Source

// Copyright (C) 2017 The Android Open Source Project
//
// 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 com.googlesource.gerrit.plugins.findowners;

import static com.google.common.truth.Truth.assertThat;
import static com.googlesource.gerrit.plugins.findowners.Config.OWNERS;
import static com.googlesource.gerrit.plugins.findowners.Config.OWNERS_FILE_NAME;
import static com.googlesource.gerrit.plugins.findowners.Config.REJECT_ERROR_IN_OWNERS;

import com.google.common.base.Function;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Lists;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.Emails;
import com.google.gerrit.server.config.PluginConfig;
import com.google.gerrit.server.git.validators.CommitValidationMessage;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.io.FileUtils;
import org.eclipse.jgit.api.AddCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

/** Test OwnersValidator, which checks syntax of changed OWNERS files. */
@RunWith(JUnit4.class)
public class OwnersValidatorTest {

    private static class MockedEmails extends Emails {
        Set<String> registered;

        MockedEmails() {
            super(null, null, null);
            registered = ImmutableSet.of("u1@g.com", "u2@g.com", "u2.m@g.com", "user1@google.com",
                    "u1+review@g.com");
        }

        @Override
        public ImmutableSetMultimap<String, Account.Id> getAccountsFor(String... emails) {
            // Used by checkEmails; each email should have exactly one Account.Id
            ImmutableSetMultimap.Builder<String, Account.Id> builder = ImmutableSetMultimap.builder();
            int id = 1000000;
            for (String s : registered) {
                builder.put(s, new Account.Id(++id));
            }
            return builder.build();
        }
    }

    private File repoFolder;
    private Repository repo;

    @Before
    public void init() throws IOException {
        repoFolder = File.createTempFile("Git", "");
        repoFolder.delete();
        repo = FileRepositoryBuilder.create(new File(repoFolder, ".git"));
        repo.create();
    }

    @After
    public void cleanup() throws IOException {
        repo.close();
        if (repoFolder.exists()) {
            FileUtils.deleteDirectory(repoFolder);
        }
    }

    private static final String OWNERS_ANDROID = "OWNERS.android"; // alternative OWNERS file name
    private static final PluginConfig ANDROID_CONFIG = createAndroidConfig(); // use OWNERS_ANDROID
    private static final PluginConfig EMPTY_CONFIG = new PluginConfig("", new Config());
    private static final PluginConfig ENABLED_CONFIG = createEnabledConfig(); // use OWNERS
    private static final PluginConfig DISABLED_CONFIG = createDisabledConfig();

    @Test
    public void chekIsActiveAndFileName() throws Exception {
        // This check should be enabled in project.config, default is not active.
        assertThat(OwnersValidator.isActive(EMPTY_CONFIG)).isFalse();
        assertThat(OwnersValidator.isActive(ENABLED_CONFIG)).isTrue();
        assertThat(OwnersValidator.isActive(ANDROID_CONFIG)).isTrue();
        assertThat(OwnersValidator.isActive(DISABLED_CONFIG)).isFalse();
        // Default file name is "OWNERS".
        assertThat(OwnersValidator.getOwnersFileName(EMPTY_CONFIG)).isEqualTo(OWNERS);
        assertThat(OwnersValidator.getOwnersFileName(ENABLED_CONFIG)).isEqualTo(OWNERS);
        assertThat(OwnersValidator.getOwnersFileName(DISABLED_CONFIG)).isEqualTo(OWNERS);
        assertThat(OwnersValidator.getOwnersFileName(ANDROID_CONFIG)).isEqualTo(OWNERS_ANDROID);
    }

    private static final ImmutableMap<String, String> FILES_WITHOUT_OWNERS = ImmutableMap.of("README", "any\n",
            "d1/test.c", "int x;\n");

    @Test
    public void testNoOwners() throws Exception {
        try (RevWalk rw = new RevWalk(repo)) {
            RevCommit c = makeCommit(rw, "Commit no OWNERS.", FILES_WITHOUT_OWNERS);
            assertThat(validate(rw, c, false, ENABLED_CONFIG)).isEmpty();
            assertThat(validate(rw, c, true, ENABLED_CONFIG)).isEmpty();
        }
    }

    private static final ImmutableMap<String, String> FILES_WITH_NO_ERROR = ImmutableMap.of(OWNERS,
            "\n\n#comments ...\n  ###  more comments\n" + "   user1@google.com # comment\n" + "u1+review@g.com###\n"
                    + " * # everyone can approve\n" + "per-file   *.py=u2.m@g.com   \n"
                    + "per-file *.c, *.java ,A*bp = u1@g.com, u1+review@g.com  ,u2.m@g.com#comment\n"
                    + "  per-file *.txt = * # everyone can approve #  \n" + "  set   noparent  # comment#\n");

    private static final ImmutableSet<String> EXPECTED_VERBOSE_OUTPUT = ImmutableSet.of("MSG: validate: " + OWNERS,
            "MSG: owner: u1+review@g.com", "MSG: owner: u1@g.com", "MSG: owner: u2.m@g.com",
            "MSG: owner: user1@google.com");

    @Test
    public void testGoodInput() throws Exception {
        try (RevWalk rw = new RevWalk(repo)) {
            RevCommit c = makeCommit(rw, "Commit good files", FILES_WITH_NO_ERROR);
            assertThat(validate(rw, c, false, ENABLED_CONFIG)).isEmpty();
            assertThat(validate(rw, c, true, ENABLED_CONFIG)).containsExactlyElementsIn(EXPECTED_VERBOSE_OUTPUT);
        }
    }

    private static final ImmutableMap<String, String> FILES_WITH_WRONG_SYNTAX = ImmutableMap.of("README",
            "# some content\nu2@g.com\n", OWNERS, "\nwrong syntax\n#comment\nuser1@google.com\n", "d2/" + OWNERS,
            "u1@g.com\nu3@g.com\n*\n", "d3/" + OWNERS, "\nfile: common/Owners\n");

    private static final ImmutableSet<String> EXPECTED_WRONG_SYNTAX = ImmutableSet.of(
            "ERROR: syntax: " + OWNERS + ":2: wrong syntax", "ERROR: unknown: u3@g.com at d2/" + OWNERS + ":2",
            "ERROR: ignored: d3/" + OWNERS + ":2: file: common/Owners");

    private static final ImmutableSet<String> EXPECTED_VERBOSE_WRONG_SYNTAX = ImmutableSet.of(
            "MSG: validate: d3/" + OWNERS, "MSG: validate: d2/" + OWNERS, "MSG: validate: " + OWNERS,
            "MSG: owner: user1@google.com", "MSG: owner: u1@g.com", "MSG: owner: u3@g.com",
            "ERROR: syntax: " + OWNERS + ":2: wrong syntax", "ERROR: unknown: u3@g.com at d2/" + OWNERS + ":2",
            "ERROR: ignored: d3/" + OWNERS + ":2: file: common/Owners");

    @Test
    public void testWrongSyntax() throws Exception {
        try (RevWalk rw = new RevWalk(repo)) {
            RevCommit c = makeCommit(rw, "Commit wrong syntax", FILES_WITH_WRONG_SYNTAX);
            assertThat(validate(rw, c, false, ENABLED_CONFIG)).containsExactlyElementsIn(EXPECTED_WRONG_SYNTAX);
            assertThat(validate(rw, c, true, ENABLED_CONFIG))
                    .containsExactlyElementsIn(EXPECTED_VERBOSE_WRONG_SYNTAX);
        }
    }

    private static final ImmutableMap<String, String> FILES_WITH_WRONG_EMAILS = ImmutableMap.of("d1/" + OWNERS,
            "u1@g.com\n", "d2/" + OWNERS_ANDROID, "u2@g.com\n");

    private static final ImmutableSet<String> EXPECTED_VERBOSE_DEFAULT = ImmutableSet
            .of("MSG: validate: d1/" + OWNERS, "MSG: owner: u1@g.com");

    private static final ImmutableSet<String> EXPECTED_VERBOSE_ANDROID = ImmutableSet
            .of("MSG: validate: d2/" + OWNERS_ANDROID, "MSG: owner: u2@g.com");

    @Test
    public void checkWrongEmails() throws Exception {
        try (RevWalk rw = new RevWalk(repo)) {
            RevCommit c = makeCommit(rw, "Commit Default", FILES_WITH_WRONG_EMAILS);
            assertThat(validate(rw, c, true, ENABLED_CONFIG)).containsExactlyElementsIn(EXPECTED_VERBOSE_DEFAULT);
        }
    }

    @Test
    public void checkAndroidOwners() throws Exception {
        try (RevWalk rw = new RevWalk(repo)) {
            RevCommit c = makeCommit(rw, "Commit Android", FILES_WITH_WRONG_EMAILS);
            assertThat(validate(rw, c, true, ANDROID_CONFIG)).containsExactlyElementsIn(EXPECTED_VERBOSE_ANDROID);
        }
    }

    private static PluginConfig createEnabledConfig() {
        PluginConfig c = new PluginConfig("", new Config());
        c.setBoolean(REJECT_ERROR_IN_OWNERS, true);
        return c;
    }

    private static PluginConfig createDisabledConfig() {
        PluginConfig c = new PluginConfig("", new Config());
        c.setBoolean(REJECT_ERROR_IN_OWNERS, false);
        return c;
    }

    private static PluginConfig createAndroidConfig() {
        PluginConfig c = createEnabledConfig();
        c.setString(OWNERS_FILE_NAME, OWNERS_ANDROID);
        return c;
    }

    private RevCommit makeCommit(RevWalk rw, String message, Map<String, String> fileStrings)
            throws IOException, GitAPIException {
        Map<File, byte[]> fileBytes = new HashMap<>();
        for (String path : fileStrings.keySet()) {
            fileBytes.put(new File(repo.getDirectory().getParent(), path),
                    fileStrings.get(path).getBytes(StandardCharsets.UTF_8));
        }
        return makeCommit(rw, repo, message, fileBytes);
    }

    private List<String> validate(RevWalk rw, RevCommit c, boolean verbose, PluginConfig cfg) throws Exception {
        MockedEmails myEmails = new MockedEmails();
        OwnersValidator validator = new OwnersValidator(null, null, myEmails);
        String ownersFileName = OwnersValidator.getOwnersFileName(cfg);
        List<CommitValidationMessage> m = validator.performValidation(c, rw, ownersFileName, verbose);
        return transformMessages(m);
    }

    private static String generateFilePattern(File f, Git git) {
        return f.getAbsolutePath().replace(git.getRepository().getWorkTree().getAbsolutePath(), "").substring(1);
    }

    private static void addFiles(Git git, Map<File, byte[]> files) throws IOException, GitAPIException {
        AddCommand ac = git.add();
        for (File f : files.keySet()) {
            if (!f.exists()) {
                FileUtils.touch(f);
            }
            if (files.get(f) != null) {
                FileUtils.writeByteArrayToFile(f, files.get(f));
            }
            ac = ac.addFilepattern(generateFilePattern(f, git));
        }
        ac.call();
    }

    private static RevCommit makeCommit(RevWalk rw, Repository repo, String message, Map<File, byte[]> files)
            throws IOException, GitAPIException {
        try (Git git = new Git(repo)) {
            if (files != null) {
                addFiles(git, files);
            }
            return rw.parseCommit(git.commit().setMessage(message).call());
        }
    }

    private static List<String> transformMessages(List<CommitValidationMessage> messages) {
        return Lists.transform(messages, new Function<CommitValidationMessage, String>() {
            @Override
            public String apply(CommitValidationMessage input) {
                String pre = (input.isError()) ? "ERROR: " : "MSG: ";
                return pre + input.getMessage();
            }
        });
    }

    @Test
    public void testTransformer() {
        List<CommitValidationMessage> messages = new LinkedList<>();
        messages.add(new CommitValidationMessage("a message", false));
        messages.add(new CommitValidationMessage("an error", true));
        Set<String> expected = ImmutableSet.of("ERROR: an error", "MSG: a message");
        assertThat(transformMessages(messages)).containsExactlyElementsIn(expected);
    }
}