Search SignalAccounts by profile name

...and fixup some tests
This commit is contained in:
Michael Kirk 2017-11-28 16:23:54 -08:00
parent 06f52deaf9
commit 71bafcc8f0
8 changed files with 71 additions and 79 deletions

View File

@ -279,7 +279,6 @@
45E7A6A81E71CA7E00D44FB5 /* DisplayableTextFilterTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E7A6A61E71CA7E00D44FB5 /* DisplayableTextFilterTest.swift */; };
45F170AC1E2F0351003FC1F2 /* CallAudioSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45F170AB1E2F0351003FC1F2 /* CallAudioSession.swift */; };
45F170AD1E2F0351003FC1F2 /* CallAudioSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45F170AB1E2F0351003FC1F2 /* CallAudioSession.swift */; };
45F170AF1E2F0393003FC1F2 /* CallAudioSessionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45F170AE1E2F0393003FC1F2 /* CallAudioSessionTest.swift */; };
45F170BB1E2FC5D3003FC1F2 /* CallAudioService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45F170BA1E2FC5D3003FC1F2 /* CallAudioService.swift */; };
45F170BC1E2FC5D3003FC1F2 /* CallAudioService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45F170BA1E2FC5D3003FC1F2 /* CallAudioService.swift */; };
45F170CC1E310E22003FC1F2 /* WeakTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45F170CB1E310E22003FC1F2 /* WeakTimer.swift */; };
@ -866,7 +865,6 @@
45E615151E8C590B0018AD52 /* DisplayableText.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisplayableText.swift; sourceTree = "<group>"; };
45E7A6A61E71CA7E00D44FB5 /* DisplayableTextFilterTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisplayableTextFilterTest.swift; sourceTree = "<group>"; };
45F170AB1E2F0351003FC1F2 /* CallAudioSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallAudioSession.swift; sourceTree = "<group>"; };
45F170AE1E2F0393003FC1F2 /* CallAudioSessionTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallAudioSessionTest.swift; sourceTree = "<group>"; };
45F170B31E2F0A6A003FC1F2 /* RTCAudioSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RTCAudioSession.h; sourceTree = "<group>"; };
45F170BA1E2FC5D3003FC1F2 /* CallAudioService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallAudioService.swift; sourceTree = "<group>"; };
45F170CB1E310E22003FC1F2 /* WeakTimer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WeakTimer.swift; sourceTree = "<group>"; };
@ -1837,7 +1835,6 @@
B660F6731C29867F00687D6E /* call */ = {
isa = PBXGroup;
children = (
45F170AE1E2F0393003FC1F2 /* CallAudioSessionTest.swift */,
456F6E2E1E261D1000FD2210 /* PeerConnectionClientTest.swift */,
);
path = call;
@ -2928,7 +2925,6 @@
458E383A1D6699FA0094BD24 /* OWSDeviceProvisioningURLParserTest.m in Sources */,
452D1EE81DCA90D100A57EC4 /* MesssagesBubblesSizeCalculatorTest.swift in Sources */,
451DE9FE1DC1A28200810E42 /* SyncPushTokensJob.swift in Sources */,
45F170AF1E2F0393003FC1F2 /* CallAudioSessionTest.swift in Sources */,
456F6E231E24133500FD2210 /* Platform.swift in Sources */,
4539B5871F79348F007141FF /* PushRegistrationManager.swift in Sources */,
4504493A1F45EE7D002D1ADA /* NSString+OWS.m in Sources */,

View File

@ -31,6 +31,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic) BOOL shouldNotifyDelegateOfUpdatedContacts;
@property (nonatomic) BOOL hasUpdatedContactsAtLeastOnce;
@property (nonatomic) OWSProfileManager *profileManager;
@property (nonatomic, readonly) AnySearcher *signalAccountSearcher;
@end
@ -59,6 +60,8 @@ NS_ASSUME_NONNULL_BEGIN
[self updateContacts];
self.shouldNotifyDelegateOfUpdatedContacts = NO;
_signalAccountSearcher = [self buildAccountSearcher];
[self observeNotifications];
return self;
@ -99,6 +102,24 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark - Contacts
- (AnySearcher *)buildAccountSearcher
{
return [[AnySearcher alloc] initWithIndexer:^NSString *_Nonnull(id _Nonnull obj) {
if (![obj isKindOfClass:[SignalAccount class]]) {
OWSFail(@"unexpected item in searcher");
return @"";
}
SignalAccount *signalAccount = (SignalAccount *)obj;
NSString *recipientId = signalAccount.recipientId;
NSString *contactName = [self.contactsManager displayNameForPhoneIdentifier:recipientId];
NSString *profileName = [self.contactsManager profileNameForRecipientId:recipientId];
return [NSString stringWithFormat:@"%@ %@ %@", recipientId, contactName, profileName];
}];
}
- (nullable SignalAccount *)signalAccountForRecipientId:(NSString *)recipientId
{
OWSAssert([NSThread isMainThread]);
@ -173,37 +194,6 @@ NS_ASSUME_NONNULL_BEGIN
}
}
- (BOOL)doesSignalAccount:(SignalAccount *)signalAccount matchSearchTerm:(NSString *)searchTerm
{
OWSAssert(signalAccount);
OWSAssert(searchTerm.length > 0);
if ([signalAccount.contact.fullName.lowercaseString containsString:searchTerm.lowercaseString]) {
return YES;
}
NSString *asPhoneNumber = [PhoneNumber removeFormattingCharacters:searchTerm];
if (asPhoneNumber.length > 0 && [signalAccount.recipientId containsString:asPhoneNumber]) {
return YES;
}
return NO;
}
- (BOOL)doesSignalAccount:(SignalAccount *)signalAccount matchSearchTerms:(NSArray<NSString *> *)searchTerms
{
OWSAssert(signalAccount);
OWSAssert(searchTerms.count > 0);
for (NSString *searchTerm in searchTerms) {
if (![self doesSignalAccount:signalAccount matchSearchTerm:searchTerm]) {
return NO;
}
}
return YES;
}
- (NSArray<NSString *> *)searchTermsForSearchString:(NSString *)searchText
{
return [[[searchText ows_stripped]
@ -225,7 +215,7 @@ NS_ASSUME_NONNULL_BEGIN
return [self.signalAccounts
filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(SignalAccount *signalAccount,
NSDictionary<NSString *, id> *_Nullable bindings) {
return [self doesSignalAccount:signalAccount matchSearchTerms:searchTerms];
return [self.signalAccountSearcher item:signalAccount doesMatchQuery:searchText];
}]];
}

View File

@ -36,7 +36,16 @@ class Searcher<T> {
}
private func stem(string: String) -> [String] {
return normalize(string: string).components(separatedBy: .whitespaces)
var normalized = normalize(string: string)
// Remove any phone number formatting from the search terms
let nonformattingScalars = normalized.unicodeScalars.lazy.filter {
!CharacterSet.punctuationCharacters.contains($0)
}
normalized = String(String.UnicodeScalarView(nonformattingScalars))
return normalized.components(separatedBy: .whitespacesAndNewlines)
}
private func normalize(string: String) -> String {

View File

@ -4,6 +4,7 @@
import XCTest
import PromiseKit
import SignalServiceKit
struct VerificationFailedError: Error { }
struct FailedToGetRPRegistrationTokenError: Error { }

View File

@ -3,6 +3,7 @@
//
import XCTest
import SignalServiceKit
/**
* This is a brittle test, which will break if our layout changes.
@ -23,7 +24,12 @@ class MesssagesBubblesSizeCalculatorTest: XCTestCase {
func viewItemForText(_ text: String?) -> ConversationViewItem {
let interaction = TSOutgoingMessage(timestamp: 0, in: thread, messageBody: text)
interaction.save()
let viewItem = ConversationViewItem(tsInteraction:interaction, isGroupThread:false)
var viewItem: ConversationViewItem!
interaction.dbReadWriteConnection().readWrite { transaction in
viewItem = ConversationViewItem(interaction: interaction, isGroupThread: false, transaction: transaction)
}
viewItem.shouldShowDate = false
viewItem.shouldHideRecipientStatus = true
return viewItem

View File

@ -8,6 +8,7 @@
#import <SignalServiceKit/TSAttachmentStream.h>
#import <SignalServiceKit/TSOutgoingMessage.h>
#import <XCTest/XCTest.h>
#import <YapDatabase/YapDatabaseConnection.h>
@interface ConversationViewItem (Testing)
@ -64,8 +65,10 @@
OWSAssert([[NSFileManager defaultManager] fileExistsAtPath:filePath]);
TSAttachmentStream *attachment = [[TSAttachmentStream alloc] initWithContentType:mimeType sourceFilename:nil];
DataSource *dataSource = [DataSourcePath dataSourceWithFilePath:filePath];
TSAttachmentStream *attachment = [[TSAttachmentStream alloc] initWithContentType:mimeType
byteCount:(UInt32)dataSource.dataLength
sourceFilename:nil];
BOOL success = [attachment writeDataSource:dataSource];
OWSAssert(success);
[attachment save];
@ -75,7 +78,12 @@
TSOutgoingMessage *message =
[[TSOutgoingMessage alloc] initWithTimestamp:1 inThread:nil messageBody:nil attachmentIds:attachmentIds];
[message save];
ConversationViewItem *viewItem = [[ConversationViewItem alloc] initWithInteraction:message isGroupThread:NO];
__block ConversationViewItem *viewItem = nil;
[TSYapDatabaseObject.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
viewItem = [[ConversationViewItem alloc] initWithInteraction:message isGroupThread:NO transaction:transaction];
}];
return viewItem;
}

View File

@ -1,35 +0,0 @@
// Copyright © 2017 Open Whisper Systems. All rights reserved.
//
import XCTest
import AVKit
import WebRTC
/**
* These tests are obtuse - they just assert the exact implementation of the methods. Normally I wouldn't include them,
* but these methods make use of a header not included in the standard distribution of the WebRTC.framework. We've
* included the header in our local project, and test the methods here to make sure that they are still available when
* we upgrade the framework.
*
* If they are failing, it's possible the RTCAudioSession header, and our usage of it, need to be updated.
*/
class CallAudioSessionTest: XCTestCase {
func testAudioSession() {
let rtcAudioSession = RTCAudioSession.sharedInstance()
// Sanity Check
XCTAssertFalse(rtcAudioSession.useManualAudio)
CallAudioSession().configure()
XCTAssertTrue(rtcAudioSession.useManualAudio)
XCTAssertFalse(rtcAudioSession.isAudioEnabled)
CallAudioSession().start()
XCTAssertTrue(rtcAudioSession.useManualAudio)
XCTAssertTrue(rtcAudioSession.isAudioEnabled)
CallAudioSession().stop()
XCTAssertTrue(rtcAudioSession.useManualAudio)
XCTAssertFalse(rtcAudioSession.isAudioEnabled)
}
}

View File

@ -9,14 +9,15 @@ class SearcherTest: XCTestCase {
struct TestCharacter {
let name: String
let description: String
let phoneNumber: String?
}
let smerdyakov = TestCharacter(name: "Pavel Fyodorovich Smerdyakov", description: "A rusty hue in the sky")
let stinkingLizaveta = TestCharacter(name: "Stinking Lizaveta", description: "object of pity")
let regularLizaveta = TestCharacter(name: "Lizaveta", description: "")
let smerdyakov = TestCharacter(name: "Pavel Fyodorovich Smerdyakov", description: "A rusty hue in the sky", phoneNumber: nil)
let stinkingLizaveta = TestCharacter(name: "Stinking Lizaveta", description: "object of pity", phoneNumber: "+13235555555")
let regularLizaveta = TestCharacter(name: "Lizaveta", description: "", phoneNumber: "1 (415) 555-5555")
let indexer = { (character: TestCharacter) in
return "\(character.name) \(character.description)"
return "\(character.name) \(character.description) \(character.phoneNumber ?? "")"
}
var searcher: Searcher<TestCharacter> {
@ -57,4 +58,20 @@ class SearcherTest: XCTestCase {
XCTAssert(searcher.matches(item: stinkingLizaveta, query: "Lizaveta St"))
XCTAssert(searcher.matches(item: stinkingLizaveta, query: " Lizaveta St "))
}
func testFormattingChars() {
XCTAssert(searcher.matches(item: stinkingLizaveta, query:"323"))
XCTAssert(searcher.matches(item: stinkingLizaveta, query:"1-323-555-5555"))
XCTAssert(searcher.matches(item: stinkingLizaveta, query:"13235555555"))
XCTAssert(searcher.matches(item: stinkingLizaveta, query:"+1-323"))
XCTAssert(searcher.matches(item: stinkingLizaveta, query:"Liza +1-323"))
// Sanity check, match both by names
XCTAssert(searcher.matches(item: stinkingLizaveta, query:"Liza"))
XCTAssert(searcher.matches(item: regularLizaveta, query:"Liza"))
// Disambiguate the two Liza's by area code
XCTAssert(searcher.matches(item: stinkingLizaveta, query:"Liza 323"))
XCTAssertFalse(searcher.matches(item: regularLizaveta, query:"Liza 323"))
}
}