diff --git a/CSSystemInfoHelper.podspec b/CSSystemInfoHelper.podspec index 3998735..4f429f8 100644 --- a/CSSystemInfoHelper.podspec +++ b/CSSystemInfoHelper.podspec @@ -15,7 +15,7 @@ Pod::Spec.new do |s| s.module_name = "CSSystemInfoHelper" s.source_files = "CSSystemInfoHelper/Sources/*.{h,m}", "CSSystemInfoHelper/Headers/CSSystemInfoHelper/*.h" - s.public_header_files = "CSSystemInfoHelper/Headers/CSSystemInfoHelper/CSSystemInfoHelper.h" + s.public_header_files = "CSSystemInfoHelper/Headers/CSSystemInfoHelper/CSSystemInfoHelper.h", "CSSystemInfoHelper/Headers/CSSystemInfoHelper/CSNetworkInterface.h" s.ios.deployment_target = "9.0" s.ios.frameworks = "Foundation" diff --git a/CSSystemInfoHelper.xcodeproj/project.pbxproj b/CSSystemInfoHelper.xcodeproj/project.pbxproj index 2f31cb8..32867fd 100644 --- a/CSSystemInfoHelper.xcodeproj/project.pbxproj +++ b/CSSystemInfoHelper.xcodeproj/project.pbxproj @@ -7,10 +7,18 @@ objects = { /* Begin PBXBuildFile section */ + AD143BA1267C7D350094D246 /* CSSystemInfoProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = AD143B9F267C7D350094D246 /* CSSystemInfoProvider.h */; }; + AD143BA2267C7D350094D246 /* CSSystemInfoProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = AD143BA0267C7D350094D246 /* CSSystemInfoProvider.m */; }; + AD143BA4267C80760094D246 /* CSSystemInfoHelper+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = AD143BA3267C80760094D246 /* CSSystemInfoHelper+Internal.h */; }; + AD143BA7267C87450094D246 /* CSSystemInfoProviderMock.m in Sources */ = {isa = PBXBuildFile; fileRef = AD143BA6267C87450094D246 /* CSSystemInfoProviderMock.m */; }; + AD352510267CB84700A3A929 /* CSSystemInfoHelperDeprecatedTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AD35250F267CB84700A3A929 /* CSSystemInfoHelperDeprecatedTests.m */; }; AD3BFDF0267AA56600FF8076 /* CSSystemInfoHelper.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AD3BFDE6267AA56600FF8076 /* CSSystemInfoHelper.framework */; }; AD3BFDF5267AA56600FF8076 /* CSSystemInfoHelperTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AD3BFDF4267AA56600FF8076 /* CSSystemInfoHelperTests.m */; }; AD3BFE07267AA5BD00FF8076 /* CSSystemInfoHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = AD3BFE03267AA5BD00FF8076 /* CSSystemInfoHelper.h */; settings = {ATTRIBUTES = (Public, ); }; }; AD3BFE08267AA5BD00FF8076 /* CSSystemInfoHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = AD3BFE05267AA5BD00FF8076 /* CSSystemInfoHelper.m */; }; + ADB06B24267B5A2400D4F0E3 /* CSNetworkInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = ADB06B22267B5A2400D4F0E3 /* CSNetworkInterface.h */; settings = {ATTRIBUTES = (Public, ); }; }; + ADB06B25267B5A2400D4F0E3 /* CSNetworkInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB06B23267B5A2400D4F0E3 /* CSNetworkInterface.m */; }; + ADB06B27267B5B6E00D4F0E3 /* CSNetworkInterface+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = ADB06B26267B5B6E00D4F0E3 /* CSNetworkInterface+Internal.h */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -24,6 +32,12 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + AD143B9F267C7D350094D246 /* CSSystemInfoProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CSSystemInfoProvider.h; sourceTree = ""; }; + AD143BA0267C7D350094D246 /* CSSystemInfoProvider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CSSystemInfoProvider.m; sourceTree = ""; }; + AD143BA3267C80760094D246 /* CSSystemInfoHelper+Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CSSystemInfoHelper+Internal.h"; sourceTree = ""; }; + AD143BA5267C87450094D246 /* CSSystemInfoProviderMock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSystemInfoProviderMock.h; sourceTree = ""; }; + AD143BA6267C87450094D246 /* CSSystemInfoProviderMock.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSystemInfoProviderMock.m; sourceTree = ""; }; + AD35250F267CB84700A3A929 /* CSSystemInfoHelperDeprecatedTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CSSystemInfoHelperDeprecatedTests.m; sourceTree = ""; }; AD3BFDE6267AA56600FF8076 /* CSSystemInfoHelper.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CSSystemInfoHelper.framework; sourceTree = BUILT_PRODUCTS_DIR; }; AD3BFDEF267AA56600FF8076 /* CSSystemInfoHelperTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CSSystemInfoHelperTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; AD3BFDF4267AA56600FF8076 /* CSSystemInfoHelperTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CSSystemInfoHelperTests.m; sourceTree = ""; }; @@ -31,6 +45,10 @@ AD3BFE03267AA5BD00FF8076 /* CSSystemInfoHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSystemInfoHelper.h; sourceTree = ""; }; AD3BFE05267AA5BD00FF8076 /* CSSystemInfoHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSystemInfoHelper.m; sourceTree = ""; }; AD3BFE06267AA5BD00FF8076 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + ADB06B22267B5A2400D4F0E3 /* CSNetworkInterface.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CSNetworkInterface.h; sourceTree = ""; }; + ADB06B23267B5A2400D4F0E3 /* CSNetworkInterface.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CSNetworkInterface.m; sourceTree = ""; }; + ADB06B26267B5B6E00D4F0E3 /* CSNetworkInterface+Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CSNetworkInterface+Internal.h"; sourceTree = ""; }; + ADB06B28267B608800D4F0E3 /* Errors.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Errors.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -52,12 +70,20 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + AD2D059D267ABDAC006B76EB /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; AD3BFDDC267AA56600FF8076 = { isa = PBXGroup; children = ( AD3BFE00267AA5BD00FF8076 /* CSSystemInfoHelper */, AD3BFDF3267AA56600FF8076 /* CSSystemInfoHelperTests */, AD3BFDE7267AA56600FF8076 /* Products */, + AD2D059D267ABDAC006B76EB /* Frameworks */, ); sourceTree = ""; }; @@ -73,7 +99,10 @@ AD3BFDF3267AA56600FF8076 /* CSSystemInfoHelperTests */ = { isa = PBXGroup; children = ( + AD143BA5267C87450094D246 /* CSSystemInfoProviderMock.h */, + AD143BA6267C87450094D246 /* CSSystemInfoProviderMock.m */, AD3BFDF4267AA56600FF8076 /* CSSystemInfoHelperTests.m */, + AD35250F267CB84700A3A929 /* CSSystemInfoHelperDeprecatedTests.m */, AD3BFDF6267AA56600FF8076 /* Info.plist */, ); path = CSSystemInfoHelperTests; @@ -101,6 +130,7 @@ isa = PBXGroup; children = ( AD3BFE03267AA5BD00FF8076 /* CSSystemInfoHelper.h */, + ADB06B22267B5A2400D4F0E3 /* CSNetworkInterface.h */, ); path = CSSystemInfoHelper; sourceTree = ""; @@ -108,7 +138,13 @@ AD3BFE04267AA5BD00FF8076 /* Sources */ = { isa = PBXGroup; children = ( + AD143BA3267C80760094D246 /* CSSystemInfoHelper+Internal.h */, AD3BFE05267AA5BD00FF8076 /* CSSystemInfoHelper.m */, + ADB06B26267B5B6E00D4F0E3 /* CSNetworkInterface+Internal.h */, + ADB06B23267B5A2400D4F0E3 /* CSNetworkInterface.m */, + AD143B9F267C7D350094D246 /* CSSystemInfoProvider.h */, + AD143BA0267C7D350094D246 /* CSSystemInfoProvider.m */, + ADB06B28267B608800D4F0E3 /* Errors.h */, ); path = Sources; sourceTree = ""; @@ -120,7 +156,11 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + ADB06B27267B5B6E00D4F0E3 /* CSNetworkInterface+Internal.h in Headers */, + AD143BA4267C80760094D246 /* CSSystemInfoHelper+Internal.h in Headers */, AD3BFE07267AA5BD00FF8076 /* CSSystemInfoHelper.h in Headers */, + AD143BA1267C7D350094D246 /* CSSystemInfoProvider.h in Headers */, + ADB06B24267B5A2400D4F0E3 /* CSNetworkInterface.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -169,6 +209,7 @@ AD3BFDDD267AA56600FF8076 /* Project object */ = { isa = PBXProject; attributes = { + LastSwiftUpdateCheck = 1250; LastUpgradeCheck = 1250; TargetAttributes = { AD3BFDE5267AA56600FF8076 = { @@ -220,6 +261,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + ADB06B25267B5A2400D4F0E3 /* CSNetworkInterface.m in Sources */, + AD143BA2267C7D350094D246 /* CSSystemInfoProvider.m in Sources */, AD3BFE08267AA5BD00FF8076 /* CSSystemInfoHelper.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -228,7 +271,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + AD143BA7267C87450094D246 /* CSSystemInfoProviderMock.m in Sources */, AD3BFDF5267AA56600FF8076 /* CSSystemInfoHelperTests.m in Sources */, + AD352510267CB84700A3A929 /* CSSystemInfoHelperDeprecatedTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/CSSystemInfoHelper.xcodeproj/xcshareddata/xcschemes/CSSystemInfoHelper.xcscheme b/CSSystemInfoHelper.xcodeproj/xcshareddata/xcschemes/CSSystemInfoHelper.xcscheme new file mode 100644 index 0000000..4cfdb55 --- /dev/null +++ b/CSSystemInfoHelper.xcodeproj/xcshareddata/xcschemes/CSSystemInfoHelper.xcscheme @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CSSystemInfoHelper/Headers/CSSystemInfoHelper/CSNetworkInterface.h b/CSSystemInfoHelper/Headers/CSSystemInfoHelper/CSNetworkInterface.h new file mode 100644 index 0000000..4674309 --- /dev/null +++ b/CSSystemInfoHelper/Headers/CSSystemInfoHelper/CSNetworkInterface.h @@ -0,0 +1,26 @@ +// +// CSInterfaceAddress.h +// CSSystemInfoHelper +// +// Created by Cătălin Stan on 17/06/2021. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface CSNetworkInterface : NSObject + +@property (nonatomic, readonly, strong) NSString *name; +@property (nonatomic, readonly, strong) NSString *address; + +@property (nonatomic, readonly) sa_family_t family; +@property (nonatomic, readonly, strong) NSString *familyName; + ++ (instancetype)new NS_UNAVAILABLE; +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/CSSystemInfoHelper/Headers/CSSystemInfoHelper/CSSystemInfoHelper.h b/CSSystemInfoHelper/Headers/CSSystemInfoHelper/CSSystemInfoHelper.h index 6d236c9..083627e 100644 --- a/CSSystemInfoHelper/Headers/CSSystemInfoHelper/CSSystemInfoHelper.h +++ b/CSSystemInfoHelper/Headers/CSSystemInfoHelper/CSSystemInfoHelper.h @@ -11,6 +11,8 @@ FOUNDATION_EXPORT double CSSystemInfoHelperVersionNumber; FOUNDATION_EXPORT const unsigned char CSSystemInfoHelperVersionString[]; +#import + NS_ASSUME_NONNULL_BEGIN typedef NSString * CSSystemInfoKey NS_TYPED_EXTENSIBLE_ENUM; @@ -26,6 +28,9 @@ FOUNDATION_EXPORT CSSystemInfoKey const CSSystemInfoKeyVersion; /// Machine hardware platform. FOUNDATION_EXPORT CSSystemInfoKey const CSSystemInfoKeyMachine; +/// Constant returned when no IP address could be deternimed +FOUNDATION_EXPORT NSString * const CSSystemInfoHelperIPAddressNone DEPRECATED_ATTRIBUTE; + /// The CSSystemInfoHelper class provides easy-access to some useful system /// information that would otherwise require some more elaborate code. @interface CSSystemInfoHelper : NSObject @@ -37,14 +42,10 @@ FOUNDATION_EXPORT CSSystemInfoKey const CSSystemInfoKeyMachine; /// @name Getting IP Addresses -/// A dictionary where the keys are interface names and the values are -/// the IP addresses associated with those interfaces. +/// An array of @c CSNetworkInterface objects representing all the IPv4 and IPv6 +/// interfaces configured, in the order of discovery, as returned by @c getifaddrs(3) /// @note Check the @c getifaddrs(3) manual page for more information. -@property (nonatomic, readonly, strong) NSDictionary * AllIPAddresses; - -/// The IP Address of "en0". -/// @note This is a convenience method for `AllIPAddresses[@"en0"]`. -@property (nonatomic, readonly, strong) NSString *IPAddress; +@property (nonatomic, readonly, strong, nullable) NSArray *networkInterfaces; /// @name Getting @c uname System Information @@ -75,6 +76,22 @@ FOUNDATION_EXPORT CSSystemInfoKey const CSSystemInfoKeyMachine; /// Get the UUID of the current device @property (nonatomic, readonly, strong) NSString * platformUUID API_UNAVAILABLE(ios, tvos, watchos); +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +#pragma mark - Deprecated + +/// @name Deprecated + +/// A dictionary where the keys are interface names and the values are +/// the IP addresses associated with those interfaces. +/// @note Check the @c getifaddrs(3) manual page for more information. +@property (nonatomic, readonly, strong) NSDictionary * AllIPAddresses DEPRECATED_MSG_ATTRIBUTE("Use 'networkInterfaces' instead."); + +/// The IP Address of "en0". +/// @note This is a convenience method for `AllIPAddresses[@"en0"]`. +@property (nonatomic, readonly, strong) NSString *IPAddress DEPRECATED_MSG_ATTRIBUTE("Use 'networkInterfaces' instead."); + @end NS_ASSUME_NONNULL_END diff --git a/CSSystemInfoHelper/Sources/CSNetworkInterface+Internal.h b/CSSystemInfoHelper/Sources/CSNetworkInterface+Internal.h new file mode 100644 index 0000000..cf79bed --- /dev/null +++ b/CSSystemInfoHelper/Sources/CSNetworkInterface+Internal.h @@ -0,0 +1,23 @@ +// +// CSInterfaceAddress+Internal.h +// CSSystemInfoHelper +// +// Created by Cătălin Stan on 17/06/2021. +// + +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface CSNetworkInterface () + +- (instancetype)initWithName:(NSString *)name + address:(NSString *)address + family:(sa_family_t)family +NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/CSSystemInfoHelper/Sources/CSNetworkInterface.m b/CSSystemInfoHelper/Sources/CSNetworkInterface.m new file mode 100644 index 0000000..0d85c1d --- /dev/null +++ b/CSSystemInfoHelper/Sources/CSNetworkInterface.m @@ -0,0 +1,126 @@ +// +// CSInterfaceAddress.m +// CSSystemInfoHelper +// +// Created by Cătălin Stan on 17/06/2021. +// + +#import "CSNetworkInterface+Internal.h" + +#include + +#import "Errors.h" + +static const char * const af_consts[AF_MAX] = { + "AF_UNSPEC", + "AF_UNIX", + "AF_INET", + "AF_IMPLINK", + "AF_PUP", + "AF_CHAOS", + "AF_NS", + "AF_ISO", + "AF_ECMA", + "AF_DATAKIT", + "AF_CCITT", + "AF_SNA", + "AF_DECnet", + "AF_DLI", + "AF_LAT", + "AF_HYLINK", + "AF_APPLETALK", + "AF_ROUTE", + "AF_LINK", + "pseudo_AF_XTP", + "AF_COIP", + "AF_CNT", + "pseudo_AF_RTIP", + "AF_IPX", + "AF_SIP", + "pseudo_AF_PIP", + "-", + "AF_NDRV", + "AF_ISDN", + "pseudo_AF_KEY", + "AF_INET6", + "AF_NATM", + "AF_SYSTEM", + "AF_NETBIOS", + "AF_PPP", + "AF_HDRCMPLT", + "AF_RESERVED_36", + "AF_IEEE80211", + "-", + "AF_UTUN", + "AF_VSOCK", +}; +static const char * const af_descs[AF_MAX] = { + "unspecified", + "local to host (pipes)", + "internetwork: UDP, TCP, etc.", + "arpanet imp addresses", + "pup protocols: e.g. BSP", + "mit CHAOS protocols", + "XEROX NS protocols", + "ISO protocols", + "European computer manufacturers", + "datakit protocols", + "CCITT protocols, X.25 etc", + "IBM SNA", + "DECnet", + "DEC Direct data link interface", + "LAT", + "NSC Hyperchannel", + "Apple Talk", + "Internal Routing Protocol", + "Link layer interface", + "eXpress Transfer Protocol (no AF)", + "connection-oriented IP, aka ST II", + "Computer Network Technology", + "Help Identify RTIP packets", + "Novell Internet Protocol", + "Simple Internet Protocol", + "Help Identify PIP packets", + "n/a", + "Network Driver 'raw' access", + "Integrated Services Digital Network", + "Internal key-management function", + "IPv6", + "native ATM access", + "Kernel event messages", + "NetBIOS", + "PPP communication protocol", + "Used by BPF to not rewrite headers in interface output routine", + "Reserved for internal usage", + "IEEE 802.11 protocol", + "n/a", + "n/a", + "VM Sockets", +}; + +NS_INLINE const char *af_const_str(sa_family_t af) { + return af_consts[MIN(MAX(0, af), AF_MAX - 1)]; +} + +NS_INLINE const char *af_desc(sa_family_t af) { + return af_descs[MIN(MAX(0, af), AF_MAX - 1)]; +} + +@implementation CSNetworkInterface + +- (instancetype)initWithName:(NSString *)name address:(NSString *)address family:(sa_family_t)family { + self = [super init]; + if (self) { + _name = name; + _address = address; + _family = family; + _familyName = @(af_const_str(_family)); + } + return self; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"%@ name:%@ address:%@ family:%@ (%s)", super.description, _name, _address,_familyName, af_desc(_family)]; +} + +@end diff --git a/CSSystemInfoHelper/Sources/CSSystemInfoHelper+Internal.h b/CSSystemInfoHelper/Sources/CSSystemInfoHelper+Internal.h new file mode 100644 index 0000000..74bf092 --- /dev/null +++ b/CSSystemInfoHelper/Sources/CSSystemInfoHelper+Internal.h @@ -0,0 +1,21 @@ +// +// CSSystemInfoHelper+Internal.h +// CSSystemInfoHelper +// +// Created by Cătălin Stan on 18/06/2021. +// + +#import +#import "CSSystemInfoProvider.h" + +NS_ASSUME_NONNULL_BEGIN + +FOUNDATION_EXPORT NSString * const CSSystemInfoHelperDefaultInterface DEPRECATED_ATTRIBUTE; + +@interface CSSystemInfoHelper () + +- (instancetype)initWithSystemInfoProvider:(id _Nullable)systemInfoProvider NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/CSSystemInfoHelper/Sources/CSSystemInfoHelper.m b/CSSystemInfoHelper/Sources/CSSystemInfoHelper.m index 68a8aa7..0f80968 100644 --- a/CSSystemInfoHelper/Sources/CSSystemInfoHelper.m +++ b/CSSystemInfoHelper/Sources/CSSystemInfoHelper.m @@ -7,63 +7,61 @@ // #import +#import "CSSystemInfoHelper+Internal.h" -#import -#import #import -//#import #import -NSString * const CSSystemInfoKeySysname = @"CSSystemInfoSysname"; -NSString * const CSSystemInfoKeyNodename = @"CSSystemInfoNodename"; -NSString * const CSSystemInfoKeyRelease = @"CSSystemInfoRelease"; -NSString * const CSSystemInfoKeyVersion = @"CSSystemInfoVersion"; -NSString * const CSSystemInfoKeyMachine = @"CSSystemInfoMachine"; +#import "CSNetworkInterface+Internal.h" +#import "CSSystemInfoProvider.h" +#import "Errors.h" + +CSSystemInfoKey const CSSystemInfoKeySysname = @"CSSystemInfoSysname"; +CSSystemInfoKey const CSSystemInfoKeyNodename = @"CSSystemInfoNodename"; +CSSystemInfoKey const CSSystemInfoKeyRelease = @"CSSystemInfoRelease"; +CSSystemInfoKey const CSSystemInfoKeyVersion = @"CSSystemInfoVersion"; +CSSystemInfoKey const CSSystemInfoKeyMachine = @"CSSystemInfoMachine"; + +NSString * const CSSystemInfoHelperIPAddressNone = @"(none)"; +NSString * const CSSystemInfoHelperDefaultInterface = @"en0"; + __attribute__((objc_direct_members)) @interface CSSystemInfoHelper () -@end -@implementation CSSystemInfoHelper +@property (nonatomic, readonly, strong, nullable) id systemInfoProvider; -static CSSystemInfoHelper* sharedHelper; +@end -+ (void)initialize { - sharedHelper = [[CSSystemInfoHelper alloc] init]; -} +@implementation CSSystemInfoHelper + (instancetype)sharedHelper { + static CSSystemInfoHelper* sharedHelper; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedHelper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:CSSystemInfoProvider.sharedProvider]; + }); return sharedHelper; } -- (NSDictionary *)AllIPAddresses { - static NSMutableDictionary * allIPAddresses; - if (!allIPAddresses) { - allIPAddresses = [NSMutableDictionary dictionaryWithCapacity:4]; - struct ifaddrs * interfaces = NULL; - struct ifaddrs * addr = NULL; - int success = 0; - success = getifaddrs(&interfaces); - if (success == 0) { - addr = interfaces; - while(addr != NULL) { - if(addr->ifa_addr->sa_family == AF_INET) { - allIPAddresses[[NSString stringWithUTF8String:addr->ifa_name]] = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)addr->ifa_addr)->sin_addr)]; - } - addr = addr->ifa_next; - } - } - freeifaddrs(interfaces); +- (instancetype)initWithSystemInfoProvider:(id)systemInfoProvider { + self = [super init]; + if (self != nil) { + _systemInfoProvider = systemInfoProvider; } - return allIPAddresses; + return self; } -- (NSString *)IPAddress { - static NSString *IPAddress; - if (!IPAddress) { - IPAddress = self.AllIPAddresses[@"en0"]; +- (NSArray *)networkInterfaces { + NSArray *networkInterfaces; + + NSError *error; + if (!(networkInterfaces = [self.systemInfoProvider queryNetworkInterfaces:&error])) { + NSLog(@"Error loading network interfaces: %@. %@.", error.localizedDescription, error.localizedFailureReason); + return nil; } - return IPAddress; + + return networkInterfaces; } - (NSDictionary *)systemInfo { @@ -137,4 +135,46 @@ - (NSString *)platformUUID { } #endif +#pragma mark - Deprecated + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" + +- (NSDictionary *)AllIPAddresses { + NSArray *networkInterfaces = self.networkInterfaces; + NSMutableDictionary *allIPAddresses = [NSMutableDictionary dictionaryWithCapacity:networkInterfaces.count]; + for (CSNetworkInterface *interface in networkInterfaces) { + if (interface.family != AF_INET) { + continue; + } + allIPAddresses[interface.name] = interface.address; + } + return allIPAddresses; +} + +- (NSString *)IPAddress { + NSString *firstInterfaceAddress; + NSArray *networkInterfaces = self.networkInterfaces; + for (CSNetworkInterface *interface in networkInterfaces) { + if (interface.family != AF_INET) { + continue; + } + if ([interface.name isEqualToString:CSSystemInfoHelperDefaultInterface]) { + return interface.address; + break; + } + if (!firstInterfaceAddress) { + firstInterfaceAddress = interface.address; + } + } + + NSString *IPAddress; + if (!(IPAddress = firstInterfaceAddress)) { + IPAddress = CSSystemInfoHelperIPAddressNone; + } + return IPAddress; +} + +#pragma clang diagnostic pop + @end diff --git a/CSSystemInfoHelper/Sources/CSSystemInfoProvider.h b/CSSystemInfoHelper/Sources/CSSystemInfoProvider.h new file mode 100644 index 0000000..0b3ab8f --- /dev/null +++ b/CSSystemInfoHelper/Sources/CSSystemInfoProvider.h @@ -0,0 +1,29 @@ +// +// CSSystemInfoProvider.h +// CSSystemInfoHelper +// +// Created by Cătălin Stan on 18/06/2021. +// + +#import +#import + +@class CSNetworkInterface; + +NS_ASSUME_NONNULL_BEGIN + +@protocol CSSystemInfoProviderProtocol + +- (nullable NSArray *)queryNetworkInterfaces:(NSError *__autoreleasing *)error NS_WARN_UNUSED_RESULT; + +@end + +@interface CSSystemInfoProvider : NSObject + +@property (class, nonatomic, readonly) CSSystemInfoProvider *sharedProvider; + +- (nullable CSNetworkInterface *)interfaceAddressWithAddr:(struct ifaddrs *)addr error:(NSError *__autoreleasing *)error NS_WARN_UNUSED_RESULT; + +@end + +NS_ASSUME_NONNULL_END diff --git a/CSSystemInfoHelper/Sources/CSSystemInfoProvider.m b/CSSystemInfoHelper/Sources/CSSystemInfoProvider.m new file mode 100644 index 0000000..a6643a7 --- /dev/null +++ b/CSSystemInfoHelper/Sources/CSSystemInfoProvider.m @@ -0,0 +1,86 @@ +// +// CSSystemInfoProvider.m +// CSSystemInfoHelper +// +// Created by Cătălin Stan on 18/06/2021. +// + +#import "CSSystemInfoProvider.h" + +#include +#include +#include +#include + +#import "CSNetworkInterface+Internal.h" +#import "Errors.h" + +@implementation CSSystemInfoProvider + ++ (CSSystemInfoProvider *)sharedProvider { + static CSSystemInfoProvider *sharedProvider; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedProvider = [CSSystemInfoProvider new]; + }); + return sharedProvider; +} + +- (CSNetworkInterface *)interfaceAddressWithAddr:(struct ifaddrs *)addr error:(NSError *__autoreleasing _Nullable *)error { + struct sockaddr *ifa_addr = addr->ifa_addr; + sa_family_t family = ifa_addr->sa_family; + socklen_t len = INET_ADDRSTRLEN; + void *src = &(((struct sockaddr_in *)ifa_addr)->sin_addr); + if (family != AF_INET) { + len = INET6_ADDRSTRLEN; + src = &(((struct sockaddr_in6 *)ifa_addr)->sin6_addr); + } + + char buf[len / sizeof(char)]; + if (!inet_ntop(family, src, buf, len)) { + if (error) { + NSString *reason; + if (errno == EAFNOSUPPORT) { + reason = [NSString stringWithFormat:@"Interface '%s' has unsupported address family %d", addr->ifa_name, family]; + } + *error = NSPosixError(errno, reason, nil); + } + return nil; + } + + return [[CSNetworkInterface alloc] initWithName:@(addr->ifa_name) address:@(buf) family:family]; +} + +#pragma mark - CSSystemInfoProviderProtocol + +- (NSArray *)queryNetworkInterfaces:(NSError *__autoreleasing *)error { + NSMutableArray *result = [NSMutableArray arrayWithCapacity:6]; // lo0, en0, en1 (IPv4, IPv6) + struct ifaddrs *interfaces = NULL; + if (getifaddrs(&interfaces)) { + if (error) { + *error = NSPosixError(errno, nil, nil); + } + return nil; + } + + struct ifaddrs *addr = interfaces; + do { + NSError *err; + CSNetworkInterface *address; + if (!(address = [self interfaceAddressWithAddr:addr error:&err])) { + if (err.code != EAFNOSUPPORT || err.domain != NSPOSIXErrorDomain) { + freeifaddrs(interfaces); + return nil; + } + + continue; + } + + [result addObject:address]; + } while ((addr = addr->ifa_next)); + + freeifaddrs(interfaces); + return result; +} + +@end diff --git a/CSSystemInfoHelper/Sources/Errors.h b/CSSystemInfoHelper/Sources/Errors.h new file mode 100644 index 0000000..fd66767 --- /dev/null +++ b/CSSystemInfoHelper/Sources/Errors.h @@ -0,0 +1,33 @@ +// +// Errors.h +// CSSystemInfoHelper +// +// Created by Cătălin Stan on 17/06/2021. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +NS_INLINE NSError * +NSErrorMake(NSErrorDomain domain, + NSInteger code, + NSString *_Nullable description, + NSString *_Nullable reason, + NSDictionary *_Nullable userInfo) { + NSMutableDictionary *info = [NSMutableDictionary dictionaryWithCapacity:userInfo.count + 2]; + info[NSLocalizedDescriptionKey] = description; + info[NSLocalizedFailureReasonErrorKey] = reason; + if (userInfo) { + [info addEntriesFromDictionary:userInfo]; + } + return [NSError errorWithDomain:domain code:code userInfo:info]; +} + +NS_INLINE NSError * +NSPosixError(int errnum, NSString *_Nullable reason, NSDictionary *_Nullable userInfo) { + return NSErrorMake(NSPOSIXErrorDomain, (NSInteger)errnum, @(strerror(errnum)), reason, userInfo); +} + +NS_ASSUME_NONNULL_END + diff --git a/CSSystemInfoHelperTests/CSSystemInfoHelperDeprecatedTests.m b/CSSystemInfoHelperTests/CSSystemInfoHelperDeprecatedTests.m new file mode 100644 index 0000000..287a67b --- /dev/null +++ b/CSSystemInfoHelperTests/CSSystemInfoHelperDeprecatedTests.m @@ -0,0 +1,216 @@ +// +// CSSystemInfoHelperDeprecatedTests.m +// CSSystemInfoHelperTests +// +// Created by Cătălin Stan on 18/06/2021. +// + +#import + +#import + +#import "CSSystemInfoProviderMock.h" +#import "CSNetworkInterface+Internal.h" +#import "CSSystemInfoHelper+Internal.h" + +@interface CSSystemInfoHelperDeprecatedTests : XCTestCase + +@property (nonatomic, readonly) NSArray *randomIPv4Interfaces; +@property (nonatomic, readonly) NSArray *randomIPv6Interfaces; +@property (nonatomic, readonly) NSArray *randomInterfaces; + +@property (nonatomic, readonly) CSNetworkInterface *defaultIPv4NetworkInterface; +@property (nonatomic, readonly) CSNetworkInterface *randomIPv4Interface; + +@end + +@implementation CSSystemInfoHelperDeprecatedTests + + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" + +- (void)test_AllIPAddresses_withFailingSystemInfoProvider_ShouldNotBeNil { + CSSystemInfoProviderMock *provider = [CSSystemInfoProviderMock failingProviderWithError:nil]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:provider]; + + XCTAssertNotNil(helper.AllIPAddresses); +} + +- (void)test_AllIPAddresses_withFailingSystemInfoProvider_ShouldContainZeroObjects { + CSSystemInfoProviderMock *provider = [CSSystemInfoProviderMock failingProviderWithError:nil]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:provider]; + + XCTAssertEqual(0, helper.AllIPAddresses.count); +} + +- (void)test_AllIPAddresses_withNoNetworkInterfaces_ShouldNotBeNil { + CSSystemInfoProviderMock *provider = [CSSystemInfoProviderMock succeedingProviderWithNetworkInterfaces:NSArray.array]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:provider]; + + XCTAssertNotNil(helper.AllIPAddresses); +} + +- (void)test_AllIPAddresses_withNoNetworkInterfaces_ShouldContainZeroObjects { + CSSystemInfoProviderMock *provider = [CSSystemInfoProviderMock succeedingProviderWithNetworkInterfaces:NSArray.array]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:provider]; + + XCTAssertEqual(0, helper.AllIPAddresses.count); +} + +- (void)test_AllIPAddresses_withNoIPv4NetworkInterfaces_ShouldNotBeNil { + CSSystemInfoProviderMock *provider = [CSSystemInfoProviderMock succeedingProviderWithNetworkInterfaces:self.randomIPv6Interfaces]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:provider]; + + XCTAssertNotNil(helper.AllIPAddresses); +} + +- (void)test_AllIPAddresses_withNoIPv4NetworkInterfaces_ShouldContainZeroObjects { + CSSystemInfoProviderMock *provider = [CSSystemInfoProviderMock succeedingProviderWithNetworkInterfaces:self.randomIPv6Interfaces]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:provider]; + + XCTAssertEqual(0, helper.AllIPAddresses.count); +} + +- (void)test_AllIPAddresses_withIPv4NetworkInterfaces_ShouldContainAllInterfaces { + NSArray *expectedInterfaces = self.randomIPv4Interfaces; + CSSystemInfoProviderMock *provider = [CSSystemInfoProviderMock succeedingProviderWithNetworkInterfaces:expectedInterfaces]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:provider]; + + NSDictionary *result = helper.AllIPAddresses; + for (CSNetworkInterface *interface in expectedInterfaces) { + XCTAssertNotNil(result[interface.name]); + XCTAssertEqual(result[interface.name], interface.address); + } +} + +- (void)test_AllIPAddresses_withIPv4NetworkInterfaces_ShouldContainSameNumberOfInterfaces { + NSArray *expectedInterfaces = self.randomIPv4Interfaces; + CSSystemInfoProviderMock *provider = [CSSystemInfoProviderMock succeedingProviderWithNetworkInterfaces:expectedInterfaces]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:provider]; + + XCTAssertEqual(expectedInterfaces.count, helper.AllIPAddresses.count); +} + +- (void)test_AllIPAddresses_withMixedNetworkInterfaces_ShouldContainAllIPv4Interfaces { + NSArray *interfaces = self.randomInterfaces; + CSSystemInfoProviderMock *provider = [CSSystemInfoProviderMock succeedingProviderWithNetworkInterfaces:interfaces]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:provider]; + NSArray *expectedInterfaces = [interfaces filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(CSNetworkInterface *_Nullable evaluatedObject, NSDictionary * _Nullable bindings) { + return evaluatedObject.family == AF_INET; + }]]; + + NSDictionary *result = helper.AllIPAddresses; + for (CSNetworkInterface *interface in expectedInterfaces) { + XCTAssertNotNil(result[interface.name]); + XCTAssertEqual(result[interface.name], interface.address); + } +} + +- (void)test_AllIPAddresses_withMixedNetworkInterfaces_ShouldContainSameNumberOfInterfaces { + NSArray *interfaces = self.randomInterfaces; + CSSystemInfoProviderMock *provider = [CSSystemInfoProviderMock succeedingProviderWithNetworkInterfaces:interfaces]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:provider]; + NSArray *expectedInterfaces = [interfaces filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(CSNetworkInterface *_Nullable evaluatedObject, NSDictionary * _Nullable bindings) { + return evaluatedObject.family == AF_INET; + }]]; + + XCTAssertEqual(expectedInterfaces.count, helper.AllIPAddresses.count); +} + + +- (void)test_IPAddress_withFailingSystemInfoProvider_ShouldNotBeNil { + CSSystemInfoProviderMock *provider = [CSSystemInfoProviderMock failingProviderWithError:nil]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:provider]; + + XCTAssertNotNil(helper.IPAddress); +} + +- (void)test_IPAddress_withFailingSystemInfoProvider_ShouldReturnIPAddressNone { + CSSystemInfoProviderMock *provider = [CSSystemInfoProviderMock failingProviderWithError:nil]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:provider]; + + XCTAssertEqual(helper.IPAddress, CSSystemInfoHelperIPAddressNone); +} + +- (void)test_IPAddress_withNoNetworkInterfaces_ShouldNotBeNil { + CSSystemInfoProviderMock *provider = [CSSystemInfoProviderMock succeedingProviderWithNetworkInterfaces:@[]]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:provider]; + + XCTAssertNotNil(helper.IPAddress); +} + +- (void)test_IPAddress_withNoNetworkInterfaces_ShouldReturnIPAddressNone { + CSSystemInfoProviderMock *provider = [CSSystemInfoProviderMock succeedingProviderWithNetworkInterfaces:@[]]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:provider]; + + XCTAssertEqual(helper.IPAddress, CSSystemInfoHelperIPAddressNone); +} + +- (void)test_IPAddress_withNoIPv4NetworkInterfaces_ShouldNotBeNil { + CSSystemInfoProviderMock *provider = [CSSystemInfoProviderMock succeedingProviderWithNetworkInterfaces:self.randomIPv6Interfaces]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:provider]; + + XCTAssertNotNil(helper.IPAddress); +} + +- (void)test_IPAddress_withNoIPv4NetworkInterfaces_ShouldReturnIPAddressNone { + CSSystemInfoProviderMock *provider = [CSSystemInfoProviderMock succeedingProviderWithNetworkInterfaces:self.randomIPv6Interfaces]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:provider]; + + XCTAssertEqual(helper.IPAddress, CSSystemInfoHelperIPAddressNone); +} + +- (void)test_IPAddress_withDefaultNetworkInterface_ShouldReturnDefaultInterface { + CSNetworkInterface *expectedInterface = self.defaultIPv4NetworkInterface; + NSArray *interfaces = self.randomInterfaces; + interfaces = [interfaces arrayByAddingObject:expectedInterface]; + CSSystemInfoProviderMock *provider = [CSSystemInfoProviderMock succeedingProviderWithNetworkInterfaces:interfaces]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:provider]; + + XCTAssertEqual(helper.IPAddress, expectedInterface.address); +} + +- (void)test_IPAddress_withNoDefaultNetworkInterface_ShouldReturnFirstInterface { + NSArray *interfaces = self.randomIPv4Interfaces; + CSSystemInfoProviderMock *provider = [CSSystemInfoProviderMock succeedingProviderWithNetworkInterfaces:interfaces]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:provider]; + + XCTAssertEqual(helper.IPAddress, interfaces.firstObject.address); +} + +#pragma mark - Private Helpers + +- (NSArray *)randomInterfaces:(sa_family_t)family { + NSUInteger count = 16; + NSMutableArray *result = [NSMutableArray arrayWithCapacity:count]; + for(NSUInteger i = 0; i < count; i++) { + [result addObject:[self randomInterface:family]]; + } + return result; +} +- (NSArray *)randomInterfaces { + return [self randomInterfaces:AF_UNSPEC]; +} +- (NSArray *)randomIPv6Interfaces { + return [self randomInterfaces:AF_INET6]; +} +- (NSArray *)randomIPv4Interfaces { + return [self randomInterfaces:AF_INET]; +} + +- (CSNetworkInterface *)defaultIPv4NetworkInterface { + return [[CSNetworkInterface alloc] initWithName:CSSystemInfoHelperDefaultInterface address:NSUUID.UUID.UUIDString family:AF_INET]; +} + +- (CSNetworkInterface *)randomIPv4Interface { + return [self randomInterface:AF_INET]; +} + +- (CSNetworkInterface *)randomInterface:(sa_family_t)family { + sa_family_t f = family != AF_UNSPEC ? family : (arc4random() % 2 == 0 ? AF_INET : AF_INET6); + return [[CSNetworkInterface alloc] initWithName:NSUUID.UUID.UUIDString address:NSUUID.UUID.UUIDString family:f]; +} + +#pragma clang diagnostic pop +@end diff --git a/CSSystemInfoHelperTests/CSSystemInfoHelperTests.m b/CSSystemInfoHelperTests/CSSystemInfoHelperTests.m index 0003944..2d144a5 100644 --- a/CSSystemInfoHelperTests/CSSystemInfoHelperTests.m +++ b/CSSystemInfoHelperTests/CSSystemInfoHelperTests.m @@ -7,7 +7,9 @@ // #import -#import + +#import "CSSystemInfoProviderMock.h" +#import "CSSystemInfoHelper+Internal.h" @interface CSSystemInfoHelperTests : XCTestCase @@ -25,34 +27,29 @@ - (void)test_sharedHelper_IsSigleton { XCTAssertEqual(helper1.hash, helper2.hash); } -- (void)test_AllIPAddresses_ShouldNotBeNil { - CSSystemInfoHelper *helper = [CSSystemInfoHelper new]; - XCTAssertNotNil(helper.AllIPAddresses); -} - -- (void)test_AllIPAddresses_ShouldContainAtLeastOneEntry { - CSSystemInfoHelper *helper = [CSSystemInfoHelper new]; - XCTAssertGreaterThan(helper.AllIPAddresses.count, 0); +- (void)test_networkInterfaces_withFailingSystemInfoProvider_ShouldBeNil { + CSSystemInfoProviderMock *provider = [CSSystemInfoProviderMock failingProviderWithError:nil]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:provider]; + + XCTAssertNil(helper.networkInterfaces); } -- (void)test_IPAddress_DoesNotThrow { - CSSystemInfoHelper *helper = [CSSystemInfoHelper new]; - XCTAssertNoThrow(helper.IPAddress); -} - -- (void)test_IPAddress_ShouldNotBeNil { - CSSystemInfoHelper *helper = [CSSystemInfoHelper new]; - XCTAssertNotNil(helper.IPAddress); +- (void)test_networkInterfaces_withSucceedingSystemInfoProvider_ShouldBeIdenticalToProvidedInterfaces { + NSArray *expectedInterfaces = [NSArray array]; + CSSystemInfoProviderMock *provider = [CSSystemInfoProviderMock succeedingProviderWithNetworkInterfaces:expectedInterfaces]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:provider]; + + XCTAssertEqual(expectedInterfaces, helper.networkInterfaces); } - (void)test_SystemInfo_DoesNotThow { - CSSystemInfoHelper *helper = [CSSystemInfoHelper new]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:nil]; XCTAssertNoThrow(helper.systemInfo); } - (void)test_SystemInfo_AllKeys_ShouldNotBeNil { - CSSystemInfoHelper *helper = [CSSystemInfoHelper new]; - + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:nil]; + XCTAssertNotNil(helper.systemInfo[CSSystemInfoKeySysname], @"The key CSSystemInfoKeySysname is not pressent"); XCTAssertNotNil(helper.systemInfo[CSSystemInfoKeyNodename], @"The key CSSystemInfoKeyNodename is not pressent"); XCTAssertNotNil(helper.systemInfo[CSSystemInfoKeyRelease], @"The key CSSystemInfoKeyRelease is not pressent"); @@ -61,8 +58,8 @@ - (void)test_SystemInfo_AllKeys_ShouldNotBeNil { } - (void)test_SystemInfo_AllKeys_ShouldNotBeEmpty { - CSSystemInfoHelper *helper = [CSSystemInfoHelper new]; - + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:nil]; + XCTAssertGreaterThan(helper.systemInfo[CSSystemInfoKeySysname].length, 0, @"The key CSSystemInfoKeySysname is empty"); XCTAssertGreaterThan(helper.systemInfo[CSSystemInfoKeyNodename].length, 0, @"The key CSSystemInfoKeyNodename is empty"); XCTAssertGreaterThan(helper.systemInfo[CSSystemInfoKeyRelease].length, 0, @"The key CSSystemInfoKeyRelease is empty"); @@ -71,73 +68,73 @@ - (void)test_SystemInfo_AllKeys_ShouldNotBeEmpty { } - (void)test_SystemInfoString_DoesNotThrow { - CSSystemInfoHelper *helper = [CSSystemInfoHelper new]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:nil]; XCTAssertNoThrow(helper.systemInfoString); } - (void)test_SystemInfoString_ShouldNotBeNil { - CSSystemInfoHelper *helper = [CSSystemInfoHelper new]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:nil]; XCTAssertNotNil(helper.systemInfoString); } - (void)test_SystemInfoString_ShouldNotBeEmpty { - CSSystemInfoHelper *helper = [CSSystemInfoHelper new]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:nil]; XCTAssertGreaterThan(helper.systemInfoString.length, 0); } - (void)test_SystemVersionString_DoesNotThrow { - CSSystemInfoHelper *helper = [CSSystemInfoHelper new]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:nil]; XCTAssertNoThrow(helper.systemVersionString); } - (void)test_SystemVersionString_ShouldNotBeNil { - CSSystemInfoHelper *helper = [CSSystemInfoHelper new]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:nil]; XCTAssertNotNil(helper.systemVersionString); } - (void)test_SystemVersionString_ShouldNotBeEmpty { - CSSystemInfoHelper *helper = [CSSystemInfoHelper new]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:nil]; XCTAssertGreaterThan(helper.systemVersionString.length, 0); } - (void)test_MemoryUsage_DoesNotThrow { - CSSystemInfoHelper *helper = [CSSystemInfoHelper new]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:nil]; XCTAssertNoThrow(helper.memoryUsage); } - (void)test_MemoryUsage_IsGreaterThan0 { - CSSystemInfoHelper *helper = [CSSystemInfoHelper new]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:nil]; XCTAssertGreaterThan(helper.memoryUsage, 0); } - (void)test_MemoryUsageString_DoesNotThrow { - CSSystemInfoHelper *helper = [CSSystemInfoHelper new]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:nil]; XCTAssertNoThrow(helper.memoryUsageString); } - (void)test_MemoryUsageString_ShouldNotBeNil { - CSSystemInfoHelper *helper = [CSSystemInfoHelper new]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:nil]; XCTAssertNotNil(helper.memoryUsageString); } - (void)test_MemoryUsageString_ShouldNotBeEmpty { - CSSystemInfoHelper *helper = [CSSystemInfoHelper new]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:nil]; XCTAssertGreaterThan(helper.memoryUsageString.length, 0); } #if TARGET_OS_OSX - (void)test_PlatformUUID_DoesNotThrow { - CSSystemInfoHelper *helper = [CSSystemInfoHelper new]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:nil]; XCTAssertNoThrow(helper.platformUUID); } - (void)test_PlatformUUID_ShouldNotBeNil { - CSSystemInfoHelper *helper = [CSSystemInfoHelper new]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:nil]; XCTAssertNotNil(helper.platformUUID); } - (void)test_PlatformUUID_ShouldNotBeEmpty { - CSSystemInfoHelper *helper = [CSSystemInfoHelper new]; + CSSystemInfoHelper *helper = [[CSSystemInfoHelper alloc] initWithSystemInfoProvider:nil]; XCTAssertGreaterThan(helper.platformUUID.length, 0); } #endif diff --git a/CSSystemInfoHelperTests/CSSystemInfoProviderMock.h b/CSSystemInfoHelperTests/CSSystemInfoProviderMock.h new file mode 100644 index 0000000..7aa672f --- /dev/null +++ b/CSSystemInfoHelperTests/CSSystemInfoProviderMock.h @@ -0,0 +1,20 @@ +// +// MyClass.h +// +// +// Created by Cătălin Stan on 18/06/2021. +// + +#import +#import "CSSystemInfoProvider.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface CSSystemInfoProviderMock : NSObject + ++ (instancetype)failingProviderWithError:(NSError *_Nullable)error; ++ (instancetype)succeedingProviderWithNetworkInterfaces:(NSArray *)networkInterfaces; + +@end + +NS_ASSUME_NONNULL_END diff --git a/CSSystemInfoHelperTests/CSSystemInfoProviderMock.m b/CSSystemInfoHelperTests/CSSystemInfoProviderMock.m new file mode 100644 index 0000000..35a17e7 --- /dev/null +++ b/CSSystemInfoHelperTests/CSSystemInfoProviderMock.m @@ -0,0 +1,46 @@ +// +// MyClass.m +// +// +// Created by Cătălin Stan on 18/06/2021. +// + +#import "CSSystemInfoProviderMock.h" +#import "CSNetworkInterface+Internal.h" + +@interface CSSystemInfoProviderMock () + +@property BOOL shouldSucceed; +@property NSError *error; +@property NSArray *networkInterfaces; + +@end + +@implementation CSSystemInfoProviderMock + ++ (instancetype)failingProviderWithError:(NSError *)error { + CSSystemInfoProviderMock * provider = [CSSystemInfoProviderMock new]; + provider.shouldSucceed = NO; + provider.error = error; + return provider; +} + ++ (instancetype)succeedingProviderWithNetworkInterfaces:(NSArray *)networkInterfaces { + CSSystemInfoProviderMock * provider = [CSSystemInfoProviderMock new]; + provider.shouldSucceed = YES; + provider.networkInterfaces = networkInterfaces; + return provider; +} + +- (nullable NSArray *)queryNetworkInterfaces:(NSError *__autoreleasing _Nullable * _Nullable)error { + if (!self.shouldSucceed) { + if (error) { + *error = self.error; + } + return nil; + } + + return self.networkInterfaces; +} + +@end diff --git a/Package.swift b/Package.swift index 5b5602f..dc906c0 100644 --- a/Package.swift +++ b/Package.swift @@ -30,6 +30,7 @@ let package = Package( path: "CSSystemInfoHelperTests", exclude: ["Info.plist"], cSettings: [ - .headerSearchPath("../CSSystemInfoHelper/Headers")]) + .headerSearchPath("../CSSystemInfoHelper/Headers"), + .headerSearchPath("../CSSystemInfoHelper/Sources")]) ] )