From 30c1cd0c656bdec79fb075ffd963a2b07118b81f Mon Sep 17 00:00:00 2001 From: Takahiro Ueda Date: Wed, 20 Nov 2024 13:02:31 +0900 Subject: [PATCH 1/7] chore(check.rb): update RuboCop config to 1.68 - Update RuboCop configuration to align with version 1.68. - Fix all offenses, including newly detected ones due to the update. - Minor code refactoring. --- check/.rubocop.yml | 249 ++++++++++++++++++++++++++++++++++++++++++++- check/check.rb | 91 +++++++++-------- 2 files changed, 292 insertions(+), 48 deletions(-) diff --git a/check/.rubocop.yml b/check/.rubocop.yml index f28c3522..4976b99e 100644 --- a/check/.rubocop.yml +++ b/check/.rubocop.yml @@ -1,5 +1,9 @@ -# AllCops: -# TargetRubyVersion: 2.1 +AllCops: + TargetRubyVersion: 2.0 + +# We learned to do multiplication before addition in school. +Lint/AmbiguousOperatorPrecedence: + Enabled: false # Allow several expressions on the same line. Style/Semicolon: @@ -30,6 +34,9 @@ Style/WhileUntilModifier: Style/NegatedIf: Enabled: false +Style/NegatedIfElseCondition: + Enabled: false + Style/NegatedWhile: Enabled: false @@ -81,13 +88,22 @@ Metrics/PerceivedComplexity: # For now. +Style/ClassVars: + Enabled: false + +Style/Documentation: + Enabled: false + Style/FormatStringToken: EnforcedStyle: unannotated -Style/PerlBackrefs: +Style/GlobalVars: + Enabled: false + +Style/OpenStructUse: Enabled: false -Style/SlicingWithRange: # OK only for Ruby 2.6+ +Style/PerlBackrefs: Enabled: false Style/SpecialGlobalVars: @@ -95,3 +111,228 @@ Style/SpecialGlobalVars: Style/StderrPuts: Enabled: false + +##### + +# Enable all other cops. + +Gemspec/AddRuntimeDependency: # new in 1.65 + Enabled: true +Gemspec/DeprecatedAttributeAssignment: # new in 1.30 + Enabled: true +Gemspec/DevelopmentDependencies: # new in 1.44 + Enabled: true +Gemspec/RequireMFA: # new in 1.23 + Enabled: true +Layout/LineContinuationLeadingSpace: # new in 1.31 + Enabled: true +Layout/LineContinuationSpacing: # new in 1.31 + Enabled: true +Layout/LineEndStringConcatenationIndentation: # new in 1.18 + Enabled: true +Layout/SpaceBeforeBrackets: # new in 1.7 + Enabled: true +Lint/AmbiguousAssignment: # new in 1.7 + Enabled: true +Lint/AmbiguousRange: # new in 1.19 + Enabled: true +Lint/ConstantOverwrittenInRescue: # new in 1.31 + Enabled: true +Lint/DeprecatedConstants: # new in 1.8 + Enabled: true +Lint/DuplicateBranch: # new in 1.3 + Enabled: true +Lint/DuplicateMagicComment: # new in 1.37 + Enabled: true +Lint/DuplicateMatchPattern: # new in 1.50 + Enabled: true +Lint/DuplicateRegexpCharacterClassElement: # new in 1.1 + Enabled: true +Lint/DuplicateSetElement: # new in 1.67 + Enabled: true +Lint/EmptyBlock: # new in 1.1 + Enabled: true +Lint/EmptyClass: # new in 1.3 + Enabled: true +Lint/EmptyInPattern: # new in 1.16 + Enabled: true +Lint/IncompatibleIoSelectWithFiberScheduler: # new in 1.21 + Enabled: true +Lint/ItWithoutArgumentsInBlock: # new in 1.59 + Enabled: true +Lint/LambdaWithoutLiteralBlock: # new in 1.8 + Enabled: true +Lint/LiteralAssignmentInCondition: # new in 1.58 + Enabled: true +Lint/MixedCaseRange: # new in 1.53 + Enabled: true +Lint/NoReturnInBeginEndBlocks: # new in 1.2 + Enabled: true +Lint/NonAtomicFileOperation: # new in 1.31 + Enabled: true +Lint/NumberedParameterAssignment: # new in 1.9 + Enabled: true +Lint/OrAssignmentToConstant: # new in 1.9 + Enabled: true +Lint/RedundantDirGlobSort: # new in 1.8 + Enabled: true +Lint/RedundantRegexpQuantifiers: # new in 1.53 + Enabled: true +Lint/RefinementImportMethods: # new in 1.27 + Enabled: true +Lint/RequireRangeParentheses: # new in 1.32 + Enabled: true +Lint/RequireRelativeSelfPath: # new in 1.22 + Enabled: true +Lint/SymbolConversion: # new in 1.9 + Enabled: true +Lint/ToEnumArguments: # new in 1.1 + Enabled: true +Lint/TripleQuotes: # new in 1.9 + Enabled: true +Lint/UnescapedBracketInRegexp: # new in 1.68 + Enabled: true +Lint/UnexpectedBlockArity: # new in 1.5 + Enabled: true +Lint/UnmodifiedReduceAccumulator: # new in 1.1 + Enabled: true +Lint/UselessNumericOperation: # new in 1.66 + Enabled: true +Lint/UselessRescue: # new in 1.43 + Enabled: true +Lint/UselessRuby2Keywords: # new in 1.23 + Enabled: true +Metrics/CollectionLiteralLength: # new in 1.47 + Enabled: true +Naming/BlockForwarding: # new in 1.24 + Enabled: true +Security/CompoundHash: # new in 1.28 + Enabled: true +Security/IoMethods: # new in 1.22 + Enabled: true +Style/AmbiguousEndlessMethodDefinition: # new in 1.68 + Enabled: true +Style/ArgumentsForwarding: # new in 1.1 + Enabled: true +Style/ArrayIntersect: # new in 1.40 + Enabled: true +Style/BitwisePredicate: # new in 1.68 + Enabled: true +Style/CollectionCompact: # new in 1.2 + Enabled: true +Style/CombinableDefined: # new in 1.68 + Enabled: true +Style/ComparableClamp: # new in 1.44 + Enabled: true +Style/ConcatArrayLiterals: # new in 1.41 + Enabled: true +Style/DataInheritance: # new in 1.49 + Enabled: true +Style/DirEmpty: # new in 1.48 + Enabled: true +Style/DocumentDynamicEvalDefinition: # new in 1.1 + Enabled: true +Style/EmptyHeredoc: # new in 1.32 + Enabled: true +Style/EndlessMethod: # new in 1.8 + Enabled: true +Style/EnvHome: # new in 1.29 + Enabled: true +Style/ExactRegexpMatch: # new in 1.51 + Enabled: true +Style/FetchEnvVar: # new in 1.28 + Enabled: true +Style/FileEmpty: # new in 1.48 + Enabled: true +Style/FileRead: # new in 1.24 + Enabled: true +Style/FileWrite: # new in 1.24 + Enabled: true +Style/HashConversion: # new in 1.10 + Enabled: true +Style/HashExcept: # new in 1.7 + Enabled: true +Style/IfWithBooleanLiteralBranches: # new in 1.9 + Enabled: true +Style/InPatternThen: # new in 1.16 + Enabled: true +Style/KeywordArgumentsMerging: # new in 1.68 + Enabled: true +Style/MagicCommentFormat: # new in 1.35 + Enabled: true +Style/MapCompactWithConditionalBlock: # new in 1.30 + Enabled: true +Style/MapIntoArray: # new in 1.63 + Enabled: true +Style/MapToHash: # new in 1.24 + Enabled: true +Style/MapToSet: # new in 1.42 + Enabled: true +Style/MinMaxComparison: # new in 1.42 + Enabled: true +Style/MultilineInPatternThen: # new in 1.16 + Enabled: true +Style/NestedFileDirname: # new in 1.26 + Enabled: true +Style/NilLambda: # new in 1.3 + Enabled: true +Style/NumberedParameters: # new in 1.22 + Enabled: true +Style/NumberedParametersLimit: # new in 1.22 + Enabled: true +Style/ObjectThen: # new in 1.28 + Enabled: true +Style/OperatorMethodCall: # new in 1.37 + Enabled: true +Style/QuotedSymbols: # new in 1.16 + Enabled: true +Style/RedundantArgument: # new in 1.4 + Enabled: true +Style/RedundantArrayConstructor: # new in 1.52 + Enabled: true +Style/RedundantConstantBase: # new in 1.40 + Enabled: true +Style/RedundantCurrentDirectoryInPath: # new in 1.53 + Enabled: true +Style/RedundantDoubleSplatHashBraces: # new in 1.41 + Enabled: true +Style/RedundantEach: # new in 1.38 + Enabled: true +Style/RedundantFilterChain: # new in 1.52 + Enabled: true +Style/RedundantHeredocDelimiterQuotes: # new in 1.45 + Enabled: true +Style/RedundantInitialize: # new in 1.27 + Enabled: true +Style/RedundantInterpolationUnfreeze: # new in 1.66 + Enabled: true +Style/RedundantLineContinuation: # new in 1.49 + Enabled: true +Style/RedundantRegexpArgument: # new in 1.53 + Enabled: true +Style/RedundantRegexpConstructor: # new in 1.52 + Enabled: true +Style/RedundantSelfAssignmentBranch: # new in 1.19 + Enabled: true +Style/RedundantStringEscape: # new in 1.37 + Enabled: true +Style/ReturnNilInPredicateMethodDefinition: # new in 1.53 + Enabled: true +Style/SafeNavigationChainLength: # new in 1.68 + Enabled: true +Style/SelectByRegexp: # new in 1.22 + Enabled: true +Style/SendWithLiteralMethodName: # new in 1.64 + Enabled: true +Style/SingleLineDoEndBlock: # new in 1.57 + Enabled: true +Style/StringChars: # new in 1.12 + Enabled: true +Style/SuperArguments: # new in 1.64 + Enabled: true +Style/SuperWithArgsParentheses: # new in 1.58 + Enabled: true +Style/SwapValues: # new in 1.1 + Enabled: true +Style/YAMLFileRead: # new in 1.53 + Enabled: true diff --git a/check/check.rb b/check/check.rb index bc166a05..b169de2a 100755 --- a/check/check.rb +++ b/check/check.rb @@ -57,23 +57,25 @@ def read_env_str(key, default_value) # Get a positive integer from the given environment variable. def read_env_positive_int(key, default_value) + if default_value <= 0 + raise ArgumentError, "invalid default_value: #{default_value}" + end + value = default_value if ENV.key?(key) begin value = Integer(ENV[key]) + raise ArgumentError if value <= 0 rescue ArgumentError, TypeError - warn("environment variable ignored: #{key} is not integer: #{ENV[key]}") + warn("environment variable ignored: #{key} is not positive integer: #{ENV[key]}") + value = default_value end end - if value <= 0 - warn("environment variable ignored: #{key} must be positive: #{ENV[key]}") - value = default_value - end value end # Get the total size of the physical memory available on the host machine. -def get_total_physical_memory +def total_physical_memory platform = RbConfig::CONFIG["host_os"].downcase if platform.include?("linux") mem_info = `free -b | grep Mem` @@ -84,8 +86,6 @@ def get_total_physical_memory elsif platform.include?("mingw") || platform.include?("mswin") mem_info = `wmic ComputerSystem get TotalPhysicalMemory` mem_info.split[1].to_i - else - nil end end @@ -278,7 +278,7 @@ def github? def total_memory @@total_memory_mutex.synchronize do if @@cached_total_memory.nil? - result = get_total_physical_memory + result = total_physical_memory if result.nil? @@cached_total_memory = -1 else @@ -294,7 +294,7 @@ def total_memory def reveal_newlines(str) if FormTest.cfg.show_newlines - str.gsub(/\r/, "").gsub(/\n/, "\n") + str.gsub("\r", "").gsub("\n", "\n") else str end @@ -318,9 +318,7 @@ def setup_files cleanup_files @tmpdir = TempDir.mktmpdir("#{self.class.name}_") nfiles.times do |i| - File.open(File.join(@tmpdir, "#{i + 1}.frm"), "w") do |file| - file.write(info.sources[i]) - end + File.write(File.join(@tmpdir, "#{i + 1}.frm"), info.sources[i]) end end @@ -380,8 +378,8 @@ def do_test(&block) @raw_stdout = @stdout @raw_stderr = @stderr if windows? - @stdout = @stdout.gsub(/\r\n/, "\n") - @stderr = @stderr.gsub(/\r\n/, "\n") + @stdout = @stdout.gsub("\r\n", "\n") + @stderr = @stderr.gsub("\r\n", "\n") end # MesPrint inevitably inserts newline characters when a line exceeds # its length limit. To verify error/warning messages, here we remove @@ -512,7 +510,7 @@ def execute_impl(cmd, timeout, chdir) stderr_lines: stderr_lines, combined_lines: combined_lines, exit_status: exit_status, - finished_in_time: finished_in_time, + finished_in_time: finished_in_time } end @@ -616,12 +614,12 @@ def file(filename) # On Windows, we convert newline characters in the file into # the Unix-style newline characters used in our test cases. if windows? - result = result.gsub(/\r\n/, "\n") + result = result.gsub("\r\n", "\n") end return result end rescue StandardError - $stderr.puts("warning: failed to read '#{filename}'") + warn("failed to read '#{filename}'") end "" end @@ -635,9 +633,7 @@ def read(filename) def write(filename, text) fname = File.join(@tmpdir, filename) FileUtils.mkdir_p(File.dirname(fname)) - File.open(fname, "w") do |f| - f.write(text) - end + File.write(fname, text) end # The working directory for the test. @@ -930,7 +926,7 @@ def make_ruby_file(filename) end if !ulimits.nil? ulimits.map! { |s| "ulimit #{s}; " } - ulimits = ulimits.join("") + ulimits = ulimits.join line += "def ulimits; %(#{ulimits}) end; " end if !time_dilation.nil? @@ -1053,14 +1049,14 @@ def test_enabled?(name) # construct regular expressions (wildcards: '*' and '?') @name_patterns.length.times do |i| if !@name_patterns[i].is_a?(Regexp) - s = @name_patterns[i].to_s.gsub("\*", ".*").tr("\?", ".") + s = @name_patterns[i].to_s.gsub("*", ".*").tr("?", ".") s = "^#{s}$" @name_patterns[i] = Regexp.new(s) end end @exclude_patterns.length.times do |i| if !@exclude_patterns[i].is_a?(Regexp) - s = @exclude_patterns[i].to_s.gsub("\*", ".*").tr("\?", ".") + s = @exclude_patterns[i].to_s.gsub("*", ".*").tr("?", ".") s = "^#{s}$" @exclude_patterns[i] = Regexp.new(s) end @@ -1114,8 +1110,8 @@ def delete(classname) @classes_info.delete("Test_#{classname}") # It seems difficult to delete a class. # Instead, remove the test method. - klass = Object.const_get("Test_#{classname}".to_sym) - klass.send(:remove_method, "test_#{classname}".to_sym) + klass = Object.const_get(:"Test_#{classname}") + klass.send(:remove_method, :"test_#{classname}") end end @@ -1189,13 +1185,11 @@ def check tmpdir = TempDir.mktmpdir("ver_") begin frmname = File.join(tmpdir, "ver.frm") - File.open(frmname, "w") do |f| - f.write(<<-'TEST_FRM') - #- - Off finalstats; - .end - TEST_FRM - end + File.write(frmname, <<-TEST_FRM) + #- + Off finalstats; + .end + TEST_FRM @head = "" out, _status = Open3.capture2e("#{@form_bin} #{frmname}") @@ -1269,16 +1263,25 @@ def check end @form_cmd = cmdlist.join(" ") # Check the output header. - out, _err, status = Open3.capture3("#{@form_cmd} #{frmname}") + out, err, status = Open3.capture3("#{@form_cmd} #{frmname}") if status.success? - @head = out.split("\n").first + form_version_line = out.split("\n").first + if form_version_line.nil? + warn("failed to get the actual version of FORM") + else + @head = form_version_line + end else fatal("failed to execute '#{@form_cmd}'") end if !@valgrind.nil? - # Include valgrind version information. - out, _status = Open3.capture2e("#{@form_cmd} #{frmname}") - @head += "\n" + out.split("\n").select { |line| line.include?("Valgrind") }.first + # Include Valgrind version information. + valgrind_version_line = err.split("\n").select { |line| line.include?("Valgrind") }.first + if valgrind_version_line.nil? + warn("failed to get the version of Valgrind") + else + @head += "\n#{valgrind_version_line}" + end end ensure FileUtils.rm_rf(tmpdir) @@ -1517,7 +1520,7 @@ def main # --path option. if !opts.path.nil? - ENV["PATH"] = "#{opts.path}#{File::PATH_SEPARATOR}#{ENV['PATH']}" + ENV["PATH"] = "#{opts.path}#{File::PATH_SEPARATOR}#{ENV.fetch('PATH', '')}" end # Set FORMPATH @@ -1551,8 +1554,8 @@ def main opts.valgrind_opts, opts.wordsize, opts.ncpu, - opts.timeout > 1 ? opts.timeout : 1, - opts.retries > 1 ? opts.retries : 1, + [opts.timeout, 1].max, + [opts.retries, 1].max, opts.stat, opts.full, opts.verbose, @@ -1658,11 +1661,11 @@ def format_time(time, max_time) end t = Float(t) h = Integer(t / 3600) - t = t % 3600 + t %= 3600 m = Integer(t / 60) - t = t % 60 + t %= 60 s = Integer(t) - t = t % 1 + t %= 1 ms = Integer(t * 1000) format("%s%02d:%02d:%02d.%03d", overflow ? ">" : " ", h, m, s, ms) end From a2fa9a1cac24a3390d202091a2739f2dc1b59249 Mon Sep 17 00:00:00 2001 From: Takahiro Ueda Date: Mon, 18 Nov 2024 18:00:38 +0900 Subject: [PATCH 2/7] test(check.rb): update Ruby minimum requirement from 1.9 to 2.0 Close #580. --- INSTALL | 4 ++-- check/check.rb | 4 ++-- configure.ac | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/INSTALL b/INSTALL index c83bef13..b1e86cf7 100644 --- a/INSTALL +++ b/INSTALL @@ -30,7 +30,7 @@ like the GNU glibc. The threaded version of FORM needs a Posix compliant implementation of threads (pthreads). The parallel version ParFORM needs an MPI implementation. -The automated test suite of FORM requires Ruby version >= 1.9 and the testing +The automated test suite of FORM requires Ruby version >= 2.0 and the testing framework "test/unit". For the latter, you may need to install test-unit gem separately. @@ -174,7 +174,7 @@ can be used. Testing ======= -If Ruby version >= 1.9 and "test/unit" are installed on your system, the +If Ruby version >= 2.0 and "test/unit" are installed on your system, the configure script enables the automated test suite. Then you can run it by make check diff --git a/check/check.rb b/check/check.rb index b169de2a..9e917f91 100755 --- a/check/check.rb +++ b/check/check.rb @@ -8,8 +8,8 @@ # rubocop:enable all # Check the Ruby version. -if RUBY_VERSION < "1.9.0" - warn("ruby 1.9 required for the test suite") +if RUBY_VERSION < "2.0.0" + warn("ruby 2.0 required for the test suite") exit(1) end diff --git a/configure.ac b/configure.ac index 394a83f4..a42e22d9 100644 --- a/configure.ac +++ b/configure.ac @@ -1000,13 +1000,13 @@ AM_CONDITIONAL(CONFIG_MAKEINDEX, [test "x$MAKEINDEX" != x]) AM_CONDITIONAL(CONFIG_HTLATEX, [test "x$HTLATEX" != x]) AM_CONDITIONAL(CONFIG_LATEX2HTML, [test "x$LATEX2HTML" != x]) -# Check for Ruby >= 1.9 and test/unit. +# Check for Ruby >= 2.0 and test/unit. AC_PATH_PROG(RUBY, ruby, "") ok=yes test "x$RUBY" = x && ok=no if test "x$ok" = xyes; then - AC_MSG_CHECKING([whether ruby >= 1.9]) - $RUBY -e 'exit(1) if RUBY_VERSION < "1.9.0"' >/dev/null 2>&1 || ok=no + AC_MSG_CHECKING([whether ruby >= 2.0]) + $RUBY -e 'exit(1) if RUBY_VERSION < "2.0.0"' >/dev/null 2>&1 || ok=no AC_MSG_RESULT([$ok]) fi if test "x$ok" = xyes; then From d950e7949667aa98e206eb3f791f3b763c1d3e02 Mon Sep 17 00:00:00 2001 From: Takahiro Ueda Date: Thu, 9 Jan 2025 16:01:24 +0900 Subject: [PATCH 3/7] test(check.rb): --stat details before summary Previously, the detailed output from the --stat option appeared after the test suite summary, making it harder to access the summary when the details were lengthy. This patch tries to display the --stat details before the summary, which should work with test-unit >= 3.0.9. It also tries to use the "failure" colour for failed test lines in the details. --- check/check.rb | 97 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 77 insertions(+), 20 deletions(-) diff --git a/check/check.rb b/check/check.rb index 9e917f91..9e3d6e10 100755 --- a/check/check.rb +++ b/check/check.rb @@ -155,8 +155,8 @@ def self.cleanup at_exit { TempDir.cleanup } end -# Register a finalization function before loading test/unit. -at_exit { defined?(finalize) && finalize } +# Register a function before loading test/unit. +at_exit { defined?(output_detailed_statistics) && output_detailed_statistics } # We use test/unit, which is now not in the standard library. begin @@ -166,6 +166,36 @@ def self.cleanup exit(1) end +# Try a monkey patch to call output_detailed_statistics() before the test suite summary. +begin + require "test/unit/ui/console/testrunner" + + module Test + module Unit + module UI + module Console + class TestRunner + alias old_output_statistics output_statistics + alias _old_output output + alias _old_output_summary_marker output_summary_marker + + def output_statistics(*args, **kwargs, &block) + if need_output_detailed_statistics? + output_summary_marker + output_detailed_statistics(method(:output)) + output_summary_marker + end + old_output_statistics(*args, **kwargs, &block) + end + end + end + end + end + end +rescue NameError, LoadError + # do nothing +end + # Find the path to a program. def which(name) result = nil @@ -1565,17 +1595,33 @@ def main puts(FormTest.cfg.head) end -def finalize - return if FormTest.cfg.nil? || !FormTest.cfg.stat +def need_output_detailed_statistics? + # Check if already done. + $output_detailed_statistics_done ||= false + return false if $output_detailed_statistics_done - infos = FormTest.tests.classes_info_list + # Check if --stat enabled. + return false if FormTest.cfg.nil? || !FormTest.cfg.stat - return if infos.empty? + # Check if enabled test cases exist. + return false if FormTest.tests.classes_info_list.empty? + + true +end + +def output_detailed_statistics(output = nil) + return if !need_output_detailed_statistics? + + $output_detailed_statistics_done = true + + # method to output + output ||= method(:puts) # Print detailed statistics. term_width = IO.console_size[1] + infos = FormTest.tests.classes_info_list max_foldname_width = infos.map { |info| info.foldname.length }.max max_where_width = infos.map { |info| info.where.length }.max + 2 status_width = 7 @@ -1589,8 +1635,6 @@ def finalize bar_width = 40 end - puts("default timeout: #{FormTest.cfg.timeout}s") - infos.each do |info| (0..info.sources.length - 1).each do |i| t = 0 @@ -1602,22 +1646,35 @@ def finalize timeout *= info.time_dilation end if i == 0 - puts(format("%s %s %s %s%s", - lpad(info.foldname, max_foldname_width), - lpad("(#{info.where})", max_where_width), - lpad(info.status.nil? ? "UNKNOWN" : info.status, status_width), - bar_str(t, timeout, bar_width), - format_time(t, timeout))) + s = format("%s %s %s %s%s", + lpad(info.foldname, max_foldname_width), + lpad("(#{info.where})", max_where_width), + lpad(info.status.nil? ? "UNKNOWN" : info.status, status_width), + bar_str(t, timeout, bar_width), + format_time(t, timeout)) else - puts(format("%s %s %s %s%s", - lpad("", max_foldname_width), - lpad("", max_where_width), - lpad("", status_width), - bar_str(t, timeout, bar_width), - format_time(t, timeout))) + s = format("%s %s %s %s%s", + lpad("", max_foldname_width), + lpad("", max_where_width), + lpad("", status_width), + bar_str(t, timeout, bar_width), + format_time(t, timeout)) + end + if !info.status.nil? && info.status == "FAILED" + begin + output.call(s, color("failure")) + rescue StandardError + output.call(s) + end + else + output.call(s) end end end + + output.call("Default timeout: #{FormTest.cfg.timeout}s") + + true end # Return the string with padding to left. From 79b69d4d206f6f3ecc272a24569f4849a6cd3118 Mon Sep 17 00:00:00 2001 From: Takahiro Ueda Date: Wed, 20 Nov 2024 13:41:51 +0900 Subject: [PATCH 4/7] test(check.rb): detect wordsize using both header and preprocessor Starting with version 5.0.0, the output header is expected to no longer print 32-bits or 64-bits. The test suite is updated to detect the wordsize using the preprocessor calculator even when the header does not provide this information. --- check/check.rb | 52 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/check/check.rb b/check/check.rb index 9e3d6e10..d0af1fa2 100755 --- a/check/check.rb +++ b/check/check.rb @@ -1247,15 +1247,57 @@ def check system("#{form_bin} #{frmname}") fatal("failed to get the version of '#{@form}'") end + # Check the wordsize. + # Method 1: from the output header. + # Method 2: 2^64 = 0 (mod 2^64) => 64-bit machine => sizeof(WORD) = 4, etc. + wordsize1 = nil + wordsize2 = nil + if @head =~ /FORM[^(]*\([^)]*\)\s*(\d+)-bits/ + wordsize1 = $1.to_i / 16 + end + frmname2 = File.join(tmpdir, "ws.frm") + File.write(frmname2, <<-TEST_FRM) + #do w={2,4,8} + #message wordtest,`w',{2^(`w'*16-1)},{2^(`w'*16)} + #enddo + .end + TEST_FRM + out, _err, status = Open3.capture3("#{@form_bin} #{frmname2}") + if status.success? + out.split("\n").each do |output_line| + if output_line =~ /~~~wordtest,(\d+),(-?\d+),(-?\d+)/ + w = $1.to_i + x = $2.to_i + y = $3.to_i + if x != 0 && y == 0 + wordsize2 = w + break + end + end + end + end + if !@wordsize.nil? + if !wordsize1.nil? && @wordsize != wordsize1 + warn("--wordsize=#{@wordsize} specified but the header of '#{@form}' indicates the wordsize = #{wordsize1}") + end + if !wordsize2.nil? && @wordsize != wordsize2 + warn("--wordsize=#{@wordsize} specified but the preprocessor calculator of '#{@form}' determined wordsize = #{wordsize2}") + end + end if @wordsize.nil? - if @head =~ /FORM[^(]*\([^)]*\)\s*(\d+)-bits/ - @wordsize = $1.to_i / 16 + if !wordsize1.nil? && !wordsize2.nil? && wordsize1 != wordsize2 + warn("the header of '#{@form}' indicates the wordsize = #{wordsize1} but the preprocessor calculator determined wordsize = #{wordsize2}") + elsif !wordsize1.nil? + @wordsize = wordsize1 else - warn("failed to get the wordsize of '#{@form}'") - warn("assuming wordsize = 4") - @wordsize = 4 + @wordsize = wordsize2 end end + if @wordsize.nil? + warn("failed to get the wordsize of '#{@form}'") + warn("assuming wordsize = 4") + @wordsize = 4 + end # Prepare for mpirun if @is_mpi @mpirun_bin = which(@mpirun) From 9a88396f3501759a083a1d1073d004ede4b60384 Mon Sep 17 00:00:00 2001 From: Takahiro Ueda Date: Fri, 22 Nov 2024 23:47:53 +0900 Subject: [PATCH 5/7] test(check.rb): relax warning detection pattern The new pattern detects warnings printed in non-canonical forms. Close #581. --- check/check.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/check/check.rb b/check/check.rb index d0af1fa2..2d082a3c 100755 --- a/check/check.rb +++ b/check/check.rb @@ -691,9 +691,9 @@ def finished? # true if the FORM job put warning messages. def warning?(expected_message = nil) if expected_message.nil? - @stdout =~ /(^|\R)\S+ Line \d+ --> Warning/ + @stdout.include?("Warning:") else - @cleaned_stdout =~ Regexp.new("(^|\\R)\\S+ Line \\d+ --> Warning: .*#{Regexp.escape(expected_message)}") + @cleaned_stdout =~ Regexp.new("Warning: .*#{Regexp.escape(expected_message)}") end end From c392424c2e4338c1cb028e8ee98784c406271f1e Mon Sep 17 00:00:00 2001 From: Takahiro Ueda Date: Fri, 20 Dec 2024 19:53:27 +0900 Subject: [PATCH 6/7] fix(test): remove buffer size warnings These setup parameters are suggested by @jodavies. --- check/fixes.frm | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/check/fixes.frm b/check/fixes.frm index 33d61f60..a77ac168 100644 --- a/check/fixes.frm +++ b/check/fixes.frm @@ -998,10 +998,9 @@ assert result("test") =~ expr("0") *--#] Issue95 : *--#[ Issue95b : #- -#:filepatches 4 -#:largesize 25600 +#:filepatches 16 +#:largepatches 20 #:maxtermsize 200 -#:smallsize 12800 #:termsinsmall 16 Off stats; @@ -3134,7 +3133,7 @@ assert succeeded? assert result("test") =~ expr("0") *--#] Issue544 : *--#[ Issue563 : -#: SubTermsInSmall 50 +#: SubTermsInSmall 64 CFunction f,g; Symbol a; From b484d7b5ca47aef3a2c501cafeab3d41cac15a56 Mon Sep 17 00:00:00 2001 From: Takahiro Ueda Date: Thu, 9 Jan 2025 16:02:03 +0900 Subject: [PATCH 7/7] docs(check): update the test suite README --- check/README.md | 258 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 212 insertions(+), 46 deletions(-) diff --git a/check/README.md b/check/README.md index f20d210a..797e2037 100644 --- a/check/README.md +++ b/check/README.md @@ -1,87 +1,89 @@ -FORM Test Suite -=============== +# FORM Test Suite This directory contains a collection of test cases that can be used for verifying the behaviour of FORM. It also has a script to run the test cases and check the results. -Prerequisites -------------- +## Prerequisites The test runner script is written in [Ruby](https://www.ruby-lang.org/) -and requires Ruby 1.9 or later. The script uses the so-called `test/unit` +and requires Ruby 2.0 or later. The script uses the so-called `test/unit` library. In some Linux distributions the library is installed together with Ruby, while some distributions may have the library as an optional package, or one may need to manually install [test-unit](http://test-unit.github.io/test-unit/en/) via the `gem` command. -Currently, the script runs only on Unix-like systems. -Usage ------ +## Usage ### From the build system To use the test suite from the automatic build system (see also the [INSTALL](../INSTALL) file), -run +run the following command: -``` +```bash # in the root build directory make check ``` which tests the executables (release versions) compiled by the build system. -### Testing in the standalone mode +### Testing in standalone mode Alternatively, one can run the test runner script directly: -``` +```bash # in the "check" directory ./check.rb ``` -By default, it tests `form` found in $PATH. -To check another executable, give the path as a command line option: +By default, this tests `form` found in `$PATH`. +To test another executable, specify its path as a command-line argument: -``` +```bash ./check.rb /path/to/form ``` -One can specify a TFORM (or ParFORM) executable in this way. +One can also specify a TFORM (or ParFORM) executable in this way. TFORM and ParFORM will be run with 4 CPUs (can be changed by the `--cpu N` option). By default, all test cases in all FORM files (`*.frm`) found in the `check` directory (not in subdirectories) are used. To select test cases or FORM files -to be run, give their names as command line options, for example, +to be run, specify their names as command-line arguments. For example: -``` -./check.rb examples.frm +```bash ./check.rb Issue8 +./check.rb 'divmod_*' +./check.rb examples.frm ``` -For more advanced options, see the help message shown by the `--help` option. +For more advanced options, refer to the help message using the `--help` option. -Writing tests -------------- +## Writing tests ### Where to add test cases? -Currently, the standard test set (run by default) consists of 3 files: +Currently, the standard test set (run by default) consists of 4 files: -- `examples.frm`: Examples found in the manual. +- `examples.frm`: Examples provided in the manual. - `features.frm`: Test cases for newly added features. - `fixes.frm`: Test cases for bug fixes. +- `user.frm`: Test cases contributed by users. Each test case in these files should finish in a short time: the timeout is set to 10 seconds. Bigger tests that take more time are put in subdirectories -(e.g., forcer) and should be specified by command-line options when the test -suite is invoked. +(e.g., `forcer`) and should be specified by command-line options when the test +suite is invoked: + +```bash +./check.rb -C forcer # The Forcer library must be available in FORMPATH. +``` ### Structure of a test case -A test case is given as a fold in a FORM file. A simple example is: +A test case is given as a fold in a FORM file. +The following is a simple example: ``` *--#[ Test1 : @@ -95,16 +97,22 @@ assert result("F") =~ expr("1 + 2*x + x^2") ``` The fold name `Test1` gives the name of the test case, which should be unique. -The part before `.end` is a normal FORM program. After `.end`, one can write -a Ruby program to check the results. In this example, `assert` method (which is -provided by some unit test class) is used for checking whether its argument is -`true`. The first assertion checks `succeeded?`, which gives `true` if the FORM -successfully finishes. The second assertion checks the printed result of the -expression `F` by a regular expression matching (`=~`). In the left-hand side, -`result("F")` returns the (lastly) printed output for the expression `F` as -a string. In the right-hand side, `expr("...")` makes a regular expression with -removing white spaces in its argument. Since `expr()` removes all white spaces, -one can also put new lines, for example, +The part before `.end` is a normal FORM program. +After `.end`, one can write a Ruby program to check the results. + +The `assert` method checks whether its argument evaluates to `true`. +In this example: +- The first assertion verifies `succeeded?`, which returns `true` if the FORM finishes successfully. +- The second assertion checks the printed result of the +expression `F` by a regular expression matching (`=~`). + - On the left-hand side, `result("F")` returns the (lastly) printed output + for the expression `F` as a string. + - On the right-hand side, `expr("...")` creates a regular expression + by removing white spaces in its argument. + +Since `expr()` removes all white spaces, +one can include new lines in the argument. +For example: ``` *--#[ Test2 : @@ -123,13 +131,171 @@ assert result("F") =~ expr(" which is convenient to copy and paste a long output from a terminal. -### Tips +Two or more FORM programs, separated by `.end`, can be put in a test case. +The part after the last `.end` is considered as a Ruby program. +For example: +``` +*--#[ Test3 : +S x; +G F = (1+x)^2; +P; +.store +Save out.sav; +.end +Load out.sav; +L G = F; +P; +.end +assert succeeded? +assert result("F") =~ expr("1 + 2*x + x^2") +assert result("G") =~ expr("1 + 2*x + x^2") +*--#] Test3 : +``` + +Some test cases need to run only under specific conditions. +In such cases, one can use special instructions starting with `#`. +For example: +``` +*--#[ Test4 : +S x; +L F = +#pipe echo "(1+x)^2" +; +P; +.end +#require unix? +assert succeeded? +assert result("F") =~ expr("1 + 2*x + x^2") +*--#] Test4 : +``` +In this example, `#require unix?` ensures that the test runs +only on Unix, where `#pipe` is expected to work. + +### Available methods + +#### Execution configuration + +- `timeout → integer or float` + Timeout duration in seconds. +- `ncpu → integer` + Number of assigned CPUs. +- `total_memory → integer` + Total physical memory available in bytes. +- `serial? → bool` + `true` if FORM is the serial version, otherwise `false`. +- `threaded? → bool` + `true` if FORM is the multithreaded version (TFORM), otherwise `false`. +- `mpi? → bool` + `true` if FORM is the MPI version (ParFORM), otherwise `false`. +- `valgrind? → bool` + `true` if FORM is running under Valgrind, otherwise `false`. +- `wordsize → integer` + Word size in bytes used by FORM (`4` on 64-bit systems). +- `cygwin? → bool` + `true` if running on Cygwin, otherwise `false`. +- `mac? → bool` + `true` if running on macOS, otherwise `false`. +- `linux? → bool` + `true` if running on Linux, otherwise `false`. +- `unix? → bool` + `true` if running on Unix, otherwise `false`. +- `windows? → bool` + `true` if running on Windows, otherwise `false`. +- `travis? → bool` + `true` if running on Travis CI, otherwise `false`. +- `github? → bool` + `true` if running on GitHub Actions, otherwise `false`. + +#### Job status + +- `return_value → integer` + Exit status of the FORM job. +- `finished? → bool` + `true` if the FORM job finished within the timeout, otherwise `false`. +- `succeeded? → bool` + `true` if the FORM job finished without any problems, otherwise `false`. +- `warning? → bool` + `true` if the FORM job issued a warning, otherwise `false`. +- `warning?(expected_message : string) → bool` + `true` if the FORM job issued the expected warning, otherwise `false`. + +The following methods are similar to `warning?`, +but they check for preprocessor errors, compile-time errors, +and run-time errors, respectively: + +- `preprocess_error? → bool` + `preprocess_error?(expected_message : string) → bool` +- `compile_error? → bool` + `compile_error?(expected_message : string) → bool` +- `runtime_error? → bool` + `runtime_error?(expected_message : string) → bool` + +#### Standard streams + +- `stdout → string` + Standard output of the FORM job. +- `stderr → string` + Standard error of the FORM job. + +#### Expressions + +The following methods assume the default format for printing expressions: + +- `result(expr_name : string) → string` + The last printed output of the specified expression. +- `result(expr_name : string, index : integer) → string` + The printed output of the specified expression at the given index (zero-based). +- `exact_result(expr_name : string) → string` + `exact_result(expr_name : string, index : integer) → string` + Similar to `result`, but returns the exact output, preserving line breaks and whitespaces. + +The following methods assume the default format for statistics: + +- `nterms(expr_name : string) → integer` + `nterms(expr_name : string, index : integer) → integer` + The number of terms as reported in the statistics for the specified expression. +- `bytesize(expr_name : string) → integer` + `bytesize(expr_name : string, index : integer) → integer` + The size in bytes as reported in the statistics for the specified expression. + +#### Helper methods + +- `exact_pattern(str : string) → regexp` + Regular expression constructed from the given text with escaping any special characters. +- `pattern(str : string) → regexp` + Similar to `exact_pattern`, but ignores whitespaces. +- `expr(str : string) → regexp` + Similar to `pattern`, but matches only with the whole expression. +- `file(filename : string) → string` + `read(filename : string) → string` + Text in the specified file. +- `write(filename : string, text : string) → nil` + Writes a text into the specified file. + +### Available instructions -- To verify that FORM finishes with a certain error, one can use - `assert compile_error?` or `assert runtime_error?`. -- Two or more FORM programs, separated by `.end`, can be put in a test case. - Then the part after the last `.end` is for Ruby. -- To skip a test case for some condition, one can specify it by `#pend_if`. - (See the result of grepping `pend_if` in the existing files.) -- When a test case requires other text files, one can use `#prepare write`. - (See the result of grepping `prepare` in the existing files.) +- `#require ` + Ensures that the test is executed only if the specified `` is met. +- `#pend_if ` + Marks the test as pending if the specified `` is met. +- `#prepare ` + Executes the given `` before running the test. + For example: + ``` + #prepare write "foo.prc", "#procedure foo\n#message foo\n#endprocedure" + ``` +- `#ulimit ` + Sets the resource limits. This is done via the `ulimit` command. + For example: + ``` + #require linux? + #ulimit -v 8_000_000 + ``` + This sets the maximum amount of virtual memory available to + 8,000,000 KiB (~ 8GB). +- `#time_dilation ` + Multiplies the timeout by the specified `` factor. + For example: + ``` + #time_dilation 2.0 + ```