def proc_batch(files): """Runs each task in the pipeline on batches of files. These tasks read and write to the files directly. They are given a list of all files at once to avoid spawning too many subprocesses. Keyword arguments: files -- list of file names Returns true if all tasks succeeded. """ all_success = True for subtask in task_pipeline: work = [] for name in files: config_file = Config(os.path.dirname(name), ".styleguide") if subtask.should_process_file(config_file, name): work.append(name) if work: if verbose1 or verbose2: print("Running", type(subtask).__name__) if verbose2: for name in work: print(" on", name) all_success &= subtask.run_batch(config_file, work) return all_success
def run(self, output_type): """Runs the task on each input list element, then compares the resulting output against the corresponding output list element. output_type == FILE: output list contains file contents output_type == STDOUT: output list contains stdout contents Keyword Arguments: output_type -- the type of output stored in the output list """ assert len(self.inputs) == len(self.outputs) config_file = Config(os.path.abspath(os.getcwd()), ".styleguide") for i in range(len(self.inputs)): if self.task.should_process_file(config_file, self.inputs[i][0]): print("Running test {}...".format(i)) if output_type == OutputType.FILE: output, success = self.task.run_pipeline( config_file, self.inputs[i][0], self.inputs[i][1]) elif output_type == OutputType.STDOUT: saved_stdout = sys.stdout sys.stdout = io.StringIO() _output, success = self.task.run_pipeline( config_file, self.inputs[i][0], self.inputs[i][1]) sys.stdout.seek(0) output = sys.stdout.read() sys.stdout = saved_stdout assert output == self.outputs[i][0] assert success == self.outputs[i][2]
def proc_batch(files): """Runs each task in the pipeline on batches of files. These tasks read and write to the files directly. They are given a list of all files at once to avoid spawning too many subprocesses. Keyword arguments: files -- list of file names Returns true if all tasks succeeded. """ all_success = True for subtask in task_pipeline: work = [] for name in files: config_file = Config(os.path.dirname(name), ".styleguide") if subtask.should_process_file(config_file, name): work.append(name) if work: # Conservative estimate for max argument length. 32767 is from the # Win32 docs for CreateProcessA(), but the limit appears to be lower # than that in practice. MAX_WIN32_ARGS_LEN = 32767 * 7 / 8 for subwork in chunks(work, MAX_WIN32_ARGS_LEN): if verbose1 or verbose2: print("Running", type(subtask).__name__) if verbose2: for name in subwork: print(" on", name) all_success &= subtask.run_batch(config_file, subwork) return all_success
def run_pipeline(self, config_file, name, lines): linesep = super().get_linesep(lines) license_template = Config.read_file( os.path.dirname(os.path.abspath(name)), ".styleguide-license") # Get year when file was most recently modified in Git history # # Committer date is used instead of author date (the one shown by "git # log" because the year the file was last modified in the history should # be used. Author dates can be older than this or even out of order in # the log. cmd = ["git", "log", "-n", "1", "--format=%ci", "--", name] last_year = subprocess.run(cmd, stdout=subprocess.PIPE).stdout.decode()[:4] # Check if file has uncomitted changes in the working directory cmd = ["git", "diff-index", "--quiet", "HEAD", "--", name] has_uncommitted_changes = subprocess.run(cmd).returncode # If file hasn't been committed yet or has changes in the working # directory, use current calendar year as end of copyright year range if last_year == "" or has_uncommitted_changes: last_year = str(date.today().year) success, first_year, appendix = self.__try_regex( lines, last_year, license_template) if not success: success, first_year, appendix = self.__try_string_search( lines, last_year, license_template) output = "" # Determine copyright range and trailing padding if first_year != last_year: year_range = first_year + "-" + last_year else: year_range = first_year for line in license_template: # Insert copyright year range line = line.replace("{year}", year_range) # Insert padding which expands to the 80th column. If there is more # than one padding token, the line may contain fewer than 80 # characters due to rounding during the padding width calculation. PADDING_TOKEN = "{padding}" padding_count = line.count(PADDING_TOKEN) if padding_count: padding = 80 - len(line) + len(PADDING_TOKEN) * padding_count padding_width = int(padding / padding_count) line = line.replace(PADDING_TOKEN, " " * padding_width) output += line + linesep # Copy rest of original file into new one output += appendix return output, True
def test_usingdeclaration(): task = UsingDeclaration() inputs = [] outputs = [] # Before class block inputs.append(("./Test.h", "using std::chrono;" + os.linesep + \ "class Test {" + os.linesep + \ "}" + os.linesep)) outputs.append(("./Test.h: 1: 'using std::chrono;' in global namespace\n", False, False)) # Inside enum block inputs.append(("./Test.h", "enum Test {" + os.linesep + \ " using std::chrono;" + os.linesep + \ "}" + os.linesep)) outputs.append(("", False, True)) # After { block inputs.append(("./Test.h", "{" + os.linesep + \ "}" + os.linesep + \ "using std::chrono;" + os.linesep)) outputs.append(("./Test.h: 3: 'using std::chrono;' in global namespace\n", False, False)) # Before class block with NOLINT inputs.append(("./Test.h", "using std::chrono; // NOLINT" + os.linesep + \ "class Test {" + os.linesep + \ "}" + os.linesep)) outputs.append(("", False, True)) assert len(inputs) == len(outputs) config_file = Config(os.path.abspath(os.getcwd()), ".styleguide") saved_stdout = sys.stdout for i in range(len(inputs)): new_stdout = io.StringIO() sys.stdout = new_stdout unused_output, file_changed, success = task.run_pipeline( config_file, inputs[i][0], inputs[i][1]) sys.stdout = saved_stdout new_stdout.seek(0) output = new_stdout.read() assert output == outputs[i][0] assert file_changed == outputs[i][1] assert success == outputs[i][2]
def test_config(): config_file = Config(os.path.abspath(os.getcwd()), ".styleguide") assert config_file.is_modifiable_file("." + os.sep + "wpiformat" + os.sep + "javaguidelink.png") assert config_file.is_generated_file("." + os.sep + "wpiformat" + os.sep + "wpiformat" + os.sep + "cpplint.py") assert not config_file.is_generated_file("." + os.sep + "wpiformat" + os.sep + "diff_cpplint.py") assert not config_file.is_generated_file("." + os.sep + "wpiformat" + os.sep + "update_cpplint.py") assert not config_file.is_modifiable_file("." + os.sep + "wpiformat" + os.sep + "license.txt")
def test_whitespace(): task = Whitespace() inputs = [] outputs = [] file_appendix = \ "#pragma once" + os.linesep + \ os.linesep + \ "#include <iostream>" + os.linesep + \ os.linesep + \ "int main() {" + os.linesep + \ " std::cout << \"Hello World!\";" + os.linesep + \ "}" + os.linesep # Empty file inputs.append(("./Test.h", "")) outputs.append(("", False, True)) # No trailing whitespace inputs.append(("./Test.h", file_appendix)) outputs.append((file_appendix, False, True)) # Two spaces trailing inputs.append( ("./Test.h", "#pragma once" + os.linesep + os.linesep + "#include <iostream>" + os.linesep + os.linesep + "int main() { " + os.linesep + " std::cout << \"Hello World!\"; " + os.linesep + "}" + os.linesep)) outputs.append((file_appendix, True, True)) # Two tabs trailing inputs.append(( "./Test.h", "#pragma once" + os.linesep + os.linesep + "#include <iostream>" + os.linesep + os.linesep + "int main() {\t\t" + os.linesep + " std::cout << \"Hello World!\";\t\t" + os.linesep + "}" + os.linesep)) outputs.append((file_appendix, True, True)) assert len(inputs) == len(outputs) config_file = Config(os.path.abspath(os.getcwd()), ".styleguide") for i in range(len(inputs)): output, file_changed, success = task.run_pipeline( config_file, inputs[i][0], inputs[i][1]) assert output == outputs[i][0] assert file_changed == outputs[i][1] assert success == outputs[i][2]
def proc_pipeline(name): """Runs the contents of each files through the task pipeline. If the contents were modified at any point, the result is written back out to the file. Keyword arguments: name -- file name string """ config_file = Config(os.path.dirname(name), ".styleguide") if verbose1 or verbose2: with print_lock: print("Processing", name) if verbose2: for subtask in task_pipeline: if subtask.should_process_file(config_file, name): print(" with " + type(subtask).__name__) lines = "" with open(name, "r") as file: try: lines = file.read() except UnicodeDecodeError: print("Error: " + name + " contains characters not in UTF-8. " "Should this be considered a generated file?") return False file_changed = False # The success flag is aggregated across multiple file processing results all_success = True for subtask in task_pipeline: if subtask.should_process_file(config_file, name): lines, changed, success = subtask.run_pipeline( config_file, name, lines) file_changed |= changed all_success &= success if file_changed: with open(name, "wb") as file: file.write(lines.encode()) # After file is written, reset file_changed flag file_changed = False return all_success
def test_newline(): task = Newline() inputs = [] outputs = [] file_appendix = \ "#pragma once" + os.linesep + \ os.linesep + \ "#include <iostream>" + os.linesep + \ os.linesep + \ "int main() {" + os.linesep + \ " std::cout << \"Hello World!\";" + os.linesep + \ "}" # Empty file inputs.append(("./Test.h", "")) outputs.append(("\n", True, True)) # No newline inputs.append(("./Test.h", file_appendix)) outputs.append((file_appendix + os.linesep, True, True)) # One newline inputs.append((inputs[1][0], inputs[1][1] + os.linesep)) outputs.append((outputs[1][0], False, True)) # Two newlines inputs.append((inputs[1][0], inputs[1][1] + os.linesep + os.linesep)) outputs.append((outputs[1][0], True, True)) # .bat file with no "./" prefix inputs.append(("test.bat", inputs[1][1].replace(os.linesep, "\r\n"))) outputs.append((outputs[1][0].replace(os.linesep, "\r\n"), True, True)) assert len(inputs) == len(outputs) config_file = Config(os.path.abspath(os.getcwd()), ".styleguide") for i in range(len(inputs)): output, file_changed, success = task.run_pipeline( config_file, inputs[i][0], inputs[i][1]) assert output == outputs[i][0] assert file_changed == outputs[i][1] assert success == outputs[i][2]
def run_pipeline(self, config_file, name, lines): linesep = Task.get_linesep(lines) license_template = Config.read_file( os.path.dirname(os.path.abspath(name)), ".styleguide-license") success, year, appendix = self.__try_regex(lines, license_template) if not success: success, year, appendix = self.__try_string_search( lines, license_template) output = "" # Determine copyright range and trailing padding if year != self.__current_year: year = year + "-" + self.__current_year for line in license_template: # Insert copyright year range line = line.replace("{year}", year) # Insert padding which expands to the 80th column. If there is more # than one padding token, the line may contain fewer than 80 # characters due to rounding during the padding width calculation. PADDING_TOKEN = "{padding}" padding_count = line.count(PADDING_TOKEN) if padding_count: padding = 80 - len(line) + len(PADDING_TOKEN) * padding_count padding_width = int(padding / padding_count) line = line.replace(PADDING_TOKEN, " " * padding_width) output += line + linesep # Copy rest of original file into new one output += appendix return (output, lines != output, True)
def test_usingnamespacestd(): task = UsingNamespaceStd() inputs = [] outputs = [] warning_str = "avoid \"using namespace std;\" in production software. While it is used in introductory C++, it pollutes the global namespace with standard library symbols.\n" # Hello World inputs.append(("./Main.cpp", "using namespace std;" + os.linesep + \ os.linesep + \ "int main() {" + os.linesep + \ " cout << \"Hello World!\"" + os.linesep + \ "}" + os.linesep)) outputs.append(("Warning: ./Main.cpp: 1: " + warning_str, False, True)) # Inside braces and not first line inputs.append(("./Main.cpp", "int main() {" + os.linesep + \ " using namespace std;" + os.linesep + \ " cout << \"Hello World!\"" + os.linesep + \ "}" + os.linesep)) outputs.append(("Warning: ./Main.cpp: 2: " + warning_str, False, True)) # std::chrono inputs.append(("./Main.cpp", "#include <thread>" + os.linesep + \ os.linesep + \ "int main() {" + os.linesep + \ " using namespace std::chrono;" + os.linesep + \ os.linesep + \ " std::this_thread::sleep_for(10ms);" + os.linesep + \ "}" + os.linesep)) outputs.append(("Warning: ./Main.cpp: 4: " + warning_str, False, True)) # Ignore std::literals inputs.append( ("./Main.cpp", "using namespace std::literals;" + os.linesep)) outputs.append(("", False, True)) # Ignore std::chrono_literals inputs.append( ("./Main.cpp", "using namespace std::chrono_literals;" + os.linesep)) outputs.append(("", False, True)) # Ignore std::placeholders inputs.append( ("./Main.cpp", "using namespace std::placeholders;" + os.linesep)) outputs.append(("", False, True)) assert len(inputs) == len(outputs) config_file = Config(os.path.abspath(os.getcwd()), ".styleguide") saved_stdout = sys.stdout for i in range(len(inputs)): new_stdout = io.StringIO() sys.stdout = new_stdout unused_output, file_changed, success = task.run_pipeline( config_file, inputs[i][0], inputs[i][1]) sys.stdout = saved_stdout new_stdout.seek(0) output = new_stdout.read() print("Running test {}...".format(i)) print("output=", output) print("outputs[i][0]=", outputs[i][0]) assert output == outputs[i][0] assert file_changed == outputs[i][1] assert success == outputs[i][2]
def test_includeorder(): task = IncludeOrder() inputs = [] outputs = [] # cpp source including related header with wrong include braces and C++ sys # before C sys headers inputs.append(("./Utility.cpp", "#include <Utility.h>" + os.linesep + \ os.linesep + \ "#include <sstream>" + os.linesep + \ os.linesep + \ "#include <cxxabi.h>" + os.linesep + \ "#include <execinfo.h>" + os.linesep + \ os.linesep + \ "#include \"HAL/HAL.h\"" + os.linesep + \ "#include \"Task.h\"" + os.linesep + \ "#include \"nivision.h\"" + os.linesep)) outputs.append(( "#include \"Utility.h\"" + os.linesep + \ os.linesep + \ "#include <cxxabi.h>" + os.linesep + \ "#include <execinfo.h>" + os.linesep + \ os.linesep + \ "#include <sstream>" + os.linesep + \ os.linesep + \ "#include \"HAL/HAL.h\"" + os.linesep + \ "#include \"Task.h\"" + os.linesep + \ "#include \"nivision.h\"" + os.linesep, True, True)) # Ensure quotes around C and C++ std header includes are replaced with # angle brackets and they are properly sorted into two groups inputs.append(("./Test.h", "#include \"stdio.h\"" + os.linesep + \ "#include \"iostream\"" + os.linesep + \ "#include \"memory\"" + os.linesep + \ "#include \"signal.h\"" + os.linesep)) outputs.append(( "#include <signal.h>" + os.linesep + \ "#include <stdio.h>" + os.linesep + \ os.linesep + \ "#include <iostream>" + os.linesep + \ "#include <memory>" + os.linesep, True, True)) # Ensure NOLINT headers take precedence over overrides and have newline # inserted inputs.append(("./Test.h", "#include \"ctre/PDP.h\" // NOLINT" + os.linesep + \ "#include <atomic>" + os.linesep + \ "#include <condition_variable>" + os.linesep + \ "#include <thread>" + os.linesep)) outputs.append(( "#include \"ctre/PDP.h\" // NOLINT" + os.linesep + \ os.linesep + \ "#include <atomic>" + os.linesep + \ "#include <condition_variable>" + os.linesep + \ "#include <thread>" + os.linesep, True, True)) # Check sorting for at least one header from each group except related # header. Test.inc isn't considered related in headers. inputs.append(("./Test.h", "#include \"MyHeader.h\"" + os.linesep + \ "#include <stdio.h>" + os.linesep + \ "#include \"Test.inc\"" + os.linesep + \ "#include <sys/time.h>" + os.linesep + \ "#include <fstream>" + os.linesep + \ "#include <boost/algorithm/string/replace.hpp>" + os.linesep)) outputs.append(( "#include <stdio.h>" + os.linesep + \ "#include <sys/time.h>" + os.linesep + \ os.linesep + \ "#include <fstream>" + os.linesep + \ os.linesep + \ "#include <boost/algorithm/string/replace.hpp>" + os.linesep + \ os.linesep + \ "#include \"MyHeader.h\"" + os.linesep + \ "#include \"Test.inc\"" + os.linesep, True, True)) # Verify "other header" isn't identified as C system include inputs.append(("./Test.h", "#include <OtherHeader.h>" + os.linesep + \ "#include <sys/time.h>" + os.linesep)) outputs.append(( "#include <sys/time.h>" + os.linesep + \ os.linesep + \ "#include <OtherHeader.h>" + os.linesep, True, True)) # Verify newline is added between last header and code after it inputs.append(("./Test.cpp", "#include \"MyFile.h\"" + os.linesep + \ os.linesep + \ "#include <iostream>" + os.linesep + \ "namespace std {" + os.linesep + \ "}" + os.linesep)) outputs.append(( "#include <iostream>" + os.linesep + \ os.linesep + \ "#include \"MyFile.h\"" + os.linesep + \ os.linesep + \ "namespace std {" + os.linesep + \ "}" + os.linesep, True, True)) # Verify newlines are removed between last header and code after it inputs.append(("./Test.cpp", "#include \"MyFile.h\"" + os.linesep + \ os.linesep + \ "#include <iostream>" + os.linesep + \ os.linesep + \ os.linesep + \ os.linesep + \ "namespace std {" + os.linesep + \ "}" + os.linesep)) outputs.append((outputs[len(outputs) - 1][0], True, True)) # Ensure headers stay grouped together between license header and other code inputs.append(("./Test.cpp", "// Copyright (c) Company Name 2016." + os.linesep + \ "#include <iostream>" + os.linesep + \ "#include \"Test.h\"" + os.linesep + \ "namespace std {" + os.linesep + \ "}" + os.linesep)) outputs.append(( "// Copyright (c) Company Name 2016." + os.linesep + \ "#include \"Test.h\"" + os.linesep + \ os.linesep + \ "#include <iostream>" + os.linesep + \ os.linesep + \ "namespace std {" + os.linesep + \ "}" + os.linesep, True, True)) # Verify headers are sorted across #ifdef inputs.append(("./Error.h", "#pragma once" + os.linesep + \ os.linesep + \ "#include <stdint.h>" + os.linesep + \ os.linesep + \ "#include <string>" + os.linesep + \ os.linesep + \ "#ifdef _WIN32" + os.linesep + \ "#include <Windows.h>" + os.linesep + \ "// This is a comment" + os.linesep + \ "#undef GetMessage" + os.linesep + \ "#endif" + os.linesep + \ os.linesep + \ "#include <string>" + os.linesep + \ os.linesep + \ "#include \"Base.h\"" + os.linesep + \ "#include \"llvm/StringRef.h\"" + os.linesep)) outputs.append(( "#pragma once" + os.linesep + \ os.linesep + \ "#include <stdint.h>" + os.linesep + \ os.linesep + \ "#include <string>" + os.linesep + \ os.linesep + \ "#ifdef _WIN32" + os.linesep + \ "#include <Windows.h>" + os.linesep + \ "// This is a comment" + os.linesep + \ "#undef GetMessage" + os.linesep + \ "#endif" + os.linesep + \ os.linesep + \ "#include \"Base.h\"" + os.linesep + \ "#include \"llvm/StringRef.h\"" + os.linesep, True, True)) # Verify "#ifdef _WIN32" acts as barrier for out-of-order includes inputs.append(("./Error.h", "#pragma once" + os.linesep + \ os.linesep + \ "#include <stdint.h>" + os.linesep + \ os.linesep + \ "#include <string>" + os.linesep + \ os.linesep + \ "#ifdef _WIN32" + os.linesep + \ "#include <string>" + os.linesep + \ os.linesep + \ "#include <Windows.h>" + os.linesep + \ "#endif" + os.linesep + \ os.linesep + \ "#include <string>" + os.linesep + \ os.linesep + \ "#include \"Base.h\"" + os.linesep + \ "#include \"llvm/StringRef.h\"" + os.linesep)) outputs.append((inputs[len(inputs) - 1][1], False, True)) # Verify "#ifdef __linux__" is sorted in correct category below other # headers inputs.append(("./Error.h", "#pragma once" + os.linesep + \ os.linesep + \ "#include <stdlib.h>" + os.linesep + \ os.linesep + \ "#include <string>" + os.linesep + \ os.linesep + \ "#ifdef __linux__" + os.linesep + \ "#include <stdio.h>" + os.linesep + \ "#endif" + os.linesep + \ os.linesep + \ "#include \"Base.h\"" + os.linesep + \ "#include \"llvm/StringRef.h\"" + os.linesep)) outputs.append(( "#pragma once" + os.linesep + \ os.linesep + \ "#include <stdlib.h>" + os.linesep + \ os.linesep + \ "#ifdef __linux__" + os.linesep + \ "#include <stdio.h>" + os.linesep + \ "#endif" + os.linesep + \ os.linesep + \ "#include <string>" + os.linesep + \ os.linesep + \ "#include \"Base.h\"" + os.linesep + \ "#include \"llvm/StringRef.h\"" + os.linesep, True, True)) # Verify "#ifdef __linux__" is included in output if no headers are in same # category inputs.append(("./Error.h", "#pragma once" + os.linesep + \ os.linesep + \ "#ifdef __linux__" + os.linesep + \ "#include <stdio.h>" + os.linesep + \ "#endif" + os.linesep + \ os.linesep + \ "#include <string>" + os.linesep + \ os.linesep + \ "#include \"Base.h\"" + os.linesep + \ "#include \"llvm/StringRef.h\"" + os.linesep)) outputs.append((inputs[len(inputs) - 1][1], False, True)) # Verify #ifdef blocks in a row are handled properly inputs.append(("./Log.cpp", "#include \"Log.h\"" + os.linesep + \ os.linesep + \ "#include <cstdio>" + os.linesep + \ os.linesep + \ "#ifdef _WIN32" + os.linesep + \ "#include <cstdlib>" + os.linesep + \ "#else" + os.linesep + \ "#include <cstring>" + os.linesep + \ "#endif" + os.linesep + \ os.linesep + \ "#ifdef __APPLE__" + os.linesep + \ "#include <libgen.h>" + os.linesep + \ "#endif" + os.linesep + \ os.linesep + \ "#ifdef __ANDROID__" + os.linesep + \ "#include <libgen.h>" + os.linesep + \ "#endif" + os.linesep + \ os.linesep + \ "using namespace cs;" + os.linesep)) outputs.append(( "#include \"Log.h\"" + os.linesep + \ os.linesep + \ "#ifdef __APPLE__" + os.linesep + \ "#include <libgen.h>" + os.linesep + \ "#endif" + os.linesep + \ os.linesep + \ "#ifdef __ANDROID__" + os.linesep + \ "#include <libgen.h>" + os.linesep + \ "#endif" + os.linesep + \ os.linesep + \ "#include <cstdio>" + os.linesep + \ os.linesep + \ "#ifdef _WIN32" + os.linesep + \ "#include <cstdlib>" + os.linesep + \ "#else" + os.linesep + \ "#include <cstring>" + os.linesep + \ "#endif" + os.linesep + \ os.linesep + \ "using namespace cs;" + os.linesep, True, True)) # Verify comments aren't mangled inputs.append(("./cscore.h", "/* C API */" + os.linesep + \ "#include \"cscore_c.h\"" + os.linesep + \ os.linesep + \ "#ifdef __cplusplus" + os.linesep + \ "/* C++ API */" + os.linesep + \ "#include \"cscore_cpp.h\"" + os.linesep + \ "#include \"cscore_oo.h\"" + os.linesep + \ "#endif /* __cplusplus */" + os.linesep)) outputs.append((inputs[len(inputs) - 1][1], False, True)) # Check recursive #ifdef inputs.append(("./Test.h", "#include <algorithm>" + os.linesep + \ os.linesep + \ "#ifdef __linux__" + os.linesep + \ "#include <sys/socket.h>" + os.linesep + \ "#include <sys/syscall.h>" + os.linesep + \ "#ifdef __GNUC__ > 4" + os.linesep + \ "#include <algorithm>" + os.linesep + \ "#endif" + os.linesep + \ "#endif" + os.linesep)) outputs.append(( "#include <algorithm>" + os.linesep + \ os.linesep + \ "#ifdef __linux__" + os.linesep + \ "#include <sys/socket.h>" + os.linesep + \ "#include <sys/syscall.h>" + os.linesep + \ os.linesep + \ "#ifdef __GNUC__ > 4" + os.linesep + \ "#include <algorithm>" + os.linesep + \ "#endif" + os.linesep + \ "#endif" + os.linesep, True, True)) # Verify extra newline from #endif is removed inputs.append(("./Test.h", "#include \"HAL/HAL.h\"" + os.linesep + \ "#include \"NotifyListener.h\"" + os.linesep + \ os.linesep + \ "#ifdef __cplusplus" + os.linesep + \ "extern \"C\" {" + os.linesep + \ "#endif" + os.linesep + \ os.linesep + \ "void HALSIM_ResetSPIAccelerometerData(int32_t index);" + \ os.linesep)) outputs.append((inputs[len(inputs) - 1][1], False, True)) # Large test inputs.append(("./UsbCameraImpl.cpp", "#include <algorithm>" + os.linesep + \ os.linesep + \ "#include \"Handle.h\"" + os.linesep + \ "#include \"Log.h\"" + os.linesep + \ "#include \"Notifier.h\"" + os.linesep + \ "#include \"UsbUtil.h\"" + os.linesep + \ "#include \"c_util.h\"" + os.linesep + \ "#include \"cscore_cpp.h\"" + os.linesep + \ os.linesep + \ "#ifdef __linux__" + os.linesep + \ "#include <dirent.h>" + os.linesep + \ "#include <fcntl.h>" + os.linesep + \ "#include <libgen.h>" + os.linesep + \ "#include <linux/kernel.h>" + os.linesep + \ "#include <linux/types.h>" + os.linesep + \ "#include <linux/videodev2.h>" + os.linesep + \ "#include <sys/eventfd.h>" + os.linesep + \ "#include <sys/inotify.h>" + os.linesep + \ "#include <sys/ioctl.h>" + os.linesep + \ "#include <sys/select.h>" + os.linesep + \ "#include <sys/stat.h>" + os.linesep + \ "#include <sys/time.h>" + os.linesep + \ "#include <sys/types.h>" + os.linesep + \ "#include <unistd.h>" + os.linesep + \ os.linesep + \ "#elif defined(_WIN32)" + os.linesep + \ os.linesep + \ "#endif" + os.linesep + \ os.linesep + \ "using namespace cs;" + os.linesep)) outputs.append(( "#ifdef __linux__" + os.linesep + \ "#include <dirent.h>" + os.linesep + \ "#include <fcntl.h>" + os.linesep + \ "#include <libgen.h>" + os.linesep + \ "#include <linux/kernel.h>" + os.linesep + \ "#include <linux/types.h>" + os.linesep + \ "#include <linux/videodev2.h>" + os.linesep + \ "#include <sys/eventfd.h>" + os.linesep + \ "#include <sys/inotify.h>" + os.linesep + \ "#include <sys/ioctl.h>" + os.linesep + \ "#include <sys/select.h>" + os.linesep + \ "#include <sys/stat.h>" + os.linesep + \ "#include <sys/time.h>" + os.linesep + \ "#include <sys/types.h>" + os.linesep + \ "#include <unistd.h>" + os.linesep + \ "#elif defined(_WIN32)" + os.linesep + \ "#endif" + os.linesep + \ os.linesep + \ "#include <algorithm>" + os.linesep + \ os.linesep + \ "#include \"Handle.h\"" + os.linesep + \ "#include \"Log.h\"" + os.linesep + \ "#include \"Notifier.h\"" + os.linesep + \ "#include \"UsbUtil.h\"" + os.linesep + \ "#include \"c_util.h\"" + os.linesep + \ "#include \"cscore_cpp.h\"" + os.linesep + \ os.linesep + \ "using namespace cs;" + os.linesep, True, True)) # Verify relevant headers are found and sorted correctly inputs.append(("./PDP.cpp", "#include <memory>" + os.linesep + \ os.linesep + \ "#include \"HAL/PDP.h\"" + os.linesep + \ os.linesep + \ "#include \"ctre/PDP.h\"" + os.linesep + \ os.linesep + \ "using namespace hal;" + os.linesep)) outputs.append(( "#include \"HAL/PDP.h\"" + os.linesep + \ os.linesep + \ "#include <memory>" + os.linesep + \ os.linesep + \ "#include \"ctre/PDP.h\"" + os.linesep + \ os.linesep + \ "using namespace hal;" + os.linesep, True, True)) # Check for idempotence inputs.append(("./PDP.cpp", outputs[len(outputs) - 1][0])) outputs.append((inputs[len(inputs) - 1][1], False, True)) # Verify subgroups are sorted inputs.append(("./PDP.cpp", "#include \"support/jni_util.h\"" + os.linesep + \ "#include \"llvm/SmallString.h\"" + os.linesep + \ "#include \"llvm/raw_ostream.h\"" + os.linesep)) outputs.append(( "#include \"llvm/SmallString.h\"" + os.linesep + \ "#include \"llvm/raw_ostream.h\"" + os.linesep + \ "#include \"support/jni_util.h\"" + os.linesep, True, True)) # Verify duplicate headers are removed inputs.append(("./PDP.cpp", "#include <memory>" + os.linesep + \ "#include \"ctre/PDP.h\"" + os.linesep + \ os.linesep + \ "#include \"HAL/PDP.h\"" + os.linesep + \ os.linesep + \ "#include \"ctre/PDP.h\"" + os.linesep + \ "#include <memory>" + os.linesep + \ os.linesep + \ "using namespace hal;" + os.linesep)) outputs.append(( "#include \"HAL/PDP.h\"" + os.linesep + \ os.linesep + \ "#include <memory>" + os.linesep + \ os.linesep + \ "#include \"ctre/PDP.h\"" + os.linesep + \ os.linesep + \ "using namespace hal;" + os.linesep, True, True)) # Verify source file inclusion is disallowed inputs.append(("./Test.h", "#include <memory>" + os.linesep + \ os.linesep + \ "#include \"Stuff.cpp\"" + os.linesep, False, False)) outputs.append((inputs[len(inputs) - 1][1], False, False)) assert len(inputs) == len(outputs) config_file = Config(os.path.abspath(os.getcwd()), ".styleguide") for i in range(len(inputs)): output, file_changed, success = task.run_pipeline( config_file, inputs[i][0], inputs[i][1]) assert output == outputs[i][0] assert file_changed == outputs[i][1] assert success == outputs[i][2]
def run_pipeline(self, config_file, name, lines): linesep = Task.get_linesep(lines) license_template = Config.read_file( os.path.dirname(os.path.abspath(name)), ".styleguide-license") # Strip newlines at top of file stripped_lines = lines.lstrip().split(linesep) # If a comment at the beginning of the file is considered a license, it # is replaced with an updated license. Otherwise, a license header is # inserted before it. first_comment_is_license = False license_end = 0 # Regex for tokenizing on comment boundaries token_regex = re.compile("^/\*|\*/|^//") in_multiline_comment = False for line in stripped_lines: # If part of comment contains "Copyright (c)", comment is license. if "Copyright (c)" in line: first_comment_is_license = True line_has_comment = False for match in token_regex.finditer(line): # If any comment token was matched, the line has a comment line_has_comment = True token = match.group() if token == "/*": in_multiline_comment = True elif token == "*/": in_multiline_comment = False if not in_multiline_comment and not line_has_comment: break else: license_end += 1 # If comment at beginning of file is non-empty license, update it if first_comment_is_license and license_end > 0: file_parts = \ [linesep.join(stripped_lines[0:license_end]), linesep + linesep.join(stripped_lines[license_end:]).lstrip()] else: file_parts = ["", linesep + lines.lstrip()] # Default year when none is found is current one year = self.__current_year year_regex = re.compile("Copyright \(c\).*\s(20..)") modify_copyright = False for line in file_parts[0].split(linesep): match = year_regex.search(line) # If license contains copyright pattern, extract the first year if match: year = match.group(1) modify_copyright = True break output = "" # Determine copyright range and trailing padding if modify_copyright and year != self.__current_year: year = year + "-" + self.__current_year for line in license_template: # Insert copyright year range line = line.replace("{year}", year) # Insert padding which expands to the 80th column. If there is more # than one padding token, the line may contain fewer than 80 # characters due to rounding during the padding width calculation. PADDING_TOKEN = "{padding}" padding_count = line.count(PADDING_TOKEN) if padding_count: padding = 80 - len(line) + len(PADDING_TOKEN) * padding_count padding_width = int(padding / padding_count) line = line.replace(PADDING_TOKEN, " " * padding_width) output += line + linesep # Copy rest of original file into new one if len(file_parts) > 1: output += file_parts[1] return (output, lines != output, True)
def test_includeguard(): task = IncludeGuard() inputs = [] outputs = [] # Fix incorrect include guard inputs.append(("./Test.h", "#ifndef WRONG_H" + os.linesep + \ "#define WRONG_C" + os.linesep + \ os.linesep + \ "#endif" + os.linesep)) outputs.append(( "#ifndef STYLEGUIDE_WPIFORMAT_TEST_H_" + os.linesep + \ "#define STYLEGUIDE_WPIFORMAT_TEST_H_" + os.linesep + \ os.linesep + \ "#endif // STYLEGUIDE_WPIFORMAT_TEST_H_" + os.linesep, True, True)) # Ensure nested preprocessor statements are handled properly for incorrect # include guard inputs.append(("./Test.h", "#ifndef WRONG_H" + os.linesep + \ "#define WRONG_C" + os.linesep + \ os.linesep + \ "#if SOMETHING" + os.linesep + \ "// do something" + os.linesep + \ "#endif" + os.linesep + \ "#endif" + os.linesep)) outputs.append(( "#ifndef STYLEGUIDE_WPIFORMAT_TEST_H_" + os.linesep + \ "#define STYLEGUIDE_WPIFORMAT_TEST_H_" + os.linesep + \ os.linesep + \ "#if SOMETHING" + os.linesep + \ "// do something" + os.linesep + \ "#endif" + os.linesep + \ "#endif // STYLEGUIDE_WPIFORMAT_TEST_H_" + os.linesep, True, True)) # Don't touch correct include guard inputs.append(("./Test.h", "#ifndef STYLEGUIDE_WPIFORMAT_TEST_H_" + os.linesep + \ "#define STYLEGUIDE_WPIFORMAT_TEST_H_" + os.linesep + \ os.linesep + \ "#endif // STYLEGUIDE_WPIFORMAT_TEST_H_" + os.linesep)) outputs.append((inputs[len(inputs) - 1][1], False, True)) # Fail on missing include guard inputs.append(("./Test.h", "// Empty file" + os.linesep)) outputs.append((inputs[len(inputs) - 1][1], False, False)) # Verify pragma once counts as include guard inputs.append(("./Test.h", "#pragma once" + os.linesep)) outputs.append((inputs[len(inputs) - 1][1], False, True)) assert len(inputs) == len(outputs) config_file = Config(os.path.abspath(os.getcwd()), ".styleguide") for i in range(len(inputs)): output, file_changed, success = task.run_pipeline( config_file, inputs[i][0], inputs[i][1]) assert output == outputs[i][0] assert file_changed == outputs[i][1] assert success == outputs[i][2]
def main(): # Parse command-line arguments parser = argparse.ArgumentParser( description= "Runs all formatting tasks on the code base. This should be invoked from a directory within the project." ) parser.add_argument( "-v", dest="verbose1", action="store_true", help="verbosity level 1 (prints names of processed files)") parser.add_argument( "-vv", dest="verbose2", action="store_true", help= "verbosity level 2 (prints names of processed files and tasks run on them)" ) parser.add_argument( "-j", dest="jobs", type=int, default=mp.cpu_count(), help="number of jobs to run (default is number of cores)") parser.add_argument( "-y", dest="year", type=int, default=date.today().year, help= "year to use when updating license headers (default is current year)") parser.add_argument( "-clang", dest="clang_version", type=str, default="", help= "version suffix for clang-format (invokes \"clang-format-CLANG_VERSION\" or \"clang-format\" if no suffix provided)" ) parser.add_argument( "-f", dest="file", type=str, default="", nargs="+", help= "file or directory names (can be path relative to python invocation directory or absolute path)" ) args = parser.parse_args() # All discovered files are relative to Git repo root directory, so find the # root. root_path = Task.get_repo_root() if root_path == "": print("Error: not invoked within a Git repository", file=sys.stderr) sys.exit(1) # If no files explicitly specified if not args.file: # Delete temporary files from previous incomplete run files = [ os.path.join(dp, f) for dp, dn, fn in os.walk(root_path) for f in fn if f.endswith(".tmp") ] for f in files: os.remove(f) # Recursively create list of files in given directory files = [ os.path.join(dp, f) for dp, dn, fn in os.walk(root_path) for f in fn ] if not files: print("Error: no files found to format", file=sys.stderr) sys.exit(1) else: files = [] for name in args.file: # If a directory was specified, recursively expand it if os.path.isdir(name): files.extend([ os.path.join(dp, f) for dp, dn, fn in os.walk(name) for f in fn ]) else: files.append(name) # Convert relative paths of files to absolute paths files = [os.path.abspath(name) for name in files] # Don't run tasks on Git metadata files = [name for name in files if os.sep + ".git" + os.sep not in name] # Don't check for changes in or run tasks on ignored files files = filter_ignored_files(files) # Create list of all changed files changed_file_list = [] output_list = subprocess.run( ["git", "diff", "--name-only", "master"], stdout=subprocess.PIPE).stdout.split() for line in output_list: changed_file_list.append(root_path + os.sep + line.strip().decode("ascii")) # Don't run tasks on modifiable or generated files work = [] for name in files: config_file = Config(os.path.dirname(name), ".styleguide") if config_file.is_modifiable_file(name): continue if config_file.is_generated_file(name): # Emit warning if a generated file was editted if name in changed_file_list: print("Warning: generated file '" + name + "' modified") continue work.append(name) files = work # If there are no files left, do nothing if len(files) == 0: sys.exit(0) # Prepare file batches for batch tasks chunksize = math.ceil(len(files) / args.jobs) file_batches = [ files[i:i + chunksize] for i in range(0, len(files), chunksize) ] # IncludeOrder is run after Stdlib so any C std headers changed to C++ or # vice versa are sorted properly. ClangFormat is run after the other tasks # so it can clean up their formatting. task_pipeline = [ BraceComment(), CIdentList(), IncludeGuard(), LicenseUpdate(str(args.year)), JavaClass(), Newline(), Stdlib(), IncludeOrder(), UsingDeclaration(), UsingNamespaceStd(), Whitespace() ] run_pipeline(task_pipeline, args, files) task_pipeline = [ClangFormat(args.clang_version)] run_batch(task_pipeline, args, file_batches) # These tasks fix clang-format formatting task_pipeline = [Jni()] run_pipeline(task_pipeline, args, files) # Lint is run last since previous tasks can affect its output. task_pipeline = [PyFormat(), Lint()] run_batch(task_pipeline, args, file_batches)
def test_stdlib(): task = Stdlib() inputs = [] outputs = [] inputs.append(("./Main.cpp", "#include <cstdint>" + os.linesep + \ "#include <stdlib.h>" + os.linesep + \ os.linesep + \ "int main() {" + os.linesep + \ " auto mem = static_cast<std::uint8_t*>(malloc(5));" + os.linesep + \ " std::int32_t i = 4;" + os.linesep + \ " int32_t a = -2;" + os.linesep + \ " std::uint_fast16_t j = 5;" + os.linesep + \ " std::uint_fast16_t* k = &j;" + os.linesep + \ " std::uint_fast16_t** l = &k;" + os.linesep + \ " std::uint_fast16_t ** m = l;" + os.linesep + \ " free(mem);" + os.linesep + \ "}" + os.linesep)) outputs.append(( "#include <stdint.h>" + os.linesep + \ "#include <cstdlib>" + os.linesep + \ os.linesep + \ "int main() {" + os.linesep + \ " auto mem = static_cast<uint8_t*>(std::malloc(5));" + os.linesep + \ " int32_t i = 4;" + os.linesep + \ " int32_t a = -2;" + os.linesep + \ " uint_fast16_t j = 5;" + os.linesep + \ " uint_fast16_t* k = &j;" + os.linesep + \ " uint_fast16_t** l = &k;" + os.linesep + \ " uint_fast16_t ** m = l;" + os.linesep + \ " std::free(mem);" + os.linesep + \ "}" + os.linesep, True, True)) # FILE should be recognized as type here inputs.append(("./Class.cpp", "static FILE* Class::file = nullptr;")) outputs.append(("static std::FILE* Class::file = nullptr;", True, True)) # FILE should not be recognized as type here inputs.append(("./Class.cpp", "static int Class::error1 = ERR_FILE;" + os.linesep + \ "#define FILE_LOG(level)" + os.linesep + \ "if (level > FILELog::ReportingLevel())" + os.linesep)) outputs.append(( "static int Class::error1 = ERR_FILE;" + os.linesep + \ "#define FILE_LOG(level)" + os.linesep + \ "if (level > FILELog::ReportingLevel())" + os.linesep, False, True)) # Remove "std::" from beginning of line inputs.append(("./Class.cpp", "/**" + os.linesep + \ " *" + os.linesep + \ " */" + os.linesep + \ "std::size_t SizeUleb128(uint64_t val) {" + os.linesep + \ " uint32_t result = 0;" + os.linesep)) outputs.append(("/**" + os.linesep + \ " *" + os.linesep + \ " */" + os.linesep + \ "size_t SizeUleb128(uint64_t val) {" + os.linesep + \ " uint32_t result = 0;" + os.linesep, True, True)) # Remove "std::" from beginning of file inputs.append(("./Class.cpp", "std::size_t SizeUleb128(uint64_t val) {" + os.linesep + \ " uint32_t result = 0;" + os.linesep)) outputs.append(("size_t SizeUleb128(uint64_t val) {" + os.linesep + \ " uint32_t result = 0;" + os.linesep, True, True)) # Don't prepend "std::" to function name if it's a function definition # "time()" in other contexts is a C standard library function. inputs.append( ("./Test.cpp", "uint64_t time() const { return m_val.last_change; }" + os.linesep)) outputs.append((inputs[len(inputs) - 1][1], False, True)) # Test detection of type within static_cast<> inputs.append(("./Test.cpp", "static_cast<std::uint64_t>(x);" + os.linesep)) outputs.append(("static_cast<uint64_t>(x);" + os.linesep, True, True)) # Types followed by semicolon should match inputs.append(("./Main.cpp", "typedef integer std::uint8_t;")) outputs.append(("typedef integer uint8_t;", True, True)) assert len(inputs) == len(outputs) config_file = Config(os.path.abspath(os.getcwd()), ".styleguide") for i in range(len(inputs)): output, file_changed, success = task.run_pipeline( config_file, inputs[i][0], inputs[i][1]) assert output == outputs[i][0] assert file_changed == outputs[i][1] assert success == outputs[i][2]
def test_licenseupdate(): year = str(date.today().year) task = LicenseUpdate(year) inputs = [] outputs = [] file_appendix = \ "#pragma once" + os.linesep + \ os.linesep + \ "#include <iostream>" + os.linesep + \ os.linesep + \ "int main() {" + os.linesep + \ " std::cout << \"Hello World!\";" + os.linesep + \ "}" # pragma once at top of file inputs.append(("./Test.h", file_appendix)) outputs.append(( "/* Company Name */" + os.linesep + "/* Copyright (c) {} Company Name. All Rights Reserved. */" .format(year) + os.linesep + os.linesep + file_appendix, True, True)) # pragma once at top of file preceded by newline temp = (inputs[len(inputs) - 1][0], os.linesep + inputs[len(inputs) - 1][1]) inputs.append(temp) outputs.append(outputs[len(outputs) - 1]) # File containing up-to-date license preceded by newline inputs.append(( "./Test.h", os.linesep + "/* Company Name */" + os.linesep + "/* Copyright (c) {} Company Name. All Rights Reserved. */" .format(year) + os.linesep + os.linesep + file_appendix)) outputs.append((inputs[len(inputs) - 1][1].lstrip(), True, True)) # File containing up-to-date range license inputs.append(( "./Test.h", "/* Company Name */" + os.linesep + "// Copyright (c) 2011-{} Company Name. All Rights Reserved.".format( year) + os.linesep + os.linesep + file_appendix)) outputs.append(( "/* Company Name */" + os.linesep + "/* Copyright (c) 2011-{} Company Name. All Rights Reserved. */" .format(year) + os.linesep + os.linesep + file_appendix, True, True)) # File containing up-to-date license with one year inputs.append(( "./Test.h", "/* Company Name */" + os.linesep + "/* Copyright (c) {} Company Name. All Rights Reserved. */" .format(year) + os.linesep + os.linesep + file_appendix)) outputs.append((inputs[len(inputs) - 1][1], False, True)) # File with three newlines between license and include guard inputs.append(( "./Test.h", "/* Company Name */" + os.linesep + "/* Copyright (c) {} Company Name. All Rights Reserved. */" .format(year) + os.linesep + os.linesep + os.linesep + file_appendix)) outputs.append((outputs[len(outputs) - 1][0], True, True)) # File with only one newline between license and include guard inputs.append(( "./Test.h", "/* Company Name */" + os.linesep + "/* Copyright (c) {} Company Name. All Rights Reserved. */" .format(year) + os.linesep + file_appendix)) outputs.append((outputs[len(outputs) - 1][0], True, True)) # File with multiline comment spanning multiple lines of license header inputs.append(( "./Test.h", "/*" + os.linesep + " * Autogenerated file! Do not manually edit this file. This version is regenerated" + os.linesep + " * any time the publish task is run, or when this file is deleted." + os.linesep + " */" + os.linesep + os.linesep + "const char* WPILibVersion = \"\";")) outputs.append(( "/* Company Name */" + os.linesep + "/* Copyright (c) {} Company Name. All Rights Reserved. */" .format(year) + os.linesep + os.linesep + inputs[len(inputs) - 1][1], True, True)) # File containing license year range in different postion than template # (If the year isn't extracted, the range will be replaced with one year and # the test will fail.) inputs.append(( "./Test.h", "/* Company Name */" + os.linesep + "/* Copyright (c) Company Name 2011-{}. All Rights Reserved. */" .format(year) + os.linesep + os.linesep + file_appendix)) outputs.append(( "/* Company Name */" + os.linesep + "/* Copyright (c) 2011-{} Company Name. All Rights Reserved. */" .format(year) + os.linesep + os.linesep + file_appendix, True, True)) # File excluded from license update isn't modified inputs.append(("./Excluded.h", "/* Copyright (c) Company Name 2011-{}. */".format(year) + os.linesep + os.linesep + file_appendix)) outputs.append(("/* Copyright (c) Company Name 2011-{}. */".format(year) + os.linesep + os.linesep + file_appendix, False, True)) assert len(inputs) == len(outputs) config_file = Config(os.path.abspath(os.getcwd()), ".styleguide") for i in range(len(inputs)): # Ensure files excluded from license update won't be processed if inputs[i][0] == "./Excluded.h": assert not task.should_process_file(config_file, inputs[i][0]) else: assert task.should_process_file(config_file, inputs[i][0]) if task.should_process_file(config_file, inputs[i][0]): output, file_changed, success = task.run_pipeline( config_file, inputs[i][0], inputs[i][1]) assert output == outputs[i][0] assert file_changed == outputs[i][1] assert success == outputs[i][2]
def test_bracecomment(): task = BraceComment() inputs = [] outputs = [] # Empty anonymous namespace inputs.append(("./Test.h", "namespace {" + os.linesep + \ "}// comment" + os.linesep)) outputs.append(( "namespace {" + os.linesep + \ "} // namespace" + os.linesep, True, True)) # Anonymous namespace containing comment inputs.append(("./Test.h", "namespace {" + os.linesep + \ " // comment" + os.linesep + \ "}// comment" + os.linesep)) outputs.append(( "namespace {" + os.linesep + \ " // comment" + os.linesep + \ "} // namespace" + os.linesep, True, True)) # namespace inputs.append(("./Test.h", "namespace hal {" + os.linesep + \ " // comment" + os.linesep + \ "}// comment" + os.linesep)) outputs.append(( "namespace hal {" + os.linesep + \ " // comment" + os.linesep + \ "} // namespace hal" + os.linesep, True, True)) # namespace with leftover input inputs.append(("./Test.h", "// comment before namespace" + os.linesep + \ "namespace hal {" + os.linesep + \ " // comment" + os.linesep + \ "}// comment" + os.linesep + \ "// comment after namespace" + os.linesep)) outputs.append(( "// comment before namespace" + os.linesep + \ "namespace hal {" + os.linesep + \ " // comment" + os.linesep + \ "} // namespace hal" + os.linesep + \ "// comment after namespace" + os.linesep, True, True)) # Braces within namespace inputs.append(("./Test.h", "namespace {" + os.linesep + \ os.linesep + \ "struct AnalogGyro {" + os.linesep + \ " HAL_AnalogInputHandle handle;" + os.linesep + \ " double voltsPerDegreePerSecond;" + os.linesep + \ " double offset;" + os.linesep + \ " int32_t center;" + os.linesep + \ "}" + os.linesep + \ os.linesep + \ "}" + os.linesep)) outputs.append(( "namespace {" + os.linesep + \ os.linesep + \ "struct AnalogGyro {" + os.linesep + \ " HAL_AnalogInputHandle handle;" + os.linesep + \ " double voltsPerDegreePerSecond;" + os.linesep + \ " double offset;" + os.linesep + \ " int32_t center;" + os.linesep + \ "}" + os.linesep + \ os.linesep + \ "} // namespace" + os.linesep, True, True)) # extern "C" inputs.append(("./Test.h", "extern \"C\" {" + os.linesep + \ " // nothing" + os.linesep + \ "}// comment" + os.linesep)) outputs.append(( "extern \"C\" {" + os.linesep + \ " // nothing" + os.linesep + \ "} // extern \"C\"" + os.linesep, True, True)) # Nested brackets should be handled properly inputs.append(("./Test.cpp", "extern \"C\" {" + os.linesep + \ "void func() {" + os.linesep + \ " if (1) {" + os.linesep + \ " } else if (1) {" + os.linesep + \ " } else {" + os.linesep + \ " }" + os.linesep + \ "}" + os.linesep + \ "} // extern \"C\"" + os.linesep)) outputs.append((inputs[len(inputs) - 1][1], False, True)) # Nested brackets on same line inputs.append(("./Test.cpp", "namespace wpi {" + os.linesep + \ "{{}}" + os.linesep + \ "} // namespace java" + os.linesep)) outputs.append(( "namespace wpi {" + os.linesep + \ "{{}}" + os.linesep + \ "} // namespace wpi" + os.linesep, True, True)) # Handle single-line statements correctly inputs.append( ("./Test.cpp", "namespace hal { Type typeName; }" + os.linesep)) outputs.append( ("namespace hal { Type typeName; } // namespace hal" + os.linesep, True, True)) # Two incorrect comments inputs.append(("./Test.h", "namespace {" + os.linesep + \ " // nothing" + os.linesep + \ "}// comment" + os.linesep + \ "namespace Name {" + os.linesep + \ " // nothing" + os.linesep + \ "}" + os.linesep)) outputs.append(( "namespace {" + os.linesep + \ " // nothing" + os.linesep + \ "} // namespace" + os.linesep + \ "namespace Name {" + os.linesep + \ " // nothing" + os.linesep + \ "} // namespace Name" + os.linesep, True, True)) # Don't touch correct comment inputs.append(("./Test.h", "namespace {" + os.linesep + \ " // nothing" + os.linesep + \ "} // namespace" + os.linesep)) outputs.append((inputs[len(inputs) - 1][1], False, True)) assert len(inputs) == len(outputs) config_file = Config(os.path.abspath(os.getcwd()), ".styleguide") for i in range(len(inputs)): output, file_changed, success = task.run_pipeline( config_file, inputs[i][0], inputs[i][1]) assert output == outputs[i][0] assert file_changed == outputs[i][1] assert success == outputs[i][2]
def test_licenseupdate(): year = str(date.today().year) task = LicenseUpdate(year) test = TaskTest(task) file_appendix = \ "#pragma once" + os.linesep + \ os.linesep + \ "#include <iostream>" + os.linesep + \ os.linesep + \ "int main() {" + os.linesep + \ " std::cout << \"Hello World!\";" + os.linesep + \ "}" # pragma once at top of file test.add_input("./Test.h", file_appendix) test.add_output( "/* Company Name */" + os.linesep + "/* Copyright (c) {} Company Name. All Rights Reserved. */". format(year) + os.linesep + os.linesep + file_appendix, True, True) # pragma once at top of file preceded by newline test.add_input("./Test.h", os.linesep + file_appendix) test.add_output( "/* Company Name */" + os.linesep + "/* Copyright (c) {} Company Name. All Rights Reserved. */". format(year) + os.linesep + os.linesep + file_appendix, True, True) # File containing up-to-date license preceded by newline test.add_input( "./Test.h", os.linesep + "/* Company Name */" + os.linesep + "/* Copyright (c) {} Company Name. All Rights Reserved. */". format(year) + os.linesep + os.linesep + file_appendix) test.add_output( "/* Company Name */" + os.linesep + "/* Copyright (c) {} Company Name. All Rights Reserved. */". format(year) + os.linesep + os.linesep + file_appendix, True, True) # File containing up-to-date range license test.add_input( "./Test.h", "/* Company Name */" + os.linesep + "// Copyright (c) 2011-{} Company Name. All Rights Reserved.".format( year) + os.linesep + os.linesep + file_appendix) test.add_output( "/* Company Name */" + os.linesep + "/* Copyright (c) 2011-{} Company Name. All Rights Reserved. */". format(year) + os.linesep + os.linesep + file_appendix, True, True) # File containing up-to-date license with one year test.add_input( "./Test.h", "/* Company Name */" + os.linesep + "/* Copyright (c) {} Company Name. All Rights Reserved. */". format(year) + os.linesep + os.linesep + file_appendix) test.add_latest_input_as_output(True) # File with three newlines between license and include guard test.add_input( "./Test.h", "/* Company Name */" + os.linesep + "/* Copyright (c) {} Company Name. All Rights Reserved. */". format(year) + os.linesep + os.linesep + os.linesep + file_appendix) test.add_output( "/* Company Name */" + os.linesep + "/* Copyright (c) {} Company Name. All Rights Reserved. */". format(year) + os.linesep + os.linesep + file_appendix, True, True) # File with only one newline between license and include guard test.add_input( "./Test.h", "/* Company Name */" + os.linesep + "/* Copyright (c) {} Company Name. All Rights Reserved. */". format(year) + os.linesep + file_appendix) test.add_output( "/* Company Name */" + os.linesep + "/* Copyright (c) {} Company Name. All Rights Reserved. */". format(year) + os.linesep + os.linesep + file_appendix, True, True) # File with multiline comment spanning multiple lines of license header test.add_input( "./Test.h", "/*" + os.linesep + " * Autogenerated file! Do not manually edit this file. This version is regenerated" + os.linesep + " * any time the publish task is run, or when this file is deleted." + os.linesep + " */" + os.linesep + os.linesep + "const char* WPILibVersion = \"\";") test.add_output( "/* Company Name */" + os.linesep + "/* Copyright (c) {} Company Name. All Rights Reserved. */". format(year) + os.linesep + os.linesep + "/*" + os.linesep + " * Autogenerated file! Do not manually edit this file. This version is regenerated" + os.linesep + " * any time the publish task is run, or when this file is deleted." + os.linesep + " */" + os.linesep + os.linesep + "const char* WPILibVersion = \"\";", True, True) # File containing license year range in different postion than template # (If the year isn't extracted, the range will be replaced with one year and # the test will fail.) test.add_input( "./Test.h", "/* Company Name */" + os.linesep + "/* Copyright (c) Company Name 2011-{}. All Rights Reserved. */". format(year) + os.linesep + os.linesep + file_appendix) test.add_output( "/* Company Name */" + os.linesep + "/* Copyright (c) 2011-{} Company Name. All Rights Reserved. */". format(year) + os.linesep + os.linesep + file_appendix, True, True) # Ensure "/*" after "*/" on same line is detected test.add_input("./Test.h", "/*----------------------------------------------------------------------------*/" + os.linesep + \ "/* Copyright (c) 2011 FIRST. All Rights Reserved. */" + os.linesep + \ "/* Open Source Software - may be modified and shared by FRC teams. The code */" + os.linesep + \ "/* must be accompanied by the FIRST BSD license file in the root directory of */" + os.linesep + \ "/* the project. */" + os.linesep + \ "/*----------------------------------------------------------------------------*//*" + os.linesep + \ os.linesep + \ "blah" + os.linesep + \ os.linesep + \ "*/" + os.linesep) test.add_output( "/* Company Name */" + os.linesep + "/* Copyright (c) 2011-{} Company Name. All Rights Reserved. */". format(year) + os.linesep + os.linesep, True, True) # File excluded from license update isn't modified test.add_input( "./Excluded.h", "/* Copyright (c) Company Name 2011-{}. */".format(year) + os.linesep + os.linesep + file_appendix) test.add_output( "/* Copyright (c) Company Name 2011-{}. */".format(year) + os.linesep + os.linesep + file_appendix, False, True) # Ensure license regex matches test.add_input("./Test.h", "/* Company Name */" + os.linesep + \ "/* Copyright (c) 1992-2015 Company Name. All Rights Reserved. */" + os.linesep + \ os.linesep + file_appendix) test.add_output( "/* Company Name */" + os.linesep + \ "/* Copyright (c) 1992-2018 Company Name. All Rights Reserved. */" + os.linesep + \ os.linesep + file_appendix, True, True) # Ensure excluded files won't be processed config_file = Config(os.path.abspath(os.getcwd()), ".styleguide") assert not task.should_process_file(config_file, "./Excluded.h") test.run(OutputType.FILE)
def test_licenseupdate(): year = str(date.today().year) task = LicenseUpdate() test = TaskTest(task) file_appendix = \ "#pragma once" + os.linesep + \ os.linesep + \ "#include <iostream>" + os.linesep + \ os.linesep + \ "int main() {" + os.linesep + \ " std::cout << \"Hello World!\";" + os.linesep + \ "}" # pragma once at top of file test.add_input("./Test.h", file_appendix) test.add_output( "/* Company Name */" + os.linesep + "/* Copyright (c) {} Company Name. All Rights Reserved. */" .format(year) + os.linesep + os.linesep + file_appendix, True, True) # pragma once at top of file preceded by newline test.add_input("./Test.h", os.linesep + file_appendix) test.add_output( "/* Company Name */" + os.linesep + "/* Copyright (c) {} Company Name. All Rights Reserved. */" .format(year) + os.linesep + os.linesep + file_appendix, True, True) # File containing up-to-date license preceded by newline test.add_input( "./Test.h", os.linesep + "/* Company Name */" + os.linesep + "/* Copyright (c) {} Company Name. All Rights Reserved. */" .format(year) + os.linesep + os.linesep + file_appendix) test.add_output( "/* Company Name */" + os.linesep + "/* Copyright (c) {} Company Name. All Rights Reserved. */" .format(year) + os.linesep + os.linesep + file_appendix, True, True) # File containing up-to-date range license test.add_input( "./Test.h", "/* Company Name */" + os.linesep + "// Copyright (c) 2011-{} Company Name. All Rights Reserved.".format( year) + os.linesep + os.linesep + file_appendix) test.add_output( "/* Company Name */" + os.linesep + "/* Copyright (c) 2011-{} Company Name. All Rights Reserved. */" .format(year) + os.linesep + os.linesep + file_appendix, True, True) # File containing up-to-date license with one year test.add_input( "./Test.h", "/* Company Name */" + os.linesep + "/* Copyright (c) {} Company Name. All Rights Reserved. */" .format(year) + os.linesep + os.linesep + file_appendix) test.add_latest_input_as_output(True) # File with three newlines between license and include guard test.add_input( "./Test.h", "/* Company Name */" + os.linesep + "/* Copyright (c) {} Company Name. All Rights Reserved. */" .format(year) + os.linesep + os.linesep + os.linesep + file_appendix) test.add_output( "/* Company Name */" + os.linesep + "/* Copyright (c) {} Company Name. All Rights Reserved. */" .format(year) + os.linesep + os.linesep + file_appendix, True, True) # File with only one newline between license and include guard test.add_input( "./Test.h", "/* Company Name */" + os.linesep + "/* Copyright (c) {} Company Name. All Rights Reserved. */" .format(year) + os.linesep + file_appendix) test.add_output( "/* Company Name */" + os.linesep + "/* Copyright (c) {} Company Name. All Rights Reserved. */" .format(year) + os.linesep + os.linesep + file_appendix, True, True) # File with multiline comment spanning multiple lines of license header test.add_input( "./Test.h", "/*" + os.linesep + " * Autogenerated file! Do not manually edit this file. This version is regenerated" + os.linesep + " * any time the publish task is run, or when this file is deleted." + os.linesep + " */" + os.linesep + os.linesep + "const char* WPILibVersion = \"\";") test.add_output( "/* Company Name */" + os.linesep + "/* Copyright (c) {} Company Name. All Rights Reserved. */" .format(year) + os.linesep + os.linesep + "/*" + os.linesep + " * Autogenerated file! Do not manually edit this file. This version is regenerated" + os.linesep + " * any time the publish task is run, or when this file is deleted." + os.linesep + " */" + os.linesep + os.linesep + "const char* WPILibVersion = \"\";", True, True) # File containing license year range in different postion than template # (If the year isn't extracted, the range will be replaced with one year and # the test will fail.) test.add_input( "./Test.h", "/* Company Name */" + os.linesep + "/* Copyright (c) Company Name 2011-{}. All Rights Reserved. */" .format(year) + os.linesep + os.linesep + file_appendix) test.add_output( "/* Company Name */" + os.linesep + "/* Copyright (c) 2011-{} Company Name. All Rights Reserved. */" .format(year) + os.linesep + os.linesep + file_appendix, True, True) # Ensure "/*" after "*/" on same line is detected test.add_input("./Test.h", "/*----------------------------------------------------------------------------*/" + os.linesep + \ "/* Copyright (c) 2011 FIRST. All Rights Reserved. */" + os.linesep + \ "/* Open Source Software - may be modified and shared by FRC teams. The code */" + os.linesep + \ "/* must be accompanied by the FIRST BSD license file in the root directory of */" + os.linesep + \ "/* the project. */" + os.linesep + \ "/*----------------------------------------------------------------------------*//*" + os.linesep + \ os.linesep + \ "blah" + os.linesep + \ os.linesep + \ "*/" + os.linesep) test.add_output( "/* Company Name */" + os.linesep + "/* Copyright (c) 2011-{} Company Name. All Rights Reserved. */" .format(year) + os.linesep + os.linesep, True, True) # File excluded from license update isn't modified test.add_input( "./Excluded.h", "/* Copyright (c) Company Name 2011-{}. */".format(year) + os.linesep + os.linesep + file_appendix) test.add_output( "/* Copyright (c) Company Name 2011-{}. */".format(year) + os.linesep + os.linesep + file_appendix, False, True) # Ensure license regex matches test.add_input("./Test.h", "/* Company Name */" + os.linesep + \ "/* Copyright (c) 1992-2015 Company Name. All Rights Reserved. */" + os.linesep + \ os.linesep + file_appendix) test.add_output( "/* Company Name */" + os.linesep + \ "/* Copyright (c) 1992-" + year + " Company Name. All Rights Reserved. */" + os.linesep + \ os.linesep + file_appendix, True, True) # Ensure excluded files won't be processed config_file = Config(os.path.abspath(os.getcwd()), ".styleguide") assert not task.should_process_file(config_file, "./Excluded.h") # Create git repo to test license years for commits with OpenTemporaryDirectory(): subprocess.run(["git", "init", "-q"]) # Add base files with open(".styleguide-license", "w") as file: file.write("// Copyright (c) {year}") with open(".styleguide", "w") as file: file.write("cppSrcFileInclude {\n" + r"\.cpp$") subprocess.run(["git", "add", ".styleguide-license"]) subprocess.run(["git", "add", ".styleguide"]) subprocess.run(["git", "commit", "-q", "-m", "\"Initial commit\""]) # Add file with commit date of last year and range through this year with open("last-year.cpp", "w") as file: file.write(f"// Copyright (c) 2017-{year}") subprocess.run(["git", "add", "last-year.cpp"]) subprocess.run(["git", "commit", "-q", "-m", "\"Last year\""]) last_iso_year = f"{int(year) - 1}-01-01T00:00:00" subprocess.Popen([ "git", "commit", "-q", "--amend", "--no-edit", f"--date={last_iso_year}" ], env={ **os.environ, "GIT_COMMITTER_DATE": last_iso_year }).wait() # Add file with commit date of this year and range through this year with open("this-year.cpp", "w") as file: file.write(f"// Copyright (c) 2017-{year}") subprocess.run(["git", "add", "this-year.cpp"]) subprocess.run(["git", "commit", "-q", "-m", "\"This year\""]) # Add file with commit date of next year and range through this year with open("next-year.cpp", "w") as file: file.write(f"// Copyright (c) 2017-{year}") subprocess.run(["git", "add", "next-year.cpp"]) subprocess.run(["git", "commit", "-q", "-m", "\"Next year\""]) next_iso_year = f"{int(year) + 1}-01-01T00:00:00" subprocess.Popen([ "git", "commit", "-q", "--amend", "--no-edit", f"--date={next_iso_year}" ], env={ **os.environ, "GIT_COMMITTER_DATE": next_iso_year }).wait() # Create uncommitted file with no year Path("no-year.cpp").touch() # Run wpiformat on last-year.cpp with open("last-year.cpp", "r") as input: lines = input.read() output, changed, success = task.run_pipeline(config_file, "last-year.cpp", lines) assert output == f"// Copyright (c) 2017-{int(year) - 1}\n\n" # Run wpiformat on this-year.cpp with open("last-year.cpp", "r") as input: lines = input.read() output, changed, success = task.run_pipeline(config_file, "this-year.cpp", lines) assert output == f"// Copyright (c) 2017-{year}\n\n" # Run wpiformat on next-year.cpp with open("next-year.cpp", "r") as input: lines = input.read() output, changed, success = task.run_pipeline(config_file, "next-year.cpp", lines) assert output == f"// Copyright (c) 2017-{int(year) + 1}\n\n" # Run wpiformat on no-year.cpp # Should have current calendar year with open("no-year.cpp", "r") as input: lines = input.read() output, changed, success = task.run_pipeline(config_file, "no-year.cpp", lines) assert output == f"// Copyright (c) {year}\n\n" test.run(OutputType.FILE)