#!/usr/bin/env ruby # # process_test_files.rb # # Copyright 2016 Tony Stone # # 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. # # Created by Tony Stone on 5/4/16. # require 'date' require 'getoptlong' require 'fileutils' require 'pathname' include FileUtils # # This ruby script will auto generate LinuxMain.swift and the +XCTest.swift extension files for Swift Package Manager on Linux platforms. # def extractCopyright(log) startYear = 0 endYear = 0 indices = log.enum_for(:scan, /(?=Date:)/).map do Regexp.last_match.offset(0).first end # If there are no years, assume this year. if indices.count == 0 return "#{Date.today.year}" end # Return one year if there is only one year if indices.count == 1 year = log[indices[0]+27..indices[0]+31] return "#{year}" end # Return a year range indices.each_with_index do |ind, i| year = log[ind+27..ind+31] # Seed start year if i == 0 startYear = Integer(year) end # For all other years following if Integer(year) > endYear endYear = Integer(year) end if Integer(year) < startYear startYear = Integer(year) end end # If the years end up being the same if startYear == endYear return "#{startYear}" end # Otherwise, return the year range return "#{startYear}-#{endYear}" end def header(fileName) log = %x(git log --follow -p #{fileName}) copyrightYears = extractCopyright(log).strip string = <<-eos //===----------------------------------------------------------------------===// // // This source file is part of the SwiftNIO open source project // // Copyright (c) #{copyrightYears} Apple Inc. and the SwiftNIO project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information // See CONTRIBUTORS.txt for the list of SwiftNIO project authors // // SPDX-License-Identifier: Apache-2.0 // //===----------------------------------------------------------------------===// // // // import XCTest /// /// NOTE: This file was generated by generate_linux_tests.rb /// /// Do NOT edit this file directly as it will be regenerated automatically when needed. /// eos string .sub('', File.basename(fileName)) .sub('', Time.now.to_s) end def createExtensionFile(fileName, classes) extensionFile = fileName.sub! '.swift', '+XCTest.swift' print 'Creating file: ' + extensionFile + "\n" File.open(extensionFile, 'w') do |file| file.write header(extensionFile) file.write "\n" for classArray in classes file.write 'extension ' + classArray[0] + " {\n\n" file.write ' @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings")' +"\n" file.write ' static var allTests : [(String, (' + classArray[0] + ") -> () throws -> Void)] {\n" file.write " return [\n" for funcName in classArray[1] file.write ' ("' + funcName + '", ' + funcName + "),\n" end file.write " ]\n" file.write " }\n" file.write "}\n\n" end end end def createLinuxMain(testsDirectory, allTestSubDirectories, files) fileName = testsDirectory + '/LinuxMain.swift' print 'Creating file: ' + fileName + "\n" File.open(fileName, 'w') do |file| file.write header(fileName) file.write "\n" file.write "#if !compiler(>=5.5)\n" file.write "#if os(Linux) || os(FreeBSD) || os(Android)\n" for testSubDirectory in allTestSubDirectories.sort { |x, y| x <=> y } file.write ' @testable import ' + testSubDirectory + "\n" end file.write "\n" file.write '@available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings")' + "\n" file.write "@main\n" file.write "class LinuxMainRunner {\n" file.write ' @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings")' + "\n" file.write " static func main() {\n" file.write " XCTMain([\n" testCases = [] for classes in files for classArray in classes testCases << classArray[0] end end for testCase in testCases.sort { |x, y| x <=> y } file.write ' testCase(' + testCase + ".allTests),\n" end file.write " ])\n" file.write " }\n" file.write "}\n" file.write "#endif\n" file.write "#else\n" file.write "#error(\"on Swift 5.5 and newer, --enable-test-discovery is required\")\n" file.write "#endif\n" end end def parseSourceFile(fileName) puts 'Parsing file: ' + fileName + "\n" classes = [] currentClass = nil inIfLinux = false inElse = false ignore = false # # Read the file line by line # and parse to find the class # names and func names # File.readlines(fileName).each do |line| if inIfLinux if /\#else/.match(line) inElse = true ignore = true else if /\#end/.match(line) inElse = false inIfLinux = false ignore = false end end else if /\#if[ \t]+os\(Linux\)/.match(line) inIfLinux = true ignore = false end end next if ignore # Match class or func match = line[/class[ \t]+[a-zA-Z0-9_]*(?=[ \t]*:[ \t]*XCTestCase)|func[ \t]+test[a-zA-Z0-9_]*(?=[ \t]*\(\))/, 0] if match if match[/class/, 0] == 'class' className = match.sub(/^class[ \t]+/, '') # # Create a new class / func structure # and add it to the classes array. # currentClass = [className, []] classes << currentClass else # Must be a func funcName = match.sub(/^func[ \t]+/, '') # # Add each func name the the class / func # structure created above. # currentClass[1] << funcName end end end classes end # # Main routine # # testsDirectory = 'Tests' options = GetoptLong.new(['--tests-dir', GetoptLong::OPTIONAL_ARGUMENT]) options.quiet = true begin options.each do |option, value| case option when '--tests-dir' testsDirectory = value end end rescue GetoptLong::InvalidOption end allTestSubDirectories = [] allFiles = [] Dir[testsDirectory + '/*'].each do |subDirectory| next unless File.directory?(subDirectory) directoryHasClasses = false Dir[subDirectory + '/*Test{s,}.swift'].each do |fileName| next unless File.file? fileName fileClasses = parseSourceFile(fileName) # # If there are classes in the # test source file, create an extension # file for it. # next unless fileClasses.count > 0 createExtensionFile(fileName, fileClasses) directoryHasClasses = true allFiles << fileClasses end if directoryHasClasses allTestSubDirectories << Pathname.new(subDirectory).split.last.to_s end end # # Last step is the create a LinuxMain.swift file that # references all the classes and funcs in the source files. # if allFiles.count > 0 createLinuxMain(testsDirectory, allTestSubDirectories, allFiles) end # eof