--- /dev/null
+#include "test.h"
+#include <malloc/malloc.h>
+#include <objc/objc-runtime.h>
+
+@interface SuperMethods { } @end
+@implementation SuperMethods
++(void)SuperMethodClass { }
++(void)SuperMethodClass2 { }
+-(void)SuperMethodInstance { }
+-(void)SuperMethodInstance2 { }
+@end
+
+@interface SubMethods { } @end
+@implementation SubMethods
++(void)SubMethodClass { }
++(void)SubMethodClass2 { }
+-(void)SubMethodInstance { }
+-(void)SubMethodInstance2 { }
+@end
+
+@interface SuperMethods (Category) @end
+@implementation SuperMethods (Category)
++(void)SuperMethodClassCat { }
++(void)SuperMethodClassCat2 { }
+-(void)SuperMethodInstanceCat { }
+-(void)SuperMethodInstanceCat2 { }
+@end
+
+@interface SubMethods (Category) @end
+@implementation SubMethods (Category)
++(void)SubMethodClassCat { }
++(void)SubMethodClassCat2 { }
+-(void)SubMethodInstanceCat { }
+-(void)SubMethodInstanceCat2 { }
+@end
+
+
+@interface FourMethods @end
+@implementation FourMethods
+-(void)one { }
+-(void)two { }
+-(void)three { }
+-(void)four { }
+@end
+
+@interface NoMethods @end
+@implementation NoMethods @end
+
+static int isNamed(Method m, const char *name)
+{
+ return (method_getName(m) == sel_registerName(name));
+}
+
+int main()
+{
+ // Class SubMethods has not yet been touched, so runtime must attach
+ // the lazy categories
+ Method *methods;
+ unsigned int count;
+ Class cls;
+
+ cls = objc_getClass("SubMethods");
+ testassert(cls);
+
+ testprintf("calling class_copyMethodList(SubMethods) (should be unmethodized)\n");
+
+ count = 100;
+ methods = class_copyMethodList(cls, &count);
+ testassert(methods);
+ testassert(count == 4);
+ // First two methods should be the category methods,
+ // followed by the class methods
+ testassert((isNamed(methods[0], "SubMethodInstanceCat") &&
+ isNamed(methods[1], "SubMethodInstanceCat2"))
+ ||
+ (isNamed(methods[1], "SubMethodInstanceCat") &&
+ isNamed(methods[0], "SubMethodInstanceCat2")));
+ testassert((isNamed(methods[2], "SubMethodInstance") &&
+ isNamed(methods[3], "SubMethodInstance2"))
+ ||
+ (isNamed(methods[3], "SubMethodInstance") &&
+ isNamed(methods[2], "SubMethodInstance2")));
+ // methods[] should be null-terminated
+ testassert(methods[4] == NULL);
+ free(methods);
+
+ testprintf("calling class_copyMethodList(SubMethods(meta)) (should be unmethodized)\n");
+
+ count = 100;
+ methods = class_copyMethodList(cls->isa, &count);
+ testassert(methods);
+ testassert(count == 4);
+ // First two methods should be the category methods,
+ // followed by the class methods
+ testassert((isNamed(methods[0], "SubMethodClassCat") &&
+ isNamed(methods[1], "SubMethodClassCat2"))
+ ||
+ (isNamed(methods[1], "SubMethodClassCat") &&
+ isNamed(methods[0], "SubMethodClassCat2")));
+ testassert((isNamed(methods[2], "SubMethodClass") &&
+ isNamed(methods[3], "SubMethodClass2"))
+ ||
+ (isNamed(methods[3], "SubMethodClass") &&
+ isNamed(methods[2], "SubMethodClass2")));
+ // methods[] should be null-terminated
+ testassert(methods[4] == NULL);
+ free(methods);
+
+ // Check null-termination - this method list block would be 16 bytes
+ // if it weren't for the terminator
+ count = 100;
+ cls = objc_getClass("FourMethods");
+ methods = class_copyMethodList(cls, &count);
+ testassert(methods);
+ testassert(count == 4);
+ testassert(malloc_size(methods) >= 5 * sizeof(Method));
+ testassert(methods[3] != NULL);
+ testassert(methods[4] == NULL);
+ free(methods);
+
+ // Check NULL count parameter
+ methods = class_copyMethodList(cls, NULL);
+ testassert(methods);
+ testassert(methods[4] == NULL);
+ testassert(methods[3] != NULL);
+ free(methods);
+
+ // Check NULL class parameter
+ count = 100;
+ methods = class_copyMethodList(NULL, &count);
+ testassert(!methods);
+ testassert(count == 0);
+
+ // Check NULL class and count
+ methods = class_copyMethodList(NULL, NULL);
+ testassert(!methods);
+
+ // Check class with no methods
+ count = 100;
+ cls = objc_getClass("NoMethods");
+ methods = class_copyMethodList(cls, &count);
+ testassert(!methods);
+ testassert(count == 0);
+
+ succeed(__FILE__);
+}