// Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/apple/foundation_util.h" #include #include #include #include #include "base/apple/scoped_cftyperef.h" #include "base/files/file_path.h" #include "base/format_macros.h" #include "base/strings/stringprintf.h" #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" #import "testing/gtest_mac.h" namespace base::apple { TEST(FoundationUtilTest, CFCast) { // Build out the CF types to be tested as empty containers. ScopedCFTypeRef test_array( CFArrayCreate(nullptr, nullptr, 0, &kCFTypeArrayCallBacks)); ScopedCFTypeRef test_array_mutable( CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks)); ScopedCFTypeRef test_bag( CFBagCreate(nullptr, nullptr, 0, &kCFTypeBagCallBacks)); ScopedCFTypeRef test_bag_mutable( CFBagCreateMutable(nullptr, 0, &kCFTypeBagCallBacks)); CFTypeRef test_bool = kCFBooleanTrue; ScopedCFTypeRef test_data(CFDataCreate(nullptr, nullptr, 0)); ScopedCFTypeRef test_data_mutable(CFDataCreateMutable(nullptr, 0)); ScopedCFTypeRef test_date(CFDateCreate(nullptr, 0)); ScopedCFTypeRef test_dict(CFDictionaryCreate( nullptr, nullptr, nullptr, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); ScopedCFTypeRef test_dict_mutable( CFDictionaryCreateMutable(nullptr, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); int int_val = 256; ScopedCFTypeRef test_number( CFNumberCreate(nullptr, kCFNumberIntType, &int_val)); CFTypeRef test_null = kCFNull; ScopedCFTypeRef test_set( CFSetCreate(nullptr, nullptr, 0, &kCFTypeSetCallBacks)); ScopedCFTypeRef test_set_mutable( CFSetCreateMutable(nullptr, 0, &kCFTypeSetCallBacks)); ScopedCFTypeRef test_str(CFStringCreateWithBytes( nullptr, nullptr, 0, kCFStringEncodingASCII, false)); CFTypeRef test_str_const = CFSTR("hello"); ScopedCFTypeRef test_str_mutable( CFStringCreateMutable(nullptr, 0)); // Make sure the allocations of CF types are good. EXPECT_TRUE(test_array); EXPECT_TRUE(test_array_mutable); EXPECT_TRUE(test_bag); EXPECT_TRUE(test_bag_mutable); EXPECT_TRUE(test_bool); EXPECT_TRUE(test_data); EXPECT_TRUE(test_data_mutable); EXPECT_TRUE(test_date); EXPECT_TRUE(test_dict); EXPECT_TRUE(test_dict_mutable); EXPECT_TRUE(test_number); EXPECT_TRUE(test_null); EXPECT_TRUE(test_set); EXPECT_TRUE(test_set_mutable); EXPECT_TRUE(test_str); EXPECT_TRUE(test_str_const); EXPECT_TRUE(test_str_mutable); // Casting the CFTypeRef objects correctly provides the same pointer. EXPECT_EQ(test_array.get(), CFCast(test_array.get())); EXPECT_EQ(test_array_mutable.get(), CFCast(test_array_mutable.get())); EXPECT_EQ(test_bag.get(), CFCast(test_bag.get())); EXPECT_EQ(test_bag_mutable.get(), CFCast(test_bag_mutable.get())); EXPECT_EQ(test_bool, CFCast(test_bool)); EXPECT_EQ(test_data.get(), CFCast(test_data.get())); EXPECT_EQ(test_data_mutable.get(), CFCast(test_data_mutable.get())); EXPECT_EQ(test_date.get(), CFCast(test_date.get())); EXPECT_EQ(test_dict.get(), CFCast(test_dict.get())); EXPECT_EQ(test_dict_mutable.get(), CFCast(test_dict_mutable.get())); EXPECT_EQ(test_number.get(), CFCast(test_number.get())); EXPECT_EQ(test_null, CFCast(test_null)); EXPECT_EQ(test_set.get(), CFCast(test_set.get())); EXPECT_EQ(test_set_mutable.get(), CFCast(test_set_mutable.get())); EXPECT_EQ(test_str.get(), CFCast(test_str.get())); EXPECT_EQ(test_str_const, CFCast(test_str_const)); EXPECT_EQ(test_str_mutable.get(), CFCast(test_str_mutable.get())); // When given an incorrect CF cast, provide nullptr. EXPECT_FALSE(CFCast(test_array.get())); EXPECT_FALSE(CFCast(test_array_mutable.get())); EXPECT_FALSE(CFCast(test_bag.get())); EXPECT_FALSE(CFCast(test_bag_mutable.get())); EXPECT_FALSE(CFCast(test_bool)); EXPECT_FALSE(CFCast(test_data.get())); EXPECT_FALSE(CFCast(test_data_mutable.get())); EXPECT_FALSE(CFCast(test_date.get())); EXPECT_FALSE(CFCast(test_dict.get())); EXPECT_FALSE(CFCast(test_dict_mutable.get())); EXPECT_FALSE(CFCast(test_number.get())); EXPECT_FALSE(CFCast(test_null)); EXPECT_FALSE(CFCast(test_set.get())); EXPECT_FALSE(CFCast(test_set_mutable.get())); EXPECT_FALSE(CFCast(test_str.get())); EXPECT_FALSE(CFCast(test_str_const)); EXPECT_FALSE(CFCast(test_str_mutable.get())); // Giving a nullptr provides a nullptr. EXPECT_FALSE(CFCast(nullptr)); EXPECT_FALSE(CFCast(nullptr)); EXPECT_FALSE(CFCast(nullptr)); EXPECT_FALSE(CFCast(nullptr)); EXPECT_FALSE(CFCast(nullptr)); EXPECT_FALSE(CFCast(nullptr)); EXPECT_FALSE(CFCast(nullptr)); EXPECT_FALSE(CFCast(nullptr)); EXPECT_FALSE(CFCast(nullptr)); EXPECT_FALSE(CFCast(nullptr)); // CFCastStrict: correct cast results in correct pointer being returned. EXPECT_EQ(test_array.get(), CFCastStrict(test_array.get())); EXPECT_EQ(test_array_mutable.get(), CFCastStrict(test_array_mutable.get())); EXPECT_EQ(test_bag.get(), CFCastStrict(test_bag.get())); EXPECT_EQ(test_bag_mutable.get(), CFCastStrict(test_bag_mutable.get())); EXPECT_EQ(test_bool, CFCastStrict(test_bool)); EXPECT_EQ(test_data.get(), CFCastStrict(test_data.get())); EXPECT_EQ(test_data_mutable.get(), CFCastStrict(test_data_mutable.get())); EXPECT_EQ(test_date.get(), CFCastStrict(test_date.get())); EXPECT_EQ(test_dict.get(), CFCastStrict(test_dict.get())); EXPECT_EQ(test_dict_mutable.get(), CFCastStrict(test_dict_mutable.get())); EXPECT_EQ(test_number.get(), CFCastStrict(test_number.get())); EXPECT_EQ(test_null, CFCastStrict(test_null)); EXPECT_EQ(test_set.get(), CFCastStrict(test_set.get())); EXPECT_EQ(test_set_mutable.get(), CFCastStrict(test_set_mutable.get())); EXPECT_EQ(test_str.get(), CFCastStrict(test_str.get())); EXPECT_EQ(test_str_const, CFCastStrict(test_str_const)); EXPECT_EQ(test_str_mutable.get(), CFCastStrict(test_str_mutable.get())); // CFCastStrict: Giving a nullptr provides a nullptr. EXPECT_FALSE(CFCastStrict(nullptr)); EXPECT_FALSE(CFCastStrict(nullptr)); EXPECT_FALSE(CFCastStrict(nullptr)); EXPECT_FALSE(CFCastStrict(nullptr)); EXPECT_FALSE(CFCastStrict(nullptr)); EXPECT_FALSE(CFCastStrict(nullptr)); EXPECT_FALSE(CFCastStrict(nullptr)); EXPECT_FALSE(CFCastStrict(nullptr)); EXPECT_FALSE(CFCastStrict(nullptr)); EXPECT_FALSE(CFCastStrict(nullptr)); } TEST(FoundationUtilTest, ObjCCast) { @autoreleasepool { id test_array = @[]; id test_array_mutable = [NSMutableArray array]; id test_data = [NSData data]; id test_data_mutable = [NSMutableData dataWithCapacity:10]; id test_date = [NSDate date]; id test_dict = @{@"meaning" : @42}; id test_dict_mutable = [NSMutableDictionary dictionaryWithCapacity:10]; id test_number = @42; id test_null = [NSNull null]; id test_set = [NSSet setWithObject:@"string object"]; id test_set_mutable = [NSMutableSet setWithCapacity:10]; id test_str = [NSString string]; id test_str_const = @"bonjour"; id test_str_mutable = [NSMutableString stringWithCapacity:10]; // Make sure the allocations of NS types are good. EXPECT_TRUE(test_array); EXPECT_TRUE(test_array_mutable); EXPECT_TRUE(test_data); EXPECT_TRUE(test_data_mutable); EXPECT_TRUE(test_date); EXPECT_TRUE(test_dict); EXPECT_TRUE(test_dict_mutable); EXPECT_TRUE(test_number); EXPECT_TRUE(test_null); EXPECT_TRUE(test_set); EXPECT_TRUE(test_set_mutable); EXPECT_TRUE(test_str); EXPECT_TRUE(test_str_const); EXPECT_TRUE(test_str_mutable); // Casting the id correctly provides the same pointer. EXPECT_EQ(test_array, ObjCCast(test_array)); EXPECT_EQ(test_array_mutable, ObjCCast(test_array_mutable)); EXPECT_EQ(test_data, ObjCCast(test_data)); EXPECT_EQ(test_data_mutable, ObjCCast(test_data_mutable)); EXPECT_EQ(test_date, ObjCCast(test_date)); EXPECT_EQ(test_dict, ObjCCast(test_dict)); EXPECT_EQ(test_dict_mutable, ObjCCast(test_dict_mutable)); EXPECT_EQ(test_number, ObjCCast(test_number)); EXPECT_EQ(test_null, ObjCCast(test_null)); EXPECT_EQ(test_set, ObjCCast(test_set)); EXPECT_EQ(test_set_mutable, ObjCCast(test_set_mutable)); EXPECT_EQ(test_str, ObjCCast(test_str)); EXPECT_EQ(test_str_const, ObjCCast(test_str_const)); EXPECT_EQ(test_str_mutable, ObjCCast(test_str_mutable)); // When given an incorrect ObjC cast, provide nil. EXPECT_FALSE(ObjCCast(test_array)); EXPECT_FALSE(ObjCCast(test_array_mutable)); EXPECT_FALSE(ObjCCast(test_data)); EXPECT_FALSE(ObjCCast(test_data_mutable)); EXPECT_FALSE(ObjCCast(test_date)); EXPECT_FALSE(ObjCCast(test_dict)); EXPECT_FALSE(ObjCCast(test_dict_mutable)); EXPECT_FALSE(ObjCCast(test_number)); EXPECT_FALSE(ObjCCast(test_null)); EXPECT_FALSE(ObjCCast(test_set)); EXPECT_FALSE(ObjCCast(test_set_mutable)); EXPECT_FALSE(ObjCCast(test_str)); EXPECT_FALSE(ObjCCast(test_str_const)); EXPECT_FALSE(ObjCCast(test_str_mutable)); // Giving a nil provides a nil. EXPECT_FALSE(ObjCCast(nil)); EXPECT_FALSE(ObjCCast(nil)); EXPECT_FALSE(ObjCCast(nil)); EXPECT_FALSE(ObjCCast(nil)); EXPECT_FALSE(ObjCCast(nil)); EXPECT_FALSE(ObjCCast(nil)); EXPECT_FALSE(ObjCCast(nil)); EXPECT_FALSE(ObjCCast(nil)); // ObjCCastStrict: correct cast results in correct pointer being returned. EXPECT_EQ(test_array, ObjCCastStrict(test_array)); EXPECT_EQ(test_array_mutable, ObjCCastStrict(test_array_mutable)); EXPECT_EQ(test_data, ObjCCastStrict(test_data)); EXPECT_EQ(test_data_mutable, ObjCCastStrict(test_data_mutable)); EXPECT_EQ(test_date, ObjCCastStrict(test_date)); EXPECT_EQ(test_dict, ObjCCastStrict(test_dict)); EXPECT_EQ(test_dict_mutable, ObjCCastStrict(test_dict_mutable)); EXPECT_EQ(test_number, ObjCCastStrict(test_number)); EXPECT_EQ(test_null, ObjCCastStrict(test_null)); EXPECT_EQ(test_set, ObjCCastStrict(test_set)); EXPECT_EQ(test_set_mutable, ObjCCastStrict(test_set_mutable)); EXPECT_EQ(test_str, ObjCCastStrict(test_str)); EXPECT_EQ(test_str_const, ObjCCastStrict(test_str_const)); EXPECT_EQ(test_str_mutable, ObjCCastStrict(test_str_mutable)); // ObjCCastStrict: Giving a nil provides a nil. EXPECT_FALSE(ObjCCastStrict(nil)); EXPECT_FALSE(ObjCCastStrict(nil)); EXPECT_FALSE(ObjCCastStrict(nil)); EXPECT_FALSE(ObjCCastStrict(nil)); EXPECT_FALSE(ObjCCastStrict(nil)); EXPECT_FALSE(ObjCCastStrict(nil)); EXPECT_FALSE(ObjCCastStrict(nil)); EXPECT_FALSE(ObjCCastStrict(nil)); } } TEST(FoundationUtilTest, GetValueFromDictionary) { int one = 1, two = 2, three = 3; ScopedCFTypeRef cf_one( CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &one)); ScopedCFTypeRef cf_two( CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &two)); ScopedCFTypeRef cf_three( CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &three)); CFStringRef keys[] = {CFSTR("one"), CFSTR("two"), CFSTR("three")}; CFNumberRef values[] = {cf_one.get(), cf_two.get(), cf_three.get()}; static_assert(std::size(keys) == std::size(values), "keys and values arrays must have the same size"); ScopedCFTypeRef test_dict(CFDictionaryCreate( kCFAllocatorDefault, reinterpret_cast(keys), reinterpret_cast(values), std::size(values), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); // GetValueFromDictionary<>(_, _) should produce the correct // expected output. EXPECT_EQ(values[0], GetValueFromDictionary(test_dict.get(), CFSTR("one"))); EXPECT_EQ(values[1], GetValueFromDictionary(test_dict.get(), CFSTR("two"))); EXPECT_EQ(values[2], GetValueFromDictionary(test_dict.get(), CFSTR("three"))); // Bad input should produce bad output. EXPECT_FALSE( GetValueFromDictionary(test_dict.get(), CFSTR("four"))); EXPECT_FALSE( GetValueFromDictionary(test_dict.get(), CFSTR("one"))); } TEST(FoundationUtilTest, FilePathToNSURL) { EXPECT_NSEQ(nil, FilePathToNSURL(FilePath())); EXPECT_NSEQ([NSURL fileURLWithPath:@"/a/b"], FilePathToNSURL(FilePath("/a/b"))); } TEST(FoundationUtilTest, FilePathToNSString) { EXPECT_NSEQ(nil, FilePathToNSString(FilePath())); EXPECT_NSEQ(@"/a/b", FilePathToNSString(FilePath("/a/b"))); } TEST(FoundationUtilTest, NSStringToFilePath) { EXPECT_EQ(FilePath(), NSStringToFilePath(nil)); EXPECT_EQ(FilePath(), NSStringToFilePath(@"")); EXPECT_EQ(FilePath("/a/b"), NSStringToFilePath(@"/a/b")); } TEST(FoundationUtilTest, FilePathToCFURL) { ScopedCFTypeRef url(CFURLCreateWithFileSystemPath( nullptr, CFSTR("/a/b"), kCFURLPOSIXPathStyle, false)); EXPECT_TRUE(CFEqual(url.get(), FilePathToCFURL(FilePath("/a/b")).get())); } TEST(FoundationUtilTest, CFRangeToNSRange) { NSRange range_out; EXPECT_TRUE(CFRangeToNSRange(CFRangeMake(10, 5), &range_out)); EXPECT_EQ(10UL, range_out.location); EXPECT_EQ(5UL, range_out.length); EXPECT_FALSE(CFRangeToNSRange(CFRangeMake(-1, 5), &range_out)); EXPECT_FALSE(CFRangeToNSRange(CFRangeMake(5, -1), &range_out)); EXPECT_FALSE(CFRangeToNSRange(CFRangeMake(-1, -1), &range_out)); EXPECT_FALSE(CFRangeToNSRange(CFRangeMake(LONG_MAX, LONG_MAX), &range_out)); EXPECT_FALSE(CFRangeToNSRange(CFRangeMake(LONG_MIN, LONG_MAX), &range_out)); } TEST(StringNumberConversionsTest, FormatNSInteger) { // The PRI[dxu]NS macro assumes that NSInteger is a typedef to "int" on // 32-bit architecture and a typedef to "long" on 64-bit architecture // (respectively "unsigned int" and "unsigned long" for NSUInteger). Use // pointer incompatibility to validate this at compilation. #if defined(ARCH_CPU_64_BITS) typedef long FormatNSIntegerAsType; typedef unsigned long FormatNSUIntegerAsType; #else typedef int FormatNSIntegerAsType; typedef unsigned int FormatNSUIntegerAsType; #endif // defined(ARCH_CPU_64_BITS) NSInteger some_nsinteger; [[maybe_unused]] FormatNSIntegerAsType* pointer_to_some_nsinteger = &some_nsinteger; NSUInteger some_nsuinteger; [[maybe_unused]] FormatNSUIntegerAsType* pointer_to_some_nsuinteger = &some_nsuinteger; // Check that format specifier works correctly for NSInteger. const struct { NSInteger value; const char* expected; const char* expected_hex; } nsinteger_cases[] = { #if !defined(ARCH_CPU_64_BITS) {12345678, "12345678", "bc614e"}, {-12345678, "-12345678", "ff439eb2"}, #else {12345678, "12345678", "bc614e"}, {-12345678, "-12345678", "ffffffffff439eb2"}, {137451299150l, "137451299150", "2000bc614e"}, {-137451299150l, "-137451299150", "ffffffdfff439eb2"}, #endif // !defined(ARCH_CPU_64_BITS) }; for (const auto& nsinteger_case : nsinteger_cases) { EXPECT_EQ(nsinteger_case.expected, StringPrintf("%" PRIdNS, nsinteger_case.value)); EXPECT_EQ(nsinteger_case.expected_hex, StringPrintf("%" PRIxNS, nsinteger_case.value)); } // Check that format specifier works correctly for NSUInteger. const struct { NSUInteger value; const char* expected; const char* expected_hex; } nsuinteger_cases[] = { #if !defined(ARCH_CPU_64_BITS) {12345678u, "12345678", "bc614e"}, {4282621618u, "4282621618", "ff439eb2"}, #else {12345678u, "12345678", "bc614e"}, {4282621618u, "4282621618", "ff439eb2"}, {137451299150ul, "137451299150", "2000bc614e"}, {18446743936258252466ul, "18446743936258252466", "ffffffdfff439eb2"}, #endif // !defined(ARCH_CPU_64_BITS) }; for (const auto& nsuinteger_case : nsuinteger_cases) { EXPECT_EQ(nsuinteger_case.expected, StringPrintf("%" PRIuNS, nsuinteger_case.value)); EXPECT_EQ(nsuinteger_case.expected_hex, StringPrintf("%" PRIxNS, nsuinteger_case.value)); } } #define EXPECT_LOG_EQ(expected, val) \ EXPECT_EQ(expected, (std::ostringstream() << (val)).str()) TEST(FoundationLoggingTest, ObjCObject) { EXPECT_LOG_EQ("Hello, world!", @"Hello, world!"); } TEST(FoundationLoggingTest, ObjCNil) { EXPECT_LOG_EQ("(nil)", static_cast(nil)); } TEST(FoundationLoggingTest, CFRange) { EXPECT_LOG_EQ("{0, 100}", CFRangeMake(0, 100)); } TEST(FoundationLoggingTest, NSRange) { EXPECT_LOG_EQ("{0, 100}", NSMakeRange(0, 100)); } } // namespace base::apple