From e150e710a94488f5e571e3b3db66306743267987 Mon Sep 17 00:00:00 2001 From: Noah Gibbs Date: Sat, 16 Dec 2023 16:29:09 +0000 Subject: [PATCH] Test that we can load and run various file formats. Test code cleanup. (#510) --- lacci/lib/shoes/errors.rb | 2 + lib/scarpe/cats_cradle.rb | 19 +++++ lib/scarpe/shoes_spec.rb | 29 +------ test/test_file_loader.rb | 161 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 184 insertions(+), 27 deletions(-) create mode 100644 test/test_file_loader.rb diff --git a/lacci/lib/shoes/errors.rb b/lacci/lib/shoes/errors.rb index c4844c0a6..9d2125823 100644 --- a/lacci/lib/shoes/errors.rb +++ b/lacci/lib/shoes/errors.rb @@ -29,4 +29,6 @@ class SingletonError < Shoes::Error; end class MultipleShoesSpecRunsError < Shoes::Error; end class UnsupportedFeature < Shoes::Error; end + + class BadFilenameError < Shoes::Error; end end diff --git a/lib/scarpe/cats_cradle.rb b/lib/scarpe/cats_cradle.rb index 97493fba2..62ff47fcd 100644 --- a/lib/scarpe/cats_cradle.rb +++ b/lib/scarpe/cats_cradle.rb @@ -96,6 +96,10 @@ def event_init end end + def fiber_start + @manager_fiber.resume + end + def event_promise(event) @event_promises[event] ||= ::Scarpe::Promise.new end @@ -109,6 +113,15 @@ def on_event(event, &block) @waiting_fibers << { promise: event_promise(event), fiber: f } end + def active_fiber(&block) + f = Fiber.new do + CCInstance.instance.instance_eval(&block) + end + p = ::Scarpe::Promise.new + p.fulfilled! + @waiting_fibers << { promise: p, fiber: f } + end + def wait(promise) raise(Scarpe::InvalidPromiseError, "Must supply a promise to wait!") unless promise.is_a?(::Scarpe::Promise) @@ -116,6 +129,12 @@ def wait(promise) @manager_fiber.transfer(promise) end + def yield + p = ::Scarpe::Promise.new + p.fulfilled! + @manager_fiber.transfer(p) + end + # This returns a promise, which can be waited on using wait() def fully_updated @wrangler.promise_dom_fully_updated diff --git a/lib/scarpe/shoes_spec.rb b/lib/scarpe/shoes_spec.rb index 2f5841011..65e783464 100644 --- a/lib/scarpe/shoes_spec.rb +++ b/lib/scarpe/shoes_spec.rb @@ -26,34 +26,13 @@ def self.run_shoes_spec_test_code(code, class_name: nil, test_name: nil) class_name ||= ENV["SHOES_MINITEST_CLASS_NAME"] || "TestShoesSpecCode" test_name ||= ENV["SHOES_MINITEST_METHOD_NAME"] || "test_shoes_spec" - require_relative "cats_cradle" - - # We want Minitest assertions available in the test code. - # But this will normally run in a subprocess. So we need - # to run Minitest tests and then export the results. - - # We create a test object based on CatsCradle, which will - # run the test as straight-line code, wait for appropriate - # events and generally make things well-behaved. But the - # test DSL isn't CatsCradle. It's based on Minitest and - # ShoesSpecTest (see below). - # - # Note that that means that using CatsCradle to "bounce" - # control back and forth for evented tricks isn't really - # an option. We may need to revisit all of this later... - test_obj = Object.new - class << test_obj - include Scarpe::CatsCradle - end - Scarpe::ShoesSpecTest.test_obj = test_obj - test_obj.instance_eval do + Scarpe::CCInstance.instance.instance_eval do event_init - on_heartbeat do + on_event(:next_heartbeat) do Minitest.run ARGV shut_down_shoes_code - Scarpe::ShoesSpecTest.test_obj = nil end end @@ -66,7 +45,6 @@ class << test_obj end end -# This is based on the CatsCradle proxies initially, but will diverge over time class Scarpe::ShoesSpecProxy attr_reader :obj attr_reader :linkable_id @@ -116,9 +94,6 @@ def respond_to_missing?(method_name, include_private = false) class Scarpe::ShoesSpecTest < Minitest::Test include Scarpe::Test::HTMLAssertions - class << self - attr_accessor :test_obj - end Shoes::Drawable.drawable_classes.each do |drawable_class| finder_name = drawable_class.dsl_name diff --git a/test/test_file_loader.rb b/test/test_file_loader.rb new file mode 100644 index 000000000..b7b12efc5 --- /dev/null +++ b/test/test_file_loader.rb @@ -0,0 +1,161 @@ +# frozen_string_literal: true + +require "test_helper" + +class TestScarpeFileLoader < ShoesSpecLoggedTest + self.logger_dir = File.expand_path("#{__dir__}/../logger") + + def file_load_test(app_code, test_code, extension) + test_method_name = self.name + test_class_name = self.class.name + + sspec_file = File.expand_path(File.join __dir__, "sspec.json") + File.unlink(sspec_file) if File.exist?(sspec_file) + + test_method_name = self.name + test_class_name = self.class.name + + with_tempfiles([ + [["scarpe_log_config", ".json"], JSON.dump(log_config_for_test)], + [["scarpe_app_#{test_method_name}", extension], app_code], + [["scarpe_test_#{test_method_name}", ".rb"], test_code || ""], + ]) do |log_config, app_filename, test_filename| + if test_code + test_env = "SHOES_SPEC_TEST=\"#{test_filename}\"" + else + test_env = "" + end + + cmd = <<~TEST_CMD.gsub("\n", " ").gsub(/\s+/, " ") + SCARPE_DISPLAY_SERVICE=wv_local + SCARPE_HTML_RENDERER=calzini + SCARPE_LOG_CONFIG=\"#{log_config}\" + #{test_env} + SHOES_MINITEST_EXPORT_FILE=\"#{sspec_file}\" + SHOES_MINITEST_CLASS_NAME=\"#{test_class_name}\" + SHOES_MINITEST_METHOD_NAME=\"#{test_method_name}\" + LOCALAPPDATA=\"#{Dir.tmpdir}\" + ruby #{SCARPE_EXE} --debug --dev \"#{app_filename}\" + TEST_CMD + + return system(cmd) + end + end + + # Run a test with an app, adding Shoes-Spec code so it will exit immediately + def file_load_app_test(contents, extension, timeout: 10.0, exit_immediately: true) + test_code = <<~TEST_CODE + timeout #{timeout} + #{exit_immediately ? "exit_on_first_heartbeat" : ""} + TEST_CODE + + file_load_test(contents, test_code, extension) + end + + def file_load_spec_test(contents, extension) + file_load_test(contents, nil, extension) + end + + def test_file_loader_simple_rb_file + with_tempfile("any_file_touch", "") do |tmp_location| + File.unlink(tmp_location) if File.exist?(tmp_location) + + assert_equal true, file_load_app_test(<<~CODE, ".rb") + Shoes.app do + button "OK" + File.write(#{tmp_location.inspect}, "foo") + end + CODE + + assert_equal "foo", File.read(tmp_location) + end + end + + def test_file_loader_simple_rb_file_runs + with_tempfile("any_file_touch", "") do |tmp_location| + File.unlink(tmp_location) if File.exist?(tmp_location) + + assert_equal true, file_load_app_test(<<~CODE, ".rb") + File.write(#{tmp_location.inspect}, "foo") + CODE + + assert_equal "foo", File.read(tmp_location) + end + end + + def test_file_loader_sspec_file_runs + with_tempfiles([["app_file_touch", ""], ["test_file_touch", ""]]) do |app_tmp_location, test_tmp_location| + File.unlink(app_tmp_location) if File.exist?(app_tmp_location) + File.unlink(test_tmp_location) if File.exist?(test_tmp_location) + + assert_equal true, file_load_spec_test(<<~CODE, ".sspec") + --- + ----------- app code + Shoes.app do + button "OK" + File.write(#{app_tmp_location.inspect}, "foo") + end + ----------- test code + assert_equal "OK", button().text + File.write(#{test_tmp_location.inspect}, "bar") + CODE + + assert_equal "foo", File.read(app_tmp_location) + assert_equal "bar", File.read(test_tmp_location) + end + end + + def test_file_loader_simple_app_with_shoes_spec_test_code_runs + with_tempfiles([ + ["app_file_touch", ""], + ["test_file_touch", ""], + ]) do |app_tmp_location, test_tmp_location| + test_code = <<~TEST_CODE + assert_equal "OK", button().text + File.write(#{test_tmp_location.inspect}, "quux") + TEST_CODE + + with_tempfile("shoes_spec_code", test_code) do |sspec_location| + + File.unlink(app_tmp_location) if File.exist?(app_tmp_location) + File.unlink(test_tmp_location) if File.exist?(test_tmp_location) + + assert_equal true, file_load_test(<<~CODE, test_code, ".rb") + Shoes.app do + button "OK" + File.write(#{app_tmp_location.inspect}, "baz") + end + CODE + + assert_equal "baz", File.read(app_tmp_location) + assert_equal "quux", File.read(test_tmp_location) + end + end + end + + def test_file_loader_sspec_app_raise_fails + assert_equal false, file_load_spec_test(<<~CODE, ".sspec") + --- + ----------- app code + Shoes.app do + button "OK" + raise "ERROR!" + end + ----------- test code + assert_equal "OK", button().text + CODE + end + + def test_file_loader_sspec_test_raise_fails + assert_equal false, file_load_spec_test(<<~CODE, ".sspec") + --- + ----------- app code + Shoes.app do + button "OK" + raise "ERROR!" + end + ----------- test code + assert_equal "OK", button().text + CODE + end +end