|
1 | 1 | // Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information. |
2 | 2 |
|
3 | 3 | using System; |
| 4 | +using System.Collections.Generic; |
4 | 5 | using System.Linq; |
5 | 6 | using ClangSharp.Interop; |
6 | 7 | using NUnit.Framework; |
@@ -111,6 +112,151 @@ @interface MyClass |
111 | 112 | Assert.That(methodStaticMethod.Selector, Is.EqualTo("staticMethod"), "methodStaticMethod.Selector"); |
112 | 113 | } |
113 | 114 |
|
| 115 | + [Test] |
| 116 | + public void Method_Family() |
| 117 | + { |
| 118 | + AssertNeedNewClangSharp(); |
| 119 | + |
| 120 | + var inputContents = $@" |
| 121 | +@interface NSObject |
| 122 | + -(void) none; |
| 123 | + -(void) noneWithArgument:(int)arg; |
| 124 | + -(void) something; |
| 125 | + -(void) autoreleaseNone; |
| 126 | + -(void) deallocNone; |
| 127 | + -(void) finalizeNone; |
| 128 | + -(void) releaseNone; |
| 129 | + -(void) retainNone; |
| 130 | + -(unsigned long) retainCountNone; |
| 131 | + -(instancetype) selfNone; |
| 132 | + -(instancetype) initializeNone; |
| 133 | + -(void) performSelectorNone; |
| 134 | +
|
| 135 | + +(instancetype) alloc; |
| 136 | + +(instancetype) allocWithZone:(void*)zone; |
| 137 | + +(instancetype) _alloc; |
| 138 | + +(instancetype) __alloc; |
| 139 | + +(instancetype) _allocWithZone:(void*)zone; |
| 140 | +
|
| 141 | + -(instancetype) copy; |
| 142 | + -(instancetype) copyWithZone:(void*)zone; |
| 143 | + -(instancetype) _copy; |
| 144 | + -(instancetype) __copyWithZone:(void*)zone; |
| 145 | +
|
| 146 | + -(instancetype) init; |
| 147 | + -(instancetype) initWithValue:(int)value; |
| 148 | + -(instancetype) _init; |
| 149 | + -(instancetype) __initWithValue:(int)value; |
| 150 | +
|
| 151 | + -(instancetype) mutableCopy; |
| 152 | + -(instancetype) mutableCopyWithZone:(void*)zone; |
| 153 | + -(instancetype) _mutableCopy; |
| 154 | + -(instancetype) __mutableCopyWithZone:(void*)zone; |
| 155 | +
|
| 156 | + +(instancetype) new; |
| 157 | + +(instancetype) newWithValue:(int)value; |
| 158 | + +(instancetype) _new; |
| 159 | + +(instancetype) __newWithValue:(int)value; |
| 160 | +
|
| 161 | + -(void) autorelease; |
| 162 | + -(void) dealloc; |
| 163 | + -(void) finalize; |
| 164 | + -(void) release; |
| 165 | + -(void) retain; |
| 166 | + -(unsigned long) retainCount; |
| 167 | + -(instancetype) self; |
| 168 | + +(void) initialize; |
| 169 | + -(id) performSelector:(SEL)selector; |
| 170 | +@end |
| 171 | +
|
| 172 | +@interface MyClass |
| 173 | + -(instancetype) instanceMethod; |
| 174 | + +(MyClass*) staticMethod; |
| 175 | +@end |
| 176 | +"; |
| 177 | + |
| 178 | + using var translationUnit = CreateTranslationUnit(inputContents, "objective-c++"); |
| 179 | + |
| 180 | + var classes = translationUnit.TranslationUnitDecl.Decls.OfType<ObjCInterfaceDecl>().ToList(); |
| 181 | + Assert.That(classes.Count, Is.GreaterThanOrEqualTo(2), $"At least two classes"); |
| 182 | + |
| 183 | + foreach (var cls in classes) |
| 184 | + { |
| 185 | + var methods = cls.Methods.ToList(); |
| 186 | + if (methods.Count == 0) |
| 187 | + { |
| 188 | + continue; |
| 189 | + } |
| 190 | + |
| 191 | + var take = new Func<Func<string, bool>, ObjCMethodFamily, IEnumerable<ObjCMethodDecl>>((Func<string, bool> filter, ObjCMethodFamily family) => { |
| 192 | + var taken = new List<ObjCMethodDecl>(); |
| 193 | + for (var v = methods.Count - 1; v >= 0; v--) |
| 194 | + { |
| 195 | + var method = methods[v]; |
| 196 | + if (filter(method.Selector)) |
| 197 | + { |
| 198 | + methods.RemoveAt(v); |
| 199 | + taken.Add(method); |
| 200 | + } |
| 201 | + } |
| 202 | + |
| 203 | + Assert.That(taken.Count, Is.GreaterThanOrEqualTo(0), $"family {family} not found, methods remaining: {string.Join(", ", methods.Select(p => p.Selector))}"); |
| 204 | + return taken; |
| 205 | + }); |
| 206 | + |
| 207 | + var assertFamily = new Action<Func<string, bool>, ObjCMethodFamily>((Func<string, bool> filter, ObjCMethodFamily family) => { |
| 208 | + var methodsInFamily = take(filter, family); |
| 209 | + foreach (var method in methodsInFamily) |
| 210 | + { |
| 211 | + Assert.That(method.MethodFamily, Is.EqualTo(family), $"MethodFamily for {method.Selector}"); |
| 212 | + } |
| 213 | + }); |
| 214 | + |
| 215 | + var isSelectorFamily = new Func<string, string, bool>((string selector, string family) => { |
| 216 | + selector = selector.TrimStart('_'); |
| 217 | + if (selector == family) |
| 218 | + { |
| 219 | + return true; |
| 220 | + } |
| 221 | + if (!selector.StartsWith(family, StringComparison.Ordinal)) |
| 222 | + { |
| 223 | + return false; |
| 224 | + } |
| 225 | + var nextChar = selector[family.Length]; |
| 226 | + if (nextChar == ':' || char.IsUpper(nextChar)) |
| 227 | + { |
| 228 | + return true; |
| 229 | + } |
| 230 | + return false; |
| 231 | + }); |
| 232 | + |
| 233 | + Assert.Multiple(() => { |
| 234 | + assertFamily(s => isSelectorFamily(s, "alloc"), ObjCMethodFamily.Alloc); |
| 235 | + assertFamily(s => isSelectorFamily(s, "copy"), ObjCMethodFamily.Copy); |
| 236 | + assertFamily(s => isSelectorFamily(s, "init"), ObjCMethodFamily.Init); |
| 237 | + assertFamily(s => isSelectorFamily(s, "mutableCopy"), ObjCMethodFamily.MutableCopy); |
| 238 | + assertFamily(s => isSelectorFamily(s, "new"), ObjCMethodFamily.New); |
| 239 | + assertFamily(s => s == "autorelease", ObjCMethodFamily.Autorelease); |
| 240 | + assertFamily(s => s == "dealloc", ObjCMethodFamily.Dealloc); |
| 241 | + assertFamily(s => s == "finalize", ObjCMethodFamily.Finalize); |
| 242 | + assertFamily(s => s == "release", ObjCMethodFamily.Release); |
| 243 | + assertFamily(s => s == "retain", ObjCMethodFamily.Retain); |
| 244 | + assertFamily(s => s == "retainCount", ObjCMethodFamily.RetainCount); |
| 245 | + assertFamily(s => s == "self", ObjCMethodFamily.Self); |
| 246 | + assertFamily(s => s == "initialize", ObjCMethodFamily.Initialize); |
| 247 | + assertFamily(s => s.StartsWith("performSelector:", StringComparison.Ordinal), ObjCMethodFamily.PerformSelector); |
| 248 | + |
| 249 | + Assert.That(methods.Count, Is.GreaterThan(0), $"No remaining methods in {cls.Name}"); |
| 250 | + |
| 251 | + // None of the remaining methods should belong to a family |
| 252 | + foreach (var method in methods) |
| 253 | + { |
| 254 | + Assert.That(method.MethodFamily, Is.EqualTo(ObjCMethodFamily.None), $"MethodFamily for {method.Selector}"); |
| 255 | + } |
| 256 | + }); |
| 257 | + } |
| 258 | + } |
| 259 | + |
114 | 260 | [Test] |
115 | 261 | public void Category_TypeParamList() |
116 | 262 | { |
|
0 commit comments