// Copyright 2015 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.devtools.build.lib.packages.util; import static com.google.common.truth.Truth.assertWithMessage; import com.google.common.collect.Iterables; import com.google.devtools.build.docgen.DocCheckerUtils; import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider; import com.google.devtools.build.lib.runtime.BlazeCommand; import com.google.devtools.build.lib.runtime.BlazeCommandUtils; import com.google.devtools.build.lib.runtime.BlazeModule; import com.google.devtools.build.lib.runtime.BlazeRuntime; import com.google.devtools.common.options.Options; import com.google.devtools.common.options.OptionsBase; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Utility functions for validating correctness of Bazel documentation. */ public abstract class DocumentationTestUtil { private DocumentationTestUtil() {} private static final Pattern CODE_FLAG_PATTERN = Pattern.compile( "--([a-z_\\[\\]]*)<\\/code>", Pattern.CASE_INSENSITIVE); /** * Validates that a user manual {@code documentationSource} contains only * the flags actually provided by a given set of modules. */ public static void validateUserManual( List> modules, ConfiguredRuleClassProvider ruleClassProvider, String documentationSource) throws IOException { // if there is a class missing, one can find it using // find . -name "*.java" -exec grep -Hn "@Option(name = " {} \; | grep "xxx" // where 'xxx' is a flag name. List blazeModules = BlazeRuntime.createModules(modules); Map optionsMap = new HashMap<>(); // collect all startup options for (Class optionsClass : BlazeCommandUtils.getStartupOptions(blazeModules)) { optionsMap.putAll(Options.getDefaults(optionsClass).asMap()); } // collect all command options List blazeCommands = new ArrayList<>(); blazeCommands.addAll(BlazeRuntime.getBuiltinCommandList()); for (BlazeModule module : blazeModules) { Iterables.addAll(blazeCommands, module.getCommands()); } for (BlazeCommand command : blazeCommands) { for (Class optionClass : BlazeCommandUtils.getOptions(command.getClass(), blazeModules, ruleClassProvider)) { optionsMap.putAll(Options.getDefaults(optionClass).asMap()); } } // check validity of option flags in manual Matcher anchorMatcher = CODE_FLAG_PATTERN.matcher(documentationSource); String flag; boolean found; while (anchorMatcher.find()) { flag = anchorMatcher.group(1); found = optionsMap.containsKey(flag); if (!found && flag.startsWith("no")) { found = optionsMap.containsKey(flag.substring(2)); } if (!found && flag.startsWith("[no]")) { found = optionsMap.containsKey(flag.substring(4)); } assertWithMessage("flag '" + flag + "' is not a blaze option (anymore)").that(found).isTrue(); } String unclosedTag = DocCheckerUtils.getFirstUnclosedTagAndPrintHelp(documentationSource); assertWithMessage("Unclosed tag found: " + unclosedTag).that(unclosedTag).isNull(); } }