mirror of
https://github.com/TextureGroup/Texture.git
synced 2026-04-07 19:17:39 +00:00
Initial pass with diffing fixtures
This commit is contained in:
committed by
Adlai Holler
parent
58641a85ab
commit
3aee5adc7d
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// ASDKDiffingFixtures.h
|
||||
// ASDKDiffing
|
||||
//
|
||||
// Created by Adlai Holler on 6/20/17.
|
||||
//
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <IGListKit/IGListDiffKit.h>
|
||||
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ASTViewModel : NSObject <IGListDiffable>
|
||||
@property (nonatomic, readonly) NSInteger identifier; // Globally unique.
|
||||
@property (nonatomic, readonly) NSInteger contents; // Globally unique. Auto-increments.
|
||||
@property (nonatomic, nullable, copy) NSString *debugName; // Helpful. Copied when updating.
|
||||
// Returns a new view model with the next `contents` value.
|
||||
- (instancetype)viewModelByUpdating;
|
||||
|
||||
+ (void)reset;
|
||||
|
||||
@end
|
||||
|
||||
@interface ASTItem : ASTViewModel
|
||||
@end
|
||||
|
||||
@interface ASTSection : ASTViewModel
|
||||
|
||||
@end
|
||||
|
||||
@interface ASTSectionCtrl : ASDiffingSectionController
|
||||
|
||||
@end
|
||||
|
||||
@interface ASTItemNode : ASCellNode
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,136 @@
|
||||
//
|
||||
// ASDKDiffingFixtures.m
|
||||
// ASDKDiffing
|
||||
//
|
||||
// Created by Adlai Holler on 6/20/17.
|
||||
//
|
||||
//
|
||||
|
||||
#import "ASDKDiffingFixtures.h"
|
||||
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||
#import <stdatomic.h>
|
||||
|
||||
atomic_uint g_identifier;
|
||||
atomic_uint g_val;
|
||||
|
||||
void ASTReset() {
|
||||
atomic_store(&g_identifier, 0);
|
||||
atomic_store(&g_val, 0);
|
||||
}
|
||||
|
||||
NSInteger ASTNextIdentifier() {
|
||||
return atomic_fetch_add(&g_identifier, 1);
|
||||
}
|
||||
|
||||
NSInteger ASTNextContents() {
|
||||
return atomic_fetch_add(&g_val, 1);
|
||||
}
|
||||
|
||||
id ASTWithDebugNamesTable(id (NS_NOESCAPE ^block)(NSMapTable<NSNumber *, NSString *> *table)) {
|
||||
static NSMapTable<NSNumber *, NSString *> *table;
|
||||
static NSLock *lock;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
table = [NSMapTable strongToStrongObjectsMapTable];
|
||||
lock = [NSLock new];
|
||||
});
|
||||
|
||||
[lock lock];
|
||||
id result = block(table);
|
||||
[lock unlock];
|
||||
return result;
|
||||
}
|
||||
|
||||
@implementation ASTViewModel
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_identifier = ASTNextIdentifier();
|
||||
_contents = ASTNextContents();
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)viewModelByUpdating
|
||||
{
|
||||
ASTViewModel *vm = [[self class] alloc]; // No init. We control the values manually.
|
||||
vm->_identifier = _identifier;
|
||||
vm->_contents = ASTNextContents();
|
||||
return vm;
|
||||
}
|
||||
|
||||
- (NSString *)debugDescription
|
||||
{
|
||||
return ASObjectDescriptionMake(self, [self propertiesForDebugDescription]);
|
||||
}
|
||||
|
||||
- (void)setDebugName:(NSString *)debugName
|
||||
{
|
||||
ASTWithDebugNamesTable(^id(NSMapTable<NSNumber *,NSString *> *table) {
|
||||
[table setObject:[debugName copy] forKey:@(self.identifier)];
|
||||
return nil;
|
||||
});
|
||||
}
|
||||
|
||||
- (NSString *)debugName
|
||||
{
|
||||
return ASTWithDebugNamesTable(^id(NSMapTable<NSNumber *,NSString *> *table) {
|
||||
return [table objectForKey:@(self.identifier)];
|
||||
});
|
||||
}
|
||||
|
||||
- (NSMutableArray<NSDictionary *> *)propertiesForDebugDescription
|
||||
{
|
||||
__auto_type array = [NSMutableArray array];
|
||||
NSString *name = self.debugName;
|
||||
if (name.length > 0) {
|
||||
[array addObject:@{ @"name": name }];
|
||||
} else {
|
||||
[array addObject:@{ @"id": @(self.identifier) }];
|
||||
}
|
||||
[array addObject:@{ @"contents": @(self.contents) }];
|
||||
return array;
|
||||
}
|
||||
|
||||
+ (void)reset
|
||||
{
|
||||
ASTReset();
|
||||
}
|
||||
|
||||
#pragma mark - IGListDiffable
|
||||
|
||||
- (BOOL)isEqualToDiffableObject:(id<IGListDiffable>)object
|
||||
{
|
||||
ASTViewModel *vm = (ASTViewModel *)object;
|
||||
NSParameterAssert([(id)object class] == [self class]);
|
||||
NSParameterAssert(vm.identifier == self.identifier);
|
||||
return vm.contents == self.contents;
|
||||
}
|
||||
|
||||
- (id<NSObject>)diffIdentifier
|
||||
{
|
||||
return @(self.identifier);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ASTSection
|
||||
@end
|
||||
|
||||
@implementation ASTItem
|
||||
@end
|
||||
|
||||
@implementation ASTSectionCtrl
|
||||
|
||||
- (ASCellNodeBlock)nodeBlockForItemAtIndex:(NSInteger)index
|
||||
{
|
||||
return ^{
|
||||
return [ASTItemNode new];
|
||||
};
|
||||
}
|
||||
|
||||
@end
|
||||
@implementation ASTItemNode
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,106 @@
|
||||
//
|
||||
// ASDKDiffingTests.m
|
||||
// ASDKDiffingTests
|
||||
//
|
||||
// Created by Adlai Holler on 6/20/17.
|
||||
//
|
||||
//
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
#import "ASTestCase.h"
|
||||
#import "ASDKDiffingFixtures.h"
|
||||
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||
#import <OCMock/OCMock.h>
|
||||
|
||||
@interface ASDKDiffingTests : ASTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation ASDKDiffingTests {
|
||||
ASCollectionNode *collectionNode;
|
||||
UIWindow *window;
|
||||
id mockDataSource;
|
||||
NSMutableArray<ASTSection *> *sections;
|
||||
}
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
[ASTViewModel reset];
|
||||
sections = [NSMutableArray array];
|
||||
|
||||
collectionNode = [[ASCollectionNode alloc] initWithCollectionViewLayout:[UICollectionViewFlowLayout new]];
|
||||
mockDataSource = OCMStrictProtocolMock(@protocol(ASCollectionModernDataSource));
|
||||
collectionNode.modernDataSource = mockDataSource;
|
||||
window = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
|
||||
UIViewController *vc = [[UIViewController alloc] init];
|
||||
collectionNode.view.frame = vc.view.bounds;
|
||||
collectionNode.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
[vc.view addSubnode:collectionNode];
|
||||
window.rootViewController = vc;
|
||||
[window makeKeyAndVisible];
|
||||
}
|
||||
|
||||
- (void)testThatFixturesWork
|
||||
{
|
||||
ASTSection *s1 = [ASTSection new];
|
||||
ASTSection *s1_2 = [s1 viewModelByUpdating];
|
||||
s1.debugName = @"Hello";
|
||||
ASTSection *s2 = [ASTSection new];
|
||||
ASTItem *i1 = [ASTItem new];
|
||||
|
||||
// -setDebugName works
|
||||
XCTAssertEqualObjects(s1.debugName, @"Hello");
|
||||
// debugName shared between versions even if set after
|
||||
XCTAssertEqualObjects(s1_2.debugName, s1.debugName);
|
||||
// identifier starts at 0
|
||||
XCTAssertEqual(s1.identifier, 0);
|
||||
// identifier incs each new object
|
||||
XCTAssertEqual(s2.identifier, 1);
|
||||
// identifier global between classes
|
||||
XCTAssertEqual(i1.identifier, 2);
|
||||
// identifier shared between versions
|
||||
XCTAssertEqual(s1_2.identifier, s1.identifier);
|
||||
// contents inc each new object
|
||||
XCTAssertEqual(s1_2.contents, s1.contents + 1);
|
||||
// contents global between classes
|
||||
XCTAssertEqual(i1.contents, 3);
|
||||
}
|
||||
|
||||
- (void)testInitialDataLoad
|
||||
{
|
||||
NSMapTable<ASTSection *, id> *sectionToCtrl = [NSMapTable weakToWeakObjectsMapTable];
|
||||
|
||||
// It gathers section view models.
|
||||
OCMExpect([mockDataSource sectionViewModelsForCollectionNode:collectionNode])
|
||||
.andReturn(sections);
|
||||
|
||||
// It gathers section controllers for each model.
|
||||
__auto_type sectionControllers = [NSMutableArray array];
|
||||
for (ASTSection *sectionModel in sections) {
|
||||
ASTSectionCtrl *ctrl = OCMPartialMock([ASTSectionCtrl new]);
|
||||
[sectionControllers addObject:ctrl];
|
||||
|
||||
[sectionToCtrl setObject:ctrl forKey:sectionModel];
|
||||
OCMExpect([mockDataSource collectionNode:collectionNode controllerForSection:sectionModel])
|
||||
.andReturn(ctrl);
|
||||
}
|
||||
|
||||
// For each section model
|
||||
for (ASTSection *sectionModel in sectionToCtrl) {
|
||||
id mockCtrl = [sectionToCtrl objectForKey:sectionModel];
|
||||
|
||||
// It sets the new view model to the section controller
|
||||
OCMExpect([mockCtrl setViewModel:sectionModel])
|
||||
.andForwardToRealObject();
|
||||
|
||||
// It fetches a new array of item view models
|
||||
OCMExpect([mockCtrl generateItemViewModels])
|
||||
.andForwardToRealObject();
|
||||
}
|
||||
|
||||
[window layoutIfNeeded];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
||||
Reference in New Issue
Block a user