| /* |
| * Copyright 2016 The Bazel Authors. All rights reserved. |
| * |
| * 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.google.idea.blaze.base.lang.buildfile.validation; |
| |
| import com.google.idea.blaze.base.lang.buildfile.BuildFileIntegrationTestCase; |
| import com.google.idea.blaze.base.lang.buildfile.psi.BuildFile; |
| import com.google.idea.blaze.base.lang.buildfile.psi.GlobExpression; |
| import com.google.idea.blaze.base.lang.buildfile.psi.util.PsiUtils; |
| import com.intellij.codeInsight.daemon.impl.AnnotationHolderImpl; |
| import com.intellij.lang.annotation.Annotation; |
| import com.intellij.lang.annotation.AnnotationHolder; |
| import com.intellij.lang.annotation.AnnotationSession; |
| import com.intellij.psi.PsiFile; |
| |
| import java.util.List; |
| import java.util.stream.Collectors; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| |
| /** |
| * Tests glob validation. |
| */ |
| public class GlobValidationTest extends BuildFileIntegrationTestCase { |
| |
| public void testNormalGlob() { |
| BuildFile file = createBuildFile( |
| "java/com/google/BUILD", |
| "glob(['**/*.java'])"); |
| |
| assertNoErrors(file); |
| } |
| |
| public void testNamedIncludeArgument() { |
| BuildFile file = createBuildFile( |
| "java/com/google/BUILD", |
| "glob(include = ['**/*.java'])"); |
| |
| assertNoErrors(file); |
| } |
| |
| public void testAllArguments() { |
| BuildFile file = createBuildFile( |
| "java/com/google/BUILD", |
| "glob(['**/*.java'], exclude = ['test/*.java'], exclude_directories = 0)"); |
| |
| assertNoErrors(file); |
| } |
| |
| public void testEmptyExcludeList() { |
| BuildFile file = createBuildFile( |
| "java/com/google/BUILD", |
| "glob(['**/*.java'], exclude = [])"); |
| |
| assertNoErrors(file); |
| } |
| |
| public void testNoIncludesError() { |
| BuildFile file = createBuildFile( |
| "java/com/google/BUILD", |
| "glob(exclude = ['BUILD'])"); |
| |
| assertHasError(file, "Glob expression must contain at least one included string"); |
| } |
| |
| public void testSingletonExcludeArgumentError() { |
| BuildFile file = createBuildFile( |
| "java/com/google/BUILD", |
| "glob(['**/*.java'], exclude = 'BUILD')"); |
| |
| assertHasError(file, "Glob parameter 'exclude' must be a list of strings"); |
| } |
| |
| public void testSingletonIncludeArgumentError() { |
| BuildFile file = createBuildFile( |
| "java/com/google/BUILD", |
| "glob(include = '**/*.java')"); |
| |
| assertHasError(file, "Glob parameter 'include' must be a list of strings"); |
| } |
| |
| public void testInvalidExcludeDirectoriesValue() { |
| BuildFile file = createBuildFile( |
| "java/com/google/BUILD", |
| "glob(['**/*.java'], exclude = ['test/*.java'], exclude_directories = true)"); |
| |
| assertHasError(file, "exclude_directories parameter to glob must be 0 or 1"); |
| } |
| |
| public void testUnrecognizedArgumentError() { |
| BuildFile file = createBuildFile( |
| "java/com/google/BUILD", |
| "glob(['**/*.java'], exclude = ['test/*.java'], extra = 1)"); |
| |
| assertHasError(file, "Unrecognized glob argument"); |
| } |
| |
| public void testInvalidListArgumentValue() { |
| BuildFile file = createBuildFile( |
| "java/com/google/BUILD", |
| "glob(include = foo)"); |
| |
| assertHasError(file, "Glob parameter 'include' must be a list of strings"); |
| } |
| |
| public void testLocalVariableReference() { |
| BuildFile file = createBuildFile( |
| "java/com/google/BUILD", |
| "foo = ['*.java']", |
| "glob(include = foo)"); |
| |
| assertNoErrors(file); |
| } |
| |
| public void testLoadedVariableReference() { |
| BuildFile ext = createBuildFile( |
| "java/com/foo/vars.bzl", |
| "LIST_VAR = ['*']"); |
| BuildFile file = createBuildFile( |
| "java/com/google/BUILD", |
| "load('//java/com/foo:vars.bzl', 'LIST_VAR')", |
| "glob(include = LIST_VAR)"); |
| |
| assertNoErrors(file); |
| } |
| |
| public void testInvalidLoadedVariableReference() { |
| BuildFile ext = createBuildFile( |
| "java/com/foo/vars.bzl", |
| "LIST_VAR = ['*']", |
| "def function()"); |
| BuildFile file = createBuildFile( |
| "java/com/google/BUILD", |
| "load('//java/com/foo:vars.bzl', 'LIST_VAR', 'function')", |
| "glob(include = function)"); |
| |
| assertHasError(file, "Glob parameter 'include' must be a list of strings"); |
| } |
| |
| public void testUnresolvedReferenceExpression() { |
| BuildFile file = createBuildFile( |
| "java/com/google/BUILD", |
| "glob(include = ref)"); |
| |
| assertHasError(file, "Glob parameter 'include' must be a list of strings"); |
| } |
| |
| public void testPossibleListExpressionFuncallExpression() { |
| BuildFile file = createBuildFile( |
| "java/com/google/BUILD", |
| "glob(include = fn.list)"); |
| |
| assertNoErrors(file); |
| } |
| |
| public void testPossibleListExpressionParameter() { |
| BuildFile file = createBuildFile( |
| "java/com/google/BUILD", |
| "def function(param1, param2):", |
| " glob(include = param1)"); |
| |
| assertNoErrors(file); |
| } |
| |
| public void testNestedGlobs() { |
| // blaze accepts nested globs |
| BuildFile file = createBuildFile( |
| "java/com/google/BUILD", |
| "glob(glob(['*.java']))"); |
| |
| assertNoErrors(file); |
| } |
| |
| public void testKnownInvalidResolvedListExpression() { |
| BuildFile file = createBuildFile( |
| "java/com/google/BUILD", |
| "bool_literal = True", |
| "glob(bool_literal)"); |
| |
| assertHasError(file, "Glob parameter 'include' must be a list of strings"); |
| } |
| |
| public void testKnownInvalidResolvedString() { |
| BuildFile file = createBuildFile( |
| "java/com/google/BUILD", |
| "bool_literal = True", |
| "glob([bool_literal])"); |
| |
| assertHasError(file, "Glob parameter 'include' must be a list of strings"); |
| } |
| |
| public void testPossibleStringLiteralIfStatement() { |
| BuildFile file = createBuildFile( |
| "java/com/google/BUILD", |
| "glob(include = ['*.java', if test : a else b])"); |
| |
| // we don't know what the IfStatement evaluates to |
| assertNoErrors(file); |
| } |
| |
| public void testPossibleStringLiteralParameter() { |
| BuildFile file = createBuildFile( |
| "java/com/google/BUILD", |
| "def function(param1, param2):", |
| " glob(include = [param1])"); |
| |
| assertNoErrors(file); |
| } |
| |
| private void assertNoErrors(BuildFile file) { |
| assertThat(validateFile(file)).isEmpty(); |
| } |
| |
| private void assertHasError(BuildFile file, String error) { |
| assertHasError(validateFile(file), error); |
| } |
| |
| private void assertHasError(List<Annotation> annotations, String error) { |
| List<String> messages = annotations.stream() |
| .map(Annotation::getMessage) |
| .collect(Collectors.toList()); |
| |
| assertThat(messages).contains(error); |
| } |
| |
| private List<Annotation> validateFile(BuildFile file) { |
| GlobErrorAnnotator annotator = createAnnotator(file); |
| for (GlobExpression glob : PsiUtils.findAllChildrenOfClassRecursive(file, GlobExpression.class)) { |
| annotator.visitGlobExpression(glob); |
| } |
| return annotationHolder; |
| } |
| |
| private GlobErrorAnnotator createAnnotator(PsiFile file) { |
| annotationHolder = new AnnotationHolderImpl(new AnnotationSession(file)); |
| return new GlobErrorAnnotator() { |
| @Override |
| protected AnnotationHolder getHolder() { |
| return annotationHolder; |
| } |
| }; |
| } |
| |
| private AnnotationHolderImpl annotationHolder = null; |
| |
| } |