mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-18 11:30:04 +00:00
1181 lines
51 KiB
Swift
1181 lines
51 KiB
Swift
import Foundation
|
|
|
|
import UIKit
|
|
import XCTest
|
|
|
|
import Postbox
|
|
@testable import Postbox
|
|
|
|
import SwiftSignalKit
|
|
|
|
private let peerId = PeerId(namespace: 1, id: 1)
|
|
private let otherPeerId = PeerId(namespace: 1, id: 2)
|
|
private let namespace: Int32 = 1
|
|
private let authorPeerId = PeerId(namespace: 1, id: 6)
|
|
private let peer = TestPeer(id: 6, data: "abc")
|
|
private let tag1 = MessageTags(rawValue: 1 << 0)
|
|
private let tag2 = MessageTags(rawValue: 1 << 1)
|
|
|
|
private func ==(lhs: [Media], rhs: [Media]) -> Bool {
|
|
if lhs.count != rhs.count {
|
|
return false
|
|
}
|
|
|
|
for i in 0 ..< lhs.count {
|
|
if !lhs[i].isEqual(rhs[i]) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
private enum Entry: Equatable, CustomStringConvertible {
|
|
case Message(Int32, Int32, String, [Media], MessageFlags)
|
|
case Hole(Int32, Int32, Int32)
|
|
|
|
var description: String {
|
|
switch self {
|
|
case let .Message(id, timestamp, text, media, flags):
|
|
return "Message(\(id), \(timestamp), \(text), \(media), \(flags))"
|
|
case let .Hole(min, max, timestamp):
|
|
return "Hole(\(min), \(max), \(timestamp))"
|
|
}
|
|
}
|
|
}
|
|
|
|
private func ==(lhs: Entry, rhs: Entry) -> Bool {
|
|
switch lhs {
|
|
case let .Message(lhsId, lhsTimestamp, lhsText, lhsMedia, lhsFlags):
|
|
switch rhs {
|
|
case let .Message(rhsId, rhsTimestamp, rhsText, rhsMedia, rhsFlags):
|
|
return lhsId == rhsId && lhsTimestamp == rhsTimestamp && lhsText == rhsText && lhsMedia == rhsMedia && lhsFlags == rhsFlags
|
|
case .Hole:
|
|
return false
|
|
}
|
|
case let .Hole(lhsMin, lhsMax, lhsMaxTimestamp):
|
|
switch rhs {
|
|
case .Message:
|
|
return false
|
|
case let .Hole(rhsMin, rhsMax, rhsMaxTimestamp):
|
|
return lhsMin == rhsMin && lhsMax == rhsMax && lhsMaxTimestamp == rhsMaxTimestamp
|
|
}
|
|
}
|
|
}
|
|
|
|
private class TestEmbeddedMedia: Media, CustomStringConvertible {
|
|
var id: MediaId? { return nil }
|
|
var peerIds: [PeerId] = []
|
|
let data: String
|
|
|
|
init(data: String) {
|
|
self.data = data
|
|
}
|
|
|
|
required init(decoder: Decoder) {
|
|
self.data = decoder.decodeStringForKey("s", orElse: "")
|
|
}
|
|
|
|
func encode(_ encoder: Encoder) {
|
|
encoder.encodeString(self.data, forKey: "s")
|
|
}
|
|
|
|
func isEqual(_ other: Media) -> Bool {
|
|
if let other = other as? TestEmbeddedMedia {
|
|
return self.data == other.data
|
|
}
|
|
return false
|
|
}
|
|
|
|
var description: String {
|
|
return "TestEmbeddedMedia(\(self.data))"
|
|
}
|
|
}
|
|
|
|
private class TestExternalMedia: Media {
|
|
let id: MediaId?
|
|
var peerIds: [PeerId] = []
|
|
let data: String
|
|
|
|
init(id: Int64, data: String) {
|
|
self.id = MediaId(namespace: namespace, id: id)
|
|
self.data = data
|
|
}
|
|
|
|
required init(decoder: Decoder) {
|
|
self.id = MediaId(namespace: decoder.decodeInt32ForKey("i.n", orElse: 0), id: decoder.decodeInt64ForKey("i.i", orElse: 0))
|
|
self.data = decoder.decodeStringForKey("s", orElse: "")
|
|
}
|
|
|
|
func encode(_ encoder: Encoder) {
|
|
encoder.encodeInt32(self.id!.namespace, forKey: "i.n")
|
|
encoder.encodeInt64(self.id!.id, forKey: "i.i")
|
|
encoder.encodeString(self.data, forKey: "s")
|
|
}
|
|
|
|
func isEqual(_ other: Media) -> Bool {
|
|
if let other = other as? TestExternalMedia {
|
|
return self.id == other.id && self.data == other.data
|
|
}
|
|
return false
|
|
}
|
|
|
|
var description: String {
|
|
return "TestExternalMedia(\(self.id!.id), \(self.data))"
|
|
}
|
|
}
|
|
|
|
private class TestPeer: Peer {
|
|
let associatedPeerId: PeerId? = nil
|
|
|
|
public let notificationSettingsPeerId: PeerId? = nil
|
|
|
|
let associatedPeerIds: [PeerId]? = nil
|
|
|
|
var indexName: PeerIndexNameRepresentation {
|
|
return .title(title: "Test", addressName: nil)
|
|
}
|
|
|
|
let id: PeerId
|
|
let data: String
|
|
|
|
init(id: Int32, data: String) {
|
|
self.id = PeerId(namespace: namespace, id: id)
|
|
self.data = data
|
|
}
|
|
|
|
required init(decoder: Decoder) {
|
|
self.id = PeerId(namespace: decoder.decodeInt32ForKey("i.n", orElse: 0), id: decoder.decodeInt32ForKey("i.i", orElse: 0))
|
|
self.data = decoder.decodeStringForKey("s", orElse: "")
|
|
}
|
|
|
|
func encode(_ encoder: Encoder) {
|
|
encoder.encodeInt32(self.id.namespace, forKey: "i.n")
|
|
encoder.encodeInt32(self.id.id, forKey: "i.i")
|
|
encoder.encodeString(self.data, forKey: "s")
|
|
}
|
|
|
|
func isEqual(_ other: Peer) -> Bool {
|
|
if let other = other as? TestPeer {
|
|
return self.id == other.id && self.data == other.data
|
|
}
|
|
return false
|
|
}
|
|
|
|
var description: String {
|
|
return "TestPeer(\(self.id.id), \(self.data))"
|
|
}
|
|
}
|
|
|
|
private enum MediaEntry: Equatable {
|
|
case Direct(Media, Int)
|
|
case MessageReference(Int32)
|
|
|
|
init(_ entry: DebugMediaEntry) {
|
|
switch entry {
|
|
case let .Direct(media, referenceCount):
|
|
self = .Direct(media, referenceCount)
|
|
case let .MessageReference(index):
|
|
self = .MessageReference(index.id.id)
|
|
}
|
|
}
|
|
}
|
|
|
|
private func ==(lhs: MediaEntry, rhs: MediaEntry) -> Bool {
|
|
switch lhs {
|
|
case let .Direct(lhsMedia, lhsReferenceCount):
|
|
switch rhs {
|
|
case let .Direct(rhsMedia, rhsReferenceCount):
|
|
return lhsMedia.isEqual(rhsMedia) && lhsReferenceCount == rhsReferenceCount
|
|
case .MessageReference:
|
|
return false
|
|
}
|
|
case let .MessageReference(lhsId):
|
|
switch rhs {
|
|
case .Direct:
|
|
return false
|
|
case let .MessageReference(rhsId):
|
|
return lhsId == rhsId
|
|
}
|
|
}
|
|
}
|
|
|
|
private extension MessageTags {
|
|
static let First = MessageTags(rawValue: 1 << 0)
|
|
static let Second = MessageTags(rawValue: 1 << 1)
|
|
}
|
|
|
|
class MessageHistoryTableTests: XCTestCase {
|
|
var valueBox: ValueBox?
|
|
var path: String?
|
|
|
|
var peerTable: PeerTable?
|
|
var globalMessageIdsTable: GlobalMessageIdsTable?
|
|
var indexTable: MessageHistoryIndexTable?
|
|
var mediaTable: MessageMediaTable?
|
|
var historyTable: MessageHistoryTable?
|
|
var historyMetadataTable: MessageHistoryMetadataTable?
|
|
var unsentTable: MessageHistoryUnsentTable?
|
|
var tagsTable: MessageHistoryTagsTable?
|
|
var readStateTable: MessageHistoryReadStateTable?
|
|
var synchronizeReadStateTable: MessageHistorySynchronizeReadStateTable?
|
|
var globallyUniqueMessageIdsTable: MessageGloballyUniqueIdTable?
|
|
var globalTagsTable: GlobalMessageHistoryTagsTable?
|
|
var reverseAssociatedTable: ReverseAssociatedPeerTable?
|
|
var textIndexTable: MessageHistoryTextIndexTable?
|
|
|
|
override class func setUp() {
|
|
super.setUp()
|
|
|
|
declareEncodable(TestEmbeddedMedia.self, f: {TestEmbeddedMedia(decoder: $0)})
|
|
declareEncodable(TestExternalMedia.self, f: {TestExternalMedia(decoder: $0)})
|
|
declareEncodable(TestPeer.self, f: {TestPeer(decoder: $0)})
|
|
}
|
|
|
|
override func setUp() {
|
|
super.setUp()
|
|
|
|
var randomId: Int64 = 0
|
|
arc4random_buf(&randomId, 8)
|
|
path = NSTemporaryDirectory() + "\(randomId)"
|
|
self.valueBox = SqliteValueBox(basePath: path!, queue: Queue.mainQueue())
|
|
|
|
let seedConfiguration = SeedConfiguration(initializeChatListWithHoles: [], initializeMessageNamespacesWithHoles: [], existingMessageTags: [.First, .Second], existingGlobalMessageTags: [], peerNamespacesRequiringMessageTextIndex: [])
|
|
|
|
self.globalMessageIdsTable = GlobalMessageIdsTable(valueBox: self.valueBox!, table: GlobalMessageIdsTable.tableSpec(5), namespace: namespace)
|
|
self.historyMetadataTable = MessageHistoryMetadataTable(valueBox: self.valueBox!, table: MessageHistoryMetadataTable.tableSpec(7))
|
|
self.unsentTable = MessageHistoryUnsentTable(valueBox: self.valueBox!, table: MessageHistoryUnsentTable.tableSpec(8))
|
|
self.tagsTable = MessageHistoryTagsTable(valueBox: self.valueBox!, table: MessageHistoryTagsTable.tableSpec(9))
|
|
self.indexTable = MessageHistoryIndexTable(valueBox: self.valueBox!, table: MessageHistoryIndexTable.tableSpec(1), globalMessageIdsTable: self.globalMessageIdsTable!, metadataTable: self.historyMetadataTable!, seedConfiguration: seedConfiguration)
|
|
self.mediaTable = MessageMediaTable(valueBox: self.valueBox!, table: MessageMediaTable.tableSpec(2))
|
|
self.readStateTable = MessageHistoryReadStateTable(valueBox: self.valueBox!, table: MessageHistoryReadStateTable.tableSpec(10))
|
|
self.synchronizeReadStateTable = MessageHistorySynchronizeReadStateTable(valueBox: self.valueBox!, table: MessageHistorySynchronizeReadStateTable.tableSpec(11))
|
|
self.globallyUniqueMessageIdsTable = MessageGloballyUniqueIdTable(valueBox: self.valueBox!, table: MessageGloballyUniqueIdTable.tableSpec(12))
|
|
self.globalTagsTable = GlobalMessageHistoryTagsTable(valueBox: self.valueBox!, table: GlobalMessageHistoryTagsTable.tableSpec(13))
|
|
self.textIndexTable = MessageHistoryTextIndexTable(valueBox: self.valueBox!, table: MessageHistoryTextIndexTable.tableSpec(15))
|
|
self.historyTable = MessageHistoryTable(valueBox: self.valueBox!, table: MessageHistoryTable.tableSpec(4), messageHistoryIndexTable: self.indexTable!, messageMediaTable: self.mediaTable!, historyMetadataTable: self.historyMetadataTable!, globallyUniqueMessageIdsTable: self.globallyUniqueMessageIdsTable!, unsentTable: self.unsentTable!, tagsTable: self.tagsTable!, globalTagsTable: self.globalTagsTable!, readStateTable: self.readStateTable!, synchronizeReadStateTable: self.synchronizeReadStateTable!, textIndexTable: self.textIndexTable!)
|
|
self.reverseAssociatedTable = ReverseAssociatedPeerTable(valueBox: self.valueBox!, table: ReverseAssociatedPeerTable.tableSpec(14))
|
|
self.peerTable = PeerTable(valueBox: self.valueBox!, table: PeerTable.tableSpec(6), reverseAssociatedTable: self.reverseAssociatedTable!)
|
|
self.peerTable!.set(peer)
|
|
}
|
|
|
|
override func tearDown() {
|
|
super.tearDown()
|
|
|
|
self.historyTable = nil
|
|
self.indexTable = nil
|
|
self.mediaTable = nil
|
|
self.peerTable = nil
|
|
self.historyMetadataTable = nil
|
|
|
|
self.valueBox = nil
|
|
let _ = try? FileManager.default.removeItem(atPath: path!)
|
|
self.path = nil
|
|
}
|
|
|
|
private func addMessage(_ id: Int32, _ timestamp: Int32, _ text: String = "", _ media: [Media] = [], _ flags: StoreMessageFlags = [], _ tags: MessageTags = []) {
|
|
var operationsByPeerId: [PeerId: [MessageHistoryOperation]] = [:]
|
|
var unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation] = []
|
|
var updatedPeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?] = [:]
|
|
var globalTagsOperations: [GlobalMessageHistoryTagsOperation] = []
|
|
let _ = self.historyTable!.addMessages([StoreMessage(id: MessageId(peerId: peerId, namespace: namespace, id: id), globallyUniqueId: nil, timestamp: timestamp, flags: flags, tags: tags, globalTags: [], forwardInfo: StoreMessageForwardInfo(authorId: peerId, sourceId: peerId, sourceMessageId: MessageId(peerId: peerId, namespace: 0, id: 10), date: 10, authorSignature: "abc"), authorId: authorPeerId, text: text, attributes: [], media: media)], location: .Random, operationsByPeerId: &operationsByPeerId, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations)
|
|
}
|
|
|
|
private func updateMessage(_ previousId: Int32, _ id: Int32, _ timestamp: Int32, _ text: String = "", _ media: [Media] = [], _ flags: StoreMessageFlags, _ tags: MessageTags) {
|
|
var operationsByPeerId: [PeerId: [MessageHistoryOperation]] = [:]
|
|
var unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation] = []
|
|
var updatedPeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?] = [:]
|
|
var globalTagsOperations: [GlobalMessageHistoryTagsOperation] = []
|
|
self.historyTable!.updateMessage(MessageId(peerId: peerId, namespace: namespace, id: previousId), message: StoreMessage(id: MessageId(peerId: peerId, namespace: namespace, id: id), globallyUniqueId: nil, timestamp: timestamp, flags: flags, tags: tags, globalTags: [], forwardInfo: nil, authorId: authorPeerId, text: text, attributes: [], media: media), operationsByPeerId: &operationsByPeerId, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations)
|
|
}
|
|
|
|
private func addHole(_ id: Int32) {
|
|
var operationsByPeerId: [PeerId: [MessageHistoryOperation]] = [:]
|
|
var unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation] = []
|
|
var updatedPeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?] = [:]
|
|
var globalTagsOperations: [GlobalMessageHistoryTagsOperation] = []
|
|
self.historyTable!.addHoles([MessageId(peerId: peerId, namespace: namespace, id: id)], operationsByPeerId: &operationsByPeerId, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations)
|
|
}
|
|
|
|
private func removeMessages(_ ids: [Int32]) {
|
|
var operationsByPeerId: [PeerId: [MessageHistoryOperation]] = [:]
|
|
var unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation] = []
|
|
var updatedPeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?] = [:]
|
|
var globalTagsOperations: [GlobalMessageHistoryTagsOperation] = []
|
|
self.historyTable!.removeMessages(ids.map({ MessageId(peerId: peerId, namespace: namespace, id: $0) }), operationsByPeerId: &operationsByPeerId, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations)
|
|
}
|
|
|
|
private func fillHole(_ id: Int32, _ fillType: HoleFill, _ messages: [(Int32, Int32, String, [Media])], _ tagMask: MessageTags? = nil) {
|
|
var operationsByPeerId: [PeerId: [MessageHistoryOperation]] = [:]
|
|
var unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation] = []
|
|
var updatedPeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?] = [:]
|
|
var globalTagsOperations: [GlobalMessageHistoryTagsOperation] = []
|
|
self.historyTable!.fillHole(MessageId(peerId: peerId, namespace: namespace, id: id), fillType: fillType, tagMask: tagMask, messages: messages.map({ StoreMessage(id: MessageId(peerId: peerId, namespace: namespace, id: $0.0), globallyUniqueId: nil, timestamp: $0.1, flags: [], tags: [], globalTags: [], forwardInfo: nil, authorId: authorPeerId, text: $0.2, attributes: [], media: $0.3) }), operationsByPeerId: &operationsByPeerId, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations)
|
|
}
|
|
|
|
private func fillMultipleHoles(_ id: Int32, _ fillType: HoleFill, _ messages: [(Int32, Int32, String, [Media])], _ tagMask: MessageTags? = nil, _ tags: MessageTags = []) {
|
|
var operationsByPeerId: [PeerId: [MessageHistoryOperation]] = [:]
|
|
var unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation] = []
|
|
var updatedPeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?] = [:]
|
|
var globalTagsOperations: [GlobalMessageHistoryTagsOperation] = []
|
|
self.historyTable!.fillMultipleHoles(mainHoleId: MessageId(peerId: peerId, namespace: namespace, id: id), fillType: fillType, tagMask: tagMask, messages: messages.map({ StoreMessage(id: MessageId(peerId: peerId, namespace: namespace, id: $0.0), globallyUniqueId: nil, timestamp: $0.1, flags: [], tags: tags, globalTags: [], forwardInfo: nil, authorId: authorPeerId, text: $0.2, attributes: [], media: $0.3) }), operationsByPeerId: &operationsByPeerId, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations)
|
|
}
|
|
|
|
private func expectEntries(_ entries: [Entry], tagMask: MessageTags? = nil) {
|
|
var stableIds = Set<UInt32>()
|
|
|
|
let list: [RenderedMessageHistoryEntry]
|
|
if let tagMask = tagMask {
|
|
list = self.historyTable!.debugList(tagMask, peerId: peerId, peerTable: self.peerTable!)
|
|
} else {
|
|
list = self.historyTable!.debugList(peerId, peerTable: self.peerTable!)
|
|
}
|
|
|
|
let actualEntries = list.map({ entry -> Entry in
|
|
let stableId: UInt32
|
|
switch entry {
|
|
case let .RenderedMessage(message):
|
|
stableId = message.stableId
|
|
if let messagePeer = message.author {
|
|
if !peer.isEqual(messagePeer) {
|
|
XCTFail("Expected peer \(peer), actual: \(messagePeer)")
|
|
}
|
|
} else {
|
|
XCTFail("Expected peer \(peer), actual: nil")
|
|
}
|
|
if stableIds.contains(stableId) {
|
|
XCTFail("Stable id not unique \(stableId)")
|
|
} else {
|
|
stableIds.insert(stableId)
|
|
}
|
|
return .Message(message.id.id, message.timestamp, message.text, message.media, message.flags)
|
|
case let .Hole(hole):
|
|
stableId = hole.stableId
|
|
if stableIds.contains(stableId) {
|
|
XCTFail("Stable id not unique \(stableId)")
|
|
} else {
|
|
stableIds.insert(stableId)
|
|
}
|
|
return .Hole(hole.min, hole.maxIndex.id.id, hole.maxIndex.timestamp)
|
|
}
|
|
})
|
|
if actualEntries != entries {
|
|
XCTFail("Expected\n\(entries)\nActual\n\(actualEntries)")
|
|
}
|
|
}
|
|
|
|
private func expectUnsent(_ indices: [Int32]) {
|
|
let actualUnsent = self.unsentTable!.get().map({ $0.id })
|
|
var match = true
|
|
if actualUnsent.count == indices.count {
|
|
for i in 0 ..< indices.count {
|
|
if indices[i] != actualUnsent[i] {
|
|
match = false
|
|
break
|
|
}
|
|
}
|
|
} else {
|
|
match = false
|
|
}
|
|
if !match {
|
|
XCTFail("Expected\n\(indices)\nActual\n\(actualUnsent)")
|
|
}
|
|
}
|
|
|
|
private func expectMedia(_ media: [MediaEntry]) {
|
|
let actualMedia = self.mediaTable!.debugList().map({MediaEntry($0)})
|
|
if media != actualMedia {
|
|
XCTFail("Expected\n\(media)\nActual\n\(actualMedia)")
|
|
}
|
|
}
|
|
|
|
func testInsertMessageIntoEmpty() {
|
|
addMessage(100, 100, "t100")
|
|
addMessage(200, 200, "t200")
|
|
|
|
expectEntries([.Message(100, 100, "t100", [], []), .Message(200, 200, "t200", [], [])])
|
|
}
|
|
|
|
func testInsertMessageIgnoreOverwrite() {
|
|
addMessage(100, 100, "t100")
|
|
addMessage(100, 200, "t200")
|
|
|
|
expectEntries([.Message(100, 100, "t100", [], [])])
|
|
}
|
|
|
|
func testInsertMessageWithEmbeddedMedia() {
|
|
addMessage(100, 100, "t100", [TestEmbeddedMedia(data: "abc1")])
|
|
|
|
expectEntries([.Message(100, 100, "t100", [TestEmbeddedMedia(data: "abc1")], [])])
|
|
expectMedia([])
|
|
}
|
|
|
|
func testInsertMessageWithExternalMedia() {
|
|
let media = TestExternalMedia(id: 10, data: "abc1")
|
|
addMessage(100, 100, "t100", [media])
|
|
|
|
expectEntries([.Message(100, 100, "t100", [media], [])])
|
|
expectMedia([.MessageReference(100)])
|
|
}
|
|
|
|
func testUnembedExternalMedia() {
|
|
let media = TestExternalMedia(id: 10, data: "abc1")
|
|
addMessage(100, 100, "t100", [media])
|
|
addMessage(200, 200, "t200", [media])
|
|
|
|
expectEntries([.Message(100, 100, "t100", [media], []), .Message(200, 200, "t200", [media], [])])
|
|
expectMedia([.Direct(media, 2)])
|
|
}
|
|
|
|
func testIgnoreOverrideExternalMedia() {
|
|
let media = TestExternalMedia(id: 10, data: "abc1")
|
|
let media1 = TestExternalMedia(id: 10, data: "abc2")
|
|
addMessage(100, 100, "t100", [media])
|
|
addMessage(200, 200, "t200", [media1])
|
|
|
|
expectEntries([.Message(100, 100, "t100", [media], []), .Message(200, 200, "t200", [media], [])])
|
|
expectMedia([.Direct(media, 2)])
|
|
}
|
|
|
|
func testRemoveSingleMessage() {
|
|
addMessage(100, 100, "t100", [])
|
|
|
|
removeMessages([100])
|
|
|
|
expectEntries([])
|
|
expectMedia([])
|
|
}
|
|
|
|
func testRemoveMessageWithEmbeddedMedia() {
|
|
let media = TestEmbeddedMedia(data: "abc1")
|
|
addMessage(100, 100, "t100", [media])
|
|
self.removeMessages([100])
|
|
|
|
expectEntries([])
|
|
expectMedia([])
|
|
}
|
|
|
|
func testRemoveOnlyReferenceToExternalMedia() {
|
|
let media = TestExternalMedia(id: 10, data: "abc1")
|
|
addMessage(100, 100, "t100", [media])
|
|
removeMessages([100])
|
|
|
|
expectEntries([])
|
|
expectMedia([])
|
|
}
|
|
|
|
func testRemoveReferenceToExternalMedia() {
|
|
let media = TestExternalMedia(id: 10, data: "abc1")
|
|
addMessage(100, 100, "t100", [media])
|
|
addMessage(200, 200, "t200", [media])
|
|
removeMessages([100])
|
|
|
|
expectEntries([.Message(200, 200, "t200", [media], [])])
|
|
expectMedia([.Direct(media, 1)])
|
|
|
|
removeMessages([200])
|
|
|
|
expectEntries([])
|
|
expectMedia([])
|
|
}
|
|
|
|
func testAddHoleToEmpty() {
|
|
addHole(100)
|
|
expectEntries([.Hole(1, Int32.max, Int32.max)])
|
|
}
|
|
|
|
func testAddHoleToFullHole() {
|
|
addHole(100)
|
|
expectEntries([.Hole(1, Int32.max, Int32.max)])
|
|
addHole(110)
|
|
expectEntries([.Hole(1, Int32.max, Int32.max)])
|
|
}
|
|
|
|
func testAddMessageToFullHole() {
|
|
addHole(100)
|
|
expectEntries([.Hole(1, Int32.max, Int32.max)])
|
|
addMessage(90, 90, "m90")
|
|
expectEntries([.Hole(1, 89, 90), .Message(90, 90, "m90", [], []), .Hole(91, Int32.max, Int32.max)])
|
|
}
|
|
|
|
func testAddMessageDividingUpperHole() {
|
|
addHole(100)
|
|
expectEntries([.Hole(1, Int32.max, Int32.max)])
|
|
addMessage(90, 90, "m90")
|
|
expectEntries([.Hole(1, 89, 90), .Message(90, 90, "m90", [], []), .Hole(91, Int32.max, Int32.max)])
|
|
addMessage(100, 100, "m100")
|
|
expectEntries([.Hole(1, 89, 90), .Message(90, 90, "m90", [], []), .Hole(91, 99, 100), .Message(100, 100, "m100", [], []), .Hole(101, Int32.max, Int32.max)])
|
|
}
|
|
|
|
func testAddMessageDividingLowerHole() {
|
|
addHole(100)
|
|
expectEntries([.Hole(1, Int32.max, Int32.max)])
|
|
addMessage(90, 90, "m90")
|
|
expectEntries([.Hole(1, 89, 90), .Message(90, 90, "m90", [], []), .Hole(91, Int32.max, Int32.max)])
|
|
addMessage(80, 80, "m80")
|
|
expectEntries([.Hole(1, 79, 80), .Message(80, 80, "m80", [], []), .Hole(81, 89, 90), .Message(90, 90, "m90", [], []), .Hole(91, Int32.max, Int32.max)])
|
|
}
|
|
|
|
func testAddMessageOffsettingUpperHole() {
|
|
addHole(100)
|
|
expectEntries([.Hole(1, Int32.max, Int32.max)])
|
|
|
|
addMessage(90, 90, "m90")
|
|
expectEntries([.Hole(1, 89, 90), .Message(90, 90, "m90", [], []), .Hole(91, Int32.max, Int32.max)])
|
|
addMessage(91, 91, "m91")
|
|
expectEntries([.Hole(1, 89, 90), .Message(90, 90, "m90", [], []), .Message(91, 91, "m91", [], []), .Hole(92, Int32.max, Int32.max)])
|
|
}
|
|
|
|
func testAddMessageOffsettingLowerHole() {
|
|
addHole(100)
|
|
expectEntries([.Hole(1, Int32.max, Int32.max)])
|
|
|
|
addMessage(90, 90, "m90")
|
|
expectEntries([.Hole(1, 89, 90), .Message(90, 90, "m90", [], []), .Hole(91, Int32.max, Int32.max)])
|
|
addMessage(89, 89, "m89")
|
|
expectEntries([.Hole(1, 88, 89), .Message(89, 89, "m89", [], []), .Message(90, 90, "m90", [], []), .Hole(91, Int32.max, Int32.max)])
|
|
}
|
|
|
|
func testAddMessageOffsettingLeftmostHole() {
|
|
addHole(100)
|
|
expectEntries([.Hole(1, Int32.max, Int32.max)])
|
|
|
|
addMessage(1, 1, "m1")
|
|
|
|
expectEntries([.Message(1, 1, "m1", [], []), .Hole(2, Int32.max, Int32.max)])
|
|
}
|
|
|
|
func testAddMessageRemovingLefmostHole() {
|
|
addHole(100)
|
|
expectEntries([.Hole(1, Int32.max, Int32.max)])
|
|
|
|
addMessage(2, 2, "m2")
|
|
expectEntries([.Hole(1, 1, 2), .Message(2, 2, "m2", [], []), .Hole(3, Int32.max, Int32.max)])
|
|
|
|
addMessage(1, 1, "m1")
|
|
expectEntries([.Message(1, 1, "m1", [], []), .Message(2, 2, "m2", [], []), .Hole(3, Int32.max, Int32.max)])
|
|
}
|
|
|
|
func testAddHoleLowerThanMessage() {
|
|
addMessage(100, 100, "m100")
|
|
addHole(1)
|
|
|
|
expectEntries([.Hole(1, 99, 100), .Message(100, 100, "m100", [], [])])
|
|
}
|
|
|
|
func testAddHoleHigherThanMessage() {
|
|
addMessage(100, 100, "m100")
|
|
addHole(200)
|
|
|
|
expectEntries([.Message(100, 100, "m100", [], []), .Hole(101, Int32.max, Int32.max)])
|
|
}
|
|
|
|
func testIgnoreHigherHole() {
|
|
addHole(200)
|
|
expectEntries([.Hole(1, Int32.max, Int32.max)])
|
|
addHole(400)
|
|
expectEntries([.Hole(1, Int32.max, Int32.max)])
|
|
}
|
|
|
|
func testIgnoreHigherHoleAfterMessage() {
|
|
addMessage(100, 100, "m100")
|
|
addHole(200)
|
|
expectEntries([.Message(100, 100, "m100", [], []), .Hole(101, Int32.max, Int32.max)])
|
|
addHole(400)
|
|
expectEntries([.Message(100, 100, "m100", [], []), .Hole(101, Int32.max, Int32.max)])
|
|
}
|
|
|
|
func testAddHoleBetweenMessages() {
|
|
addMessage(100, 100, "m100")
|
|
addMessage(200, 200, "m200")
|
|
addHole(150)
|
|
|
|
expectEntries([.Message(100, 100, "m100", [], []), .Hole(101, 199, 200), .Message(200, 200, "m200", [], [])])
|
|
}
|
|
|
|
func testFillHoleEmpty() {
|
|
fillHole(1, HoleFill(complete: true, direction: .UpperToLower), [])
|
|
expectEntries([])
|
|
}
|
|
|
|
func testFillHoleComplete() {
|
|
addHole(100)
|
|
|
|
fillHole(1, HoleFill(complete: true, direction: .UpperToLower), [(100, 100, "m100", []), (200, 200, "m200", [])])
|
|
expectEntries([.Message(100, 100, "m100", [], []), .Message(200, 200, "m200", [], [])])
|
|
}
|
|
|
|
func testFillHoleUpperToLowerPartial() {
|
|
addHole(100)
|
|
|
|
fillHole(1, HoleFill(complete: false, direction: .UpperToLower), [(100, 100, "m100", []), (200, 200, "m200", [])])
|
|
expectEntries([.Hole(1, 99, 100), .Message(100, 100, "m100", [], []), .Message(200, 200, "m200", [], [])])
|
|
}
|
|
|
|
func testFillHoleUpperToLowerToBounds() {
|
|
addHole(100)
|
|
|
|
fillHole(1, HoleFill(complete: false, direction: .UpperToLower), [(1, 1, "m1", []), (200, 200, "m200", [])])
|
|
expectEntries([.Message(1, 1, "m1", [], []), .Message(200, 200, "m200", [], [])])
|
|
}
|
|
|
|
func testFillHoleLowerToUpperToBounds() {
|
|
addHole(100)
|
|
|
|
fillHole(1, HoleFill(complete: false, direction: .LowerToUpper), [(100, 100, "m100", []), (Int32.max, 200, "m200", [])])
|
|
expectEntries([.Message(100, 100, "m100", [], []), .Message(Int32.max, 200, "m200", [], [])])
|
|
}
|
|
|
|
func testFillHoleLowerToUpperPartial() {
|
|
addHole(100)
|
|
|
|
fillHole(1, HoleFill(complete: false, direction: .LowerToUpper), [(100, 100, "m100", []), (200, 200, "m200", [])])
|
|
expectEntries([.Message(100, 100, "m100", [], []), .Message(200, 200, "m200", [], []), .Hole(201, Int32.max, Int32.max)])
|
|
}
|
|
|
|
func testFillHoleBetweenMessagesUpperToLower() {
|
|
addHole(1)
|
|
|
|
addMessage(100, 100, "m100")
|
|
addMessage(200, 200, "m200")
|
|
|
|
fillHole(199, HoleFill(complete: false, direction: .UpperToLower), [(150, 150, "m150", [])])
|
|
|
|
expectEntries([.Hole(1, 99, 100), .Message(100, 100, "m100", [], []), .Hole(101, 149, 150), .Message(150, 150, "m150", [], []), .Message(200, 200, "m200", [], []), .Hole(201, Int32.max, Int32.max)])
|
|
}
|
|
|
|
func testFillHoleBetweenMessagesLowerToUpper() {
|
|
addHole(1)
|
|
|
|
addMessage(100, 100, "m100")
|
|
addMessage(200, 200, "m200")
|
|
|
|
fillHole(199, HoleFill(complete: false, direction: .LowerToUpper), [(150, 150, "m150", [])])
|
|
|
|
expectEntries([.Hole(1, 99, 100), .Message(100, 100, "m100", [], []), .Message(150, 150, "m150", [], []), .Hole(151, 199, 200), .Message(200, 200, "m200", [], []), .Hole(201, Int32.max, Int32.max)])
|
|
}
|
|
|
|
func testFillHoleBetweenMessagesComplete() {
|
|
addHole(1)
|
|
|
|
addMessage(100, 100, "m100")
|
|
addMessage(200, 200, "m200")
|
|
|
|
fillHole(199, HoleFill(complete: true, direction: .UpperToLower), [(150, 150, "m150", [])])
|
|
|
|
expectEntries([.Hole(1, 99, 100), .Message(100, 100, "m100", [], []), .Message(150, 150, "m150", [], []), .Message(200, 200, "m200", [], []), .Hole(201, Int32.max, Int32.max)])
|
|
}
|
|
|
|
func testFillHoleBetweenMessagesWithMessage() {
|
|
addMessage(200, 200, "m200")
|
|
addMessage(202, 202, "m202")
|
|
addHole(201)
|
|
addMessage(201, 201, "m201")
|
|
|
|
expectEntries([.Message(200, 200, "m200", [], []), .Message(201, 201, "m201", [], []), .Message(202, 202, "m202", [], [])])
|
|
}
|
|
|
|
func testFillHoleWithNoMessagesComplete() {
|
|
addMessage(100, 100, "m100")
|
|
addHole(1)
|
|
|
|
fillHole(99, HoleFill(complete: true, direction: .UpperToLower), [])
|
|
|
|
expectEntries([.Message(100, 100, "m100", [], [])])
|
|
}
|
|
|
|
func testFillHoleIgnoreOverMessage() {
|
|
addMessage(100, 100, "m100")
|
|
addMessage(101, 101, "m101")
|
|
|
|
fillHole(100, HoleFill(complete: true, direction: .UpperToLower), [(90, 90, "m90", [])])
|
|
|
|
expectEntries([.Message(90, 90, "m90", [], []), .Message(100, 100, "m100", [], []), .Message(101, 101, "m101", [], [])])
|
|
}
|
|
|
|
func testFillHoleWithOverflow() {
|
|
addMessage(100, 100, "m100")
|
|
addMessage(200, 200, "m200")
|
|
addHole(150)
|
|
|
|
fillHole(199, HoleFill(complete: false, direction: .UpperToLower), [(150, 150, "m150", []), (300, 300, "m300", [])])
|
|
|
|
expectEntries([.Message(100, 100, "m100", [], []), .Hole(101, 149, 150), .Message(150, 150, "m150", [], []), .Message(200, 200, "m200", [], []), .Message(300, 300, "m300", [], [])])
|
|
}
|
|
|
|
func testIgnoreHoleOverMessageBetweenMessages() {
|
|
addMessage(199, 199, "m199")
|
|
addMessage(200, 200, "m200")
|
|
addHole(200)
|
|
|
|
expectEntries([.Message(199, 199, "m199", [], []), .Message(200, 200, "m200", [], [])])
|
|
}
|
|
|
|
func testMergeHoleAfterDeletingMessage() {
|
|
addMessage(100, 100, "m100")
|
|
addHole(1)
|
|
addHole(200)
|
|
|
|
expectEntries([.Hole(1, 99, 100), .Message(100, 100, "m100", [], []), .Hole(101, Int32.max, Int32.max)])
|
|
|
|
removeMessages([100])
|
|
|
|
expectEntries([.Hole(1, Int32.max, Int32.max)])
|
|
}
|
|
|
|
func testMergeHoleLowerAfterDeletingMessage() {
|
|
addMessage(100, 100, "m100")
|
|
addHole(1)
|
|
addMessage(200, 200, "m200")
|
|
|
|
removeMessages([100])
|
|
|
|
expectEntries([.Hole(1, 199, 200), .Message(200, 200, "m200", [], [])])
|
|
}
|
|
|
|
func testMergeHoleUpperAfterDeletingMessage() {
|
|
addMessage(100, 100, "m100")
|
|
addMessage(200, 200, "m200")
|
|
addHole(300)
|
|
|
|
removeMessages([200])
|
|
|
|
expectEntries([.Message(100, 100, "m100", [], []), .Hole(101, Int32.max, Int32.max)])
|
|
}
|
|
|
|
func testExtendLowerHoleAfterDeletingMessage() {
|
|
addMessage(100, 100, "m100")
|
|
addHole(100)
|
|
|
|
removeMessages([100])
|
|
|
|
expectEntries([.Hole(1, Int32.max, Int32.max)])
|
|
}
|
|
|
|
func testExtendUpperHoleAfterDeletingMessage() {
|
|
addMessage(100, 100, "m100")
|
|
addHole(101)
|
|
|
|
removeMessages([100])
|
|
|
|
expectEntries([.Hole(1, Int32.max, Int32.max)])
|
|
}
|
|
|
|
func testDeleteMessageBelowMessage() {
|
|
addMessage(100, 100, "m100")
|
|
addMessage(200, 200, "m200")
|
|
removeMessages([100])
|
|
|
|
expectEntries([.Message(200, 200, "m200", [], [])])
|
|
}
|
|
|
|
func testDeleteMessageAboveMessage() {
|
|
addMessage(100, 100, "m100")
|
|
addMessage(200, 200, "m200")
|
|
removeMessages([200])
|
|
|
|
expectEntries([.Message(100, 100, "m100", [], [])])
|
|
}
|
|
|
|
func testDeleteMessageBetweenMessages() {
|
|
addMessage(100, 100, "m100")
|
|
addMessage(200, 200, "m200")
|
|
addMessage(300, 300, "m300")
|
|
removeMessages([200])
|
|
|
|
expectEntries([.Message(100, 100, "m100", [], []), .Message(300, 300, "m300", [], [])])
|
|
}
|
|
|
|
func testAddUnsent() {
|
|
addMessage(100, 100, "m100", [], [.Unsent])
|
|
expectEntries([.Message(100, 100, "m100", [], [.Unsent])])
|
|
expectUnsent([100])
|
|
}
|
|
|
|
func testRemoveUnsent() {
|
|
addMessage(100, 100, "m100", [], [.Unsent])
|
|
expectEntries([.Message(100, 100, "m100", [], [.Unsent])])
|
|
expectUnsent([100])
|
|
|
|
removeMessages([100])
|
|
expectEntries([])
|
|
expectUnsent([])
|
|
}
|
|
|
|
func testUpdateUnsentToSentSameIndex() {
|
|
addMessage(100, 100, "m100", [], [.Unsent])
|
|
expectEntries([.Message(100, 100, "m100", [], [.Unsent])])
|
|
expectUnsent([100])
|
|
|
|
updateMessage(100, 100, 100, "m100", [], [], [])
|
|
expectEntries([.Message(100, 100, "m100", [], [])])
|
|
expectUnsent([])
|
|
}
|
|
|
|
func testUpdateUnsentToFailed() {
|
|
addMessage(100, 100, "m100", [], [.Unsent])
|
|
expectEntries([.Message(100, 100, "m100", [], [.Unsent])])
|
|
expectUnsent([100])
|
|
|
|
updateMessage(100, 100, 100, "m100", [], [.Unsent, .Failed], [])
|
|
expectEntries([.Message(100, 100, "m100", [], [.Unsent, .Failed])])
|
|
expectUnsent([])
|
|
}
|
|
|
|
func testUpdateDifferentIndex() {
|
|
addMessage(100, 100, "m100", [], [.Unsent])
|
|
expectEntries([.Message(100, 100, "m100", [], [.Unsent])])
|
|
expectUnsent([100])
|
|
|
|
updateMessage(100, 200, 200, "m100", [], [], [])
|
|
expectEntries([.Message(200, 200, "m100", [], [])])
|
|
expectUnsent([])
|
|
}
|
|
|
|
func testUpdateDifferentIndexBreakHole() {
|
|
addHole(1)
|
|
|
|
addMessage(100, 100, "m100", [], [.Unsent])
|
|
expectEntries([.Hole(1, 99, 100), .Message(100, 100, "m100", [], [.Unsent]), .Hole(101, Int32.max, Int32.max)])
|
|
expectUnsent([100])
|
|
|
|
updateMessage(100, 200, 200, "m100", [], [], [])
|
|
expectEntries([.Hole(1, 199, 200), .Message(200, 200, "m100", [], []), .Hole(201, Int32.max, Int32.max)])
|
|
expectUnsent([])
|
|
}
|
|
|
|
func testInsertTaggedIntoEmpty() {
|
|
addMessage(100, 100, "m100", [], [], MessageTags(rawValue: 1))
|
|
expectEntries([.Message(100, 100, "m100", [], [])], tagMask: MessageTags(rawValue: 1))
|
|
}
|
|
|
|
func testInsertMultipleTagsIntoEmpty() {
|
|
addMessage(200, 200, "m200", [], [], MessageTags(rawValue: 2))
|
|
addMessage(100, 100, "m100", [], [], MessageTags(rawValue: 1 | 2))
|
|
expectEntries([.Message(100, 100, "m100", [], [])], tagMask: MessageTags(rawValue: 1))
|
|
expectEntries([.Message(100, 100, "m100", [], []), .Message(200, 200, "m200", [], [])], tagMask: MessageTags(rawValue: 2))
|
|
}
|
|
|
|
func testRemoveSingleTagged() {
|
|
addMessage(100, 100, "m100", [], [], MessageTags(rawValue: 1))
|
|
removeMessages([100])
|
|
|
|
expectEntries([], tagMask: MessageTags(rawValue: 1))
|
|
}
|
|
|
|
func testRemoveMultipleTagged() {
|
|
addMessage(200, 200, "m200", [], [], MessageTags(rawValue: 2))
|
|
addMessage(100, 100, "m100", [], [], MessageTags(rawValue: 1 | 2))
|
|
removeMessages([100])
|
|
expectEntries([], tagMask: MessageTags(rawValue: 1))
|
|
expectEntries([.Message(200, 200, "m200", [], [])], tagMask: MessageTags(rawValue: 2))
|
|
}
|
|
|
|
func testTagsInsertHoleIntoEmpty() {
|
|
addHole(1)
|
|
expectEntries([.Hole(1, Int32.max, Int32.max)], tagMask: [.First])
|
|
expectEntries([.Hole(1, Int32.max, Int32.max)], tagMask: [.Second])
|
|
}
|
|
|
|
func testTagsBreakHoleWithMessage() {
|
|
addHole(1)
|
|
addMessage(100, 100, "m100", [], [], [.First])
|
|
|
|
expectEntries([.Hole(1, 99, 100), .Message(100, 100, "m100", [], []), .Hole(101, Int32.max, Int32.max)], tagMask: [.First])
|
|
expectEntries([.Hole(1, 99, 100), .Hole(101, Int32.max, Int32.max)], tagMask: [.Second])
|
|
}
|
|
|
|
func testTagsFillHoleUpperToLowerAllTags() {
|
|
addMessage(100, 100, "m100", [], [], [.First])
|
|
addMessage(200, 200, "m200", [], [], [.Second])
|
|
addHole(150)
|
|
|
|
fillHole(199, HoleFill(complete: false, direction: .UpperToLower), [(180, 180, "m180", [])])
|
|
|
|
expectEntries([.Message(100, 100, "m100", [], []), .Hole(101, 179, 180)], tagMask: [.First])
|
|
expectEntries([.Hole(101, 179, 180), .Message(200, 200, "m200", [], [])], tagMask: [.Second])
|
|
expectEntries([.Message(100, 100, "m100", [], []), .Hole(101, 179, 180), .Message(180, 180, "m180", [], []), .Message(200, 200, "m200", [], [])])
|
|
}
|
|
|
|
func testTagsFillHoleLowerToUpperAllTags() {
|
|
addMessage(100, 100, "m100", [], [], [.First])
|
|
addMessage(200, 200, "m200", [], [], [.Second])
|
|
addHole(150)
|
|
|
|
fillHole(199, HoleFill(complete: false, direction: .LowerToUpper), [(180, 180, "m180", [])])
|
|
|
|
expectEntries([.Message(100, 100, "m100", [], []), .Hole(181, 199, 200)], tagMask: [.First])
|
|
expectEntries([.Hole(181, 199, 200), .Message(200, 200, "m200", [], [])], tagMask: [.Second])
|
|
expectEntries([.Message(100, 100, "m100", [], []), .Message(180, 180, "m180", [], []), .Hole(181, 199, 200), .Message(200, 200, "m200", [], [])])
|
|
}
|
|
|
|
func testTagsFillHoleCompleteAllTags() {
|
|
addMessage(100, 100, "m100", [], [], [.First])
|
|
addMessage(200, 200, "m200", [], [], [.Second])
|
|
addHole(150)
|
|
|
|
fillHole(199, HoleFill(complete: true, direction: .UpperToLower), [(180, 180, "m180", [])])
|
|
|
|
expectEntries([.Message(100, 100, "m100", [], [])], tagMask: [.First])
|
|
expectEntries([.Message(200, 200, "m200", [], [])], tagMask: [.Second])
|
|
expectEntries([.Message(100, 100, "m100", [], []), .Message(180, 180, "m180", [], []), .Message(200, 200, "m200", [], [])])
|
|
}
|
|
|
|
func testTagsFillHoleUpperToLowerSingleTagWithMessages() {
|
|
addMessage(100, 100, "m100", [], [], [.First])
|
|
addMessage(200, 200, "m200", [], [], [.Second])
|
|
addHole(150)
|
|
|
|
fillHole(199, HoleFill(complete: false, direction: .UpperToLower), [(180, 180, "m180", [])], [.First])
|
|
|
|
expectEntries([.Message(100, 100, "m100", [], []), .Hole(101, 179, 180)], tagMask: [.First])
|
|
expectEntries([.Hole(101, 179, 180), .Hole(181, 199, 200), .Message(200, 200, "m200", [], [])], tagMask: [.Second])
|
|
expectEntries([.Message(100, 100, "m100", [], []), .Hole(101, 179, 180), .Message(180, 180, "m180", [], []), .Hole(181, 199, 200), .Message(200, 200, "m200", [], [])])
|
|
}
|
|
|
|
func testTagsFillHoleLowerToUpperSingleTagWithMessages() {
|
|
addMessage(100, 100, "m100", [], [], [.First])
|
|
addMessage(200, 200, "m200", [], [], [.Second])
|
|
addHole(150)
|
|
|
|
fillHole(199, HoleFill(complete: false, direction: .LowerToUpper), [(180, 180, "m180", [])], [.First])
|
|
|
|
expectEntries([.Message(100, 100, "m100", [], []), .Hole(181, 199, 200)], tagMask: [.First])
|
|
expectEntries([.Hole(101, 179, 180), .Hole(181, 199, 200), .Message(200, 200, "m200", [], [])], tagMask: [.Second])
|
|
expectEntries([.Message(100, 100, "m100", [], []), .Hole(101, 179, 180), .Message(180, 180, "m180", [], []), .Hole(181, 199, 200), .Message(200, 200, "m200", [], [])])
|
|
}
|
|
|
|
func testTagsFillHoleCompleteSingleTagWithMessages() {
|
|
addMessage(100, 100, "m100", [], [], [.First])
|
|
addMessage(200, 200, "m200", [], [], [.Second])
|
|
addHole(150)
|
|
|
|
fillHole(199, HoleFill(complete: true, direction: .UpperToLower), [(180, 180, "m180", [])], [.First])
|
|
|
|
expectEntries([.Message(100, 100, "m100", [], [])], tagMask: [.First])
|
|
expectEntries([.Hole(101, 179, 180), .Hole(181, 199, 200), .Message(200, 200, "m200", [], [])], tagMask: [.Second])
|
|
expectEntries([.Message(100, 100, "m100", [], []), .Hole(101, 179, 180), .Message(180, 180, "m180", [], []), .Hole(181, 199, 200), .Message(200, 200, "m200", [], [])])
|
|
}
|
|
|
|
func testTagsFillHoleUpperToLowerSingleTagWithEmpty() {
|
|
addMessage(100, 100, "m100", [], [], [.First])
|
|
addMessage(200, 200, "m200", [], [], [.Second])
|
|
addHole(150)
|
|
|
|
fillHole(199, HoleFill(complete: false, direction: .UpperToLower), [], [.First])
|
|
|
|
expectEntries([.Message(100, 100, "m100", [], [])], tagMask: [.First])
|
|
expectEntries([.Hole(101, 199, 200), .Message(200, 200, "m200", [], [])], tagMask: [.Second])
|
|
expectEntries([.Message(100, 100, "m100", [], []), .Hole(101, 199, 200), .Message(200, 200, "m200", [], [])])
|
|
}
|
|
|
|
func testTagsFillHoleLowerToUpperSingleTagWithEmpty() {
|
|
addMessage(100, 100, "m100", [], [], [.First])
|
|
addMessage(200, 200, "m200", [], [], [.Second])
|
|
addHole(150)
|
|
|
|
fillHole(199, HoleFill(complete: false, direction: .LowerToUpper), [], [.First])
|
|
|
|
expectEntries([.Message(100, 100, "m100", [], [])], tagMask: [.First])
|
|
expectEntries([.Hole(101, 199, 200), .Message(200, 200, "m200", [], [])], tagMask: [.Second])
|
|
expectEntries([.Message(100, 100, "m100", [], []), .Hole(101, 199, 200), .Message(200, 200, "m200", [], [])])
|
|
}
|
|
|
|
func testTagsFillHoleCompleteSingleTagWithEmpty() {
|
|
addMessage(100, 100, "m100", [], [], [.First])
|
|
addMessage(200, 200, "m200", [], [], [.Second])
|
|
addHole(150)
|
|
|
|
fillHole(199, HoleFill(complete: true, direction: .UpperToLower), [], [.First])
|
|
|
|
expectEntries([.Message(100, 100, "m100", [], [])], tagMask: [.First])
|
|
expectEntries([.Hole(101, 199, 200), .Message(200, 200, "m200", [], [])], tagMask: [.Second])
|
|
expectEntries([.Message(100, 100, "m100", [], []), .Hole(101, 199, 200), .Message(200, 200, "m200", [], [])])
|
|
}
|
|
|
|
func testTagsFillMultipleHolesSingleHole() {
|
|
addHole(1)
|
|
addMessage(100, 100, "m100", [], [], [.First])
|
|
addMessage(200, 200, "m200", [], [], [.First])
|
|
addMessage(300, 300, "m300", [], [], [.First])
|
|
addMessage(400, 400, "m400", [], [], [.First])
|
|
|
|
expectEntries([
|
|
.Hole(1, 99, 100),
|
|
.Message(100, 100, "m100", [], []),
|
|
.Hole(101, 199, 200),
|
|
.Message(200, 200, "m200", [], []),
|
|
.Hole(201, 299, 300),
|
|
.Message(300, 300, "m300", [], []),
|
|
.Hole(301, 399, 400),
|
|
.Message(400, 400, "m400", [], []),
|
|
.Hole(401, Int32.max, Int32.max)
|
|
], tagMask: [.First])
|
|
|
|
expectEntries([
|
|
.Hole(1, 99, 100),
|
|
.Hole(101, 199, 200),
|
|
.Hole(201, 299, 300),
|
|
.Hole(301, 399, 400),
|
|
.Hole(401, Int32.max, Int32.max)
|
|
], tagMask: [.Second])
|
|
|
|
fillMultipleHoles(500, HoleFill(complete: false, direction: .UpperToLower), [(500, 500, "m500", []), (350, 350, "m350", [])], [.Second], [.Second])
|
|
|
|
expectEntries([
|
|
.Hole(1, 99, 100),
|
|
.Hole(101, 199, 200),
|
|
.Hole(201, 299, 300),
|
|
.Hole(301, 349, 350),
|
|
.Message(350, 350, "m350", [], []),
|
|
.Message(500, 500, "m500", [], [])
|
|
], tagMask: [.Second])
|
|
|
|
expectEntries([
|
|
.Hole(1, 99, 100),
|
|
.Message(100, 100, "m100", [], []),
|
|
.Hole(101, 199, 200),
|
|
.Message(200, 200, "m200", [], []),
|
|
.Hole(201, 299, 300),
|
|
.Message(300, 300, "m300", [], []),
|
|
.Hole(301, 349, 350),
|
|
.Hole(351, 399, 400),
|
|
.Message(400, 400, "m400", [], []),
|
|
.Hole(401, 499, 500),
|
|
.Hole(501, Int32.max, Int32.max)
|
|
], tagMask: [.First])
|
|
}
|
|
|
|
func testFullTextGetEmpty() {
|
|
XCTAssert(self.textIndexTable!.search(peerId: nil, text: "abc", tags: nil).isEmpty)
|
|
}
|
|
|
|
func testFullTextMatch1() {
|
|
self.textIndexTable!.add(messageId: MessageId(peerId: peerId, namespace: 0, id: 1), text: "a b c", tags: [])
|
|
self.textIndexTable!.add(messageId: MessageId(peerId: peerId, namespace: 0, id: 2), text: "a b c d", tags: [])
|
|
self.textIndexTable!.add(messageId: MessageId(peerId: peerId, namespace: 0, id: 3), text: "c d e", tags: [])
|
|
|
|
var result = self.textIndexTable!.search(peerId: nil, text: "a", tags: nil).sorted()
|
|
let testIds1: [MessageId] = [
|
|
MessageId(peerId: peerId, namespace: 0, id: 1),
|
|
MessageId(peerId: peerId, namespace: 0, id: 2),
|
|
]
|
|
XCTAssert(result == testIds1)
|
|
|
|
result = self.textIndexTable!.search(peerId: nil, text: "c", tags: nil).sorted()
|
|
let testIds2: [MessageId] = [
|
|
MessageId(peerId: peerId, namespace: 0, id: 1),
|
|
MessageId(peerId: peerId, namespace: 0, id: 2),
|
|
MessageId(peerId: peerId, namespace: 0, id: 3)
|
|
]
|
|
XCTAssert(result == testIds2)
|
|
|
|
result = self.textIndexTable!.search(peerId: nil, text: "d", tags: nil).sorted()
|
|
let testIds3: [MessageId] = [
|
|
MessageId(peerId: peerId, namespace: 0, id: 2),
|
|
MessageId(peerId: peerId, namespace: 0, id: 3)
|
|
]
|
|
XCTAssert(result == testIds3)
|
|
|
|
result = self.textIndexTable!.search(peerId: nil, text: "a b c", tags: nil).sorted()
|
|
XCTAssert(result == testIds1)
|
|
|
|
result = self.textIndexTable!.search(peerId: nil, text: "a b c d e", tags: nil).sorted()
|
|
let testIds4: [MessageId] = [
|
|
]
|
|
XCTAssert(result == testIds4)
|
|
|
|
self.textIndexTable!.remove(messageId: MessageId(peerId: peerId, namespace: 0, id: 2))
|
|
let testIds5: [MessageId] = [
|
|
MessageId(peerId: peerId, namespace: 0, id: 1),
|
|
MessageId(peerId: peerId, namespace: 0, id: 3)
|
|
]
|
|
result = self.textIndexTable!.search(peerId: nil, text: "c", tags: nil).sorted()
|
|
XCTAssert(result == testIds5)
|
|
}
|
|
|
|
func testFullTextMatchLocal() {
|
|
self.textIndexTable!.add(messageId: MessageId(peerId: peerId, namespace: 0, id: 1), text: "a b c", tags: [])
|
|
self.textIndexTable!.add(messageId: MessageId(peerId: peerId, namespace: 0, id: 2), text: "a b c d", tags: [])
|
|
self.textIndexTable!.add(messageId: MessageId(peerId: otherPeerId, namespace: 0, id: 1), text: "c d e", tags: [])
|
|
self.textIndexTable!.add(messageId: MessageId(peerId: otherPeerId, namespace: 0, id: 2), text: "d e f", tags: [])
|
|
|
|
var result = self.textIndexTable!.search(peerId: peerId, text: "a", tags: nil).sorted()
|
|
let testIds1: [MessageId] = [
|
|
MessageId(peerId: peerId, namespace: 0, id: 1),
|
|
MessageId(peerId: peerId, namespace: 0, id: 2),
|
|
]
|
|
XCTAssert(result == testIds1)
|
|
|
|
result = self.textIndexTable!.search(peerId: otherPeerId, text: "c", tags: nil).sorted()
|
|
let testIds2: [MessageId] = [
|
|
MessageId(peerId: otherPeerId, namespace: 0, id: 1),
|
|
]
|
|
XCTAssert(result == testIds2)
|
|
|
|
result = self.textIndexTable!.search(peerId: otherPeerId, text: "d", tags: nil).sorted()
|
|
let testIds3: [MessageId] = [
|
|
MessageId(peerId: otherPeerId, namespace: 0, id: 1),
|
|
MessageId(peerId: otherPeerId, namespace: 0, id: 2)
|
|
]
|
|
XCTAssert(result == testIds3)
|
|
}
|
|
|
|
func testFullTextMatchLocalTags() {
|
|
self.textIndexTable!.add(messageId: MessageId(peerId: peerId, namespace: 0, id: 1), text: "a b c", tags: [])
|
|
self.textIndexTable!.add(messageId: MessageId(peerId: peerId, namespace: 0, id: 2), text: "a b c d", tags: [])
|
|
self.textIndexTable!.add(messageId: MessageId(peerId: peerId, namespace: 0, id: 3), text: "a b c", tags: [tag1])
|
|
self.textIndexTable!.add(messageId: MessageId(peerId: peerId, namespace: 0, id: 4), text: "a b c", tags: [tag1, tag2])
|
|
self.textIndexTable!.add(messageId: MessageId(peerId: peerId, namespace: 0, id: 5), text: "a b c", tags: [tag1, tag2])
|
|
self.textIndexTable!.add(messageId: MessageId(peerId: peerId, namespace: 0, id: 6), text: "a b c", tags: [tag2])
|
|
|
|
var result = self.textIndexTable!.search(peerId: peerId, text: "a b c", tags: nil).sorted()
|
|
let testIds1: [MessageId] = [
|
|
MessageId(peerId: peerId, namespace: 0, id: 1),
|
|
MessageId(peerId: peerId, namespace: 0, id: 2),
|
|
MessageId(peerId: peerId, namespace: 0, id: 3),
|
|
MessageId(peerId: peerId, namespace: 0, id: 4),
|
|
MessageId(peerId: peerId, namespace: 0, id: 5),
|
|
MessageId(peerId: peerId, namespace: 0, id: 6),
|
|
]
|
|
XCTAssert(result == testIds1)
|
|
|
|
result = self.textIndexTable!.search(peerId: peerId, text: "a b c", tags: [tag1]).sorted()
|
|
let testIds2: [MessageId] = [
|
|
MessageId(peerId: peerId, namespace: 0, id: 3),
|
|
MessageId(peerId: peerId, namespace: 0, id: 4),
|
|
MessageId(peerId: peerId, namespace: 0, id: 5),
|
|
]
|
|
XCTAssert(result == testIds2)
|
|
|
|
result = self.textIndexTable!.search(peerId: peerId, text: "a b c", tags: [tag2]).sorted()
|
|
let testIds3: [MessageId] = [
|
|
MessageId(peerId: peerId, namespace: 0, id: 4),
|
|
MessageId(peerId: peerId, namespace: 0, id: 5),
|
|
MessageId(peerId: peerId, namespace: 0, id: 6),
|
|
]
|
|
XCTAssert(result == testIds3)
|
|
|
|
result = self.textIndexTable!.search(peerId: peerId, text: "a b c", tags: [tag1, tag2]).sorted()
|
|
let testIds4: [MessageId] = [
|
|
MessageId(peerId: peerId, namespace: 0, id: 4),
|
|
MessageId(peerId: peerId, namespace: 0, id: 5),
|
|
]
|
|
XCTAssert(result == testIds4)
|
|
}
|
|
|
|
func testFullTextEscape1() {
|
|
self.textIndexTable!.add(messageId: MessageId(peerId: peerId, namespace: 0, id: 1), text: "abc' def'", tags: [])
|
|
var result = self.textIndexTable!.search(peerId: nil, text: "abc'", tags: nil).sorted()
|
|
let testIds1: [MessageId] = [
|
|
MessageId(peerId: peerId, namespace: 0, id: 1)
|
|
]
|
|
XCTAssert(result == testIds1)
|
|
|
|
result = self.textIndexTable!.search(peerId: nil, text: "abc' def'", tags: nil).sorted()
|
|
XCTAssert(result == testIds1)
|
|
|
|
result = self.textIndexTable!.search(peerId: nil, text: "abc' AND def", tags: nil).sorted()
|
|
XCTAssert(result.isEmpty)
|
|
}
|
|
}
|