| Index: ios/net/clients/crn_forwarding_network_client_factory_unittest.mm
|
| diff --git a/ios/net/clients/crn_forwarding_network_client_factory_unittest.mm b/ios/net/clients/crn_forwarding_network_client_factory_unittest.mm
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..735c7f512aeb104256d650ac4ea84edcd38ed098
|
| --- /dev/null
|
| +++ b/ios/net/clients/crn_forwarding_network_client_factory_unittest.mm
|
| @@ -0,0 +1,220 @@
|
| +// Copyright 2014 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#import <Foundation/Foundation.h>
|
| +#import <objc/runtime.h>
|
| +
|
| +#include "base/mac/scoped_nsobject.h"
|
| +#include "base/strings/string_util.h"
|
| +#include "base/strings/sys_string_conversions.h"
|
| +#import "ios/net/clients/crn_forwarding_network_client_factory.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +@interface TestFactoryA : CRNForwardingNetworkClientFactory
|
| +@end
|
| +@implementation TestFactoryA
|
| +@end
|
| +
|
| +// B must appear before A
|
| +@interface TestFactoryB : CRNForwardingNetworkClientFactory
|
| +@end
|
| +@implementation TestFactoryB
|
| ++ (instancetype)mustApplyBefore { return [TestFactoryA class]; }
|
| +@end
|
| +
|
| +// C must appear after A
|
| +@interface TestFactoryC : CRNForwardingNetworkClientFactory
|
| +@end
|
| +@implementation TestFactoryC
|
| ++ (instancetype)mustApplyAfter { return [TestFactoryA class]; }
|
| +@end
|
| +
|
| +// D must appear after B and before C
|
| +@interface TestFactoryD : CRNForwardingNetworkClientFactory
|
| +@end
|
| +@implementation TestFactoryD
|
| ++ (instancetype)mustApplyAfter { return [TestFactoryB class]; }
|
| ++ (instancetype)mustApplyBefore { return [TestFactoryC class]; }
|
| +@end
|
| +
|
| +// E and F form a loop
|
| +@interface TestFactoryE : CRNForwardingNetworkClientFactory
|
| +@end
|
| +@interface TestFactoryF : CRNForwardingNetworkClientFactory
|
| +@end
|
| +@implementation TestFactoryE
|
| ++ (instancetype)mustApplyAfter { return [TestFactoryF class]; }
|
| +@end
|
| +@implementation TestFactoryF
|
| ++ (instancetype)mustApplyAfter { return [TestFactoryE class]; }
|
| +@end
|
| +
|
| +// G must appear before B and after C, so while not a loop, it can't be
|
| +// ordered consistently.
|
| +@interface TestFactoryG : CRNForwardingNetworkClientFactory
|
| +@end
|
| +@implementation TestFactoryG
|
| ++ (instancetype)mustApplyAfter { return [TestFactoryC class]; }
|
| ++ (instancetype)mustApplyBefore { return [TestFactoryB class]; }
|
| +@end
|
| +
|
| +namespace {
|
| +
|
| +class ForwardingNetworkClientFactoryTest : public testing::Test {
|
| + public:
|
| + ForwardingNetworkClientFactoryTest() {}
|
| +
|
| + void SetUp() override {
|
| + factory_a_.reset([[TestFactoryA alloc] init]);
|
| + factory_b_.reset([[TestFactoryB alloc] init]);
|
| + factory_c_.reset([[TestFactoryC alloc] init]);
|
| + factory_d_.reset([[TestFactoryD alloc] init]);
|
| + }
|
| +
|
| + protected:
|
| + base::scoped_nsobject<TestFactoryA> factory_a_;
|
| + base::scoped_nsobject<TestFactoryB> factory_b_;
|
| + base::scoped_nsobject<TestFactoryC> factory_c_;
|
| + base::scoped_nsobject<TestFactoryD> factory_d_;
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +TEST_F(ForwardingNetworkClientFactoryTest, SortFactories) {
|
| + base::scoped_nsobject<NSMutableArray> array([[NSMutableArray alloc] init]);
|
| +
|
| + // Add a, c, b.
|
| + EXPECT_TRUE([[TestFactoryA class] orderedOK]);
|
| + EXPECT_TRUE([[TestFactoryB class] orderedOK]);
|
| + EXPECT_TRUE([[TestFactoryC class] orderedOK]);
|
| + [array addObject:factory_a_.get()];
|
| + [array addObject:factory_b_.get()];
|
| + [array addObject:factory_c_.get()];
|
| + [array sortUsingSelector:@selector(orderRelativeTo:)];
|
| + // Expect b before a.
|
| + EXPECT_LT([array indexOfObject:factory_b_.get()],
|
| + [array indexOfObject:factory_a_.get()]);
|
| + // Expect c after a.
|
| + EXPECT_GT([array indexOfObject:factory_c_.get()],
|
| + [array indexOfObject:factory_a_.get()]);
|
| +
|
| + // Add d.
|
| + EXPECT_TRUE([[TestFactoryD class] orderedOK]);
|
| + [array addObject:factory_d_.get()];
|
| + [array sortUsingSelector:@selector(orderRelativeTo:)];
|
| + // Expect previous relations unchanged.
|
| + EXPECT_LT([array indexOfObject:factory_b_.get()],
|
| + [array indexOfObject:factory_a_.get()]);
|
| + EXPECT_GT([array indexOfObject:factory_c_.get()],
|
| + [array indexOfObject:factory_a_.get()]);
|
| + // Expect b before d.
|
| + EXPECT_LT([array indexOfObject:factory_b_.get()],
|
| + [array indexOfObject:factory_d_.get()]);
|
| + // Expect c after d.
|
| + EXPECT_GT([array indexOfObject:factory_c_.get()],
|
| + [array indexOfObject:factory_d_.get()]);
|
| +
|
| + // E and F are not OK.
|
| + EXPECT_FALSE([[TestFactoryE class] orderedOK]);
|
| + EXPECT_FALSE([[TestFactoryF class] orderedOK]);
|
| +
|
| + // G is not OK.
|
| + EXPECT_FALSE([[TestFactoryG class] orderedOK]);
|
| +}
|
| +
|
| +TEST_F(ForwardingNetworkClientFactoryTest, TestSubclassImplementations) {
|
| + // Look at all the subclasses of of CRNForwardingNetworkClientFactory.
|
| + // Make sure that each one implements at least one clientHandling.. method.
|
| + Class factory_superclass = [CRNForwardingNetworkClientFactory class];
|
| + std::string superclass_name = class_getName(factory_superclass);
|
| +
|
| + base::scoped_nsobject<NSMutableArray> subclasses(
|
| + [[NSMutableArray alloc] init]);
|
| +
|
| + // Look at every known class and find those that are subclasses of
|
| + // |factory_superclass|.
|
| + int class_count = objc_getClassList(nullptr, 0);
|
| + Class* classes = nullptr;
|
| + classes = static_cast<Class*>(malloc(sizeof(Class) * class_count));
|
| + class_count = objc_getClassList(classes, class_count);
|
| +
|
| + for (NSInteger i = 0; i < class_count; i++) {
|
| + std::string class_name = class_getName(classes[i]);
|
| + // Skip the test classes defined above.
|
| + if (StartsWithASCII(class_name, "TestFactory", false))
|
| + continue;
|
| +
|
| + Class subclass_super = classes[i];
|
| + int subclassing_count = 0;
|
| + // Walk up the class hiererchy from |classes[i]| to |factory_superclass|.
|
| + do {
|
| + subclass_super = class_getSuperclass(subclass_super);
|
| + subclassing_count++;
|
| + } while (subclass_super && subclass_super != factory_superclass);
|
| +
|
| + if (subclass_super == nil)
|
| + continue;
|
| +
|
| + // If |subclassing_count| > 1 we have found a class that is a subclass of
|
| + // a subclass of |factory_superclass|, which we want to (for now) flag.
|
| + EXPECT_EQ(1, subclassing_count)
|
| + << class_name << " is an indirect subclass of " << superclass_name;
|
| +
|
| + [subclasses addObject:classes[i]];
|
| + }
|
| +
|
| + // Get all of the methods defined in ForwardingNetworkClientFactoryTest and
|
| + // compile a list of those named "clientHandling...".
|
| + base::scoped_nsobject<NSMutableArray> client_handling_methods(
|
| + [[NSMutableArray alloc] init]);
|
| + unsigned int method_count;
|
| + Method* superclass_methods =
|
| + class_copyMethodList(factory_superclass, &method_count);
|
| + for (unsigned int i = 0; i < method_count; i++) {
|
| + NSString* method_name =
|
| + NSStringFromSelector(method_getName(superclass_methods[i]));
|
| + if ([method_name hasPrefix:@"clientHandling"]) {
|
| + [client_handling_methods addObject:method_name];
|
| + }
|
| + }
|
| + free(superclass_methods);
|
| +
|
| + // The superclass should implement at least one method.
|
| + EXPECT_LT(0u, [client_handling_methods count]);
|
| +
|
| + for (Class subclass in subclasses.get()) {
|
| + Method* methods = class_copyMethodList(subclass, &method_count);
|
| + // The subclass has to have > 0 methods
|
| + EXPECT_LT(0u, method_count);
|
| +
|
| + // Collect an array of the methods the class implements, then check each
|
| + // superclass clientHandling method to see if it's in the list.
|
| + base::scoped_nsobject<NSMutableArray> method_names(
|
| + [[NSMutableArray alloc] init]);
|
| + for (unsigned int i = 0; i < method_count; i++) {
|
| + [method_names addObject:NSStringFromSelector(method_getName(methods[i]))];
|
| + }
|
| + free(methods);
|
| +
|
| + base::scoped_nsobject<NSMutableArray> subclass_implementations(
|
| + [[NSMutableArray alloc] init]);
|
| + for (NSString* superclass_method_name in client_handling_methods.get()) {
|
| + if ([method_names indexOfObject:superclass_method_name] != NSNotFound) {
|
| + [subclass_implementations addObject:superclass_method_name];
|
| + }
|
| + }
|
| +
|
| + std::string class_name = class_getName(subclass);
|
| + std::string method_list = base::SysNSStringToUTF8(
|
| + [subclass_implementations componentsJoinedByString:@", "]);
|
| + ASSERT_NE(0u, [subclass_implementations count])
|
| + << class_name
|
| + << " does not implement any required clientHandling methods.";
|
| + ASSERT_EQ(1u, [subclass_implementations count])
|
| + << class_name << " implements too many superclass methods ("
|
| + << method_list << ").";
|
| + }
|
| +
|
| + free(classes);
|
| +}
|
|
|