Skip to content
This repository has been archived by the owner on Oct 1, 2023. It is now read-only.

Unit Tests #28

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions lib/ffi/uctags.rb
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,9 @@ def ffi_const(...) = self.class.ffi_const(...)
#
# Each element is a 3-tuple of
# 1. a construct member queue
# 2. a proc (or equivalent)
# 2. a proc (or equivalent) callback
# 3. the namespace in which this construct should define under
# When ready, the proc is called with the populated member list (as a single arg) and the namespace.
# When ready, the callback is called with the populated member list (as a single arg) and the namespace.
#
# @return [Array[[Array[untyped], ^(Array[untyped], String?) -> void, String?]]
# @see #new_construct
Expand Down Expand Up @@ -157,7 +157,7 @@ def initialize(library_name)
# Prepare to build a new construct. This method is designed for every new construct to call near the beginning.
#
# `Array#slice!` off topmost entries in the {#stack} according to `@fields`.
# Invoke the procs of the removed entries in reverse order to ensure these previous constructs flush through.
# Invoke the callbacks of the removed entries in reverse order to ensure these previous constructs flush through.
# Finally, if given a block, start a new stack entry with it.
#
# {.call} processes a composite construct (e.g., a function or struct) as a sequence of consecutive components,
Expand Down Expand Up @@ -187,7 +187,7 @@ def new_construct(&blk)
else
0
end
if (prev = stack.slice!(depth..)) and not prev.empty?
if (prev = stack.slice!(depth..)) and !prev.empty?
puts "\tflushing #{prev.size} stack entries" if $VERBOSE
prev.reverse_each do|members, a_proc, namespace|
if $VERBOSE
Expand Down
18 changes: 18 additions & 0 deletions test/ffi/uctags/.unit_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true
require 'minitest/autorun'
require 'ffi/uctags'
class FFI::UCtags::UnitTest < Minitest::Spec

LIBRARY = FFI::Library::LIBC

# Subclass with patches for assisting testing
class UCtags < FFI::UCtags
def self.new = super(LIBRARY)
attr_accessor :fields
end

before do
@instance = UCtags.new
end

end
10 changes: 10 additions & 0 deletions test/ffi/uctags/.unit_test.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class FFI::UCtags::UnitTest < Minitest::Spec
LIBRARY: String

class UCtags < FFI::UCtags
public def self.new: () -> instance
attr_accessor fields: Hash[String, String]
end

@instance: UCtags
end
78 changes: 78 additions & 0 deletions test/ffi/uctags/new_construct_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# frozen_string_literal: true
require_relative '.unit_test'
class FFI::UCtags::UnitTest
describe '.new_construct' do

describe 'processing the new entry' do
it 'does not add a new stack entry if no block given' do
@instance.new_construct
assert_empty @instance.stack
end

it 'adds a new stack entry if block given' do
callback_tag = rand
@instance.new_construct { callback_tag }
assert_pattern do
#noinspection RubyCaseWithoutElseBlockInspection
case @instance.stack
in [[ [], Proc => callback, nil ]]
assert_same callback_tag, callback.()
end
end
end
end

describe 'flushing previously-queued entries' do
EMPTY_STACK_ENTRY = [[], proc {}, nil]

[
nil, # 0
'parent', # 1
'grandparent::parent' # 2
].each_with_index do|namespace, expected_size|
(namespace ? %w[struct union] : %w[(N/A)]).each do |namespace_type|
it "flushes until #{expected_size} stack entries if `@fields[#{namespace_type}]` has #{expected_size} names" do
@instance.stack.push(EMPTY_STACK_ENTRY, EMPTY_STACK_ENTRY)
@instance.fields[namespace_type] = namespace
@instance.new_construct
assert_same expected_size, @instance.stack.size
end

it "identifies the innermost namespace from `@fields[#{namespace_type}]`" do
@instance.fields[namespace_type] = namespace
return_val = @instance.new_construct {}
queued_namespace = @instance.stack.last.last
# Future-proofing: “DEPRECATED: Use assert_nil if expecting nil from [file:line]. This will fail in Minitest 6.”
if namespace
assert_equal 'parent', return_val
assert_equal 'parent', queued_namespace
else # nil
assert_nil return_val
assert_nil queued_namespace
end
end
end
end

it 'runs the queued callback with the queued args' do
args = [rand]
namespace = rand.to_s
callback = Minitest::Mock.new.expect(:call, nil, [args, namespace])
@instance.stack << [args, callback, namespace]
@instance.new_construct
assert_mock callback
end
end


=begin
# @return [String?]
# The name of the namespace this construct will define under as parsed from `@fields` (see {#process})
def (&blk)
stack << [[], blk, prev_namespace]
prev_namespace
end
=end
end

end
25 changes: 25 additions & 0 deletions test/ffi/uctags/self_new_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# frozen_string_literal: true
require_relative '.unit_test'
class FFI::UCtags::UnitTest
describe 'self.new' do

it 'loads the given library' do
assert_includes @instance.library.ffi_libraries.map(&:name), LIBRARY
end

it 'initializes instance variables' do
{
composite_types: Hash,
composite_typedefs: Hash,
composite_namespacing: Hash,
stack: Array,
fields: Hash
}.each do|attr, klass|
value = @instance.public_send attr
assert_kind_of klass, value
assert_empty value
end
end

end
end