blob: fee0569133339535f22bef3ba5dabf6c3ec5327f [file] [log] [blame]
// 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 com.google.common.flogger.FluentLogger;
import java.util.Arrays;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Test Parser class */
@RunWith(JUnit4.class)
public class ParserTest {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@Rule public Watcher watcher = new Watcher(logger);
private static String mockedTestDir() {
return "./d1/d2/";
}
private static String mockedProject() {
return "myTestProject";
}
private static Parser.Result testLine(String line) {
Parser.Result result = new Parser.Result();
// Single line parser tests do not need repoManager.
Parser parser = new Parser(null, null, mockedProject(), "master", "OWNERS");
parser.parseLine(result, mockedTestDir(), line, 3);
return result;
}
private static String testLineWarningMsg(String line) {
// expected warning message created by testLine(line)
return Parser.warningMsg("OWNERS", 3, "ignored", line);
}
private static String testLineErrorMsg(String line) {
// expected error message created by testLine(line)
return Parser.errorMsg("OWNERS", 3, "ignored unknown line", line);
}
@Test
public void emptyParserResult() {
Parser.Result result = new Parser.Result();
assertThat(result.stopLooking).isFalse();
assertThat(result.warnings).isEmpty();
assertThat(result.errors).isEmpty();
assertThat(result.owner2paths).isEmpty();
}
@Test
public void badLineTest() {
String[] lines = {"actor", "a@b@c", "**", "per-files *.gyp", "a@b.com@c.com #..."};
for (String s : lines) {
Parser.Result result = testLine(s);
assertThat(result.warnings).isEmpty();
String expected = testLineErrorMsg(s);
assertThat(result.errors).containsExactly(expected);
}
}
@Test
public void appendResultTest() {
String s1 = "a@b@c";
String s2 = "**";
String e1 = testLineErrorMsg(s1);
String e2 = testLineErrorMsg(s2);
String w1 = testLineWarningMsg("w1");
String w2 = testLineWarningMsg("w2");
String b1 = "d1/*.c";
String b2 = "d2/*.java";
Parser.Result r1 = testLine(s1);
Parser.Result r2 = testLine(s2);
assertThat(r1.warnings).isEmpty();
assertThat(r2.warnings).isEmpty();
assertThat(r1.noParentGlobs).isEmpty();
assertThat(r2.noParentGlobs).isEmpty();
assertThat(r1.errors).containsExactly(e1);
assertThat(r2.errors).containsExactly(e2);
r1.warnings.add(w1);
r2.warnings.add(w2);
r1.noParentGlobs.add(b1);
r2.noParentGlobs.add(b2);
assertThat(r1.owner2paths).isEmpty();
assertThat(r2.owner2paths).isEmpty();
r2.append(r1, "", true);
assertThat(r1.owner2paths).isEmpty();
assertThat(r2.owner2paths).isEmpty();
assertThat(r2.warnings).containsExactly(w2, w1);
assertThat(r2.noParentGlobs).containsExactly(b2, b1);
assertThat(r1.noParentGlobs).containsExactly(b1);
assertThat(r2.errors).containsExactly(e2, e1);
r1.append(r2, "", true);
assertThat(r1.owner2paths).isEmpty();
assertThat(r2.owner2paths).isEmpty();
// warnings, errors, and noParentGlobs are sets of strings.
// containsExactly does not check order of elements.
assertThat(r1.warnings).containsExactly(w1, w2);
assertThat(r1.warnings).containsExactly(w2, w1);
assertThat(r1.noParentGlobs).containsExactly(b2, b1);
assertThat(r1.errors).containsExactly(e1, e2);
assertThat(r1.errors).containsExactly(e2, e1);
}
@Test
public void commentLineTest() {
String[] lines = {"", " ", "# comment #data", "#any", " # comment"};
for (String s : lines) {
Parser.Result result = testLine(s);
assertThat(result.stopLooking).isFalse();
assertThat(result.warnings).isEmpty();
assertThat(result.errors).isEmpty();
assertThat(result.owner2paths).isEmpty();
}
}
@Test
public void emailLineTest() {
String[] lines = {"a_b-c3@google.com", " x.y.z@gmail.com # comment", "*", " * # any user"};
String[] emails = {"a_b-c3@google.com", "x.y.z@gmail.com", "*", "*"};
for (int i = 0; i < lines.length; i++) {
Parser.Result result = testLine(lines[i]);
assertThat(result.owner2paths).hasSize(1);
String[] paths = result.owner2paths.get(emails[i]).toArray(new String[1]);
assertThat(paths).hasLength(1);
assertThat(paths[0]).isEqualTo(mockedTestDir());
}
}
@Test
public void fileLineTest() {
// file: statement should work like include.
String[] lines = {"file://owners", " file: //d1/owner", "file:owner #"};
for (String s : lines) {
Parser.Result result = testLine(s);
assertThat(result.warnings).isEmpty();
assertThat(result.errors).isEmpty();
}
testOneFileLine("P0", " file: //common/OWNERS #comment", "P0", "//common/OWNERS");
testOneFileLine("P1", "file: Other : /Group ", "Other", "/Group");
testOneFileLine("P2", "file: /Common/Project: OWNER", "/Common/Project", "OWNER");
testOneFileLine("P3", " file: \tP2/D2:/D3/F.txt", "P2/D2", "/D3/F.txt");
testOneFileLine("P4", " file: \tP2/D2://D3/F.txt", "P2/D2", "//D3/F.txt");
testOneFileLine("P5", "\t file: \t P2/D2:\t/D3/F2.txt\n", "P2/D2", "/D3/F2.txt");
testOneFileLine("P6", "file: ../d1/d2/F \n", "P6", "../d1/d2/F");
}
@Test
public void noParentLineTest() {
String[] lines = {"set noparent", " set noparent", "set noparent # comment"};
for (String line : lines) {
Parser.Result result = testLine(line);
assertThat(result.stopLooking).isTrue();
testLine(line);
}
}
@Test
public void perFileGoodDirectiveTest() {
String[] directives = {
"abc@google.com#comment",
" *# comment",
" xyz@gmail.com # comment",
"a@g.com , xyz@gmail.com , * # comment",
"*,*#comment",
" a@b,c@d ",
" set noparent ",
"\tset\t\tnoparent\t",
"file://java.owners",
" file: p1/p2 : /OWNERS "
};
String[] globsList = {"*", "*,*.c", " *test*.java , *.cc, *.cpp ", "*.bp,*.mk ,A* "};
for (String directive : directives) {
for (String globs : globsList) {
String line = "per-file " + globs + "=" + directive;
Parser.Result result = testLine(line);
String[] directiveList = directive.replaceAll("#.*$", "").trim().split(Parser.COMMA, -1);
String[] globList = globs.trim().split(Parser.COMMA);
Arrays.sort(globList);
String[] owners = Parser.parsePerFileOwners(line);
assertThat(owners).hasLength(directiveList.length);
for (String email : owners) {
String e = email.trim();
assertThat(result.stopLooking).isFalse();
if (e.equals(Parser.TOK_SET_NOPARENT)) {
assertThat(result.owner2paths).isEmpty(); // no other owners in this per-file
assertThat(result.noParentGlobs).hasSize(globList.length);
for (String glob : globList) {
assertThat(result.noParentGlobs).contains(mockedTestDir() + glob);
}
} else if (e.startsWith("file:")) {
// If per-file has file: directive, it cannot have any other directive.
assertThat(e).isEqualTo(Parser.removeExtraSpaces(directive));
assertThat(owners).hasLength(1);
} else {
String[] paths = result.owner2paths.get(e).toArray(new String[1]);
assertThat(paths).hasLength(globList.length); // should not work for "set noparent"
Arrays.sort(paths);
for (int g = 0; g < globList.length; g++) {
assertThat(paths[g]).isEqualTo(mockedTestDir() + globList[g]);
}
}
}
}
}
}
@Test
public void perFileBadDirectiveTest() {
String[] directives = {
" ** ",
"a b@c .co",
"a@b@c #com",
"a.<b>@zc#",
" , a@b ",
"a@b, , c@d #",
"a@b, set noparent",
"a@b, file://java.owners",
"*,file:OWNERS"
};
for (String directive : directives) {
String line = "per-file *test*.c=" + directive;
Parser.Result result = testLine(line);
String expected = testLineErrorMsg(line);
assertThat(result.warnings).isEmpty();
assertThat(result.errors).containsExactly(expected);
}
}
private void testOneIncludeOrFileLine(
String project, String line, String keyword, String projectName, String filePath) {
String[] results = Parser.parseInclude(project, line);
assertThat(results).hasLength(3);
assertThat(results[0]).isEqualTo(keyword);
assertThat(results[1]).isEqualTo(projectName);
assertThat(results[2]).isEqualTo(filePath);
}
private void testOneFileLine(String project, String line, String projectName, String filePath) {
testOneIncludeOrFileLine(project, line, "file", projectName, filePath);
}
private void testOneIncludeLine(
String project, String line, String projectName, String filePath) {
testOneIncludeOrFileLine(project, line, "include", projectName, filePath);
}
@Test
public void includeLineTest() {
testOneIncludeLine("P0", " include /common/OWNERS #comment", "P0", "/common/OWNERS");
testOneIncludeLine("P1", "include Other : /Group ", "Other", "/Group");
testOneIncludeLine("P2", "include /Common/Project: OWNER", "/Common/Project", "OWNER");
testOneIncludeLine("P3", " include \tP2/D2:/D3/F.txt", "P2/D2", "/D3/F.txt");
testOneIncludeLine("P4", "\t include \t P2/D2:\t/D3/F2.txt\n", "P2/D2", "/D3/F2.txt");
testOneIncludeLine("P5", "include ../d1/d2/F \n", "P5", "../d1/d2/F");
}
@Test
public void getIncludeOrFileTest() {
String[] line =
new String[] {
"",
"wrong input",
"INCLUDE X",
"include //f2.txt # ",
" include P1/P2: ../f1 # ",
" file://f3 # ",
"file: P1:f3",
" per-file *.c,file.c = file: /OWNERS # ",
"per-file *=file:P1/P2: /O# ",
};
String[] expected =
new String[] {
"",
"",
"",
"include //f2.txt",
"include P1/P2:../f1",
"file://f3",
"file:P1:f3",
"file:/OWNERS",
"file:P1/P2:/O",
};
for (int i = 0; i < line.length; i++) {
assertThat(Parser.getIncludeOrFile(line[i])).isEqualTo(expected[i]);
}
}
@Test
public void errorMsgTest() {
String file = "./OWNERS";
int n = 5;
String msg = "error X";
String line = "a@@a";
String location = file + ":" + n + ": " + msg + ": [" + line + "]";
String error = "Error: " + location;
String warning = "Warning: " + location;
assertThat(Parser.errorMsg(file, n, msg, line)).isEqualTo(error);
assertThat(Parser.warningMsg(file, n, msg, line)).isEqualTo(warning);
}
}