From d50daa3698b60d737c64bb1ca0ee9ee57b19474d Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Wed, 19 May 2021 17:03:17 +0300 Subject: [PATCH 01/33] Initial commit for integration tests. Experimental. Playing with potential syntax --- test/integration/runner.rb | 3 +++ test/integration/tests/0-noop.rb | 4 ++++ test/integration/tests/simple.rb | 7 +++++++ 3 files changed, 14 insertions(+) create mode 100644 test/integration/runner.rb create mode 100644 test/integration/tests/0-noop.rb create mode 100644 test/integration/tests/simple.rb diff --git a/test/integration/runner.rb b/test/integration/runner.rb new file mode 100644 index 00000000..31acf860 --- /dev/null +++ b/test/integration/runner.rb @@ -0,0 +1,3 @@ +Dir['tests/*.rb'].sort.each do |f| + require('./' + f) +end diff --git a/test/integration/tests/0-noop.rb b/test/integration/tests/0-noop.rb new file mode 100644 index 00000000..5eb6dc10 --- /dev/null +++ b/test/integration/tests/0-noop.rb @@ -0,0 +1,4 @@ +# this test does nothing +# it's, in a way, a test of the integration testing framework + +puts 'executing noop test' diff --git a/test/integration/tests/simple.rb b/test/integration/tests/simple.rb new file mode 100644 index 00000000..cb222f81 --- /dev/null +++ b/test/integration/tests/simple.rb @@ -0,0 +1,7 @@ +add_test('basic') { |prj| + prj.set_body <<~CMAKE + CPMAddPackage("gh:cpm-cmake/testpack-adder") + add_executable(using-adder using-adder.cpp) + target_link_libraries(using-adder PRIVATE adder) + CMAKE +} From c74a739cafd39c0a1928dd4824f0a043f00d76be Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Wed, 19 May 2021 20:48:29 +0300 Subject: [PATCH 02/33] Some experimental code to setup tests --- test/integration/.gitignore | 1 + test/integration/runner.rb | 53 ++++++++++++++++++++++++++++++-- test/integration/tests/simple.rb | 4 ++- 3 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 test/integration/.gitignore diff --git a/test/integration/.gitignore b/test/integration/.gitignore new file mode 100644 index 00000000..3fec32c8 --- /dev/null +++ b/test/integration/.gitignore @@ -0,0 +1 @@ +tmp/ diff --git a/test/integration/runner.rb b/test/integration/runner.rb index 31acf860..d0ecabd8 100644 --- a/test/integration/runner.rb +++ b/test/integration/runner.rb @@ -1,3 +1,52 @@ -Dir['tests/*.rb'].sort.each do |f| - require('./' + f) +require 'fileutils' + +CPMPath = File.expand_path('../../cmake/CPM.cmake') +raise "Cannot file 'CPM.cmake' at '#{CPMPath}'" if !File.file?(CPMPath) + +CommonHeader = <<~CMAKE + cmake_minimum_required(VERSION 3.14 FATAL_ERROR) + include("#{CPMPath}") + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") +CMAKE + +TestDir = File.expand_path("./tmp/#{Time.now.strftime('%Y_%m_%d-%H_%M_%S')}") +raise "Test directory '#{TestDir}' already exists" if File.exist?(TestDir) + +class Project + def initialize(name) + @name = name + @dir = File.join(TestDir, name) + + @lists = CommonHeader + "project(#{name})\n" + + FileUtils.mkdir_p(File.join(TestDir, name)) + end + + def set_body(body) + @lists += "\n" + body + "\n" + end + + def configure() + File.write(File.join(@dir, 'CMakeLists.txt'), @lists) + end +end + +@cur_file = '' +@tests = {} +def add_test(name, func) + raise "#{@cur_file}: Test #{name} is already defined from another file" if @tests[name] + @tests[name] = func +end + +Dir['tests/*.rb'].sort.each do |file| + @cur_file = file + load './' + file +end + +# sort alphabetically +sorted_tests = @tests.to_a.sort {|a, b| a[0] <=> b[0] } + +sorted_tests.each do |name, func| + proj = Project.new(name) + func.(proj) end diff --git a/test/integration/tests/simple.rb b/test/integration/tests/simple.rb index cb222f81..929de5b2 100644 --- a/test/integration/tests/simple.rb +++ b/test/integration/tests/simple.rb @@ -1,7 +1,9 @@ -add_test('basic') { |prj| +add_test 'basic', ->(prj) { prj.set_body <<~CMAKE CPMAddPackage("gh:cpm-cmake/testpack-adder") add_executable(using-adder using-adder.cpp) target_link_libraries(using-adder PRIVATE adder) CMAKE + + prj.configure } From 2830b7046df5b3491bc0fba54e69a22eb02708e1 Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Thu, 20 May 2021 15:50:34 +0300 Subject: [PATCH 03/33] Piecewise building of CMakeLists --- test/integration/runner.rb | 49 ++++++++++++++++++++++++++++---- test/integration/tests/simple.rb | 11 ++++--- 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/test/integration/runner.rb b/test/integration/runner.rb index d0ecabd8..4a6b3bc0 100644 --- a/test/integration/runner.rb +++ b/test/integration/runner.rb @@ -1,4 +1,5 @@ require 'fileutils' +require 'open3' CPMPath = File.expand_path('../../cmake/CPM.cmake') raise "Cannot file 'CPM.cmake' at '#{CPMPath}'" if !File.file?(CPMPath) @@ -12,22 +13,58 @@ TestDir = File.expand_path("./tmp/#{Time.now.strftime('%Y_%m_%d-%H_%M_%S')}") raise "Test directory '#{TestDir}' already exists" if File.exist?(TestDir) +class CMakeListsBuilder + def initialize + @contents = '' + end + def literal(lit) + @contents += lit + "\n"; + self + end + def package(pack) + literal "CPMAddPackage(#{pack})" + end + def exe(exe, sources) + @contents += "add_executable(#{exe}\n" + @contents += sources.map { |src| + ' ' + if src['/'] + src + else + File.expand_path("./#{src}") + end + }.join("\n") + @contents += "\n)\n" + self + end + def link_libs(target, libs) + literal "target_link_libraries(#{target} #{libs})\n" + end + def to_s + @contents + end +end + class Project def initialize(name) @name = name @dir = File.join(TestDir, name) - @lists = CommonHeader + "project(#{name})\n" - FileUtils.mkdir_p(File.join(TestDir, name)) end - def set_body(body) - @lists += "\n" + body + "\n" + def build_cmake_lists(opts = {}, &block) + builder = CMakeListsBuilder.new + if !opts[:no_default_header] + builder.literal(CommonHeader) + builder.literal("project(#{@name})") + end + text = builder.instance_eval &block + + File.write(File.join(@dir, 'CMakeLists.txt'), text) end - def configure() - File.write(File.join(@dir, 'CMakeLists.txt'), @lists) + def configure(args = '') + our, err, s = Open3.capture3("cmake . #{args}", chdir: @dir) end end diff --git a/test/integration/tests/simple.rb b/test/integration/tests/simple.rb index 929de5b2..ecba9bda 100644 --- a/test/integration/tests/simple.rb +++ b/test/integration/tests/simple.rb @@ -1,9 +1,8 @@ add_test 'basic', ->(prj) { - prj.set_body <<~CMAKE - CPMAddPackage("gh:cpm-cmake/testpack-adder") - add_executable(using-adder using-adder.cpp) - target_link_libraries(using-adder PRIVATE adder) - CMAKE - + prj.build_cmake_lists { + package 'gh:cpm-cmake/testpack-adder' + exe 'using-adder', ['using-adder.cpp'] + link_libs 'using-adder', 'adder' + } prj.configure } From dbb018ef6b7e1c8b8b6f49d9d6bf680daa821aa2 Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Thu, 6 Jan 2022 10:33:08 +0200 Subject: [PATCH 04/33] First check --- test/integration/runner.rb | 43 ++++++++++++++++++++++++++++++-- test/integration/tests/simple.rb | 4 ++- test/integration/using-adder.cpp | 9 +++++++ 3 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 test/integration/using-adder.cpp diff --git a/test/integration/runner.rb b/test/integration/runner.rb index 4a6b3bc0..d66b3626 100644 --- a/test/integration/runner.rb +++ b/test/integration/runner.rb @@ -44,6 +44,15 @@ def to_s end end +class ExecuteResult + def initialize(out, err, status) + @out = out + @err = err + @status = status + end + attr :out, :err, :status +end + class Project def initialize(name) @name = name @@ -64,7 +73,7 @@ def build_cmake_lists(opts = {}, &block) end def configure(args = '') - our, err, s = Open3.capture3("cmake . #{args}", chdir: @dir) + ExecuteResult.new *Open3.capture3("cmake . #{args}", chdir: @dir) end end @@ -75,6 +84,17 @@ def add_test(name, func) @tests[name] = func end +# check funcs +class CheckFail < StandardError + def initialize(msg) + super + end +end + +def check(b) + raise CheckFail.new "expected 'true'" if !b +end + Dir['tests/*.rb'].sort.each do |file| @cur_file = file load './' + file @@ -83,7 +103,26 @@ def add_test(name, func) # sort alphabetically sorted_tests = @tests.to_a.sort {|a, b| a[0] <=> b[0] } +num_succeeded = 0 +num_failed = 0 + sorted_tests.each do |name, func| + puts "Running '#{name}'" proj = Project.new(name) - func.(proj) + begin + func.(proj) + num_succeeded += 1 + puts ' success' + rescue CheckFail => error + num_failed += 1 + STDERR.puts " #{name}: check failed '#{error.message}'" + STDERR.puts " backtrace:\n #{error.backtrace.join("\n ")}" + STDERR.puts + end end + +puts "Ran #{num_succeeded + num_failed} tests" +puts "Succeeded: #{num_succeeded}" +puts "Failed: #{num_failed}" + +exit(num_failed) diff --git a/test/integration/tests/simple.rb b/test/integration/tests/simple.rb index ecba9bda..787a28b0 100644 --- a/test/integration/tests/simple.rb +++ b/test/integration/tests/simple.rb @@ -4,5 +4,7 @@ exe 'using-adder', ['using-adder.cpp'] link_libs 'using-adder', 'adder' } - prj.configure + cfg = prj.configure + + check cfg.status.success? } diff --git a/test/integration/using-adder.cpp b/test/integration/using-adder.cpp new file mode 100644 index 00000000..d06aac57 --- /dev/null +++ b/test/integration/using-adder.cpp @@ -0,0 +1,9 @@ +#include +#include + +int main() +{ + int sum = adder::add(5, 3); + std::printf("%d\n", sum); + return 0; +} From 57386e73e83888802f2d022e02002787c6b54d06 Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Mon, 10 Jan 2022 07:47:17 +0200 Subject: [PATCH 05/33] Alternative approach. Using ruby's test/unit --- test/integration/runner-old.rb | 131 ++++++++++++++++ test/integration/runner.rb | 141 ++++-------------- .../{tests/simple.rb => simple-old.rb} | 0 test/integration/test_noop.rb | 10 ++ test/integration/tests/0-noop.rb | 4 - 5 files changed, 166 insertions(+), 120 deletions(-) create mode 100644 test/integration/runner-old.rb rename test/integration/{tests/simple.rb => simple-old.rb} (100%) create mode 100644 test/integration/test_noop.rb delete mode 100644 test/integration/tests/0-noop.rb diff --git a/test/integration/runner-old.rb b/test/integration/runner-old.rb new file mode 100644 index 00000000..db7cc971 --- /dev/null +++ b/test/integration/runner-old.rb @@ -0,0 +1,131 @@ +require 'fileutils' +require 'open3' + +CPMPath = File.expand_path('../../cmake/CPM.cmake', __dir__) +raise "Cannot file 'CPM.cmake' at '#{CPMPath}'" if !File.file?(CPMPath) + +CommonHeader = <<~CMAKE + cmake_minimum_required(VERSION 3.14 FATAL_ERROR) + include("#{CPMPath}") + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") +CMAKE + +TestDir = File.expand_path("./tmp/#{Time.now.strftime('%Y_%m_%d-%H_%M_%S')}") +raise "Test directory '#{TestDir}' already exists" if File.exist?(TestDir) + +puts "Running CPM.cmake integration tests" +puts "Temp directory: '#{TestDir}'" + +class CMakeListsBuilder + def initialize + @contents = '' + end + def literal(lit) + @contents += lit + "\n"; + self + end + def package(pack) + literal "CPMAddPackage(#{pack})" + end + def exe(exe, sources) + @contents += "add_executable(#{exe}\n" + @contents += sources.map { |src| + ' ' + if src['/'] + src + else + File.expand_path("#{src}", __dir__) + end + }.join("\n") + @contents += "\n)\n" + self + end + def link_libs(target, libs) + literal "target_link_libraries(#{target} #{libs})\n" + end + def to_s + @contents + end +end + +class ExecuteResult + def initialize(out, err, status) + @out = out + @err = err + @status = status + end + attr :out, :err, :status +end + +class Project + def initialize(name) + @name = name + @dir = File.join(TestDir, name) + + FileUtils.mkdir_p(File.join(TestDir, name)) + end + + def build_cmake_lists(opts = {}, &block) + builder = CMakeListsBuilder.new + if !opts[:no_default_header] + builder.literal(CommonHeader) + builder.literal("project(#{@name})") + end + text = builder.instance_eval &block + + File.write(File.join(@dir, 'CMakeLists.txt'), text) + end + + def configure(args = '') + ExecuteResult.new *Open3.capture3("cmake . #{args}", chdir: @dir) + end +end + +@cur_file = '' +@tests = {} +def add_test(name, func) + raise "#{@cur_file}: Test #{name} is already defined from another file" if @tests[name] + @tests[name] = func +end + +# check funcs +class CheckFail < StandardError + def initialize(msg) + super + end +end + +def check(b) + raise CheckFail.new "expected 'true'" if !b +end + +Dir['tests/*.rb'].sort.each do |file| + @cur_file = file + load './' + file +end + +# sort alphabetically +sorted_tests = @tests.to_a.sort {|a, b| a[0] <=> b[0] } + +num_succeeded = 0 +num_failed = 0 + +sorted_tests.each do |name, func| + puts "Running '#{name}'" + proj = Project.new(name) + begin + func.(proj) + num_succeeded += 1 + puts ' success' + rescue CheckFail => error + num_failed += 1 + STDERR.puts " #{name}: check failed '#{error.message}'" + STDERR.puts " backtrace:\n #{error.backtrace.join("\n ")}" + STDERR.puts + end +end + +puts "Ran #{num_succeeded + num_failed} tests" +puts "Succeeded: #{num_succeeded}" +puts "Failed: #{num_failed}" + +exit(num_failed) diff --git a/test/integration/runner.rb b/test/integration/runner.rb index d66b3626..2a0dfd6f 100644 --- a/test/integration/runner.rb +++ b/test/integration/runner.rb @@ -1,128 +1,37 @@ require 'fileutils' require 'open3' +require 'tmpdir' +require 'test/unit' -CPMPath = File.expand_path('../../cmake/CPM.cmake') -raise "Cannot file 'CPM.cmake' at '#{CPMPath}'" if !File.file?(CPMPath) - -CommonHeader = <<~CMAKE - cmake_minimum_required(VERSION 3.14 FATAL_ERROR) - include("#{CPMPath}") - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") -CMAKE +TestTmpDir = File.join(Dir.tmpdir, "cpm-itest-#{Time.now.strftime('%Y_%m_%d-%H_%M_%S')}") +raise "Test directory '#{TestTmpDir}' already exists" if File.exist?(TestTmpDir) -TestDir = File.expand_path("./tmp/#{Time.now.strftime('%Y_%m_%d-%H_%M_%S')}") -raise "Test directory '#{TestDir}' already exists" if File.exist?(TestDir) +puts "Running CPM.cmake integration tests" +puts "Temp directory: '#{TestTmpDir}'" -class CMakeListsBuilder - def initialize - @contents = '' - end - def literal(lit) - @contents += lit + "\n"; - self - end - def package(pack) - literal "CPMAddPackage(#{pack})" - end - def exe(exe, sources) - @contents += "add_executable(#{exe}\n" - @contents += sources.map { |src| - ' ' + if src['/'] - src - else - File.expand_path("./#{src}") - end - }.join("\n") - @contents += "\n)\n" - self - end - def link_libs(target, libs) - literal "target_link_libraries(#{target} #{libs})\n" - end - def to_s - @contents - end -end +CPMPath = File.expand_path('../../cmake/CPM.cmake', __dir__) +raise "Cannot file 'CPM.cmake' at '#{CPMPath}'" if !File.file?(CPMPath) -class ExecuteResult - def initialize(out, err, status) - @out = out - @err = err - @status = status - end - attr :out, :err, :status -end +# Environment variables which are read by cpm +CPM_ENV = %w( + CPM_USE_LOCAL_PACKAGES + CPM_LOCAL_PACKAGES_ONLY + CPM_DOWNLOAD_ALL + CPM_DONT_UPDATE_MODULE_PATH + CPM_DONT_CREATE_PACKAGE_LOCK + CPM_INCLUDE_ALL_IN_PACKAGE_LOCK + CPM_USE_NAMED_CACHE_DIRECTORIES + CPM_SOURCE_CACHE +) + +# Clear existing cpm-related env vars +CPM_ENV.each { ENV[_1] = nil } class Project - def initialize(name) - @name = name - @dir = File.join(TestDir, name) - - FileUtils.mkdir_p(File.join(TestDir, name)) - end - - def build_cmake_lists(opts = {}, &block) - builder = CMakeListsBuilder.new - if !opts[:no_default_header] - builder.literal(CommonHeader) - builder.literal("project(#{@name})") - end - text = builder.instance_eval &block - - File.write(File.join(@dir, 'CMakeLists.txt'), text) - end - - def configure(args = '') - ExecuteResult.new *Open3.capture3("cmake . #{args}", chdir: @dir) - end -end - -@cur_file = '' -@tests = {} -def add_test(name, func) - raise "#{@cur_file}: Test #{name} is already defined from another file" if @tests[name] - @tests[name] = func -end - -# check funcs -class CheckFail < StandardError - def initialize(msg) - super - end -end - -def check(b) - raise CheckFail.new "expected 'true'" if !b -end - -Dir['tests/*.rb'].sort.each do |file| - @cur_file = file - load './' + file -end - -# sort alphabetically -sorted_tests = @tests.to_a.sort {|a, b| a[0] <=> b[0] } - -num_succeeded = 0 -num_failed = 0 - -sorted_tests.each do |name, func| - puts "Running '#{name}'" - proj = Project.new(name) - begin - func.(proj) - num_succeeded += 1 - puts ' success' - rescue CheckFail => error - num_failed += 1 - STDERR.puts " #{name}: check failed '#{error.message}'" - STDERR.puts " backtrace:\n #{error.backtrace.join("\n ")}" - STDERR.puts + def initialize(dir) + @dir = File.join(TestTmpDir, dir) end end -puts "Ran #{num_succeeded + num_failed} tests" -puts "Succeeded: #{num_succeeded}" -puts "Failed: #{num_failed}" +# exit Test::Unit::AutoRunner::run(true, __dir__) -exit(num_failed) diff --git a/test/integration/tests/simple.rb b/test/integration/simple-old.rb similarity index 100% rename from test/integration/tests/simple.rb rename to test/integration/simple-old.rb diff --git a/test/integration/test_noop.rb b/test/integration/test_noop.rb new file mode 100644 index 00000000..02c72887 --- /dev/null +++ b/test/integration/test_noop.rb @@ -0,0 +1,10 @@ +# this test does nothing +# it's, in a way, a test of the integration testing framework + +class Noop < Test::Unit::TestCase + def test_tt + puts 'run' + assert true + assert_equal 1, 1 + end +end diff --git a/test/integration/tests/0-noop.rb b/test/integration/tests/0-noop.rb deleted file mode 100644 index 5eb6dc10..00000000 --- a/test/integration/tests/0-noop.rb +++ /dev/null @@ -1,4 +0,0 @@ -# this test does nothing -# it's, in a way, a test of the integration testing framework - -puts 'executing noop test' From 77eb0e9f7339fad25171fd75f78d69bd9436d4bf Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Mon, 10 Jan 2022 10:52:48 +0200 Subject: [PATCH 06/33] Parse CMakeCache. Separate lib --- test/integration/lib.rb | 81 +++++++++++++++++++++++++++++++++++ test/integration/runner.rb | 37 +--------------- test/integration/test_noop.rb | 7 ++- 3 files changed, 86 insertions(+), 39 deletions(-) create mode 100644 test/integration/lib.rb diff --git a/test/integration/lib.rb b/test/integration/lib.rb new file mode 100644 index 00000000..0d7b4659 --- /dev/null +++ b/test/integration/lib.rb @@ -0,0 +1,81 @@ +require 'fileutils' +require 'open3' +require 'tmpdir' +require 'test/unit' + +TestTmpDir = File.join(Dir.tmpdir, "cpm-itest-#{Time.now.strftime('%Y_%m_%d-%H_%M_%S')}") +raise "Test directory '#{TestTmpDir}' already exists" if File.exist?(TestTmpDir) + +puts "Running CPM.cmake integration tests" +puts "Temp directory: '#{TestTmpDir}'" + +CPMPath = File.expand_path('../../cmake/CPM.cmake', __dir__) +raise "Cannot find 'CPM.cmake' at '#{CPMPath}'" if !File.file?(CPMPath) + +# Environment variables which are read by cpm +CPM_ENV = %w( + CPM_USE_LOCAL_PACKAGES + CPM_LOCAL_PACKAGES_ONLY + CPM_DOWNLOAD_ALL + CPM_DONT_UPDATE_MODULE_PATH + CPM_DONT_CREATE_PACKAGE_LOCK + CPM_INCLUDE_ALL_IN_PACKAGE_LOCK + CPM_USE_NAMED_CACHE_DIRECTORIES + CPM_SOURCE_CACHE +) + +# Clear existing cpm-related env vars +CPM_ENV.each { ENV[_1] = nil } + +class Project + def initialize(dir, name) + @name = name + d = File.join(TestTmpDir, dir) + @src_dir = d + '-src' + @build_dir = d + '-build' + p @src_dir + FileUtils.mkdir_p [@src_dir, @build_dir] + end + + class CMakeCacheValue + def initialize(val, type, advanced, desc) + @val = val + @type = type + @advanced = advanced + @desc = desc + end + attr :val, :type, :advanced, :desc + alias_method :advanced?, :advanced + def inspect + "(#{val.inspect} #{type}" + (advanced? ? ' ADVANCED)' : ')') + end + end + def read_cache + vars = {} + cur_desc = '' + file = File.join(@build_dir, 'CMakeCache.txt') + File.readlines(file).each { |line| + line.strip! + next if line.empty? + next if line.start_with? '#' # comment + if line.start_with? '//' + cur_desc += line[2..] + else + m = /(.+?)(-ADVANCED)?:([A-Z]+)=(.*)/.match(line) + raise "Error parsing '#{line}' in #{file}" if !m + vars[m[1]] = CMakeCacheValue.new(m[4], m[3], !!m[2], cur_desc) + cur_desc = '' + end + } + vars + end +end + +class IntegrationTest < Test::Unit::TestCase + def make_project(name = nil) + test_name = local_name + test_name = test_name[5..] if test_name.start_with?('test_') + name = test_name if !name + Project.new "#{self.class.name.downcase}-#{test_name}", name + end +end diff --git a/test/integration/runner.rb b/test/integration/runner.rb index 2a0dfd6f..6f2eee0d 100644 --- a/test/integration/runner.rb +++ b/test/integration/runner.rb @@ -1,37 +1,4 @@ -require 'fileutils' -require 'open3' -require 'tmpdir' -require 'test/unit' +require './lib' -TestTmpDir = File.join(Dir.tmpdir, "cpm-itest-#{Time.now.strftime('%Y_%m_%d-%H_%M_%S')}") -raise "Test directory '#{TestTmpDir}' already exists" if File.exist?(TestTmpDir) - -puts "Running CPM.cmake integration tests" -puts "Temp directory: '#{TestTmpDir}'" - -CPMPath = File.expand_path('../../cmake/CPM.cmake', __dir__) -raise "Cannot file 'CPM.cmake' at '#{CPMPath}'" if !File.file?(CPMPath) - -# Environment variables which are read by cpm -CPM_ENV = %w( - CPM_USE_LOCAL_PACKAGES - CPM_LOCAL_PACKAGES_ONLY - CPM_DOWNLOAD_ALL - CPM_DONT_UPDATE_MODULE_PATH - CPM_DONT_CREATE_PACKAGE_LOCK - CPM_INCLUDE_ALL_IN_PACKAGE_LOCK - CPM_USE_NAMED_CACHE_DIRECTORIES - CPM_SOURCE_CACHE -) - -# Clear existing cpm-related env vars -CPM_ENV.each { ENV[_1] = nil } - -class Project - def initialize(dir) - @dir = File.join(TestTmpDir, dir) - end -end - -# exit Test::Unit::AutoRunner::run(true, __dir__) +exit Test::Unit::AutoRunner::run(true, __dir__) diff --git a/test/integration/test_noop.rb b/test/integration/test_noop.rb index 02c72887..4762b5da 100644 --- a/test/integration/test_noop.rb +++ b/test/integration/test_noop.rb @@ -1,10 +1,9 @@ # this test does nothing # it's, in a way, a test of the integration testing framework +require './lib' -class Noop < Test::Unit::TestCase +class Noop < IntegrationTest def test_tt - puts 'run' - assert true - assert_equal 1, 1 + make_project end end From 10c24fc8aff3cfb0f41ab3b3d566cd6919a7cf98 Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Tue, 11 Jan 2022 07:51:46 +0200 Subject: [PATCH 07/33] First integration test --- test/integration/.gitignore | 1 - test/integration/lib.rb | 106 +++++++++----- test/integration/runner-old.rb | 131 ------------------ test/integration/simple-old.rb | 10 -- .../templates/using-adder/lists.in.cmake | 11 ++ .../using-adder}/using-adder.cpp | 0 test/integration/test_noop.rb | 9 -- test/integration/test_simple.rb | 48 +++++++ 8 files changed, 134 insertions(+), 182 deletions(-) delete mode 100644 test/integration/.gitignore delete mode 100644 test/integration/runner-old.rb delete mode 100644 test/integration/simple-old.rb create mode 100644 test/integration/templates/using-adder/lists.in.cmake rename test/integration/{ => templates/using-adder}/using-adder.cpp (100%) delete mode 100644 test/integration/test_noop.rb create mode 100644 test/integration/test_simple.rb diff --git a/test/integration/.gitignore b/test/integration/.gitignore deleted file mode 100644 index 3fec32c8..00000000 --- a/test/integration/.gitignore +++ /dev/null @@ -1 +0,0 @@ -tmp/ diff --git a/test/integration/lib.rb b/test/integration/lib.rb index 0d7b4659..5a5f3475 100644 --- a/test/integration/lib.rb +++ b/test/integration/lib.rb @@ -3,38 +3,66 @@ require 'tmpdir' require 'test/unit' -TestTmpDir = File.join(Dir.tmpdir, "cpm-itest-#{Time.now.strftime('%Y_%m_%d-%H_%M_%S')}") -raise "Test directory '#{TestTmpDir}' already exists" if File.exist?(TestTmpDir) +module TestLib + TMP_DIR = File.join(Dir.tmpdir, 'cpm-test', Time.now.strftime('%Y_%m_%d-%H_%M_%S')) + CPM_PATH = File.expand_path('../../cmake/CPM.cmake', __dir__) + + TEMPLATES_DIR = File.expand_path('templates', __dir__) + + # Environment variables which are read by cpm + CPM_ENV = %w( + CPM_USE_LOCAL_PACKAGES + CPM_LOCAL_PACKAGES_ONLY + CPM_DOWNLOAD_ALL + CPM_DONT_UPDATE_MODULE_PATH + CPM_DONT_CREATE_PACKAGE_LOCK + CPM_INCLUDE_ALL_IN_PACKAGE_LOCK + CPM_USE_NAMED_CACHE_DIRECTORIES + CPM_SOURCE_CACHE + ) + def self.clear_env + CPM_ENV.each { ENV[_1] = nil } + end +end + +raise "Test directory '#{TestLib::TMP_DIR}' already exists" if File.exist?(TestLib::TMP_DIR) +raise "Cannot find 'CPM.cmake' at '#{TestLib::CPM_PATH}'" if !File.file?(TestLib::CPM_PATH) puts "Running CPM.cmake integration tests" -puts "Temp directory: '#{TestTmpDir}'" - -CPMPath = File.expand_path('../../cmake/CPM.cmake', __dir__) -raise "Cannot find 'CPM.cmake' at '#{CPMPath}'" if !File.file?(CPMPath) - -# Environment variables which are read by cpm -CPM_ENV = %w( - CPM_USE_LOCAL_PACKAGES - CPM_LOCAL_PACKAGES_ONLY - CPM_DOWNLOAD_ALL - CPM_DONT_UPDATE_MODULE_PATH - CPM_DONT_CREATE_PACKAGE_LOCK - CPM_INCLUDE_ALL_IN_PACKAGE_LOCK - CPM_USE_NAMED_CACHE_DIRECTORIES - CPM_SOURCE_CACHE -) - -# Clear existing cpm-related env vars -CPM_ENV.each { ENV[_1] = nil } +puts "Temp directory: '#{TestLib::TMP_DIR}'" class Project - def initialize(dir, name) - @name = name - d = File.join(TestTmpDir, dir) - @src_dir = d + '-src' - @build_dir = d + '-build' - p @src_dir - FileUtils.mkdir_p [@src_dir, @build_dir] + def initialize(src_dir, build_dir) + @src_dir = src_dir + @build_dir = build_dir + end + + def create_file(target_path, text) + target_path = File.join(@src_dir, target_path) + File.write target_path, text + end + + def create_file_with(target_path, source_path, args) + source_path = File.join(@src_dir, source_path) + raise "#{source_path} doesn't exist" if !File.file?(source_path) + + # tweak args + args[:cpm_path] = TestLib::CPM_PATH + args[:packages] = [args[:package]] if args[:package] # if args contain package, create the array + args[:packages] = args[:packages].join("\n") # join all packages + + src_text = File.read source_path + create_file target_path, src_text % args + end + + # common function to create ./CMakeLists.txt from ./lists.in.cmake + def create_lists_with(args) + create_file_with 'CMakeLists.txt', 'lists.in.cmake', args + end + + CommandResult = Struct.new :out, :err, :status + def configure(extra_args = '') + CommandResult.new *Open3.capture3("cmake -S #{@src_dir} -B #{@build_dir} #{extra_args}") end class CMakeCacheValue @@ -72,10 +100,26 @@ def read_cache end class IntegrationTest < Test::Unit::TestCase - def make_project(name = nil) + def setup + # Clear existing cpm-related env vars + TestLib.clear_env + end + + def make_project(template_dir = nil) test_name = local_name test_name = test_name[5..] if test_name.start_with?('test_') - name = test_name if !name - Project.new "#{self.class.name.downcase}-#{test_name}", name + + base = File.join(TestLib::TMP_DIR, self.class.name.downcase, test_name) + src_dir = base + '-src' + + FileUtils.mkdir_p src_dir + + if template_dir + template_dir = File.join(TestLib::TEMPLATES_DIR, template_dir) + raise "#{template_dir} is not a directory" if !File.directory?(template_dir) + FileUtils.copy_entry template_dir, src_dir + end + + Project.new src_dir, base + '-build' end end diff --git a/test/integration/runner-old.rb b/test/integration/runner-old.rb deleted file mode 100644 index db7cc971..00000000 --- a/test/integration/runner-old.rb +++ /dev/null @@ -1,131 +0,0 @@ -require 'fileutils' -require 'open3' - -CPMPath = File.expand_path('../../cmake/CPM.cmake', __dir__) -raise "Cannot file 'CPM.cmake' at '#{CPMPath}'" if !File.file?(CPMPath) - -CommonHeader = <<~CMAKE - cmake_minimum_required(VERSION 3.14 FATAL_ERROR) - include("#{CPMPath}") - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") -CMAKE - -TestDir = File.expand_path("./tmp/#{Time.now.strftime('%Y_%m_%d-%H_%M_%S')}") -raise "Test directory '#{TestDir}' already exists" if File.exist?(TestDir) - -puts "Running CPM.cmake integration tests" -puts "Temp directory: '#{TestDir}'" - -class CMakeListsBuilder - def initialize - @contents = '' - end - def literal(lit) - @contents += lit + "\n"; - self - end - def package(pack) - literal "CPMAddPackage(#{pack})" - end - def exe(exe, sources) - @contents += "add_executable(#{exe}\n" - @contents += sources.map { |src| - ' ' + if src['/'] - src - else - File.expand_path("#{src}", __dir__) - end - }.join("\n") - @contents += "\n)\n" - self - end - def link_libs(target, libs) - literal "target_link_libraries(#{target} #{libs})\n" - end - def to_s - @contents - end -end - -class ExecuteResult - def initialize(out, err, status) - @out = out - @err = err - @status = status - end - attr :out, :err, :status -end - -class Project - def initialize(name) - @name = name - @dir = File.join(TestDir, name) - - FileUtils.mkdir_p(File.join(TestDir, name)) - end - - def build_cmake_lists(opts = {}, &block) - builder = CMakeListsBuilder.new - if !opts[:no_default_header] - builder.literal(CommonHeader) - builder.literal("project(#{@name})") - end - text = builder.instance_eval &block - - File.write(File.join(@dir, 'CMakeLists.txt'), text) - end - - def configure(args = '') - ExecuteResult.new *Open3.capture3("cmake . #{args}", chdir: @dir) - end -end - -@cur_file = '' -@tests = {} -def add_test(name, func) - raise "#{@cur_file}: Test #{name} is already defined from another file" if @tests[name] - @tests[name] = func -end - -# check funcs -class CheckFail < StandardError - def initialize(msg) - super - end -end - -def check(b) - raise CheckFail.new "expected 'true'" if !b -end - -Dir['tests/*.rb'].sort.each do |file| - @cur_file = file - load './' + file -end - -# sort alphabetically -sorted_tests = @tests.to_a.sort {|a, b| a[0] <=> b[0] } - -num_succeeded = 0 -num_failed = 0 - -sorted_tests.each do |name, func| - puts "Running '#{name}'" - proj = Project.new(name) - begin - func.(proj) - num_succeeded += 1 - puts ' success' - rescue CheckFail => error - num_failed += 1 - STDERR.puts " #{name}: check failed '#{error.message}'" - STDERR.puts " backtrace:\n #{error.backtrace.join("\n ")}" - STDERR.puts - end -end - -puts "Ran #{num_succeeded + num_failed} tests" -puts "Succeeded: #{num_succeeded}" -puts "Failed: #{num_failed}" - -exit(num_failed) diff --git a/test/integration/simple-old.rb b/test/integration/simple-old.rb deleted file mode 100644 index 787a28b0..00000000 --- a/test/integration/simple-old.rb +++ /dev/null @@ -1,10 +0,0 @@ -add_test 'basic', ->(prj) { - prj.build_cmake_lists { - package 'gh:cpm-cmake/testpack-adder' - exe 'using-adder', ['using-adder.cpp'] - link_libs 'using-adder', 'adder' - } - cfg = prj.configure - - check cfg.status.success? -} diff --git a/test/integration/templates/using-adder/lists.in.cmake b/test/integration/templates/using-adder/lists.in.cmake new file mode 100644 index 00000000..4d0d4283 --- /dev/null +++ b/test/integration/templates/using-adder/lists.in.cmake @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.14 FATAL_ERROR) + +project(using-adder) + +include("%{cpm_path}") + +%{packages} + +add_executable(using-adder using-adder.cpp) + +target_link_libraries(using-adder adder) diff --git a/test/integration/using-adder.cpp b/test/integration/templates/using-adder/using-adder.cpp similarity index 100% rename from test/integration/using-adder.cpp rename to test/integration/templates/using-adder/using-adder.cpp diff --git a/test/integration/test_noop.rb b/test/integration/test_noop.rb deleted file mode 100644 index 4762b5da..00000000 --- a/test/integration/test_noop.rb +++ /dev/null @@ -1,9 +0,0 @@ -# this test does nothing -# it's, in a way, a test of the integration testing framework -require './lib' - -class Noop < IntegrationTest - def test_tt - make_project - end -end diff --git a/test/integration/test_simple.rb b/test/integration/test_simple.rb new file mode 100644 index 00000000..54853019 --- /dev/null +++ b/test/integration/test_simple.rb @@ -0,0 +1,48 @@ +require './lib' + +class Simple < IntegrationTest + def get_adder_data(cache) + [ + cache['CPM_PACKAGE_testpack-adder_VERSION'].val, + cache['CPM_PACKAGE_testpack-adder_SOURCE_DIR'].val, + cache['CPM_PACKAGE_testpack-adder_BINARY_DIR'].val, + ] + end + + def test_basics + prj = make_project 'using-adder' + + prj.create_lists_with package: 'CPMAddPackage("gh:cpm-cmake/testpack-adder#cad1cd4b4cdf957c5b59e30bc9a1dd200dbfc716")' + cfg_result = prj.configure + + assert cfg_result.status.success? + + cache = prj.read_cache + + assert_equal 'testpack-adder', cache['CPM_PACKAGES'].val + + ver, src, bin = get_adder_data cache + + assert_equal ver, '0' + assert File.directory? src + assert File.directory? bin + + adder_ver_file = File.join(src, 'version') + assert File.file? adder_ver_file + assert_equal 'initial', File.read(adder_ver_file).strip + + # reconfigure with new version + prj.create_lists_with package: 'CPMAddPackage("gh:cpm-cmake/testpack-adder@1.0.0")' + cfg_result = prj.configure + + cache_new = prj.read_cache + ver, src, bin = get_adder_data cache_new + + assert_equal '1.0.0', ver + + # dir shouldn't have changed + assert_equal File.dirname(adder_ver_file), src + + assert_equal '1.0.0', File.read(adder_ver_file).strip + end +end From 473e7bf8203c05374a057e1b2ec9c974ff7b4fd0 Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Wed, 12 Jan 2022 06:26:08 +0200 Subject: [PATCH 08/33] Latest Format.cmake. Passing style --- test/integration/templates/using-adder/using-adder.cpp | 9 ++++----- test/style/CMakeLists.txt | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/test/integration/templates/using-adder/using-adder.cpp b/test/integration/templates/using-adder/using-adder.cpp index d06aac57..e8f836d8 100644 --- a/test/integration/templates/using-adder/using-adder.cpp +++ b/test/integration/templates/using-adder/using-adder.cpp @@ -1,9 +1,8 @@ #include #include -int main() -{ - int sum = adder::add(5, 3); - std::printf("%d\n", sum); - return 0; +int main() { + int sum = adder::add(5, 3); + std::printf("%d\n", sum); + return 0; } diff --git a/test/style/CMakeLists.txt b/test/style/CMakeLists.txt index 2beb38ba..72491e12 100644 --- a/test/style/CMakeLists.txt +++ b/test/style/CMakeLists.txt @@ -6,7 +6,7 @@ include(${CMAKE_CURRENT_LIST_DIR}/../../cmake/CPM.cmake) CPMAddPackage( NAME Format.cmake - VERSION 1.6 + VERSION 1.7.3 GITHUB_REPOSITORY TheLartians/Format.cmake - OPTIONS "FORMAT_CHECK_CMAKE ON" + OPTIONS "CMAKE_FORMAT_EXCLUDE integration/templates" ) From 4c9ee1fedb68e917b601c0604d145ff9079724ed Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Wed, 12 Jan 2022 06:45:47 +0200 Subject: [PATCH 09/33] Allow user-provided integration test dir. Allow reuse --- test/integration/lib.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/lib.rb b/test/integration/lib.rb index 5a5f3475..94f130fb 100644 --- a/test/integration/lib.rb +++ b/test/integration/lib.rb @@ -4,7 +4,7 @@ require 'test/unit' module TestLib - TMP_DIR = File.join(Dir.tmpdir, 'cpm-test', Time.now.strftime('%Y_%m_%d-%H_%M_%S')) + TMP_DIR = ENV['CPM_INTEGRATION_TEST_DIR'] || File.join(Dir.tmpdir, 'cpm-test', Time.now.strftime('%Y_%m_%d-%H_%M_%S')) CPM_PATH = File.expand_path('../../cmake/CPM.cmake', __dir__) TEMPLATES_DIR = File.expand_path('templates', __dir__) @@ -25,7 +25,7 @@ def self.clear_env end end -raise "Test directory '#{TestLib::TMP_DIR}' already exists" if File.exist?(TestLib::TMP_DIR) +puts "Warning: test directory '#{TestLib::TMP_DIR}' already exists" if File.exist?(TestLib::TMP_DIR) raise "Cannot find 'CPM.cmake' at '#{TestLib::CPM_PATH}'" if !File.file?(TestLib::CPM_PATH) puts "Running CPM.cmake integration tests" From e3ffa2c1ff94e97290d7bb3358741ea6933ed820 Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Wed, 12 Jan 2022 07:31:59 +0200 Subject: [PATCH 10/33] Separate class with utils for cache (no longer pure Hash) --- test/integration/lib.rb | 59 +++++++++++++++++++++++---------- test/integration/test_simple.rb | 19 +++++------ 2 files changed, 49 insertions(+), 29 deletions(-) diff --git a/test/integration/lib.rb b/test/integration/lib.rb index 94f130fb..bb28ed48 100644 --- a/test/integration/lib.rb +++ b/test/integration/lib.rb @@ -65,7 +65,7 @@ def configure(extra_args = '') CommandResult.new *Open3.capture3("cmake -S #{@src_dir} -B #{@build_dir} #{extra_args}") end - class CMakeCacheValue + class CMakeCacheEntry def initialize(val, type, advanced, desc) @val = val @type = type @@ -78,24 +78,47 @@ def inspect "(#{val.inspect} #{type}" + (advanced? ? ' ADVANCED)' : ')') end end + class CMakeCache + def initialize(entries) + @entries = entries + end + def self.from_dir(dir) + entries = {} + cur_desc = '' + file = File.join(dir, 'CMakeCache.txt') + return nil if !File.file?(file) + File.readlines(file).each { |line| + line.strip! + next if line.empty? + next if line.start_with? '#' # comment + if line.start_with? '//' + cur_desc += line[2..] + else + m = /(.+?)(-ADVANCED)?:([A-Z]+)=(.*)/.match(line) + raise "Error parsing '#{line}' in #{file}" if !m + entries[m[1]] = CMakeCacheEntry.new(m[4], m[3], !!m[2], cur_desc) + cur_desc = '' + end + } + CMakeCache.new entries + end + + def [](key) + e = @entries[key] + return nil if !e + e.val + end + + def get_package_data(package) + [ + self["CPM_PACKAGE_#{package}_VERSION"], + self["CPM_PACKAGE_#{package}_SOURCE_DIR"], + self["CPM_PACKAGE_#{package}_BINARY_DIR"], + ] + end + end def read_cache - vars = {} - cur_desc = '' - file = File.join(@build_dir, 'CMakeCache.txt') - File.readlines(file).each { |line| - line.strip! - next if line.empty? - next if line.start_with? '#' # comment - if line.start_with? '//' - cur_desc += line[2..] - else - m = /(.+?)(-ADVANCED)?:([A-Z]+)=(.*)/.match(line) - raise "Error parsing '#{line}' in #{file}" if !m - vars[m[1]] = CMakeCacheValue.new(m[4], m[3], !!m[2], cur_desc) - cur_desc = '' - end - } - vars + CMakeCache.from_dir @build_dir end end diff --git a/test/integration/test_simple.rb b/test/integration/test_simple.rb index 54853019..4053570d 100644 --- a/test/integration/test_simple.rb +++ b/test/integration/test_simple.rb @@ -1,13 +1,7 @@ require './lib' class Simple < IntegrationTest - def get_adder_data(cache) - [ - cache['CPM_PACKAGE_testpack-adder_VERSION'].val, - cache['CPM_PACKAGE_testpack-adder_SOURCE_DIR'].val, - cache['CPM_PACKAGE_testpack-adder_BINARY_DIR'].val, - ] - end + P_ADDER = 'testpack-adder' def test_basics prj = make_project 'using-adder' @@ -19,9 +13,9 @@ def test_basics cache = prj.read_cache - assert_equal 'testpack-adder', cache['CPM_PACKAGES'].val + assert_equal P_ADDER, cache['CPM_PACKAGES'] - ver, src, bin = get_adder_data cache + ver, src, bin = cache.get_package_data(P_ADDER) assert_equal ver, '0' assert File.directory? src @@ -35,8 +29,11 @@ def test_basics prj.create_lists_with package: 'CPMAddPackage("gh:cpm-cmake/testpack-adder@1.0.0")' cfg_result = prj.configure - cache_new = prj.read_cache - ver, src, bin = get_adder_data cache_new + assert cfg_result.status.success? + + cache = prj.read_cache + + ver, src, bin = cache.get_package_data(P_ADDER) assert_equal '1.0.0', ver From caeaf29440d13d5ba550343390555a5b0c4466a4 Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Wed, 12 Jan 2022 07:34:33 +0200 Subject: [PATCH 11/33] Allow running of tests from any dir --- test/integration/runner.rb | 2 +- test/integration/test_simple.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/runner.rb b/test/integration/runner.rb index 6f2eee0d..ddc40823 100644 --- a/test/integration/runner.rb +++ b/test/integration/runner.rb @@ -1,4 +1,4 @@ -require './lib' +require_relative './lib' exit Test::Unit::AutoRunner::run(true, __dir__) diff --git a/test/integration/test_simple.rb b/test/integration/test_simple.rb index 4053570d..3a3096d0 100644 --- a/test/integration/test_simple.rb +++ b/test/integration/test_simple.rb @@ -1,4 +1,4 @@ -require './lib' +require_relative './lib' class Simple < IntegrationTest P_ADDER = 'testpack-adder' From 6ef5dfda5ecfa3c330429371c8abb082214d4706 Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Wed, 12 Jan 2022 07:35:43 +0200 Subject: [PATCH 12/33] Add integration tests to CI --- .github/workflows/test.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c7b90cdc..4f3f5c24 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -23,3 +23,6 @@ jobs: run: | cmake -Htest -Bbuild/test cmake --build build/test --target test-verbose + + - name: integration tests + run: ruby test/integration/runner.rb From 6f14e4d1b2573df947a3cacbc2fc25792200e9ff Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Wed, 12 Jan 2022 08:08:42 +0200 Subject: [PATCH 13/33] Use an in-source integration test directory --- .github/workflows/test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4f3f5c24..425fb542 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,3 +26,5 @@ jobs: - name: integration tests run: ruby test/integration/runner.rb + env: + CPM_INTEGRATION_TEST_DIR: ${{ env.GITHUB_WORKSPACE }}/build/integration From 7c9c42fad66d9ba784fd34c643a28efa612cdee9 Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Wed, 12 Jan 2022 08:17:58 +0200 Subject: [PATCH 14/33] Allow relative integration test dir from env --- .github/workflows/test.yml | 2 +- test/integration/lib.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 425fb542..0ab433a3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -27,4 +27,4 @@ jobs: - name: integration tests run: ruby test/integration/runner.rb env: - CPM_INTEGRATION_TEST_DIR: ${{ env.GITHUB_WORKSPACE }}/build/integration + CPM_INTEGRATION_TEST_DIR: ./build/integration diff --git a/test/integration/lib.rb b/test/integration/lib.rb index bb28ed48..63c81e8e 100644 --- a/test/integration/lib.rb +++ b/test/integration/lib.rb @@ -4,7 +4,7 @@ require 'test/unit' module TestLib - TMP_DIR = ENV['CPM_INTEGRATION_TEST_DIR'] || File.join(Dir.tmpdir, 'cpm-test', Time.now.strftime('%Y_%m_%d-%H_%M_%S')) + TMP_DIR = File.expand_path(ENV['CPM_INTEGRATION_TEST_DIR'] || File.join(Dir.tmpdir, 'cpm-test', Time.now.strftime('%Y_%m_%d-%H_%M_%S'))) CPM_PATH = File.expand_path('../../cmake/CPM.cmake', __dir__) TEMPLATES_DIR = File.expand_path('templates', __dir__) From 5b162bedc1c0a1fde28ca3e7135a6e7fbc364be2 Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Wed, 12 Jan 2022 08:39:06 +0200 Subject: [PATCH 15/33] Custom assertion for a success of CommandResult --- test/integration/lib.rb | 9 +++++++++ test/integration/test_simple.rb | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/test/integration/lib.rb b/test/integration/lib.rb index 63c81e8e..5d262b68 100644 --- a/test/integration/lib.rb +++ b/test/integration/lib.rb @@ -128,6 +128,15 @@ def setup TestLib.clear_env end + # extra assertions + + def assert_success(res) + msg = build_message(nil, "command status was expected to be a success, but failed with code and STDERR:\n\n#{res.err}", res.status.to_i) + assert_block(msg) { res.status.success? } + end + + # utils + def make_project(template_dir = nil) test_name = local_name test_name = test_name[5..] if test_name.start_with?('test_') diff --git a/test/integration/test_simple.rb b/test/integration/test_simple.rb index 3a3096d0..c5e48100 100644 --- a/test/integration/test_simple.rb +++ b/test/integration/test_simple.rb @@ -9,7 +9,7 @@ def test_basics prj.create_lists_with package: 'CPMAddPackage("gh:cpm-cmake/testpack-adder#cad1cd4b4cdf957c5b59e30bc9a1dd200dbfc716")' cfg_result = prj.configure - assert cfg_result.status.success? + assert_success cfg_result cache = prj.read_cache @@ -29,7 +29,7 @@ def test_basics prj.create_lists_with package: 'CPMAddPackage("gh:cpm-cmake/testpack-adder@1.0.0")' cfg_result = prj.configure - assert cfg_result.status.success? + assert_success cfg_result cache = prj.read_cache From 80cc580e3237ba27de64da5835e2f012e32df2ac Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Wed, 12 Jan 2022 09:18:04 +0200 Subject: [PATCH 16/33] Windows-latest-latest --- .github/workflows/test.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0ab433a3..321b788a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,7 +13,10 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, windows-latest, macos-latest] + # windows-latest is windows-2019 which carries a pretty old version of ruby (2.5) + # we need at least ruby 2.7 for the tests + # instead of dealing with installing a modern version of ruby on 2019, we'll just use windows-2022 here + os: [ubuntu-latest, windows-2022, macos-latest] steps: - name: clone From 058eabe0d21245b922c85c45c12eda3a9551ba6a Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Wed, 12 Jan 2022 11:15:27 +0200 Subject: [PATCH 17/33] Enrich CMakeCache class with more CPM data --- test/integration/lib.rb | 60 ++++++++++++++++++++------------- test/integration/test_simple.rb | 32 ++++++++---------- test/style/CMakeLists.txt | 3 ++ 3 files changed, 53 insertions(+), 42 deletions(-) diff --git a/test/integration/lib.rb b/test/integration/lib.rb index 5d262b68..824b0ebf 100644 --- a/test/integration/lib.rb +++ b/test/integration/lib.rb @@ -65,23 +65,23 @@ def configure(extra_args = '') CommandResult.new *Open3.capture3("cmake -S #{@src_dir} -B #{@build_dir} #{extra_args}") end - class CMakeCacheEntry - def initialize(val, type, advanced, desc) - @val = val - @type = type - @advanced = advanced - @desc = desc - end - attr :val, :type, :advanced, :desc - alias_method :advanced?, :advanced - def inspect - "(#{val.inspect} #{type}" + (advanced? ? ' ADVANCED)' : ')') - end - end class CMakeCache - def initialize(entries) - @entries = entries + class Entry + def initialize(val, type, advanced, desc) + @val = val + @type = type + @advanced = advanced + @desc = desc + end + attr :val, :type, :advanced, :desc + alias_method :advanced?, :advanced + def inspect + "(#{val.inspect} #{type}" + (advanced? ? ' ADVANCED)' : ')') + end end + + Package = Struct.new(:ver, :src_dir, :bin_dir) + def self.from_dir(dir) entries = {} cur_desc = '' @@ -96,26 +96,38 @@ def self.from_dir(dir) else m = /(.+?)(-ADVANCED)?:([A-Z]+)=(.*)/.match(line) raise "Error parsing '#{line}' in #{file}" if !m - entries[m[1]] = CMakeCacheEntry.new(m[4], m[3], !!m[2], cur_desc) + entries[m[1]] = Entry.new(m[4], m[3], !!m[2], cur_desc) cur_desc = '' end } CMakeCache.new entries end + def initialize(entries) + @entries = entries + + package_list = self['CPM_PACKAGES'] + @packages = if package_list + # collect package data + @packages = package_list.split(';').map { |name| + [name, Package.new( + self["CPM_PACKAGE_#{name}_VERSION"], + self["CPM_PACKAGE_#{name}_SOURCE_DIR"], + self["CPM_PACKAGE_#{name}_BINARY_DIR"] + )] + }.to_h + else + {} + end + end + + attr :entries, :packages + def [](key) e = @entries[key] return nil if !e e.val end - - def get_package_data(package) - [ - self["CPM_PACKAGE_#{package}_VERSION"], - self["CPM_PACKAGE_#{package}_SOURCE_DIR"], - self["CPM_PACKAGE_#{package}_BINARY_DIR"], - ] - end end def read_cache CMakeCache.from_dir @build_dir diff --git a/test/integration/test_simple.rb b/test/integration/test_simple.rb index c5e48100..55374ed6 100644 --- a/test/integration/test_simple.rb +++ b/test/integration/test_simple.rb @@ -7,38 +7,34 @@ def test_basics prj = make_project 'using-adder' prj.create_lists_with package: 'CPMAddPackage("gh:cpm-cmake/testpack-adder#cad1cd4b4cdf957c5b59e30bc9a1dd200dbfc716")' - cfg_result = prj.configure - - assert_success cfg_result + assert_success prj.configure cache = prj.read_cache + assert_equal 1, cache.packages.size - assert_equal P_ADDER, cache['CPM_PACKAGES'] - - ver, src, bin = cache.get_package_data(P_ADDER) - - assert_equal ver, '0' - assert File.directory? src - assert File.directory? bin + adder = cache.packages[P_ADDER] + assert_not_nil adder + assert_equal '0', adder.ver + assert File.directory? adder.src_dir + assert File.directory? adder.bin_dir - adder_ver_file = File.join(src, 'version') + adder_ver_file = File.join(adder.src_dir, 'version') assert File.file? adder_ver_file assert_equal 'initial', File.read(adder_ver_file).strip # reconfigure with new version prj.create_lists_with package: 'CPMAddPackage("gh:cpm-cmake/testpack-adder@1.0.0")' - cfg_result = prj.configure - - assert_success cfg_result + assert_success prj.configure cache = prj.read_cache + assert_equal 1, cache.packages.size - ver, src, bin = cache.get_package_data(P_ADDER) - - assert_equal '1.0.0', ver + adder = cache.packages[P_ADDER] + assert_not_nil adder + assert_equal '1.0.0', adder.ver # dir shouldn't have changed - assert_equal File.dirname(adder_ver_file), src + assert_equal File.dirname(adder_ver_file), adder.src_dir assert_equal '1.0.0', File.read(adder_ver_file).strip end diff --git a/test/style/CMakeLists.txt b/test/style/CMakeLists.txt index 72491e12..5dbf35ca 100644 --- a/test/style/CMakeLists.txt +++ b/test/style/CMakeLists.txt @@ -8,5 +8,8 @@ CPMAddPackage( NAME Format.cmake VERSION 1.7.3 GITHUB_REPOSITORY TheLartians/Format.cmake + + # We exclude cmake files from integration tests as they contain invalid lines + # of code which are used by the integration test scripts OPTIONS "CMAKE_FORMAT_EXCLUDE integration/templates" ) From aec0f0d79ac74e2c7c6ef4f66309896f732ff3e2 Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Wed, 12 Jan 2022 12:18:43 +0200 Subject: [PATCH 18/33] Added test for CPM-specific CMakeCache values --- test/integration/lib.rb | 11 ++++- .../templates/no-deps/lists.in.cmake | 7 ++++ test/integration/templates/no-deps/main.c | 6 +++ test/integration/test_basics.rb | 42 +++++++++++++++++++ test/integration/test_simple.rb | 2 +- 5 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 test/integration/templates/no-deps/lists.in.cmake create mode 100644 test/integration/templates/no-deps/main.c create mode 100644 test/integration/test_basics.rb diff --git a/test/integration/lib.rb b/test/integration/lib.rb index 824b0ebf..5323f1ca 100644 --- a/test/integration/lib.rb +++ b/test/integration/lib.rb @@ -37,6 +37,8 @@ def initialize(src_dir, build_dir) @build_dir = build_dir end + attr :src_dir, :build_dir + def create_file(target_path, text) target_path = File.join(@src_dir, target_path) File.write target_path, text @@ -47,9 +49,9 @@ def create_file_with(target_path, source_path, args) raise "#{source_path} doesn't exist" if !File.file?(source_path) # tweak args - args[:cpm_path] = TestLib::CPM_PATH + args[:cpm_path] = TestLib::CPM_PATH if !args[:cpm_path] args[:packages] = [args[:package]] if args[:package] # if args contain package, create the array - args[:packages] = args[:packages].join("\n") # join all packages + args[:packages] = args[:packages].join("\n") if args[:packages] # join all packages if any src_text = File.read source_path create_file target_path, src_text % args @@ -147,6 +149,11 @@ def assert_success(res) assert_block(msg) { res.status.success? } end + def assert_same_path(a, b) + msg = build_message(nil, " expected but was\n", a, b) + assert_block(msg) { File.identical? a, b } + end + # utils def make_project(template_dir = nil) diff --git a/test/integration/templates/no-deps/lists.in.cmake b/test/integration/templates/no-deps/lists.in.cmake new file mode 100644 index 00000000..ae4e1369 --- /dev/null +++ b/test/integration/templates/no-deps/lists.in.cmake @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.14 FATAL_ERROR) + +project(no-deps) + +include("%{cpm_path}") + +add_executable(no-deps main.c) diff --git a/test/integration/templates/no-deps/main.c b/test/integration/templates/no-deps/main.c new file mode 100644 index 00000000..d7f6d643 --- /dev/null +++ b/test/integration/templates/no-deps/main.c @@ -0,0 +1,6 @@ +#include + +int main() { + puts("Hello"); + return 0; +} diff --git a/test/integration/test_basics.rb b/test/integration/test_basics.rb new file mode 100644 index 00000000..c6e941da --- /dev/null +++ b/test/integration/test_basics.rb @@ -0,0 +1,42 @@ +require_relative './lib' + +class Basics < IntegrationTest + # test cpm coherency with no cpm-related env vars + def test_cpm_default + prj = make_project 'no-deps' + prj.create_lists_with({}) + cfg_result = prj.configure + assert_success cfg_result + + @cache = prj.read_cache + + assert_empty @cache.packages + + assert_same_path TestLib::CPM_PATH, check_and_get('CPM_FILE') + assert_same_path File.dirname(TestLib::CPM_PATH), check_and_get('CPM_DIRECTORY') + + assert_equal 'OFF', check_and_get('CPM_DRY_RUN') + assert_equal 'CPM:', check_and_get('CPM_INDENT') + assert_equal '1.0.0-development-version', check_and_get('CPM_VERSION') + + assert_equal 'OFF', check_and_get('CPM_SOURCE_CACHE', 'PATH') + assert_equal 'OFF', check_and_get('CPM_DOWNLOAD_ALL', 'BOOL') + assert_equal 'OFF', check_and_get('CPM_LOCAL_PACKAGES_ONLY', 'BOOL') + assert_equal 'OFF', check_and_get('CPM_USE_LOCAL_PACKAGES', 'BOOL') + assert_equal 'OFF', check_and_get('CPM_USE_NAMED_CACHE_DIRECTORIES', 'BOOL') + + assert_equal 'OFF', check_and_get('CPM_DONT_CREATE_PACKAGE_LOCK', 'BOOL') + assert_equal 'OFF', check_and_get('CPM_INCLUDE_ALL_IN_PACKAGE_LOCK', 'BOOL') + assert_same_path File.join(prj.build_dir, 'cpm-package-lock.cmake'), check_and_get('CPM_PACKAGE_LOCK_FILE') + + assert_equal 'OFF', check_and_get('CPM_DONT_UPDATE_MODULE_PATH', 'BOOL') + assert_same_path File.join(prj.build_dir, 'CPM_modules'), check_and_get('CPM_MODULE_PATH') + end + + def check_and_get(key, type = 'INTERNAL') + e = @cache.entries[key] + assert_not_nil e, key + assert_equal type, e.type, key + e.val + end +end diff --git a/test/integration/test_simple.rb b/test/integration/test_simple.rb index 55374ed6..80ca7efc 100644 --- a/test/integration/test_simple.rb +++ b/test/integration/test_simple.rb @@ -3,7 +3,7 @@ class Simple < IntegrationTest P_ADDER = 'testpack-adder' - def test_basics + def test_add_remove_packages prj = make_project 'using-adder' prj.create_lists_with package: 'CPMAddPackage("gh:cpm-cmake/testpack-adder#cad1cd4b4cdf957c5b59e30bc9a1dd200dbfc716")' From 0ddac3b0f04b53d590758cc3baa66ab938937ebc Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Wed, 12 Jan 2022 12:53:22 +0200 Subject: [PATCH 19/33] Style --- test/style/CMakeLists.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/style/CMakeLists.txt b/test/style/CMakeLists.txt index 5dbf35ca..9def3da1 100644 --- a/test/style/CMakeLists.txt +++ b/test/style/CMakeLists.txt @@ -8,8 +8,7 @@ CPMAddPackage( NAME Format.cmake VERSION 1.7.3 GITHUB_REPOSITORY TheLartians/Format.cmake - - # We exclude cmake files from integration tests as they contain invalid lines - # of code which are used by the integration test scripts + # We exclude cmake files from integration tests as they contain invalid lines of code which are + # used by the integration test scripts OPTIONS "CMAKE_FORMAT_EXCLUDE integration/templates" ) From d23788c5b10f0de84a69e1bb5f6b64a9d887d204 Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Wed, 12 Jan 2022 13:45:29 +0200 Subject: [PATCH 20/33] Style --- test/integration/templates/no-deps/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/templates/no-deps/main.c b/test/integration/templates/no-deps/main.c index d7f6d643..1880718d 100644 --- a/test/integration/templates/no-deps/main.c +++ b/test/integration/templates/no-deps/main.c @@ -1,6 +1,6 @@ #include int main() { - puts("Hello"); - return 0; + puts("Hello"); + return 0; } From f7ccbcd8a8737a778b1b5ff1a3c4487622b03df2 Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Wed, 12 Jan 2022 14:28:42 +0200 Subject: [PATCH 21/33] test_update_single_package --- test/integration/lib.rb | 15 +-- .../templates/using-adder/lists.in.cmake | 2 + test/integration/test_basics.rb | 9 +- test/integration/test_simple.rb | 96 ++++++++++++++----- 4 files changed, 89 insertions(+), 33 deletions(-) diff --git a/test/integration/lib.rb b/test/integration/lib.rb index 5323f1ca..1337ba59 100644 --- a/test/integration/lib.rb +++ b/test/integration/lib.rb @@ -32,12 +32,12 @@ def self.clear_env puts "Temp directory: '#{TestLib::TMP_DIR}'" class Project - def initialize(src_dir, build_dir) + def initialize(src_dir, bin_dir) @src_dir = src_dir - @build_dir = build_dir + @bin_dir = bin_dir end - attr :src_dir, :build_dir + attr :src_dir, :bin_dir def create_file(target_path, text) target_path = File.join(@src_dir, target_path) @@ -64,7 +64,10 @@ def create_lists_with(args) CommandResult = Struct.new :out, :err, :status def configure(extra_args = '') - CommandResult.new *Open3.capture3("cmake -S #{@src_dir} -B #{@build_dir} #{extra_args}") + CommandResult.new *Open3.capture3("cmake -S #{@src_dir} -B #{@bin_dir} #{extra_args}") + end + def build(extra_args = '') + CommandResult.new *Open3.capture3("cmake --build #{@bin_dir} #{extra_args}") end class CMakeCache @@ -132,7 +135,7 @@ def [](key) end end def read_cache - CMakeCache.from_dir @build_dir + CMakeCache.from_dir @bin_dir end end @@ -171,6 +174,6 @@ def make_project(template_dir = nil) FileUtils.copy_entry template_dir, src_dir end - Project.new src_dir, base + '-build' + Project.new src_dir, base + '-bin' end end diff --git a/test/integration/templates/using-adder/lists.in.cmake b/test/integration/templates/using-adder/lists.in.cmake index 4d0d4283..d4eee53e 100644 --- a/test/integration/templates/using-adder/lists.in.cmake +++ b/test/integration/templates/using-adder/lists.in.cmake @@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 3.14 FATAL_ERROR) project(using-adder) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + include("%{cpm_path}") %{packages} diff --git a/test/integration/test_basics.rb b/test/integration/test_basics.rb index c6e941da..f3db6e96 100644 --- a/test/integration/test_basics.rb +++ b/test/integration/test_basics.rb @@ -1,10 +1,13 @@ require_relative './lib' +# Tests of cpm caches and vars when no packages are used + class Basics < IntegrationTest - # test cpm coherency with no cpm-related env vars + # Test cpm caches with no cpm-related env vars def test_cpm_default prj = make_project 'no-deps' prj.create_lists_with({}) + cfg_result = prj.configure assert_success cfg_result @@ -27,10 +30,10 @@ def test_cpm_default assert_equal 'OFF', check_and_get('CPM_DONT_CREATE_PACKAGE_LOCK', 'BOOL') assert_equal 'OFF', check_and_get('CPM_INCLUDE_ALL_IN_PACKAGE_LOCK', 'BOOL') - assert_same_path File.join(prj.build_dir, 'cpm-package-lock.cmake'), check_and_get('CPM_PACKAGE_LOCK_FILE') + assert_same_path File.join(prj.bin_dir, 'cpm-package-lock.cmake'), check_and_get('CPM_PACKAGE_LOCK_FILE') assert_equal 'OFF', check_and_get('CPM_DONT_UPDATE_MODULE_PATH', 'BOOL') - assert_same_path File.join(prj.build_dir, 'CPM_modules'), check_and_get('CPM_MODULE_PATH') + assert_same_path File.join(prj.bin_dir, 'CPM_modules'), check_and_get('CPM_MODULE_PATH') end def check_and_get(key, type = 'INTERNAL') diff --git a/test/integration/test_simple.rb b/test/integration/test_simple.rb index 80ca7efc..cc203cc6 100644 --- a/test/integration/test_simple.rb +++ b/test/integration/test_simple.rb @@ -1,41 +1,89 @@ require_relative './lib' class Simple < IntegrationTest - P_ADDER = 'testpack-adder' + ADDER_PACKAGE_NAME = 'testpack-adder' - def test_add_remove_packages - prj = make_project 'using-adder' + def test_update_single_package + @prj = make_project 'using-adder' - prj.create_lists_with package: 'CPMAddPackage("gh:cpm-cmake/testpack-adder#cad1cd4b4cdf957c5b59e30bc9a1dd200dbfc716")' - assert_success prj.configure + create_with_commit_sha + update_to_version_1 + update_with_option_off_and_build + end + + def create_with_commit_sha + @prj.create_lists_with package: 'CPMAddPackage("gh:cpm-cmake/testpack-adder#cad1cd4b4cdf957c5b59e30bc9a1dd200dbfc716")' + assert_success @prj.configure - cache = prj.read_cache + cache = @prj.read_cache assert_equal 1, cache.packages.size - adder = cache.packages[P_ADDER] - assert_not_nil adder - assert_equal '0', adder.ver - assert File.directory? adder.src_dir - assert File.directory? adder.bin_dir + adder_cache = cache.packages[ADDER_PACKAGE_NAME] + assert_not_nil adder_cache + assert_equal '0', adder_cache.ver + assert File.directory? adder_cache.src_dir + assert File.directory? adder_cache.bin_dir + + @adder_ver_file = File.join(adder_cache.src_dir, 'version') + assert File.file? @adder_ver_file + assert_equal 'initial', File.read(@adder_ver_file).strip - adder_ver_file = File.join(adder.src_dir, 'version') - assert File.file? adder_ver_file - assert_equal 'initial', File.read(adder_ver_file).strip + # calculated adder values + assert_equal 'ON', cache['ADDER_BUILD_EXAMPLES'] + assert_equal 'ON', cache['ADDER_BUILD_TESTS'] + assert_equal adder_cache.src_dir, cache['adder_SOURCE_DIR'] + assert_equal adder_cache.bin_dir, cache['adder_BINARY_DIR'] + + # store for future comparisons + @adder_cache0 = adder_cache + end - # reconfigure with new version - prj.create_lists_with package: 'CPMAddPackage("gh:cpm-cmake/testpack-adder@1.0.0")' - assert_success prj.configure + def update_to_version_1 + @prj.create_lists_with package: 'CPMAddPackage("gh:cpm-cmake/testpack-adder@1.0.0")' + assert_success @prj.configure - cache = prj.read_cache + cache = @prj.read_cache assert_equal 1, cache.packages.size - adder = cache.packages[P_ADDER] - assert_not_nil adder - assert_equal '1.0.0', adder.ver + adder_cache = cache.packages[ADDER_PACKAGE_NAME] + assert_not_nil adder_cache + assert_equal '1.0.0', adder_cache.ver + + # dirs shouldn't have changed + assert_equal @adder_cache0.src_dir, adder_cache.src_dir + assert_equal @adder_cache0.bin_dir, adder_cache.bin_dir + + assert_equal '1.0.0', File.read(@adder_ver_file).strip + end + + def update_with_option_off_and_build + @prj.create_lists_with package: <<~PACK + CPMAddPackage( + NAME testpack-adder + GITHUB_REPOSITORY cpm-cmake/testpack-adder + VERSION 1.0.0 + OPTIONS "ADDER_BUILD_TESTS OFF" + ) + PACK + assert_success @prj.configure + assert_success @prj.build + + exe_dir = File.join(@prj.bin_dir, 'bin') + assert File.directory? exe_dir - # dir shouldn't have changed - assert_equal File.dirname(adder_ver_file), adder.src_dir + exes = Dir[exe_dir + '/**/*'].filter { + # on multi-configuration generators (like Visual Studio) the executables will be in bin/ + # also filter-out other articacts like .pdb or .dsym + !File.directory?(_1) && File.stat(_1).executable? + }.map { + # remove .exe extension if any (there will be one on Windows) + File.basename(_1, '.exe') + }.sort - assert_equal '1.0.0', File.read(adder_ver_file).strip + # we should end up with two executables + # * simple - the simple example from adder + # * using-adder - for this project + # ...and notably no test for adder, which must be disabled from the option override from above + assert_equal ['simple', 'using-adder'], exes end end From cef108e86ebc409449794cd77dd69f407924931b Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Wed, 12 Jan 2022 17:12:01 +0200 Subject: [PATCH 22/33] WiP for source cache test --- test/integration/lib.rb | 18 ++++- .../templates/using-fibadder/lists.in.cmake | 13 ++++ .../using-fibadder/using-fibadder.cpp | 8 +++ test/integration/test_basics.rb | 17 ++++- test/integration/test_source_cache.rb | 67 +++++++++++++++++++ 5 files changed, 119 insertions(+), 4 deletions(-) create mode 100644 test/integration/templates/using-fibadder/lists.in.cmake create mode 100644 test/integration/templates/using-fibadder/using-fibadder.cpp create mode 100644 test/integration/test_source_cache.rb diff --git a/test/integration/lib.rb b/test/integration/lib.rb index 1337ba59..2fad5f64 100644 --- a/test/integration/lib.rb +++ b/test/integration/lib.rb @@ -158,12 +158,28 @@ def assert_same_path(a, b) end # utils + class << self + def startup + @@test_dir = File.join(TestLib::TMP_DIR, self.name. + # to-underscore conversion from Rails + gsub(/::/, '/'). + gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). + gsub(/([a-z\d])([A-Z])/,'\1_\2'). + tr("-", "_"). + downcase + ) + end + end + + def cur_test_dir + @@test_dir + end def make_project(template_dir = nil) test_name = local_name test_name = test_name[5..] if test_name.start_with?('test_') - base = File.join(TestLib::TMP_DIR, self.class.name.downcase, test_name) + base = File.join(cur_test_dir, test_name) src_dir = base + '-src' FileUtils.mkdir_p src_dir diff --git a/test/integration/templates/using-fibadder/lists.in.cmake b/test/integration/templates/using-fibadder/lists.in.cmake new file mode 100644 index 00000000..5b4e679b --- /dev/null +++ b/test/integration/templates/using-fibadder/lists.in.cmake @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.14 FATAL_ERROR) + +project(using-fibadder) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + +include("%{cpm_path}") + +%{packages} + +add_executable(using-fibadder using-fibadder.cpp) + +target_link_libraries(using-fibadder fibadder) diff --git a/test/integration/templates/using-fibadder/using-fibadder.cpp b/test/integration/templates/using-fibadder/using-fibadder.cpp new file mode 100644 index 00000000..3e01478f --- /dev/null +++ b/test/integration/templates/using-fibadder/using-fibadder.cpp @@ -0,0 +1,8 @@ +#include +#include + +int main() { + int sum = fibadder::fibadd(6, 7); + std::printf("%d\n", sum); + return 0; +} diff --git a/test/integration/test_basics.rb b/test/integration/test_basics.rb index f3db6e96..35518e0e 100644 --- a/test/integration/test_basics.rb +++ b/test/integration/test_basics.rb @@ -7,9 +7,7 @@ class Basics < IntegrationTest def test_cpm_default prj = make_project 'no-deps' prj.create_lists_with({}) - - cfg_result = prj.configure - assert_success cfg_result + assert_success prj.configure @cache = prj.read_cache @@ -36,6 +34,19 @@ def test_cpm_default assert_same_path File.join(prj.bin_dir, 'CPM_modules'), check_and_get('CPM_MODULE_PATH') end + # Test when env CPM_SOURCE_CACHE is set + def test_env_cpm_source_cache + ENV['CPM_SOURCE_CACHE'] = cur_test_dir + + prj = make_project 'no-deps' + prj.create_lists_with({}) + assert_success prj.configure + + @cache = prj.read_cache + + assert_equal cur_test_dir, check_and_get('CPM_SOURCE_CACHE', 'PATH') + end + def check_and_get(key, type = 'INTERNAL') e = @cache.entries[key] assert_not_nil e, key diff --git a/test/integration/test_source_cache.rb b/test/integration/test_source_cache.rb new file mode 100644 index 00000000..a6da52a6 --- /dev/null +++ b/test/integration/test_source_cache.rb @@ -0,0 +1,67 @@ +require_relative './lib' + +# Tests with source cache + +class SourceCache < IntegrationTest + def setup + super + @cache_dir = File.join(cur_test_dir, 'cpmcache') + ENV['CPM_SOURCE_CACHE'] = @cache_dir + end + + def test_add_remove_dependency + @prj = make_project 'using-fibadder' + + create_with_fibadder + end + + def test_second_project + # @prj = make_project 'using-fibadder' + + # create_with_newer_fibadder + end + + def dir_subdirs(dir) + Dir["#{dir}/*/"] + end + + def check_package_cache(name, ver, dir_sha1) + package = @cache.packages[name] + assert_not_nil package, name + assert_equal ver, package.ver + assert package.src_dir.start_with?(@cache_dir), "#{package.src_dir} must be in #{@cache_dir}" + assert_equal dir_sha1, File.basename(package.src_dir) + end + + def create_with_fibadder + @prj.create_lists_with package: 'CPMAddPackage("gh:cpm-cmake/testpack-fibadder@1.0.0")' + assert_success @prj.configure + + @cache = @prj.read_cache + + # fibadder - adder + # \ fibonacci - Format + assert_equal 4, @cache.packages.size + + check_package_cache 'testpack-fibadder', '1.0.0', '6a17d24c95c44a169ff8ba173f52876a2ba3d137' + check_package_cache 'testpack-adder', '1.0.0', '1a4c153849d8e0cf9a3a245e5f6ab6e4722d8995' + check_package_cache 'testpack-fibonacci', '2.0', '332c789cb09b8c2f92342dfb874c82bec643daf6' + check_package_cache 'Format.cmake', '1.0', 'c5897bd28c5032d45f7f669c8fb470790d2ae156' + end + + def create_with_newer_fibadder + @prj.create_lists_with package: 'CPMAddPackage("gh:cpm-cmake/testpack-fibadder@1.1.0")' + assert_success @prj.configure + + @cache = @prj.read_cache + + # fibadder - adder + # \ fibonacci - Format + assert_equal 4, @cache.packages.size + + check_package_cache 'testpack-fibadder', '1.1.0', '6a17d24c95c44a169ff8ba173f52876a2ba3d137' + check_package_cache 'testpack-adder', '1.0.1', '1a4c153849d8e0cf9a3a245e5f6ab6e4722d8995' + check_package_cache 'testpack-fibonacci', '2.0', '332c789cb09b8c2f92342dfb874c82bec643daf6' + check_package_cache 'Format.cmake', '1.0', 'c5897bd28c5032d45f7f669c8fb470790d2ae156' + end +end From 8ba308cdba91527cb99e7d661ac7574fd47f6acb Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Thu, 13 Jan 2022 05:16:51 +0200 Subject: [PATCH 23/33] Small source_cache test --- test/integration/lib.rb | 2 + test/integration/test_a_lib.rb | 20 ++++ test/integration/test_simple.rb | 162 +++++++++++++------------- test/integration/test_source_cache.rb | 78 ++++++++----- 4 files changed, 149 insertions(+), 113 deletions(-) create mode 100644 test/integration/test_a_lib.rb diff --git a/test/integration/lib.rb b/test/integration/lib.rb index 2fad5f64..9363bd70 100644 --- a/test/integration/lib.rb +++ b/test/integration/lib.rb @@ -140,6 +140,8 @@ def read_cache end class IntegrationTest < Test::Unit::TestCase + self.test_order = :defined # run tests in order of defintion (as opposed to alphabetical) + def setup # Clear existing cpm-related env vars TestLib.clear_env diff --git a/test/integration/test_a_lib.rb b/test/integration/test_a_lib.rb new file mode 100644 index 00000000..9fd8a3ce --- /dev/null +++ b/test/integration/test_a_lib.rb @@ -0,0 +1,20 @@ +require_relative './lib' + +# Tests and experiments with the integration test framework itself + +class ALib < IntegrationTest + def test_zza + f = -> { + assert_equal 2, 2 + } + f.() + end + + def test_b + test_foo('xxx') + end + + def test_foo(xxx) + assert_equal 'xxx', xxx + end +end diff --git a/test/integration/test_simple.rb b/test/integration/test_simple.rb index cc203cc6..c6bb3f40 100644 --- a/test/integration/test_simple.rb +++ b/test/integration/test_simple.rb @@ -4,86 +4,86 @@ class Simple < IntegrationTest ADDER_PACKAGE_NAME = 'testpack-adder' def test_update_single_package - @prj = make_project 'using-adder' - - create_with_commit_sha - update_to_version_1 - update_with_option_off_and_build - end - - def create_with_commit_sha - @prj.create_lists_with package: 'CPMAddPackage("gh:cpm-cmake/testpack-adder#cad1cd4b4cdf957c5b59e30bc9a1dd200dbfc716")' - assert_success @prj.configure - - cache = @prj.read_cache - assert_equal 1, cache.packages.size - - adder_cache = cache.packages[ADDER_PACKAGE_NAME] - assert_not_nil adder_cache - assert_equal '0', adder_cache.ver - assert File.directory? adder_cache.src_dir - assert File.directory? adder_cache.bin_dir - - @adder_ver_file = File.join(adder_cache.src_dir, 'version') - assert File.file? @adder_ver_file - assert_equal 'initial', File.read(@adder_ver_file).strip - - # calculated adder values - assert_equal 'ON', cache['ADDER_BUILD_EXAMPLES'] - assert_equal 'ON', cache['ADDER_BUILD_TESTS'] - assert_equal adder_cache.src_dir, cache['adder_SOURCE_DIR'] - assert_equal adder_cache.bin_dir, cache['adder_BINARY_DIR'] - - # store for future comparisons - @adder_cache0 = adder_cache - end - - def update_to_version_1 - @prj.create_lists_with package: 'CPMAddPackage("gh:cpm-cmake/testpack-adder@1.0.0")' - assert_success @prj.configure - - cache = @prj.read_cache - assert_equal 1, cache.packages.size - - adder_cache = cache.packages[ADDER_PACKAGE_NAME] - assert_not_nil adder_cache - assert_equal '1.0.0', adder_cache.ver - - # dirs shouldn't have changed - assert_equal @adder_cache0.src_dir, adder_cache.src_dir - assert_equal @adder_cache0.bin_dir, adder_cache.bin_dir - - assert_equal '1.0.0', File.read(@adder_ver_file).strip - end - - def update_with_option_off_and_build - @prj.create_lists_with package: <<~PACK - CPMAddPackage( - NAME testpack-adder - GITHUB_REPOSITORY cpm-cmake/testpack-adder - VERSION 1.0.0 - OPTIONS "ADDER_BUILD_TESTS OFF" - ) - PACK - assert_success @prj.configure - assert_success @prj.build - - exe_dir = File.join(@prj.bin_dir, 'bin') - assert File.directory? exe_dir - - exes = Dir[exe_dir + '/**/*'].filter { - # on multi-configuration generators (like Visual Studio) the executables will be in bin/ - # also filter-out other articacts like .pdb or .dsym - !File.directory?(_1) && File.stat(_1).executable? - }.map { - # remove .exe extension if any (there will be one on Windows) - File.basename(_1, '.exe') - }.sort - - # we should end up with two executables - # * simple - the simple example from adder - # * using-adder - for this project - # ...and notably no test for adder, which must be disabled from the option override from above - assert_equal ['simple', 'using-adder'], exes + prj = make_project 'using-adder' + adder_cache0 = nil + adder_ver_file = nil + + create_with_commit_sha = -> { + prj.create_lists_with package: 'CPMAddPackage("gh:cpm-cmake/testpack-adder#cad1cd4b4cdf957c5b59e30bc9a1dd200dbfc716")' + assert_success prj.configure + + cache = prj.read_cache + assert_equal 1, cache.packages.size + + adder_cache = cache.packages[ADDER_PACKAGE_NAME] + assert_not_nil adder_cache + assert_equal '0', adder_cache.ver + assert File.directory? adder_cache.src_dir + assert File.directory? adder_cache.bin_dir + + adder_ver_file = File.join(adder_cache.src_dir, 'version') + assert File.file? adder_ver_file + assert_equal 'initial', File.read(adder_ver_file).strip + + # calculated adder values + assert_equal 'ON', cache['ADDER_BUILD_EXAMPLES'] + assert_equal 'ON', cache['ADDER_BUILD_TESTS'] + assert_equal adder_cache.src_dir, cache['adder_SOURCE_DIR'] + assert_equal adder_cache.bin_dir, cache['adder_BINARY_DIR'] + + # store for future comparisons + adder_cache0 = adder_cache + } + update_to_version_1 = -> { + prj.create_lists_with package: 'CPMAddPackage("gh:cpm-cmake/testpack-adder@1.0.0")' + assert_success prj.configure + + cache = prj.read_cache + assert_equal 1, cache.packages.size + + adder_cache = cache.packages[ADDER_PACKAGE_NAME] + assert_not_nil adder_cache + assert_equal '1.0.0', adder_cache.ver + + # dirs shouldn't have changed + assert_equal adder_cache0.src_dir, adder_cache.src_dir + assert_equal adder_cache0.bin_dir, adder_cache.bin_dir + + assert_equal '1.0.0', File.read(adder_ver_file).strip + } + update_with_option_off_and_build = -> { + prj.create_lists_with package: <<~PACK + CPMAddPackage( + NAME testpack-adder + GITHUB_REPOSITORY cpm-cmake/testpack-adder + VERSION 1.0.0 + OPTIONS "ADDER_BUILD_TESTS OFF" + ) + PACK + assert_success prj.configure + assert_success prj.build + + exe_dir = File.join(prj.bin_dir, 'bin') + assert File.directory? exe_dir + + exes = Dir[exe_dir + '/**/*'].filter { + # on multi-configuration generators (like Visual Studio) the executables will be in bin/ + # also filter-out other articacts like .pdb or .dsym + !File.directory?(_1) && File.stat(_1).executable? + }.map { + # remove .exe extension if any (there will be one on Windows) + File.basename(_1, '.exe') + }.sort + + # we should end up with two executables + # * simple - the simple example from adder + # * using-adder - for this project + # ...and notably no test for adder, which must be disabled from the option override from above + assert_equal ['simple', 'using-adder'], exes + } + + create_with_commit_sha.() + update_to_version_1.() + update_with_option_off_and_build.() end end diff --git a/test/integration/test_source_cache.rb b/test/integration/test_source_cache.rb index a6da52a6..2db9dd0d 100644 --- a/test/integration/test_source_cache.rb +++ b/test/integration/test_source_cache.rb @@ -10,34 +10,14 @@ def setup end def test_add_remove_dependency - @prj = make_project 'using-fibadder' + prj = make_project 'using-fibadder' - create_with_fibadder - end - - def test_second_project - # @prj = make_project 'using-fibadder' - - # create_with_newer_fibadder - end - - def dir_subdirs(dir) - Dir["#{dir}/*/"] - end - - def check_package_cache(name, ver, dir_sha1) - package = @cache.packages[name] - assert_not_nil package, name - assert_equal ver, package.ver - assert package.src_dir.start_with?(@cache_dir), "#{package.src_dir} must be in #{@cache_dir}" - assert_equal dir_sha1, File.basename(package.src_dir) - end + ################################### + # create + prj.create_lists_with package: 'CPMAddPackage("gh:cpm-cmake/testpack-fibadder@1.0.0")' + assert_success prj.configure - def create_with_fibadder - @prj.create_lists_with package: 'CPMAddPackage("gh:cpm-cmake/testpack-fibadder@1.0.0")' - assert_success @prj.configure - - @cache = @prj.read_cache + @cache = prj.read_cache # fibadder - adder # \ fibonacci - Format @@ -47,21 +27,55 @@ def create_with_fibadder check_package_cache 'testpack-adder', '1.0.0', '1a4c153849d8e0cf9a3a245e5f6ab6e4722d8995' check_package_cache 'testpack-fibonacci', '2.0', '332c789cb09b8c2f92342dfb874c82bec643daf6' check_package_cache 'Format.cmake', '1.0', 'c5897bd28c5032d45f7f669c8fb470790d2ae156' + + ################################### + # add one package with a newer version + prj.create_lists_with packages: [ + 'CPMAddPackage("gh:cpm-cmake/testpack-adder@1.0.1")', + 'CPMAddPackage("gh:cpm-cmake/testpack-fibadder@1.0.0")', + ] + assert_success prj.configure + + @cache = prj.read_cache + assert_equal 4, @cache.packages.size + + check_package_cache 'testpack-fibadder', '1.0.0', '6a17d24c95c44a169ff8ba173f52876a2ba3d137' + check_package_cache 'testpack-adder', '1.0.1', '84eb33c1b8db880083cefc2adf4dc3f04778cd44' + check_package_cache 'testpack-fibonacci', '2.0', '332c789cb09b8c2f92342dfb874c82bec643daf6' + check_package_cache 'Format.cmake', '1.0', 'c5897bd28c5032d45f7f669c8fb470790d2ae156' end - def create_with_newer_fibadder - @prj.create_lists_with package: 'CPMAddPackage("gh:cpm-cmake/testpack-fibadder@1.1.0")' - assert_success @prj.configure + def test_second_project + prj = make_project 'using-fibadder' + prj.create_lists_with package: 'CPMAddPackage("gh:cpm-cmake/testpack-fibadder@1.1.0")' + assert_success prj.configure - @cache = @prj.read_cache + @cache = prj.read_cache # fibadder - adder # \ fibonacci - Format assert_equal 4, @cache.packages.size - check_package_cache 'testpack-fibadder', '1.1.0', '6a17d24c95c44a169ff8ba173f52876a2ba3d137' - check_package_cache 'testpack-adder', '1.0.1', '1a4c153849d8e0cf9a3a245e5f6ab6e4722d8995' + check_package_cache 'testpack-fibadder', '1.1.0', '603d79d88d7230cc749460a0f476df862aa70ead' + check_package_cache 'testpack-adder', '1.0.1', '84eb33c1b8db880083cefc2adf4dc3f04778cd44' check_package_cache 'testpack-fibonacci', '2.0', '332c789cb09b8c2f92342dfb874c82bec643daf6' check_package_cache 'Format.cmake', '1.0', 'c5897bd28c5032d45f7f669c8fb470790d2ae156' end + + def test_cache_dir_contents + num_subdirs = -> (name) { Dir["#{File.join(@cache_dir, name.downcase)}/*/"].size } + assert_equal 2, num_subdirs.('testpack-fibadder') + assert_equal 2, num_subdirs.('testpack-adder') + assert_equal 1, num_subdirs.('testpack-fibonacci') + assert_equal 1, num_subdirs.('Format.cmake') + end + + def check_package_cache(name, ver, dir_sha1) + package = @cache.packages[name] + assert_not_nil package, name + assert_equal ver, package.ver + expected_parent_dir = File.join(@cache_dir, name.downcase) + assert package.src_dir.start_with?(expected_parent_dir), "#{package.src_dir} must be in #{expected_parent_dir}" + assert_equal dir_sha1, File.basename(package.src_dir) + end end From 74494e645f85235fbb12299a938b8b7bf4de6e9b Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Thu, 13 Jan 2022 05:28:05 +0200 Subject: [PATCH 24/33] Style --- test/integration/templates/using-fibadder/using-fibadder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/templates/using-fibadder/using-fibadder.cpp b/test/integration/templates/using-fibadder/using-fibadder.cpp index 3e01478f..5f51165f 100644 --- a/test/integration/templates/using-fibadder/using-fibadder.cpp +++ b/test/integration/templates/using-fibadder/using-fibadder.cpp @@ -1,5 +1,5 @@ -#include #include +#include int main() { int sum = fibadder::fibadd(6, 7); From 3bac9363840d47c2b3ba6f899e36fb21151c1a2e Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Thu, 13 Jan 2022 06:38:11 +0200 Subject: [PATCH 25/33] Moved env clean to cleanup to make setup methods simpler (not require super) --- test/integration/lib.rb | 7 +++++-- test/integration/test_source_cache.rb | 1 - 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/test/integration/lib.rb b/test/integration/lib.rb index 9363bd70..d10143f8 100644 --- a/test/integration/lib.rb +++ b/test/integration/lib.rb @@ -31,6 +31,9 @@ def self.clear_env puts "Running CPM.cmake integration tests" puts "Temp directory: '#{TestLib::TMP_DIR}'" +# Clean all CPM-related env vars +TestLib.clear_env + class Project def initialize(src_dir, bin_dir) @src_dir = src_dir @@ -142,8 +145,8 @@ def read_cache class IntegrationTest < Test::Unit::TestCase self.test_order = :defined # run tests in order of defintion (as opposed to alphabetical) - def setup - # Clear existing cpm-related env vars + def cleanup + # Clear cpm-related env vars which may have been set by the test TestLib.clear_env end diff --git a/test/integration/test_source_cache.rb b/test/integration/test_source_cache.rb index 2db9dd0d..0d1e1751 100644 --- a/test/integration/test_source_cache.rb +++ b/test/integration/test_source_cache.rb @@ -4,7 +4,6 @@ class SourceCache < IntegrationTest def setup - super @cache_dir = File.join(cur_test_dir, 'cpmcache') ENV['CPM_SOURCE_CACHE'] = @cache_dir end From 09def1e83856b4bfeb6064b985710bce3202411d Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Thu, 13 Jan 2022 06:50:22 +0200 Subject: [PATCH 26/33] WiP for integration test documentation --- test/integration/README.md | 141 +++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 test/integration/README.md diff --git a/test/integration/README.md b/test/integration/README.md new file mode 100644 index 00000000..bb24e2bc --- /dev/null +++ b/test/integration/README.md @@ -0,0 +1,141 @@ +# CPM.cmake Integration Tests + +The integration tests of CPM.cmake are written in Ruby. They use a custom integration test framework which extends the [Test::Unit](https://www.rubydoc.info/github/test-unit/test-unit/Test/Unit) library. + +They require Ruby 2.7.0 or later. + +## Running tests + +To run all tests from the repo root execute: + +``` +$ ruby test/integration/runner.rb +``` + +The runner will run all tests and generate a report of the exeuction. + +The current working directory doesn't matter. If you are in `/test/integration`, you can run simply `$ ruby runner.rb`. + +You can execute with `--help` (`$ ruby runner.rb --help`) to see various configuration options of the runner like running individual tests or test cases, or ones that match a regex. + +The tests themselves are situated in the Ruby scripts prefixed with `test_`. `/test/integration/test_*`. You can also run an individual test script. For example to only run the **basics** test case, you can execute `$ ruby test_basics.rb` + +The tests generate CMake scripts and execute CMake and build toolchains. By default they do this in a directory they generate in your temp path (`/tmp/cpm-test/` on Linux). You can configure the working directory of the tests with an environment variable `CPM_INTEGRATION_TEST_DIR`. For example `$ CPM_INTEGRATION_TEST_DIR=~/mycpmtest; ruby runner.rb` + +## Writing tests + +Writing tests makes use of the custom integration test framework in `lib.rb`. It is a relatively small extension of Ruby's Test::Unit library. + +### The Gist + +* Tests written in ruby scripts in this directory. The file names of tests must be prefixed with `test_` +* The file should `require_relative './lib'` to allow for individual execution (or else if will only be executable from the runner) +* A test file should contain a class which inherits from `IntegrationTest`. It can contain multiple classes, but that's typicall bad practice as it makes individual execution harder and implies dependency between the classes. +* There should be no dependency between the test scripts. Each should be executable individually and the order in which multiple ones are executed mustn't matter. +* The class should contain methods, also prefixed with `test_` which will be executed by the framework. In most cases there would be a single test method per class. +* In case there are multiple test methods, they will be executed in the order in which they are defined. +* The test methods should contain assertions which check for the expected state of things at varous points of the test's execution. + +### Notable Idiosyncrasies + +As an integration test framework based on a unit test framework it suffers from several idiosyncrasies. Make sure you familiarize yourself with them before writing integration tests. + +**No shared instance variables between methods** + +The runner will create an instance of the test class for each test method. This means that instance variables defined in a test method, *will not* be visible in another. For example: + +```ruby +class MyTest < IntegrationTest + def test_something + @x = 123 + assert_equal 123, @x # Pass. @x is 123 + end + def tese_something_else + assert_equal 123, @x # Fail! @x would be nil here + end +end +``` + +There are hacks around sharing Ruby state between methods, but we choose not to use them. If you want to initialize something for all test methods, use `setup`. + +```ruby +class MyTest < IntegrationTest + def setup + @x = 123 + end + def test_something + assert_equal 123, @x # Pass. @x is 123 thanks to setup + end + def tese_something_else + assert_equal 123, @x # Pass. @x is 123 thanks to setup + end +end +``` + +**`IntegrationTest` makes use of `Test::Unit::TestCase#cleanup`** + +After each test method the `cleanup` method is called thanks to Test::Unit. If you require the use of `cleanup` in your own tests, make sure you call `super` to also run `IntegrationTest#cleanup`. + +```ruby +class MyTest < IntegrationTest + def cleanup + super + my_cleanup + end + # ... +end +``` + +**Try to have assertions in test methods as opposed to helper methods** + +Test::Unit will display a helpful message if an assertion has failed. It will also include the line of code in the test method which caused the failure. However if an assertion is not in the test method, it will display the line which calls the method in which it is. So, please try, to have most assertions in test methods (though we acknowledge that in certain cases this is not practical). For example, if you only require scopes, try using lambdas. + +Instead of this: + +```ruby +class MyTest < IntegrationTest + def test_something + do_a + do_b + do_c + end + def do_a + # ... + end + def do_b + # ... + assert false # will display failed line as "do_b" + end + def do_c + # ... + end +end +``` + +...write this: + +```ruby +class MyTest < IntegrationTest + def test_something + do_a = -> { + # ... + } + do_b = -> { + # ... + assert false # will display failed line as "assert false" + } + do_c = -> { + # ... + } + + do_a.() + do_b.() + do_c.() + end +end +``` + + + + + From 9eab1913f1063c26855e13780bd8cb67b94c26e0 Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Thu, 13 Jan 2022 07:10:29 +0200 Subject: [PATCH 27/33] WiP for integration test documentation --- test/integration/README.md | 43 +++++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/test/integration/README.md b/test/integration/README.md index bb24e2bc..243c4174 100644 --- a/test/integration/README.md +++ b/test/integration/README.md @@ -28,14 +28,51 @@ Writing tests makes use of the custom integration test framework in `lib.rb`. It ### The Gist -* Tests written in ruby scripts in this directory. The file names of tests must be prefixed with `test_` -* The file should `require_relative './lib'` to allow for individual execution (or else if will only be executable from the runner) -* A test file should contain a class which inherits from `IntegrationTest`. It can contain multiple classes, but that's typicall bad practice as it makes individual execution harder and implies dependency between the classes. +* Tests cases are Ruby scripts in this directory. The file names must be prefixed with `test_` +* The script should `require_relative './lib'` to allow for individual execution (or else if will only be executable from the runner) +* A test case file should contain a single class which inherits from `IntegrationTest`. It *can* contain multiple classes, but that's bad practice as it makes individual execution harder and implies a dependency between the classes. * There should be no dependency between the test scripts. Each should be executable individually and the order in which multiple ones are executed mustn't matter. * The class should contain methods, also prefixed with `test_` which will be executed by the framework. In most cases there would be a single test method per class. * In case there are multiple test methods, they will be executed in the order in which they are defined. * The test methods should contain assertions which check for the expected state of things at varous points of the test's execution. + +### A simple tutorial + +Let's create a test which checks that CPM.cmake can make a specific package available. + +First we do some boilerplate. + +```ruby +require_relative './lib' + +class MyTest < IntegrationTest + # test that CPM.cmake can make https://github.com/cpm-cmake/testpack-adder/ available as a package + def test_make_adder_available + end +end +``` + +Now we have our test case class, and the single test method that we will require. Let's focus on the method's contents. The integration test framework provides us with a helper class, `Project`, which can be used for this scenario. A project has an assoiciated pair of source and binary directories in the temporary directory and it provides methods to work with them. + +We start by creating the project: + +```ruby +prj = make_project +``` + +`make_project` is method of IntegrationTest which generates a source and a binary directory for it based on the name of our test class and test method. The project doesn't contain anything yet, so let's create some source files: + +```ruby +prj.create_file 'main.cpp', <<~SRC + #include + #include + int main() { + cout << adder::add(1, 2); + return 0; + } +``` + ### Notable Idiosyncrasies As an integration test framework based on a unit test framework it suffers from several idiosyncrasies. Make sure you familiarize yourself with them before writing integration tests. From c7fd76ae0e2bc77f16ec9fb66ff1adefc03a74bc Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Thu, 13 Jan 2022 08:16:52 +0200 Subject: [PATCH 28/33] Project file creation tweaks --- test/integration/lib.rb | 20 ++++++++++---------- test/integration/test_basics.rb | 4 ++-- test/integration/test_simple.rb | 8 +++++--- test/integration/test_source_cache.rb | 6 +++--- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/test/integration/lib.rb b/test/integration/lib.rb index d10143f8..6510cf2d 100644 --- a/test/integration/lib.rb +++ b/test/integration/lib.rb @@ -42,27 +42,27 @@ def initialize(src_dir, bin_dir) attr :src_dir, :bin_dir - def create_file(target_path, text) + def create_file(target_path, text, args = {}) target_path = File.join(@src_dir, target_path) - File.write target_path, text - end - - def create_file_with(target_path, source_path, args) - source_path = File.join(@src_dir, source_path) - raise "#{source_path} doesn't exist" if !File.file?(source_path) # tweak args args[:cpm_path] = TestLib::CPM_PATH if !args[:cpm_path] args[:packages] = [args[:package]] if args[:package] # if args contain package, create the array args[:packages] = args[:packages].join("\n") if args[:packages] # join all packages if any + File.write target_path, text % args + end + + def create_file_from_template(target_path, source_path, args = {}) + source_path = File.join(@src_dir, source_path) + raise "#{source_path} doesn't exist" if !File.file?(source_path) src_text = File.read source_path - create_file target_path, src_text % args + create_file target_path, src_text, args end # common function to create ./CMakeLists.txt from ./lists.in.cmake - def create_lists_with(args) - create_file_with 'CMakeLists.txt', 'lists.in.cmake', args + def create_lists_from_default_template(args = {}) + create_file_from_template 'CMakeLists.txt', 'lists.in.cmake', args end CommandResult = Struct.new :out, :err, :status diff --git a/test/integration/test_basics.rb b/test/integration/test_basics.rb index 35518e0e..8083123c 100644 --- a/test/integration/test_basics.rb +++ b/test/integration/test_basics.rb @@ -6,7 +6,7 @@ class Basics < IntegrationTest # Test cpm caches with no cpm-related env vars def test_cpm_default prj = make_project 'no-deps' - prj.create_lists_with({}) + prj.create_lists_from_default_template assert_success prj.configure @cache = prj.read_cache @@ -39,7 +39,7 @@ def test_env_cpm_source_cache ENV['CPM_SOURCE_CACHE'] = cur_test_dir prj = make_project 'no-deps' - prj.create_lists_with({}) + prj.create_lists_from_default_template assert_success prj.configure @cache = prj.read_cache diff --git a/test/integration/test_simple.rb b/test/integration/test_simple.rb index c6bb3f40..ff175bf2 100644 --- a/test/integration/test_simple.rb +++ b/test/integration/test_simple.rb @@ -9,7 +9,8 @@ def test_update_single_package adder_ver_file = nil create_with_commit_sha = -> { - prj.create_lists_with package: 'CPMAddPackage("gh:cpm-cmake/testpack-adder#cad1cd4b4cdf957c5b59e30bc9a1dd200dbfc716")' + prj.create_lists_from_default_template package: + 'CPMAddPackage("gh:cpm-cmake/testpack-adder#cad1cd4b4cdf957c5b59e30bc9a1dd200dbfc716")' assert_success prj.configure cache = prj.read_cache @@ -35,7 +36,8 @@ def test_update_single_package adder_cache0 = adder_cache } update_to_version_1 = -> { - prj.create_lists_with package: 'CPMAddPackage("gh:cpm-cmake/testpack-adder@1.0.0")' + prj.create_lists_from_default_template package: + 'CPMAddPackage("gh:cpm-cmake/testpack-adder@1.0.0")' assert_success prj.configure cache = prj.read_cache @@ -52,7 +54,7 @@ def test_update_single_package assert_equal '1.0.0', File.read(adder_ver_file).strip } update_with_option_off_and_build = -> { - prj.create_lists_with package: <<~PACK + prj.create_lists_from_default_template package: <<~PACK CPMAddPackage( NAME testpack-adder GITHUB_REPOSITORY cpm-cmake/testpack-adder diff --git a/test/integration/test_source_cache.rb b/test/integration/test_source_cache.rb index 0d1e1751..fd8f92d3 100644 --- a/test/integration/test_source_cache.rb +++ b/test/integration/test_source_cache.rb @@ -13,7 +13,7 @@ def test_add_remove_dependency ################################### # create - prj.create_lists_with package: 'CPMAddPackage("gh:cpm-cmake/testpack-fibadder@1.0.0")' + prj.create_lists_from_default_template package: 'CPMAddPackage("gh:cpm-cmake/testpack-fibadder@1.0.0")' assert_success prj.configure @cache = prj.read_cache @@ -29,7 +29,7 @@ def test_add_remove_dependency ################################### # add one package with a newer version - prj.create_lists_with packages: [ + prj.create_lists_from_default_template packages: [ 'CPMAddPackage("gh:cpm-cmake/testpack-adder@1.0.1")', 'CPMAddPackage("gh:cpm-cmake/testpack-fibadder@1.0.0")', ] @@ -46,7 +46,7 @@ def test_add_remove_dependency def test_second_project prj = make_project 'using-fibadder' - prj.create_lists_with package: 'CPMAddPackage("gh:cpm-cmake/testpack-fibadder@1.1.0")' + prj.create_lists_from_default_template package: 'CPMAddPackage("gh:cpm-cmake/testpack-fibadder@1.1.0")' assert_success prj.configure @cache = prj.read_cache From 659e1a364d99f178c8c5cb4a498a84ee46398505 Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Thu, 13 Jan 2022 09:20:29 +0200 Subject: [PATCH 29/33] Split docs into multiple files. Complete tutorial. Reference. --- test/integration/README.md | 143 +---------------------------- test/integration/idiosyncrasies.md | 98 ++++++++++++++++++++ test/integration/reference.md | 65 +++++++++++++ test/integration/tutorial.md | 69 ++++++++++++++ test/integration/tutorial.rb | 37 ++++++++ 5 files changed, 273 insertions(+), 139 deletions(-) create mode 100644 test/integration/idiosyncrasies.md create mode 100644 test/integration/reference.md create mode 100644 test/integration/tutorial.md create mode 100644 test/integration/tutorial.rb diff --git a/test/integration/README.md b/test/integration/README.md index 243c4174..3a94d2d0 100644 --- a/test/integration/README.md +++ b/test/integration/README.md @@ -36,143 +36,8 @@ Writing tests makes use of the custom integration test framework in `lib.rb`. It * In case there are multiple test methods, they will be executed in the order in which they are defined. * The test methods should contain assertions which check for the expected state of things at varous points of the test's execution. +### More -### A simple tutorial - -Let's create a test which checks that CPM.cmake can make a specific package available. - -First we do some boilerplate. - -```ruby -require_relative './lib' - -class MyTest < IntegrationTest - # test that CPM.cmake can make https://github.com/cpm-cmake/testpack-adder/ available as a package - def test_make_adder_available - end -end -``` - -Now we have our test case class, and the single test method that we will require. Let's focus on the method's contents. The integration test framework provides us with a helper class, `Project`, which can be used for this scenario. A project has an assoiciated pair of source and binary directories in the temporary directory and it provides methods to work with them. - -We start by creating the project: - -```ruby -prj = make_project -``` - -`make_project` is method of IntegrationTest which generates a source and a binary directory for it based on the name of our test class and test method. The project doesn't contain anything yet, so let's create some source files: - -```ruby -prj.create_file 'main.cpp', <<~SRC - #include - #include - int main() { - cout << adder::add(1, 2); - return 0; - } -``` - -### Notable Idiosyncrasies - -As an integration test framework based on a unit test framework it suffers from several idiosyncrasies. Make sure you familiarize yourself with them before writing integration tests. - -**No shared instance variables between methods** - -The runner will create an instance of the test class for each test method. This means that instance variables defined in a test method, *will not* be visible in another. For example: - -```ruby -class MyTest < IntegrationTest - def test_something - @x = 123 - assert_equal 123, @x # Pass. @x is 123 - end - def tese_something_else - assert_equal 123, @x # Fail! @x would be nil here - end -end -``` - -There are hacks around sharing Ruby state between methods, but we choose not to use them. If you want to initialize something for all test methods, use `setup`. - -```ruby -class MyTest < IntegrationTest - def setup - @x = 123 - end - def test_something - assert_equal 123, @x # Pass. @x is 123 thanks to setup - end - def tese_something_else - assert_equal 123, @x # Pass. @x is 123 thanks to setup - end -end -``` - -**`IntegrationTest` makes use of `Test::Unit::TestCase#cleanup`** - -After each test method the `cleanup` method is called thanks to Test::Unit. If you require the use of `cleanup` in your own tests, make sure you call `super` to also run `IntegrationTest#cleanup`. - -```ruby -class MyTest < IntegrationTest - def cleanup - super - my_cleanup - end - # ... -end -``` - -**Try to have assertions in test methods as opposed to helper methods** - -Test::Unit will display a helpful message if an assertion has failed. It will also include the line of code in the test method which caused the failure. However if an assertion is not in the test method, it will display the line which calls the method in which it is. So, please try, to have most assertions in test methods (though we acknowledge that in certain cases this is not practical). For example, if you only require scopes, try using lambdas. - -Instead of this: - -```ruby -class MyTest < IntegrationTest - def test_something - do_a - do_b - do_c - end - def do_a - # ... - end - def do_b - # ... - assert false # will display failed line as "do_b" - end - def do_c - # ... - end -end -``` - -...write this: - -```ruby -class MyTest < IntegrationTest - def test_something - do_a = -> { - # ... - } - do_b = -> { - # ... - assert false # will display failed line as "assert false" - } - do_c = -> { - # ... - } - - do_a.() - do_b.() - do_c.() - end -end -``` - - - - - +* [A basic tutorial on writing integration tests.](tutorial.md) +* [A brief reference of the integration test framework](reference.md) +* Make sure you're familiar with the [idiosyncrasies](idiosyncrasies.md) of writing integration tests diff --git a/test/integration/idiosyncrasies.md b/test/integration/idiosyncrasies.md new file mode 100644 index 00000000..c6d4c48a --- /dev/null +++ b/test/integration/idiosyncrasies.md @@ -0,0 +1,98 @@ +# Notable Idiosyncrasies When Writing Integration Tests + +As an integration test framework based on a unit test framework the one created for CPM.cmake suffers from several idiosyncrasies. Make sure you familiarize yourself with them before writing integration tests. + +## No shared instance variables between methods + +The runner will create an instance of the test class for each test method. This means that instance variables defined in a test method, *will not* be visible in another. For example: + +```ruby +class MyTest < IntegrationTest + def test_something + @x = 123 + assert_equal 123, @x # Pass. @x is 123 + end + def tese_something_else + assert_equal 123, @x # Fail! @x would be nil here + end +end +``` + +There are hacks around sharing Ruby state between methods, but we choose not to use them. If you want to initialize something for all test methods, use `setup`. + +```ruby +class MyTest < IntegrationTest + def setup + @x = 123 + end + def test_something + assert_equal 123, @x # Pass. @x is 123 thanks to setup + end + def tese_something_else + assert_equal 123, @x # Pass. @x is 123 thanks to setup + end +end +``` + +## `IntegrationTest` makes use of `Test::Unit::TestCase#cleanup` + +After each test method the `cleanup` method is called thanks to Test::Unit. If you require the use of `cleanup` in your own tests, make sure you call `super` to also run `IntegrationTest#cleanup`. + +```ruby +class MyTest < IntegrationTest + def cleanup + super + my_cleanup + end + # ... +end +``` + +## It's better to have assertions in test methods as opposed to helper methods + +Test::Unit will display a helpful message if an assertion has failed. It will also include the line of code in the test method which caused the failure. However if an assertion is not in the test method, it will display the line which calls the method in which it is. So, please try, to have most assertions in test methods (though we acknowledge that in certain cases this is not practical). For example, if you only require scopes, try using lambdas. + +Instead of this: + +```ruby +class MyTest < IntegrationTest + def test_something + do_a + do_b + do_c + end + def do_a + # ... + end + def do_b + # ... + assert false # will display failed line as "do_b" + end + def do_c + # ... + end +end +``` + +...write this: + +```ruby +class MyTest < IntegrationTest + def test_something + do_a = -> { + # ... + } + do_b = -> { + # ... + assert false # will display failed line as "assert false" + } + do_c = -> { + # ... + } + + do_a.() + do_b.() + do_c.() + end +end +``` diff --git a/test/integration/reference.md b/test/integration/reference.md new file mode 100644 index 00000000..fd81450e --- /dev/null +++ b/test/integration/reference.md @@ -0,0 +1,65 @@ +# Integration Test Framework Refernce + +## `TestLib` + +A module for the framework. Provides global data and functionality. For ease of use the utility classes are *not* in this module. + +Provides: + +* `TMP_DIR` - the temporary directory for the current test run +* `CPM_PATH` - path to CPM.cmake. The thing that is being tested +* `TEMPLATES_DIR` - path to integration test templates +* `CPM_ENV` - an array of the names of all environment variables, which CPM.cmake may read +* `.clear_env` - a function to clear all aforementioned environment variables + +## `Project` + +A helper class to manage a CMake project. + +Provides: + +* `#initialize(src_dir, bin_dir)` - create a project with a given source and binary directory +* `#src_dir`, `#bin_dir` - get project directories +* `#create_file(target_path, text, args = {})` - create a file in the project's source directory with a given test. The `args` hash is used to interpolate markup in the text string. + * Will set `:cpm_path` in `args` to `TestLib::CPM_PATH` if not already present. + * If `:package` is present it will be added to the array `:packages` + * Will convert `:packages` form an array to a string +* `#create_file_from_template(target_path, source_path, args = {})` - create a file in the project source directory, based on another file in the project source directory. The contents of the file at `source_path` will be read and used in `create_file` +* `#create_lists_from_default_template(args = {})` - same as `create_file_from_template('CMakeLists.txt', 'lists.in.cmake', args)` +* `::CommandResult` - a struct of: + * `out` - the standard output from a command execution + * `err` - the standard error output from the execution + * `status` - the [`Process::Status`](https://ruby-doc.org/core-2.7.0/Process/Status.html) of the execution +* `#configure(extra_args = '') => CommandResult` - configure the project with optional extra args to CMake +* `#build(extra_args = '') => CommandResult` - build the project with optional extra args to CMake +* `::CMakeCache` - a helper class with the contents of a CMakeCache.txt. Provides: + * `::Entry` - a CMake cache entry of: + * `val` - the value as string + * `type` - the type as string + * `advanced?` - whether the entry is an advanced option + * `desc` - the description of the entry (can be an empty string) + * `::Package` - the CMake cache for a CPM.cmake package. A struct of: + * `ver` - the version as string + * `src_dir`, `bin_dir` - the source and binary directories of the package + * `.from_dir(dir)` - create an instance of `CMakeCache` from `/CMakeLists.txt` + * `#initialize(entries)` - create a cache from a hash of entries by name. Will populate packages. + * `#entries => {String => Entry}` - the entries of the cache + * `#packages => {String => Package}` - CPM.cmake packages by name found in the cache + * `#[](key) => String` - an entry value from an entry name. Created because the value is expected to be needed much more frequently than the entire entry data. To get a full entry use `cache.entries['name']`. +* `read_cache => CMakeCache` - reads the CMake cache in the binary directory of the project and returns it as a `CMakeCache` instance + +## `IntegrationTest` + +The class which must be a parent of all integration test case classes. It itself extends `Test::Unit::TestCase` with: + +### Assertions + +* `assert_success(res)` - assert that an instance of `Project::CommandResult` is a success +* `assert_same_path(a, b)` - assert that two strings represent the same path. For example on Windows `c:\foo` and `C:\Foo` do. + +### Utils + +* `cur_test_dir` - the directory of the current test case. A subdirectory of `TestLib::TMP_DIR` +* `make_project(template_dir = nil)` - create a project from a test method. Will create a the project's source and binary directories as subdirectories of `cur_test_dir`. + * Optionally work with a template directory, in which case it will copy the contents of the template directory (one from `templates`) in the project's source directory. + diff --git a/test/integration/tutorial.md b/test/integration/tutorial.md new file mode 100644 index 00000000..17eea960 --- /dev/null +++ b/test/integration/tutorial.md @@ -0,0 +1,69 @@ +# Integration Test Tutorial + +Let's create an integration test which checks that CPM.cmake can make a specific package available. + +First we do some boilerplate. + +```ruby +require_relative './lib' + +class MyTest < IntegrationTest + # test that CPM.cmake can make https://github.com/cpm-cmake/testpack-adder/ available as a package + def test_make_adder_available + end +end +``` + +Now we have our test case class, and the single test method that we will require. Let's focus on the method's contents. The integration test framework provides us with a helper class, `Project`, which can be used for this scenario. A project has an assoiciated pair of source and binary directories in the temporary directory and it provides methods to work with them. + +We start by creating the project: + +```ruby +prj = make_project +``` + +`make_project` is method of IntegrationTest which generates a source and a binary directory for it based on the name of our test class and test method. The project doesn't contain anything yet, so let's create some source files: + +```ruby +prj.create_file 'main.cpp', <<~SRC + #include + #include + int main() { + std::cout << adder::add(1, 2) << '\\n'; + return 0; + } +SRC +prj.create_file 'CMakeLists.txt', <<~SRC + cmake_minimum_required(VERSION 3.14 FATAL_ERROR) + project(using-adder) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + include("%{cpm_path}") + CPMAddPackage("gh:cpm-cmake/testpack-adder@1.0.0") + add_executable(using-adder main.cpp) + target_link_libraries(using-adder adder) +SRC +``` + +Note the line `include("%{cpm_path}")` when creating `CMakeLists.txt`. It contains a markup `%{cpm_path}`. `Project#create_file` will see such markups and substitute them with the appropriate values (in this case the path to CPM.cmake). + +Now that we have the two files we need it's time we configure our project. We can use the opportunity to assert that the configure is successful as we expect it to be. + +```ruby +assert_success prj.configure +``` + +Now we can read the generated `CMakeCache.txt` and assert that certain values we expect are inside. `Project` provides a method for that: `read_cache`. It will return an instance of `Project::CMakeCache` which contains the data from the cache and provides additional helper functionalities. One of them is `packages`, which is a hash of the CPM.cmake packages in the cache with their versions, binary, source directories. So let's get the cache and assert that there is only one CPM.cmake package inside ant it has the version we expect. + +```ruby +cache = prj.read_cache +assert_equal 1, cache.packages.size +assert_equal '1.0.0', cache.packages['testpack-adder'].ver +``` + +Finally let's assert that the project can be built. This would mean that CPM.cmake has made the package available to our test project and that it has the appropriate include directories and link libraries to make an executable out of `main.cpp`. + +```ruby +assert_success prj.build +``` + +You can see the entire code for this tutorial in [tutorial.rb](tutorial.rb) in this directory. diff --git a/test/integration/tutorial.rb b/test/integration/tutorial.rb new file mode 100644 index 00000000..67e44ea5 --- /dev/null +++ b/test/integration/tutorial.rb @@ -0,0 +1,37 @@ +# This file is intentionally not prefixed with test_ +# It is a tutorial for making integration tests and is not to be run from the runner +require_relative './lib' + +class Tutorial < IntegrationTest + # test that CPM.cmake can make https://github.com/cpm-cmake/testpack-adder/ available as a package + def test_make_adder_available + prj = make_project + + prj.create_file 'main.cpp', <<~SRC + #include + #include + int main() { + std::cout << adder::add(1, 2) << '\\n'; + return 0; + } + SRC + + prj.create_file 'CMakeLists.txt', <<~SRC + cmake_minimum_required(VERSION 3.14 FATAL_ERROR) + project(using-adder) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + include("%{cpm_path}") + CPMAddPackage("gh:cpm-cmake/testpack-adder@1.0.0") + add_executable(using-adder main.cpp) + target_link_libraries(using-adder adder) + SRC + + assert_success prj.configure + + cache = prj.read_cache + assert_equal 1, cache.packages.size + assert_equal '1.0.0', cache.packages['testpack-adder'].ver + + assert_success prj.build + end +end From 6b918249af0cf54539c3f67a43920b074214debe Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Thu, 13 Jan 2022 10:38:17 +0200 Subject: [PATCH 30/33] Tips --- test/integration/.gitignore | 2 ++ test/integration/README.md | 1 + test/integration/test_a_lib.rb | 20 ------------------- test/integration/tips.md | 35 ++++++++++++++++++++++++++++++++++ 4 files changed, 38 insertions(+), 20 deletions(-) create mode 100644 test/integration/.gitignore delete mode 100644 test/integration/test_a_lib.rb create mode 100644 test/integration/tips.md diff --git a/test/integration/.gitignore b/test/integration/.gitignore new file mode 100644 index 00000000..43488929 --- /dev/null +++ b/test/integration/.gitignore @@ -0,0 +1,2 @@ +# Use this to have a local integration test which is for personal experiments +test_local.rb diff --git a/test/integration/README.md b/test/integration/README.md index 3a94d2d0..59a68a6d 100644 --- a/test/integration/README.md +++ b/test/integration/README.md @@ -41,3 +41,4 @@ Writing tests makes use of the custom integration test framework in `lib.rb`. It * [A basic tutorial on writing integration tests.](tutorial.md) * [A brief reference of the integration test framework](reference.md) * Make sure you're familiar with the [idiosyncrasies](idiosyncrasies.md) of writing integration tests +* [Some tips and tricks](tips.md) diff --git a/test/integration/test_a_lib.rb b/test/integration/test_a_lib.rb deleted file mode 100644 index 9fd8a3ce..00000000 --- a/test/integration/test_a_lib.rb +++ /dev/null @@ -1,20 +0,0 @@ -require_relative './lib' - -# Tests and experiments with the integration test framework itself - -class ALib < IntegrationTest - def test_zza - f = -> { - assert_equal 2, 2 - } - f.() - end - - def test_b - test_foo('xxx') - end - - def test_foo(xxx) - assert_equal 'xxx', xxx - end -end diff --git a/test/integration/tips.md b/test/integration/tips.md new file mode 100644 index 00000000..4ed3eb7d --- /dev/null +++ b/test/integration/tips.md @@ -0,0 +1,35 @@ +# Tips and Tricks + +## Playing and experimenting + +Create a file called `test_local.rb` in this directory to have an integration test which is for your personal experiments and just playing with the integration test framework. `test_local.rb` is gitignored. + +## Speeding-up development + +Running an integration test requires configuring directories with CMake which can be quite slow. To speed-up development of integration tests consider doing the following steps: + +**Work with standalone tests** + +Instead of starting the runner, run just your integration test (`$ ruby test_your_test.rb`). This won't burden the execution with the others. + +**Export the environment variable `CPM_INTEGRATION_TEST_DIR` to some local directory** + +By default the framework generates a new temporary directory for each test run. If you override the temp directory to a specific one, rerunning the tests will work with the binary directories from the previous run and will improve the performance considerably. + +*NOTE HOWEVER* that in certain cases this may not be an option. Some tests might assert that certain artifacts in the temporary directory are missing but upon rerunning in an existing directory they will be there causing the test to fail. + +*ALSO NOTE* that this may silently affect reruns based on CMake caches from previous runs. If your test fails in peculiar ways on reruns, try a clean run. Always do a clean run before declaring a test a success. + +**Set `CPM_SOURCE_CACHE` even if the test doesn't require it** + +This is not a option for tests which explicitly check that there is no source cache. However certain tests may be indiferent to this. For such cases in development, you can add a setup function in the lines of: + +```ruby +def setup + ENV['CPM_SOURCE_CACHE'] = '/home/myself/.testcpmcache' +end +``` + +Then the packages from your test will be cached and not redownloaded every time which is a dramatic improvement in performance. + +*NOTE HOWEVER* that this may introduce subtle bugs. Always test without this dev-only addition, before declaring a test a success. From 754d6ed2c63b957a85d6932b4c2f920d071d3429 Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Fri, 14 Jan 2022 05:48:25 +0200 Subject: [PATCH 31/33] Typo --- test/integration/idiosyncrasies.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/idiosyncrasies.md b/test/integration/idiosyncrasies.md index c6d4c48a..bd4a0e96 100644 --- a/test/integration/idiosyncrasies.md +++ b/test/integration/idiosyncrasies.md @@ -12,7 +12,7 @@ class MyTest < IntegrationTest @x = 123 assert_equal 123, @x # Pass. @x is 123 end - def tese_something_else + def test_something_else assert_equal 123, @x # Fail! @x would be nil here end end @@ -28,7 +28,7 @@ class MyTest < IntegrationTest def test_something assert_equal 123, @x # Pass. @x is 123 thanks to setup end - def tese_something_else + def test_something_else assert_equal 123, @x # Pass. @x is 123 thanks to setup end end From 8aa2732145445099e64b5b13490867886bbfc910 Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Fri, 14 Jan 2022 05:49:45 +0200 Subject: [PATCH 32/33] Setup Ruby inistead of requiring windows-2022 --- .github/workflows/test.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 321b788a..50ed0b88 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,15 +13,18 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - # windows-latest is windows-2019 which carries a pretty old version of ruby (2.5) - # we need at least ruby 2.7 for the tests - # instead of dealing with installing a modern version of ruby on 2019, we'll just use windows-2022 here - os: [ubuntu-latest, windows-2022, macos-latest] + os: [ubuntu-latest, windows-latest, macos-latest] steps: - name: clone uses: actions/checkout@v2 + # ruby is required for the integration tests + - name: setup ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: 2.7 + - name: unit tests run: | cmake -Htest -Bbuild/test From 56f4d0c27c63b4b00c97e9206fa909b575e4c339 Mon Sep 17 00:00:00 2001 From: Borislav Stanimirov Date: Fri, 14 Jan 2022 06:12:21 +0200 Subject: [PATCH 33/33] Revert "Setup Ruby inistead of requiring windows-2022" This reverts commit 8aa2732145445099e64b5b13490867886bbfc910. --- .github/workflows/test.yml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 50ed0b88..321b788a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,18 +13,15 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, windows-latest, macos-latest] + # windows-latest is windows-2019 which carries a pretty old version of ruby (2.5) + # we need at least ruby 2.7 for the tests + # instead of dealing with installing a modern version of ruby on 2019, we'll just use windows-2022 here + os: [ubuntu-latest, windows-2022, macos-latest] steps: - name: clone uses: actions/checkout@v2 - # ruby is required for the integration tests - - name: setup ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: 2.7 - - name: unit tests run: | cmake -Htest -Bbuild/test