git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[RFC PATCH 2/3] test: add basic unit testing framework

From
Felipe Contreras <felipe.contreras@gmail.com>
Date
May 2, 2023, 04:11 UTC
Message-ID
<20230502041113.103385-3-felipe.contreras@gmail.com>
In-Reply-To
<20230502041113.103385-1-felipe.contreras@gmail.com>

Testing full git commands is helpful, but so it is to test specific functions, which is not easy to do from the shell, which is how the current testing framework is implemented.

A much more modern way to test specific functions is to write a binding for a modern language--like Ruby--and then use that binding on a modern testing framework using the same language.

This makes the testing framework much simpler, and also has the advantage that writing the Ruby bindings spots libification issues.

Moreover no forks are needed, and crashes are handled nicely due to Ruby's exception handling.

This is the best of all worlds.

This step doesn't add any actual unit tests, simply prepares the groundwork for writing them.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
 t/Makefile       | 13 +++++++++
 t/ruby/git.c     |  7 +++++
 t/ruby/testox.rb | 68 ++++++++++++++++++++++++++++++++++++++++++++++++
 t/unit-test.t    | 10 +++++++
 4 files changed, 98 insertions(+)
 create mode 100644 t/ruby/git.c
 create mode 100644 t/ruby/testox.rb
 create mode 100644 t/unit-test.t
diff --git a/t/Makefile b/t/Makefile
index d85e3e661d..4fdad529ab 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -47,6 +47,9 @@ CHAINLINT = '$(PERL_PATH_SQ)' chainlint.pl
 # scripts not to run the external "chainlint.pl" script themselves
 CHAINLINTSUPPRESS = GIT_TEST_EXT_CHAIN_LINT=0 && export GIT_TEST_EXT_CHAIN_LINT &&
 
+RUBY_LIBS = $(shell pkg-config --libs ruby-3.0)
+RUBY_CFLAGS = $(shell pkg-config --cflags ruby-3.0)
+
 all: $(DEFAULT_TEST_TARGET)
 
 test: pre-clean check-chainlint $(TEST_LINT)
@@ -65,6 +68,13 @@ prove: pre-clean check-chainlint $(TEST_LINT)
 $(T):
 	@echo "*** $@ ***"; '$(TEST_SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
 
+ruby/git.o: ruby/git.c
+	$(QUIET_CC)$(COMPILE.c) -o $@ $<
+
+ruby/git.so: CFLAGS := -fPIC $(RUBY_CFLAGS) -Ilib/ $(CFLAGS)
+ruby/git.so: LIBS := $(RUBY_LIBS) -Llib/ -lgit $(LIBS)
+ruby/git.so: ruby/git.o | lib/libgit.so
+
 lib/git.o: lib/git.c
 	$(QUIET_CC)$(COMPILE.c) -o $@ $<
 
@@ -74,6 +84,9 @@ lib/libgit.so: lib/git.o
 %.so::
 	$(QUIET_LINK)$(CC) -shared -o $@ $^ $(LIBS)
 
+test-unit: ruby/git.so
+	@LD_LIBRARY_PATH=lib/ ruby -Iruby unit-test.t
+
 pre-clean:
 	$(RM) -r '$(TEST_RESULTS_DIRECTORY_SQ)'
 
diff --git a/t/ruby/git.c b/t/ruby/git.c
new file mode 100644
index 0000000000..e75692c582
--- /dev/null
+++ b/t/ruby/git.c
@@ -0,0 +1,7 @@
+#include <ruby.h>
+#include <git.h>
+
+void Init_git(void)
+{
+	VALUE mod = rb_define_module("Git");
+}
diff --git a/t/ruby/testox.rb b/t/ruby/testox.rb
new file mode 100644
index 0000000000..1cc58a3ab3
--- /dev/null
+++ b/t/ruby/testox.rb
@@ -0,0 +1,68 @@
+# Copyright (c) 2023 Felipe Contreras
+
+require 'singleton'
+
+class TestOxException < StandardError
+  attr_reader :actual, :expected
+
+  def initialize(actual, expected)
+    @actual, @expected = actual, expected
+  end
+end
+
+class TestOx
+  include Singleton
+
+  @tests = []
+
+  class << self
+
+    def add(desc, &block)
+      @tests << [ desc, block ]
+    end
+
+    def run
+      success = true
+
+      puts '1..%d' % @tests.length
+
+      @tests.each_with_index do |(desc, block),i|
+        begin
+          instance.instance_exec(&block)
+          puts 'ok %d - %s' % [i + 1, desc]
+        rescue => e
+          success = false
+          puts 'not ok %d - %s' % [i + 1, desc]
+          if e.is_a?(TestOxException)
+            puts '# -%s' % e.expected
+            puts '# +%s' % e.actual
+          else
+            puts '# exception: %s' % e
+          end
+        end
+      end
+
+      return success
+    end
+
+  end
+
+  def assert(string)
+    raise string
+  end
+
+  def ok(bool)
+    raise TestOxException.new(bool.inspect, true) unless bool
+  end
+
+  def is(actual, expected)
+    raise TestOxException.new(actual, expected) unless actual == expected
+  end
+
+end
+
+def test(*args, &block)
+  TestOx.add(*args, &block)
+end
+
+at_exit { exit TestOx.run }
diff --git a/t/unit-test.t b/t/unit-test.t
new file mode 100644
index 0000000000..97d8a14ec3
--- /dev/null
+++ b/t/unit-test.t
@@ -0,0 +1,10 @@
+#!/bin/env ruby
+
+require 'testox'
+require 'git'
+
+test 'basic' do
+  ok(true)
+end
+
+# vim: ft=ruby
-- 
2.40.0+fc1
Previous: Felipe ContrerasNext: Felipe Contreras
Message 3 of 4 in “test: new testing framework for libification”
  1. 0/3 test: new testing framework for libificationFelipe Contreras, May 2, 2023
  2. 1/3 test: add simple shared libraryFelipe Contreras, May 2, 2023
  3. 2/3 test: add basic unit testing frameworkFelipe Contreras, May 2, 2023
  4. 3/3 test: add basic unit testFelipe Contreras, May 2, 2023

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.