rsi/objc/objc_test.lua

856 lines
24 KiB
Lua
Raw Permalink Normal View History

2023-08-27 12:32:38 +00:00
local glue = require'glue'
local objc = require'objc'
local ffi = require'ffi'
local pp = require'pp'
io.stdout:setvbuf'no'
io.stderr:setvbuf'no'
setmetatable(_G, {__index = objc})
--test options
local subprocess = false --run each bridgesupport test in a subprocess
objc.debug.lazyfuncs = true
objc.debug.checkredef = false
objc.debug.printcdecl = false
objc.debug.loaddeps = false
objc.debug.loadtypes = true
local bsdir = '_bridgesupport' --path where *.bridgesupport files are on Windows (tree or flat doesn't matter)
local luajit = ffi.os == 'Windows' and 'luajit' or './luajit' --luajit command for subprocess running
if ffi.os == 'OSX' then
objc.load'Foundation'
pool = NSAutoreleasePool:new()
end
--test helpers
local function printf(...)
print(string.format(...))
end
local function hr()
print(('-'):rep(80))
end
local n = 0
local function genname(prefix)
if not prefix then return genname'MyClass' end
n = n + 1
return prefix..n
end
local function errpcall(patt, ...) --pcall that should fail with a specific message
local ok, err = pcall(...)
assert(not ok)
assert(err:find(patt))
end
--test namespace
local test = {} --{name = test_func}
local eyetest = {} --{name = test_func}
local demo = {}
local tests = {tests = test, ['eye tests'] = eyetest, demos = demo}
function test.parsing()
assert(stype_ctype('[8^c]', 'arr') == 'char *arr[8]') --array of pointers
assert(stype_ctype('^[8c]', 'arr') == 'char (*arr)[8]') --pointer to array
assert(stype_ctype('[8[4c]]', 'arr') == 'char arr[8][4]') --multi-dim. array
assert(stype_ctype('[3^[8^c]]', 'arr') == 'char *(*arr[3])[8]')
assert(stype_ctype('{?="x"i"y"i""(?="ux"I"uy"I)}', nil, 'cdef') ==
'struct {\n\tint x;\n\tint y;\n\tunion {\n\t\tunsigned int ux;\n\t\tunsigned int uy;\n\t};\n}'
) --nested unnamed anonymous structs
local function mtype_ctype(mtype, ...)
return ftype_ctype(mtype_ftype(mtype), ...)
end
assert(mtype_ctype('@"Class"@:{_NSRect={_NSPoint=ff}{_NSSize=ff}}^{?}^?', 'globalFunction') ==
'id globalFunction (id, SEL, struct _NSRect, void *, void *)') --unseparated method args
assert(mtype_ctype('{_NSPoint=ff}iii', nil, true) ==
'void (*) (int, int, int)') --struct return value not supported
assert(mtype_ctype('iii{_NSPoint=ff}ii', nil, true) ==
'int (*) (int, int)') --pass-by-value struct not supported, stop at first encounter
assert(mtype_ctype('{_NSPoint=ff}ii{_NSPoint=ff}i', nil, true) ==
'void (*) (int, int)') --combined case
end
function eyetest.indent()
--_NXEvent (test indent for nested unnamed anonymous structs)
print(stype_ctype('{?="type"i"location"{?="x"i"y"i}"time"Q"flags"i"window"I"service_id"Q"ext_pid"i"data"(?="mouse"{?="subx"C"suby"C"eventNum"s"click"i"pressure"C"buttonNumber"C"subType"C"reserved2"C"reserved3"i"tablet"(?="point"{_NXTabletPointData="x"i"y"i"z"i"buttons"S"pressure"S"tilt"{?="x"s"y"s}"rotation"S"tangentialPressure"s"deviceID"S"vendor1"s"vendor2"s"vendor3"s}"proximity"{_NXTabletProximityData="vendorID"S"tabletID"S"pointerID"S"deviceID"S"systemTabletID"S"vendorPointerType"S"pointerSerialNumber"I"uniqueID"Q"capabilityMask"I"pointerType"C"enterProximity"C"reserved1"s})}"mouseMove"{?="dx"i"dy"i"subx"C"suby"C"subType"C"reserved1"C"reserved2"i"tablet"(?="point"{_NXTabletPointData="x"i"y"i"z"i"buttons"S"pressure"S"tilt"{?="x"s"y"s}"rotation"S"tangentialPressure"s"deviceID"S"vendor1"s"vendor2"s"vendor3"s}"proximity"{_NXTabletProximityData="vendorID"S"tabletID"S"pointerID"S"deviceID"S"systemTabletID"S"vendorPointerType"S"pointerSerialNumber"I"uniqueID"Q"capabilityMask"I"pointerType"C"enterProximity"C"reserved1"s})}"key"{?="origCharSet"S"repeat"s"charSet"S"charCode"S"keyCode"S"origCharCode"S"reserved1"i"keyboardType"I"reserved2"i"reserved3"i"reserved4"i"reserved5"[4i]}"tracking"{?="reserved"s"eventNum"s"trackingNum"i"userData"i"reserved1"i"reserved2"i"reserved3"i"reserved4"i"reserved5"i"reserved6"[4i]}"scrollWheel"{?="deltaAxis1"s"deltaAxis2"s"deltaAxis3"s"reserved1"s"fixedDeltaAxis1"i"fixedDeltaAxis2"i"fixedDeltaAxis3"i"pointDeltaAxis1"i"pointDeltaAxis2"i"pointDeltaAxis3"i"reserved8"[4i]}"zoom"{?="deltaAxis1"s"deltaAxis2"s"deltaAxis3"s"reserved1"s"fixedDeltaAxis1"i"fixedDeltaAxis2"i"fixedDeltaAxis3"i"pointDeltaAxis1"i"pointDeltaAxis2"i"pointDeltaAxis3"i"reserved8"[4i]}"compound"{?="reserved"s"subType"s"misc"(?="F"[11f]"L"[11i]"S"[22s]"C"[44c])}"tablet"{?="x"i"y"i"z"i"buttons"S"pressure"S"tilt"{?="x"s"y"s}"rotation"S"tangentialPressure"s"deviceID"S"vendor1"s"vendor2"s"vendor3"s"reserved"[4i]}"proximity"{?="vendorID"S"tabletID"S"pointerID"S"deviceID"S"systemTabletID"S"vendorPointerType"S"pointerSerialNumber"I"uniqueID"Q"capabilityMask"I"pointerType"C"enterProximity"C"reserved1"s"reserved2"[4i]})}', nil, 'cdef'))
end
function eyetest.tostring()
print(objc.NSNumber:numberWithDouble(0))
print(objc.NSString:alloc():initWithUTF8String'') --empty string = NSFConstantString with hi address
print(objc.NSString:alloc():initWithUTF8String'asdjfah') --real object
end
--test parsing of bridgesupport files.
--works on Windows too - just copy your bridgesupport files into whatever you set `bsdir` above.
function eyetest.bridgesupport(bsfile)
local function list_func(cmd)
return function()
return coroutine.wrap(function()
local f = io.popen(cmd)
for s in f:lines() do
coroutine.yield(s)
end
f:close()
end)
end
end
local bsfiles
if ffi.os == 'Windows' then
bsfiles = list_func('dir /B /S '..bsdir..'\\*.bridgesupport')
elseif ffi.os == 'OSX' then
bsfiles = list_func('find /System/Library/frameworks -name \'*.bridgesupport\'')
else
error'can\'t run on this OS'
end
local loaded = {}
local n = 0
local objc_load = objc.debug.load_framework --keep it, we'll patch it
function objc.debug.load_framework(path) --either `name.bridgesupport` or `name.framework` or `name.framework/name`
local name
if path:match'%.bridgesupport$' then
name = path:match'([^/\\]+)%.bridgesupport$'
else
name = path:match'/([^/]+)%.framework$' or path:match'([^/]+)$'
if ffi.os == 'Windows' then
path = bsdir..'\\'..name..'.bridgesupport'
else
path = path .. '/Resources/BridgeSupport/' .. name .. '.bridgesupport'
end
end
if loaded[name] then return end
loaded[name] = true
if glue.canopen(path) then
if ffi.os == 'OSX' then
--load the dylib first (needed for function aliases)
local dpath = path:gsub('Resources/BridgeSupport/.*$', name)
if glue.canopen(dpath) then
pcall(ffi.load, dpath, true)
end
--load the dylib with inlines first (needed for function aliases)
local dpath = path:gsub('bridgesupport$', 'dylib')
if glue.canopen(dpath) then
pcall(ffi.load, dpath, true)
end
end
objc.debug.load_bridgesupport(path)
n = n + 1
--print(n, '', name)
else
print('! not found', name, path)
end
end
local function status()
pp('errors', objc.debug.errcount)
print('globals: '..objc.debug.cnames.global[1])
print('structs: '..objc.debug.cnames.struct[1])
end
if bsfile then
objc.debug.load_framework(bsfile)
else
for bsfile in bsfiles() do
if bsfile:match'Python' then
print('skipping '..bsfile) --python bridgesupport files are non-standard and deprecated
else
--print(); print(bsfile); print(('='):rep(80))
if subprocess then
os.execute(luajit..' '..arg[0]..' bridgesupport '..bsfile)
else
objc.debug.load_framework(bsfile)
end
end
end
status()
end
objc.debug.load_framework = objc_load --put it back
end
function test.selectors()
assert(tostring(SEL'se_lec_tor') == 'se:lec:tor')
assert(tostring(SEL'se_lec_tor_') == 'se:lec:tor:')
assert(tostring(SEL'__se_lec_tor') == '__se:lec:tor')
assert(tostring(SEL'__se:lec:tor:') == '__se:lec:tor:')
end
--class, superclass, metaclass, class protocols
function test.class()
--arg. checking
errpcall('already', class, 'NSObject', 'NSString')
errpcall('superclass', class, genname(), 'MyUnknownClass')
errpcall('protocol', class, genname(), 'NSObject <MyUnknownProtocol>')
--class overloaded constructors
local cls = class('MyClassX', false) --root class
assert(classname(cls) == 'MyClassX')
assert(not superclass(cls))
--derived class
local cls = class(genname(), 'NSArray')
assert(isa(cls, 'NSArray'))
--derived + conforming
local cls = class(genname(), 'NSArray <NSStreamDelegate, NSLocking>')
assert(isa(cls, 'NSArray'))
assert(conforms(cls, 'NSStreamDelegate'))
assert(conforms(cls, 'NSLocking'))
local t = {0}
for proto in protocols(cls) do
t[proto:name()] = true
t[1] = t[1] + 1
end
assert(t[1] == 2)
assert(t.NSStreamDelegate)
assert(t.NSLocking)
--class hierarchy queries
assert(superclass(cls) == NSArray)
assert(metaclass(cls))
assert(superclass(metaclass(cls)) == metaclass'NSArray')
assert(metaclass(superclass(cls)) == metaclass'NSArray')
assert(metaclass(metaclass(cls)) == nil)
assert(isa(cls, 'NSObject'))
assert(ismetaclass(metaclass(cls)))
assert(isclass(cls))
assert(not ismetaclass(cls))
assert(not isobj(cls))
assert(isclass(metaclass(cls)))
local obj = cls:new()
assert(isobj(obj))
assert(not isclass(obj))
end
function test.refcount()
local cls = class(genname(), 'NSObject')
local inst, inst2, inst3
inst = cls:new()
assert(inst:retainCount() == 1)
inst2 = inst:retain() --same class, new cdata, new reference
assert(inst:retainCount() == 2)
inst3 = inst:retain()
assert(inst:retainCount() == 3)
inst3 = nil --release() on gc
collectgarbage()
assert(inst:retainCount() == 2)
inst3 = inst:retain()
assert(inst:retainCount() == 3)
inst:release() --manual release()
assert(inst:retainCount() == 2)
inst = nil --object already disowned by inst, refcount should not decrease
collectgarbage()
assert(inst2:retainCount() == 2)
inst, inst2, inst3 = nil
collectgarbage()
end
function test.luavars()
local cls = class(genname(), 'NSObject')
--class vars
cls.myclassvar = 'doh1'
assert(cls.myclassvar == 'doh1') --intialized
cls.myclassvar = 'doh'
assert(cls.myclassvar == 'doh') --updated
--inst vars
local inst = cls:new()
inst.myinstvar = 'DOH1'
assert(inst.myinstvar == 'DOH1') --initialized
inst.myinstvar = 'DOH'
assert(inst.myinstvar == 'DOH') --updated
--class vars from instances
assert(inst.myclassvar == 'doh') --class vars are readable from instances
inst.myclassvar = 'doh2'
assert(cls.myclassvar == 'doh2') --and they can be updated from instances
assert(inst.myclassvar == 'doh2')
--soft ref counting
local inst2 = inst:retain()
assert(inst.myinstvar == 'DOH') --2 refs
inst = nil
collectgarbage()
assert(inst2.myinstvar == 'DOH') --1 ref
inst2:release() --0 refs; instance gone, vars gone (no way to test, memory was freed)
assert(cls.myclassvar == 'doh2') --class vars still there
local i = 0
function NSObject:myMethod() i = i + 1 end
local str = toobj'hello' --create a NSString instance, which is a NSObject
str:myMethod() --instance method (str passed as self)
objc.NSString:myMethod() --class method (NSString passed as self)
assert(i == 2)
function NSObject:myMethod() i = i - 1 end --override
str:myMethod() --instance method (str passed as self)
objc.NSString:myMethod() --class method (NSString passed as self)
assert(i == 0)
end
function test.override()
objc.debug.logtopics.addmethod = true
local cls = class(genname(), 'NSObject')
local metacls = metaclass(cls)
local obj = cls:new()
local instdesc = 'hello-instance'
local classdesc = 'hello-class'
function metacls:description() --override the class method
return classdesc --note: we can return the string directly.
end
function cls:description() --override the instance method
return instdesc --note: we can return the string directly.
end
assert(objc.tolua(cls:description()) == classdesc) --class method was overriden
assert(objc.tolua(obj:description()) == instdesc) --instance method was overriden and it's different
--subclass and test again
local cls2 = class(genname(), cls)
local metacls2 = metaclass(cls2)
local obj2 = cls2:new()
function metacls2:description() --override the class method
return objc.callsuper(self, 'description'):UTF8String() .. '2'
end
function cls2:description(callsuper) --override the instance method
return objc.callsuper(self, 'description'):UTF8String() .. '2'
end
assert(objc.tolua(cls2:description()) == classdesc..'2') --class method was overriden
assert(objc.tolua(obj2:description()) == instdesc..'2') --instance method was overriden and it's different
end
function test.ivars()
local obj = NSDocInfo:new()
if ffi.abi'64bit' then
assert(ffi.typeof(obj.time) == ffi.typeof'long long')
else
assert(type(obj.time) == 'number')
end
assert(type(obj.mode) == 'number') --unsigned short
assert(ffi.typeof(obj.flags) == ffi.typeof(obj.flags)) --anonymous struct (assert that it was cached)
obj.time = 123
assert(obj.time == 123)
assert(obj.flags.isDir == 0)
obj.flags.isDir = 3 --1 bit
assert(obj.flags.isDir == 1) --1 bit was set (so this is not a luavar or anything)
end
test.properties = objc.with_properties(function()
--TODO: find another class with r/w properties. NSProgress is not public on 10.7.
local pr = NSProgress:progressWithTotalUnitCount(123)
assert(pr.totalUnitCount == 123) --as initialized
pr.totalUnitCount = 321 --read/write property
assert(pr.totalUnitCount == 321)
assert(not pcall(function() pr.indeterminate = true end)) --attempt to set read-only property
assert(pr.indeterminate == false)
end)
local timebase, last_time
function timediff()
objc.load'System'
local time
time = mach_absolute_time()
if not timebase then
timebase = ffi.new'mach_timebase_info_data_t'
mach_timebase_info(timebase)
end
local d = tonumber(time - (last_time or 0)) * timebase.numer / timebase.denom / 10^9
last_time = time
return d
end
function test.blocks()
--objc.debug.logtopics.block = true
local times = 20000
timediff()
--take 1: creating blocks in inner loops with automatic memory management of blocks.
local s = NSString:alloc():initWithUTF8String'line1\nline2\nline3'
for i=1,times do
local t = {}
--note: the signature of the block arg for enumerateLinesUsingBlock was taken from bridgesupport.
s:enumerateLinesUsingBlock(function(line, pstop)
t[#t+1] = line:UTF8String()
if #t == 2 then --stop at line 2
pstop[0] = 1
end
end)
assert(#t == 2)
assert(t[1] == 'line1')
assert(t[2] == 'line2')
--note: callbacks are slow, expensive to create, and limited in number. we have to release them often!
if i % 200 == 0 then
collectgarbage()
end
end
printf('take 1: block in loop (%d times): %4.2fs', times, timediff())
--take 2: creating a single block in the outer loop (we must give its type).
local t
local blk = toarg(NSString, 'enumerateLinesUsingBlock', 1, function(line, pstop)
t[#t+1] = line:UTF8String()
if #t == 2 then --stop at line 2
pstop[0] = 1
end
end)
local s = NSString:alloc():initWithUTF8String'line1\nline2\nline3'
for i=1,times do
t = {}
s:enumerateLinesUsingBlock(blk)
assert(#t == 2)
assert(t[1] == 'line1')
assert(t[2] == 'line2')
end
printf('take 2: single block (%d times): %4.2fs', times, timediff())
end
function test.tolua()
local n = toobj(123.5)
assert(isa(n, 'NSNumber'))
assert(tolua(n) == 123.5)
local s = toobj'hello'
assert(isa(s, 'NSString'))
assert(tolua(s) == 'hello')
local a = {1,2,6,7}
local t = toobj(a)
assert(t:count() == #a)
for i=1,#a do
assert(t:objectAtIndex(i-1):doubleValue() == a[i])
end
a = tolua(t)
assert(#a == 4)
assert(a[3] == 6)
local d = {a = 1, b = 'baz', d = {1,2,3}, [{x=1}] = {y=2}}
local t = toobj(d)
assert(t:count() == 4)
assert(tolua(t:valueForKey(toobj'a')) == d.a)
assert(tolua(t:valueForKey(toobj'b')) == d.b)
assert(tolua(t:valueForKey(toobj'd'))[2] == 2)
end
function test.args()
local s = NSString:alloc():initWithUTF8String'\xE2\x82\xAC' --euro symbol
--return string
assert(s:UTF8String() == '\xE2\x82\xAC')
--return boolean (doesn't work for methods)
assert(s:isAbsolutePath() == false)
--return null
assert(type(s:cStringUsingEncoding(NSASCIIStringEncoding)) == 'nil')
--selector arg
assert(s:respondsToSelector'methodForSelector:' == true)
--class arg
assert(NSArray:isSubclassOfClass'NSObject' == true)
assert(NSArray:isSubclassOfClass'XXX' == false)
--string arg
assert(NSString:alloc():initWithString('hey'):UTF8String() == 'hey')
--table arg for array
local a = NSArray:alloc():initWithArray{6,25,5}
assert(a:objectAtIndex(1):doubleValue() == 25)
--table arg for dictionary
local d = NSDictionary:alloc():initWithDictionary{a=5,b=7}
assert(d:valueForKey('b'):doubleValue() == 7)
end
function demo.window()
objc.load'AppKit'
local NSApp = class('NSApp', 'NSApplication <NSApplicationDelegate>')
--we need to add methods to the class before creating any objects!
--note: NSApplicationDelegate is an informal protocol brought from bridgesupport.
function NSApp:applicationShouldTerminateAfterLastWindowClosed()
print'last window closed...'
collectgarbage()
return true
end
function NSApp:applicationShouldTerminate()
print'terminating...'
return true
end
local app = NSApp:sharedApplication()
app:setDelegate(app)
app:setActivationPolicy(NSApplicationActivationPolicyRegular)
local NSWin = class('NSWin', 'NSWindow <NSWindowDelegate>')
--we need to add methods to the class before creating any objects!
--note: NSWindowDelegate is a formal protocol brought from the runtime.
function NSWin:windowWillClose()
print'window will close...'
end
local style = bit.bor(
NSTitledWindowMask,
NSClosableWindowMask,
NSMiniaturizableWindowMask,
NSResizableWindowMask)
local win = NSWin:alloc():initWithContentRect_styleMask_backing_defer(
NSMakeRect(300, 300, 500, 300), style, NSBackingStoreBuffered, false)
win:setDelegate(win)
win:setTitle"▀▄▀▄▀▄ [ Lua Rulez ] ▄▀▄▀▄▀"
app:activateIgnoringOtherApps(true)
win:makeKeyAndOrderFront(nil)
app:run()
end
function demo.speech()
objc.load'AppKit'
local speech = NSSpeechSynthesizer:new()
voiceid = NSSpeechSynthesizer:availableVoices():objectAtIndex(11)
speech:setVoice(voiceid)
speech:startSpeakingString'Calm, fitter, healthier, and more productive; A pig. In a cage. On antibiotics.'
while speech:isSpeaking() do
os.execute'sleep 1'
end
end
function demo.http() --what a dense word soup just to make a http request
objc.load'AppKit'
local app = NSApplication:sharedApplication()
local post = NSString:stringWithFormat('firstName=%@&lastName=%@&eMail=%@&message=%@',
toobj'Dude', toobj'Edud', toobj'x@y.com', toobj'message')
local postData = post:dataUsingEncoding(NSUTF8StringEncoding)
local postLength = NSString:stringWithFormat('%ld', postData:length())
NSLog('Post data: %@', post)
local request = NSMutableURLRequest:new()
request:setURL(NSURL:URLWithString'http://posttestserver.com/post.php')
request:setHTTPMethod'POST'
request:setValue_forHTTPHeaderField(postLength, 'Content-Length')
request:setValue_forHTTPHeaderField('application/x-www-form-urlencoded', 'Content-Type')
request:setHTTPBody(postData)
NSLog('%@', request)
local CD = class('ConnDelegate', 'NSObject <NSURLConnectionDelegate, NSURLConnectionDataDelegate>')
function CD:connection_didReceiveData(conn, data)
self.webData:appendData(data)
NSLog'Connection received data'
end
function CD:connection_didReceiveResponse(conn, response)
NSLog'Connection received response'
NSLog('%@', response:description())
end
function CD:connection_didFailWithError(conn, err)
NSLog('Connection error: %@', err:localizedDescription())
app:terminate(nil)
end
function CD:connectionDidFinishLoading(conn)
NSLog'Connection finished loading'
local html = NSString:alloc():initWithBytes_length_encoding(self.webData:mutableBytes(),
self.webData:length(), NSUTF8StringEncoding)
NSLog('OUTPUT:\n%@', html)
app:terminate(nil)
end
local cd = ConnDelegate:new()
cd.webData = NSMutableData:new()
local conn = NSURLConnection:alloc():initWithRequest_delegate_startImmediately(request, cd, false)
conn:start()
app:run()
end
function demo.http_gcd()
objc.load'AppKit'
local app = NSApplication:sharedApplication()
local url = NSURL:URLWithString'http://posttestserver.com/post.php'
local req = NSURLRequest:requestWithURL(url)
local queue = dispatch.main_queue --dispatch.get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
objc.debug.logtopics.block = true
local n = 0
local blk = block(function()
n = n + 1
print('called', n)
local response = ffi.new'id[1]'
local err = ffi.new'id[1]'
local data = NSURLConnection:sendSynchronousRequest_returningResponse_error(req, response, err)
print(tolua(NSString:alloc():initWithBytes_length_encoding(
data:mutableBytes(), data:length(), NSUTF8StringEncoding)))
if n == 2 then
print'---- Done. Hit Ctrl+C twice ----'
end
end)
dispatch.async(queue, blk) --increase refcount
dispatch.async(queue, blk) --increase refcount
print'queued'
blk = nil; collectgarbage() --decrease refcount (stil queued)
print'released'
app:run()
end
-- inspection ------------------------------------------------------------------------------------------------------------
local function load_many_frameworks()
objc.debug.loaddeps = true
for s in string.gmatch([[
AGL
AVFoundation
AVKit
Accelerate
Accounts
AddressBook
AppKit
AppKitScripting
AppleScriptKit
AppleScriptObjC
AppleShareClientCore
ApplicationServices
AudioToolbox
AudioUnit
AudioVideoBridging
Automator
CFNetwork
CalendarStore
Carbon
Cocoa
Collaboration
CoreAudio
CoreAudioKit
CoreData
CoreFoundation
CoreGraphics
CoreLocation
CoreMIDI
CoreMedia
CoreMediaIO
CoreServices
CoreText
CoreVideo
CoreWLAN
DVComponentGlue
DVDPlayback
DirectoryService
DiscRecording
DiscRecordingUI
DiskArbitration
DrawSprocket
EventKit
ExceptionHandling
FWAUserLib
ForceFeedback
Foundation
GLKit
GLUT
GSS
GameController
GameKit
ICADevices
IMServicePlugIn
IOBluetooth
IOBluetoothUI
IOKit
IOSurface
ImageCaptureCore
ImageIO
InputMethodKit
InstallerPlugins
InstantMessage
JavaFrameEmbedding
JavaScriptCore
Kerberos
LDAP
LatentSemanticMapping
MapKit
MediaAccessibility
MediaLibrary
MediaToolbox
NetFS
OSAKit
OpenAL
OpenCL
OpenDirectory
OpenGL
PCSC
PreferencePanes
PubSub
QTKit
Quartz
QuartzCore
QuickLook
SceneKit
ScreenSaver
Scripting
ScriptingBridge
Security
SecurityFoundation
SecurityInterface
ServiceManagement
Social
SpriteKit
StoreKit
SyncServices
System
SystemConfiguration
TWAIN
Tcl
Tk
VideoDecodeAcceleration
VideoToolbox
WebKit
]], '([^\n\r]+)') do
pcall(objc.load, s)
end
objc.debug.loaddeps = false
end
function eyetest.inspect_classes()
load_many_frameworks()
inspect.classes()
end
function eyetest.inspect_protocols()
load_many_frameworks()
inspect.protocols()
end
function eyetest.inspect_class_properties(cls)
load_many_frameworks()
inspect.class_properties(cls)
end
function eyetest.inspect_protocol_properties(proto)
load_many_frameworks()
inspect.protocol_properties(proto)
end
local function req(s)
return s and s ~= '' and s or nil
end
function eyetest.inspect_class_methods(cls, inst)
load_many_frameworks()
inspect.class_methods(req(cls), inst == 'inst')
end
function eyetest.inspect_protocol_methods(proto, inst, required)
load_many_frameworks()
inspect.protocol_methods(req(proto), inst == 'inst', required == 'required')
end
function eyetest.inspect_class_ivars(cls)
load_many_frameworks()
inspect.class_ivars(req(cls))
end
function eyetest.inspect_class(cls)
load_many_frameworks()
inspect.class(cls)
end
function eyetest.inspect_protocol(proto)
load_many_frameworks()
inspect.protocol(proto)
end
function eyetest.inspect_find(patt)
load_many_frameworks()
inspect.find(patt)
end
--------------
local function test_all(tests, ...)
for k,v in glue.sortedpairs(tests) do
if k ~= 'all' then
print(k)
hr()
tests[k](...)
end
end
end
function test.all(...)
test_all(test)
end
--cmdline interface
local function run(...)
local testname = ...
if not testname then
print('Usage: '..luajit..' '..arg[0]..' <test>')
for k,t in glue.sortedpairs(tests) do
printf('%s:', k)
for k in glue.sortedpairs(t) do
print('', k)
end
end
else
local test = test[testname] or eyetest[testname] or demo[testname]
if not test then
printf('Invalid test "%s"', tostring(testname))
os.exit(1)
end
test(select(2, ...))
print'ok'
end
end
run(...)