{"thread":{"id":"59680","subject":"[RFC PATCH 0/3] test: new testing framework for libification","startedAt":"2023-05-02T04:11:20Z","lastAt":"2023-05-02T04:11:27Z","messageCount":4,"participants":["Felipe Contreras"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"476403","messageId":"20230502041113.103385-1-felipe.contreras@gmail.com","threadId":"59680","inReplyTo":null,"subject":"[RFC PATCH 0/3] test: new testing framework for libification","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2023-05-02T04:11:10Z","receivedAt":"2023-05-02T04:11:20Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Calvin Wan proposed a new testing framework for unit tests written in C,\nand while I agree unit tests can be useful, there's no need to write\neverything in C.\n\nI'm of the belief that one should pick the language that best fit the\ntask, not write everything in the language that you happen to be already\nusing.\n\nA modern testing framework written in Ruby is much simpler, and more\npowerful than whatever can be written in C. This patch series shows an\nexample of what can be achieved with just 68 lines of Ruby code, plus a\ngit library with Ruby bindings.\n\n[1] https://lore.kernel.org/git/20230427175007.902278-1-calvinwan@google.com/\n\nFelipe Contreras (3):\n  test: add simple shared library\n  test: add basic unit testing framework\n  test: add basic unit test\n\n .gitignore       |  1 +\n t/Makefile       | 22 ++++++++++++++++\n t/lib/git.c      |  6 +++++\n t/lib/git.h      |  6 +++++\n t/ruby/git.c     | 13 +++++++++\n t/ruby/testox.rb | 68 ++++++++++++++++++++++++++++++++++++++++++++++++\n t/unit-test.t    | 14 ++++++++++\n 7 files changed, 130 insertions(+)\n create mode 100644 t/lib/git.c\n create mode 100644 t/lib/git.h\n create mode 100644 t/ruby/git.c\n create mode 100644 t/ruby/testox.rb\n create mode 100644 t/unit-test.t\n\n-- \n2.40.0+fc1\n\n"},{"id":"476404","messageId":"20230502041113.103385-2-felipe.contreras@gmail.com","threadId":"59680","inReplyTo":"20230502041113.103385-1-felipe.contreras@gmail.com","subject":"[RFC PATCH 1/3] test: add simple shared library","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2023-05-02T04:11:11Z","receivedAt":"2023-05-02T04:11:24Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"It doesn't contain anything, it's just preparation for further commits.\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n .gitignore  | 1 +\n t/Makefile  | 9 +++++++++\n t/lib/git.c | 1 +\n t/lib/git.h | 4 ++++\n 4 files changed, 15 insertions(+)\n create mode 100644 t/lib/git.c\n create mode 100644 t/lib/git.h\n\ndiff --git a/.gitignore b/.gitignore\nindex e875c59054..c249487b9d 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -237,6 +237,7 @@\n *.iobj\n *.ipdb\n *.dll\n+*.so\n .vs/\n Debug/\n Release/\ndiff --git a/t/Makefile b/t/Makefile\nindex 3e00cdd801..d85e3e661d 100644\n--- a/t/Makefile\n+++ b/t/Makefile\n@@ -65,6 +65,15 @@ prove: pre-clean check-chainlint $(TEST_LINT)\n $(T):\n \t@echo \"*** $@ ***\"; '$(TEST_SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)\n \n+lib/git.o: lib/git.c\n+\t$(QUIET_CC)$(COMPILE.c) -o $@ $<\n+\n+lib/libgit.so: CFLAGS := -fPIC -Ilib/ $(CFLAGS)\n+lib/libgit.so: lib/git.o\n+\n+%.so::\n+\t$(QUIET_LINK)$(CC) -shared -o $@ $^ $(LIBS)\n+\n pre-clean:\n \t$(RM) -r '$(TEST_RESULTS_DIRECTORY_SQ)'\n \ndiff --git a/t/lib/git.c b/t/lib/git.c\nnew file mode 100644\nindex 0000000000..40d96ee354\n--- /dev/null\n+++ b/t/lib/git.c\n@@ -0,0 +1 @@\n+#include <git.h>\ndiff --git a/t/lib/git.h b/t/lib/git.h\nnew file mode 100644\nindex 0000000000..58a28085f5\n--- /dev/null\n+++ b/t/lib/git.h\n@@ -0,0 +1,4 @@\n+#ifndef GIT_H\n+#define GIT_H\n+\n+#endif /* GIT_H */\n-- \n2.40.0+fc1\n\n"},{"id":"476405","messageId":"20230502041113.103385-3-felipe.contreras@gmail.com","threadId":"59680","inReplyTo":"20230502041113.103385-1-felipe.contreras@gmail.com","subject":"[RFC PATCH 2/3] test: add basic unit testing framework","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2023-05-02T04:11:12Z","receivedAt":"2023-05-02T04:11:26Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Testing full git commands is helpful, but so it is to test specific\nfunctions, which is not easy to do from the shell, which is how the\ncurrent testing framework is implemented.\n\nA much more modern way to test specific functions is to write a binding\nfor a modern language--like Ruby--and then use that binding on a modern\ntesting framework using the same language.\n\nThis makes the testing framework much simpler, and also has the\nadvantage that writing the Ruby bindings spots libification issues.\n\nMoreover no forks are needed, and crashes are handled nicely due to\nRuby's exception handling.\n\nThis is the best of all worlds.\n\nThis step doesn't add any actual unit tests, simply prepares the\ngroundwork for writing them.\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n t/Makefile       | 13 +++++++++\n t/ruby/git.c     |  7 +++++\n t/ruby/testox.rb | 68 ++++++++++++++++++++++++++++++++++++++++++++++++\n t/unit-test.t    | 10 +++++++\n 4 files changed, 98 insertions(+)\n create mode 100644 t/ruby/git.c\n create mode 100644 t/ruby/testox.rb\n create mode 100644 t/unit-test.t\n\ndiff --git a/t/Makefile b/t/Makefile\nindex d85e3e661d..4fdad529ab 100644\n--- a/t/Makefile\n+++ b/t/Makefile\n@@ -47,6 +47,9 @@ CHAINLINT = '$(PERL_PATH_SQ)' chainlint.pl\n # scripts not to run the external \"chainlint.pl\" script themselves\n CHAINLINTSUPPRESS = GIT_TEST_EXT_CHAIN_LINT=0 && export GIT_TEST_EXT_CHAIN_LINT &&\n \n+RUBY_LIBS = $(shell pkg-config --libs ruby-3.0)\n+RUBY_CFLAGS = $(shell pkg-config --cflags ruby-3.0)\n+\n all: $(DEFAULT_TEST_TARGET)\n \n test: pre-clean check-chainlint $(TEST_LINT)\n@@ -65,6 +68,13 @@ prove: pre-clean check-chainlint $(TEST_LINT)\n $(T):\n \t@echo \"*** $@ ***\"; '$(TEST_SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)\n \n+ruby/git.o: ruby/git.c\n+\t$(QUIET_CC)$(COMPILE.c) -o $@ $<\n+\n+ruby/git.so: CFLAGS := -fPIC $(RUBY_CFLAGS) -Ilib/ $(CFLAGS)\n+ruby/git.so: LIBS := $(RUBY_LIBS) -Llib/ -lgit $(LIBS)\n+ruby/git.so: ruby/git.o | lib/libgit.so\n+\n lib/git.o: lib/git.c\n \t$(QUIET_CC)$(COMPILE.c) -o $@ $<\n \n@@ -74,6 +84,9 @@ lib/libgit.so: lib/git.o\n %.so::\n \t$(QUIET_LINK)$(CC) -shared -o $@ $^ $(LIBS)\n \n+test-unit: ruby/git.so\n+\t@LD_LIBRARY_PATH=lib/ ruby -Iruby unit-test.t\n+\n pre-clean:\n \t$(RM) -r '$(TEST_RESULTS_DIRECTORY_SQ)'\n \ndiff --git a/t/ruby/git.c b/t/ruby/git.c\nnew file mode 100644\nindex 0000000000..e75692c582\n--- /dev/null\n+++ b/t/ruby/git.c\n@@ -0,0 +1,7 @@\n+#include <ruby.h>\n+#include <git.h>\n+\n+void Init_git(void)\n+{\n+\tVALUE mod = rb_define_module(\"Git\");\n+}\ndiff --git a/t/ruby/testox.rb b/t/ruby/testox.rb\nnew file mode 100644\nindex 0000000000..1cc58a3ab3\n--- /dev/null\n+++ b/t/ruby/testox.rb\n@@ -0,0 +1,68 @@\n+# Copyright (c) 2023 Felipe Contreras\n+\n+require 'singleton'\n+\n+class TestOxException < StandardError\n+  attr_reader :actual, :expected\n+\n+  def initialize(actual, expected)\n+    @actual, @expected = actual, expected\n+  end\n+end\n+\n+class TestOx\n+  include Singleton\n+\n+  @tests = []\n+\n+  class << self\n+\n+    def add(desc, &block)\n+      @tests << [ desc, block ]\n+    end\n+\n+    def run\n+      success = true\n+\n+      puts '1..%d' % @tests.length\n+\n+      @tests.each_with_index do |(desc, block),i|\n+        begin\n+          instance.instance_exec(&block)\n+          puts 'ok %d - %s' % [i + 1, desc]\n+        rescue => e\n+          success = false\n+          puts 'not ok %d - %s' % [i + 1, desc]\n+          if e.is_a?(TestOxException)\n+            puts '# -%s' % e.expected\n+            puts '# +%s' % e.actual\n+          else\n+            puts '# exception: %s' % e\n+          end\n+        end\n+      end\n+\n+      return success\n+    end\n+\n+  end\n+\n+  def assert(string)\n+    raise string\n+  end\n+\n+  def ok(bool)\n+    raise TestOxException.new(bool.inspect, true) unless bool\n+  end\n+\n+  def is(actual, expected)\n+    raise TestOxException.new(actual, expected) unless actual == expected\n+  end\n+\n+end\n+\n+def test(*args, &block)\n+  TestOx.add(*args, &block)\n+end\n+\n+at_exit { exit TestOx.run }\ndiff --git a/t/unit-test.t b/t/unit-test.t\nnew file mode 100644\nindex 0000000000..97d8a14ec3\n--- /dev/null\n+++ b/t/unit-test.t\n@@ -0,0 +1,10 @@\n+#!/bin/env ruby\n+\n+require 'testox'\n+require 'git'\n+\n+test 'basic' do\n+  ok(true)\n+end\n+\n+# vim: ft=ruby\n-- \n2.40.0+fc1\n\n"},{"id":"476406","messageId":"20230502041113.103385-4-felipe.contreras@gmail.com","threadId":"59680","inReplyTo":"20230502041113.103385-1-felipe.contreras@gmail.com","subject":"[RFC PATCH 3/3] test: add basic unit test","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2023-05-02T04:11:13Z","receivedAt":"2023-05-02T04:11:27Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"We simply add that function, and test that function.\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n t/lib/git.c   | 5 +++++\n t/lib/git.h   | 2 ++\n t/ruby/git.c  | 6 ++++++\n t/unit-test.t | 4 ++++\n 4 files changed, 17 insertions(+)\n\ndiff --git a/t/lib/git.c b/t/lib/git.c\nindex 40d96ee354..54206171cb 100644\n--- a/t/lib/git.c\n+++ b/t/lib/git.c\n@@ -1 +1,6 @@\n #include <git.h>\n+\n+int ok(void)\n+{\n+\treturn 1;\n+}\ndiff --git a/t/lib/git.h b/t/lib/git.h\nindex 58a28085f5..dbbac9e406 100644\n--- a/t/lib/git.h\n+++ b/t/lib/git.h\n@@ -1,4 +1,6 @@\n #ifndef GIT_H\n #define GIT_H\n \n+int ok(void);\n+\n #endif /* GIT_H */\ndiff --git a/t/ruby/git.c b/t/ruby/git.c\nindex e75692c582..e173dfe03d 100644\n--- a/t/ruby/git.c\n+++ b/t/ruby/git.c\n@@ -1,7 +1,13 @@\n #include <ruby.h>\n #include <git.h>\n \n+VALUE rb_ok(VALUE self)\n+{\n+\treturn RTEST(ok());\n+}\n+\n void Init_git(void)\n {\n \tVALUE mod = rb_define_module(\"Git\");\n+\trb_define_singleton_method(mod, \"ok\", rb_ok, 0);\n }\ndiff --git a/t/unit-test.t b/t/unit-test.t\nindex 97d8a14ec3..27303f9c49 100644\n--- a/t/unit-test.t\n+++ b/t/unit-test.t\n@@ -7,4 +7,8 @@ test 'basic' do\n   ok(true)\n end\n \n+test 'ok' do\n+  ok(Git::ok())\n+end\n+\n # vim: ft=ruby\n-- \n2.40.0+fc1\n\n"}]}