GPBSwiftTests

// Protocol Buffers - Google's data interchange format
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
import Foundation
import XCTest
// Test some usage of the ObjC library from Swift.
class GPBBridgeTests: XCTestCase {
  func testProto2Basics() {
    let msg = Message2()
    let msg2 = Message2()
    let msg3 = Message2_OptionalGroup()
    msg.optionalInt32 = 100
    msg.optionalString = "abc"
    msg.optionalEnum = .bar
    msg2.optionalString = "other"
    msg.optional = msg2
    msg3.a = 200
    msg.optionalGroup = msg3
    msg.repeatedInt32Array.addValue(300)
    msg.repeatedInt32Array.addValue(301)
    msg.repeatedStringArray.add("mno")
    msg.repeatedStringArray.add("pqr")
    msg.repeatedEnumArray.addValue(Message2_Enum.bar.rawValue)
    msg.repeatedEnumArray.addValue(Message2_Enum.baz.rawValue)
    msg.mapInt32Int32.setInt32(400, forKey:500)
    msg.mapInt32Int32.setInt32(401, forKey:501)
    msg.mapStringString.setObject("foo", forKey:"bar" as NSString)
    msg.mapStringString.setObject("abc", forKey:"xyz" as NSString)
    msg.mapInt32Enum.setEnum(Message2_Enum.bar.rawValue, forKey:600)
    msg.mapInt32Enum.setEnum(Message2_Enum.baz.rawValue, forKey:601)
    // Check has*.
    XCTAssertTrue(msg.hasOptionalInt32)
    XCTAssertTrue(msg.hasOptionalString)
    XCTAssertTrue(msg.hasOptionalEnum)
    XCTAssertTrue(msg2.hasOptionalString)
    XCTAssertTrue(msg.hasOptionalMessage)
    XCTAssertTrue(msg3.hasA)
    XCTAssertTrue(msg.hasOptionalGroup)
    XCTAssertFalse(msg.hasOptionalInt64)
    XCTAssertFalse(msg.hasOptionalFloat)
    // Check values.
    XCTAssertEqual(msg.optionalInt32, Int32(100))
    XCTAssertEqual(msg.optionalString, "abc")
    XCTAssertEqual(msg2.optionalString, "other")
    XCTAssertTrue(msg.optional === msg2)
    XCTAssertEqual(msg.optionalEnum, Message2_Enum.bar)
    XCTAssertEqual(msg3.a, Int32(200))
    XCTAssertTrue(msg.optionalGroup === msg3)
    XCTAssertEqual(msg.repeatedInt32Array.count, UInt(2))
    XCTAssertEqual(msg.repeatedInt32Array.value(at: 0), Int32(300))
    XCTAssertEqual(msg.repeatedInt32Array.value(at: 1), Int32(301))
    XCTAssertEqual(msg.repeatedStringArray.count, Int(2))
    XCTAssertEqual(msg.repeatedStringArray.object(at: 0) as? String, "mno")
    XCTAssertEqual(msg.repeatedStringArray.object(at: 1) as? String, "pqr")
    XCTAssertEqual(msg.repeatedEnumArray.count, UInt(2))
    XCTAssertEqual(msg.repeatedEnumArray.value(at: 0), Message2_Enum.bar.rawValue)
    XCTAssertEqual(msg.repeatedEnumArray.value(at: 1), Message2_Enum.baz.rawValue)
    XCTAssertEqual(msg.repeatedInt64Array.count, UInt(0))
    XCTAssertEqual(msg.mapInt32Int32.count, UInt(2))
    var intValue: Int32 = 0
    XCTAssertTrue(msg.mapInt32Int32.getInt32(&intValue, forKey: 500))
    XCTAssertEqual(intValue, Int32(400))
    XCTAssertTrue(msg.mapInt32Int32.getInt32(&intValue, forKey: 501))
    XCTAssertEqual(intValue, Int32(401))
    XCTAssertEqual(msg.mapStringString.count, Int(2))
    XCTAssertEqual(msg.mapStringString.object(forKey: "bar") as? String, "foo")
    XCTAssertEqual(msg.mapStringString.object(forKey: "xyz") as? String, "abc")
    XCTAssertEqual(msg.mapInt32Enum.count, UInt(2))
    XCTAssertTrue(msg.mapInt32Enum.getEnum(&intValue, forKey:600))
    XCTAssertEqual(intValue, Message2_Enum.bar.rawValue)
    XCTAssertTrue(msg.mapInt32Enum.getEnum(&intValue, forKey:601))
    XCTAssertEqual(intValue, Message2_Enum.baz.rawValue)
    // Clearing a string with nil.
    msg2.optionalString = nil
    XCTAssertFalse(msg2.hasOptionalString)
    XCTAssertEqual(msg2.optionalString, "")
    // Clearing a message with nil.
    msg.optionalGroup = nil
    XCTAssertFalse(msg.hasOptionalGroup)
    XCTAssertTrue(msg.optionalGroup !== msg3)  // New instance
    // Clear.
    msg.clear()
    XCTAssertFalse(msg.hasOptionalInt32)
    XCTAssertFalse(msg.hasOptionalString)
    XCTAssertFalse(msg.hasOptionalEnum)
    XCTAssertFalse(msg.hasOptionalMessage)
    XCTAssertFalse(msg.hasOptionalInt64)
    XCTAssertFalse(msg.hasOptionalFloat)
    XCTAssertEqual(msg.optionalInt32, Int32(0))
    XCTAssertEqual(msg.optionalString, "")
    XCTAssertTrue(msg.optional !== msg2)  // New instance
    XCTAssertEqual(msg.optionalEnum, Message2_Enum.foo)  // Default
    XCTAssertEqual(msg.repeatedInt32Array.count, UInt(0))
    XCTAssertEqual(msg.repeatedStringArray.count, Int(0))
    XCTAssertEqual(msg.repeatedEnumArray.count, UInt(0))
    XCTAssertEqual(msg.mapInt32Int32.count, UInt(0))
    XCTAssertEqual(msg.mapStringString.count, Int(0))
    XCTAssertEqual(msg.mapInt32Enum.count, UInt(0))
  }
  func testProto3Basics() {
    let msg = Message3()
    let msg2 = Message3()
    msg.optionalInt32 = 100
    msg.optionalString = "abc"
    msg.optionalEnum = .bar
    msg2.optionalString = "other"
    msg.optional = msg2
    msg.repeatedInt32Array.addValue(300)
    msg.repeatedInt32Array.addValue(301)
    msg.repeatedStringArray.add("mno")
    msg.repeatedStringArray.add("pqr")
    // "proto3" syntax lets enum get unknown values.
    msg.repeatedEnumArray.addValue(Message3_Enum.bar.rawValue)
    msg.repeatedEnumArray.addRawValue(666)
    SetMessage3_OptionalEnum_RawValue(msg2, 666)
    msg.mapInt32Int32.setInt32(400, forKey:500)
    msg.mapInt32Int32.setInt32(401, forKey:501)
    msg.mapStringString.setObject("foo", forKey:"bar" as NSString)
    msg.mapStringString.setObject("abc", forKey:"xyz" as NSString)
    msg.mapInt32Enum.setEnum(Message2_Enum.bar.rawValue, forKey:600)
    // "proto3" syntax lets enum get unknown values.
    msg.mapInt32Enum.setRawValue(666, forKey:601)
    // Has only exists on for message fields.
    XCTAssertTrue(msg.hasOptionalMessage)
    XCTAssertFalse(msg2.hasOptionalMessage)
    // Check values.
    XCTAssertEqual(msg.optionalInt32, Int32(100))
    XCTAssertEqual(msg.optionalString, "abc")
    XCTAssertEqual(msg2.optionalString, "other")
    XCTAssertTrue(msg.optional === msg2)
    XCTAssertEqual(msg.optionalEnum, Message3_Enum.bar)
    XCTAssertEqual(msg.repeatedInt32Array.count, UInt(2))
    XCTAssertEqual(msg.repeatedInt32Array.value(at: 0), Int32(300))
    XCTAssertEqual(msg.repeatedInt32Array.value(at: 1), Int32(301))
    XCTAssertEqual(msg.repeatedStringArray.count, Int(2))
    XCTAssertEqual(msg.repeatedStringArray.object(at: 0) as? String, "mno")
    XCTAssertEqual(msg.repeatedStringArray.object(at: 1) as? String, "pqr")
    XCTAssertEqual(msg.repeatedInt64Array.count, UInt(0))
    XCTAssertEqual(msg.repeatedEnumArray.count, UInt(2))
    XCTAssertEqual(msg.repeatedEnumArray.value(at: 0), Message3_Enum.bar.rawValue)
    XCTAssertEqual(msg.repeatedEnumArray.value(at: 1), Message3_Enum.gpbUnrecognizedEnumeratorValue.rawValue)
    XCTAssertEqual(msg.repeatedEnumArray.rawValue(at: 1), 666)
    XCTAssertEqual(msg2.optionalEnum, Message3_Enum.gpbUnrecognizedEnumeratorValue)
    XCTAssertEqual(Message3_OptionalEnum_RawValue(msg2), Int32(666))
    XCTAssertEqual(msg.mapInt32Int32.count, UInt(2))
    var intValue: Int32 = 0
    XCTAssertTrue(msg.mapInt32Int32.getInt32(&intValue, forKey:500))
    XCTAssertEqual(intValue, Int32(400))
    XCTAssertTrue(msg.mapInt32Int32.getInt32(&intValue, forKey:501))
    XCTAssertEqual(intValue, Int32(401))
    XCTAssertEqual(msg.mapStringString.count, Int(2))
    XCTAssertEqual(msg.mapStringString.object(forKey: "bar") as? String, "foo")
    XCTAssertEqual(msg.mapStringString.object(forKey: "xyz") as? String, "abc")
    XCTAssertEqual(msg.mapInt32Enum.count, UInt(2))
    XCTAssertTrue(msg.mapInt32Enum.getEnum(&intValue, forKey:600))
    XCTAssertEqual(intValue, Message2_Enum.bar.rawValue)
    XCTAssertTrue(msg.mapInt32Enum.getEnum(&intValue, forKey:601))
    XCTAssertEqual(intValue, Message3_Enum.gpbUnrecognizedEnumeratorValue.rawValue)
    XCTAssertTrue(msg.mapInt32Enum.getRawValue(&intValue, forKey:601))
    XCTAssertEqual(intValue, 666)
    // Clearing a string with nil.
    msg2.optionalString = nil
    XCTAssertEqual(msg2.optionalString, "")
    // Clearing a message with nil.
    msg.optional = nil
    XCTAssertFalse(msg.hasOptionalMessage)
    XCTAssertTrue(msg.optional !== msg2)  // New instance
    // Clear.
    msg.clear()
    XCTAssertFalse(msg.hasOptionalMessage)
    XCTAssertEqual(msg.optionalInt32, Int32(0))
    XCTAssertEqual(msg.optionalString, "")
    XCTAssertTrue(msg.optional !== msg2)  // New instance
    XCTAssertEqual(msg.optionalEnum, Message3_Enum.foo)  // Default
    XCTAssertEqual(msg.repeatedInt32Array.count, UInt(0))
    XCTAssertEqual(msg.repeatedStringArray.count, Int(0))
    XCTAssertEqual(msg.repeatedEnumArray.count, UInt(0))
    msg2.clear()
    XCTAssertEqual(msg2.optionalEnum, Message3_Enum.foo)  // Default
    XCTAssertEqual(Message3_OptionalEnum_RawValue(msg2), Message3_Enum.foo.rawValue)
    XCTAssertEqual(msg.mapInt32Int32.count, UInt(0))
    XCTAssertEqual(msg.mapStringString.count, Int(0))
    XCTAssertEqual(msg.mapInt32Enum.count, UInt(0))
  }
  func testAutoCreation() {
    let msg = Message2()
    XCTAssertFalse(msg.hasOptionalGroup)
    XCTAssertFalse(msg.hasOptionalMessage)
    // Access shouldn't result in has* but should return objects.
    let msg2 = msg.optionalGroup
    let msg3 = msg.optional.optional
    let msg4 = msg.optional
    XCTAssertNotNil(msg2)
    XCTAssertNotNil(msg3)
    XCTAssertFalse(msg.hasOptionalGroup)
    XCTAssertFalse(msg.optional.hasOptionalMessage)
    XCTAssertFalse(msg.hasOptionalMessage)
    // Setting things should trigger has* getting set.
    msg.optionalGroup.a = 10
    msg.optional.optional.optionalInt32 = 100
    XCTAssertTrue(msg.hasOptionalGroup)
    XCTAssertTrue(msg.optional.hasOptionalMessage)
    XCTAssertTrue(msg.hasOptionalMessage)
    // And they should be the same pointer as before.
    XCTAssertTrue(msg2 === msg.optionalGroup)
    XCTAssertTrue(msg3 === msg.optional.optional)
    XCTAssertTrue(msg4 === msg.optional)
    // Clear gets us new objects next time around.
    msg.clear()
    XCTAssertFalse(msg.hasOptionalGroup)
    XCTAssertFalse(msg.optional.hasOptionalMessage)
    XCTAssertFalse(msg.hasOptionalMessage)
    msg.optionalGroup.a = 20
    msg.optional.optional.optionalInt32 = 200
    XCTAssertTrue(msg.hasOptionalGroup)
    XCTAssertTrue(msg.optional.hasOptionalMessage)
    XCTAssertTrue(msg.hasOptionalMessage)
    XCTAssertTrue(msg2 !== msg.optionalGroup)
    XCTAssertTrue(msg3 !== msg.optional.optional)
    XCTAssertTrue(msg4 !== msg.optional)
    // Explicit set of a message, means autocreated object doesn't bind.
    msg.clear()
    let autoCreated = msg.optional
    XCTAssertFalse(msg.hasOptionalMessage)
    let msg5 = Message2()
    msg5.optionalInt32 = 123
    msg.optional = msg5
    XCTAssertTrue(msg.hasOptionalMessage)
    // Modifying the autocreated doesn't replaced the explicit set one.
    autoCreated?.optionalInt32 = 456
    XCTAssertTrue(msg.hasOptionalMessage)
    XCTAssertTrue(msg.optional === msg5)
    XCTAssertEqual(msg.optional.optionalInt32, Int32(123))
  }
  func testProto2OneOfSupport() {
    let msg = Message2()
    XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase.gpbUnsetOneOfCase)
    XCTAssertEqual(msg.oneofInt32, Int32(100))  // Default
    XCTAssertEqual(msg.oneofFloat, Float(110.0))  // Default
    XCTAssertEqual(msg.oneofEnum, Message2_Enum.baz)  // Default
    let autoCreated = msg.oneof  // Default create one.
    XCTAssertNotNil(autoCreated)
    XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase.gpbUnsetOneOfCase)
    msg.oneofInt32 = 10
    XCTAssertEqual(msg.oneofInt32, Int32(10))
    XCTAssertEqual(msg.oneofFloat, Float(110.0))  // Default
    XCTAssertEqual(msg.oneofEnum, Message2_Enum.baz)  // Default
    XCTAssertTrue(msg.oneof === autoCreated)  // Still the same
    XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase.oneofInt32)
    msg.oneofFloat = 20.0
    XCTAssertEqual(msg.oneofInt32, Int32(100))  // Default
    XCTAssertEqual(msg.oneofFloat, Float(20.0))
    XCTAssertEqual(msg.oneofEnum, Message2_Enum.baz)  // Default
    XCTAssertTrue(msg.oneof === autoCreated)  // Still the same
    XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase.oneofFloat)
    msg.oneofEnum = .bar
    XCTAssertEqual(msg.oneofInt32, Int32(100))  // Default
    XCTAssertEqual(msg.oneofFloat, Float(110.0))  // Default
    XCTAssertEqual(msg.oneofEnum, Message2_Enum.bar)
    XCTAssertTrue(msg.oneof === autoCreated)  // Still the same
    XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase.oneofEnum)
    // Sets via the autocreated instance.
    msg.oneof.optionalInt32 = 200
    XCTAssertEqual(msg.oneofInt32, Int32(100))  // Default
    XCTAssertEqual(msg.oneofFloat, Float(110.0))  // Default
    XCTAssertEqual(msg.oneofEnum, Message2_Enum.baz)  // Default
    XCTAssertTrue(msg.oneof === autoCreated)  // Still the same
    XCTAssertEqual(msg.oneof.optionalInt32, Int32(200))
    XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase.oneofMessage)
    // Clear the oneof.
    Message2_ClearOOneOfCase(msg)
    XCTAssertEqual(msg.oneofInt32, Int32(100))  // Default
    XCTAssertEqual(msg.oneofFloat, Float(110.0))  // Default
    XCTAssertEqual(msg.oneofEnum, Message2_Enum.baz)  // Default
    let autoCreated2 = msg.oneof  // Default create one
    XCTAssertNotNil(autoCreated2)
    XCTAssertTrue(autoCreated2 !== autoCreated)  // New instance
    XCTAssertEqual(msg.oneof.optionalInt32, Int32(0))  // Default
    XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase.gpbUnsetOneOfCase)
    msg.oneofInt32 = 10
    XCTAssertEqual(msg.oneofInt32, Int32(10))
    XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase.oneofInt32)
    // Confirm Message.clear() handles the oneof correctly.
    msg.clear()
    XCTAssertEqual(msg.oneofInt32, Int32(100))  // Default
    XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase.gpbUnsetOneOfCase)
    // Sets via the autocreated instance.
    msg.oneof.optionalInt32 = 300
    XCTAssertTrue(msg.oneof !== autoCreated)  // New instance
    XCTAssertTrue(msg.oneof !== autoCreated2)  // New instance
    XCTAssertEqual(msg.oneof.optionalInt32, Int32(300))
    XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase.oneofMessage)
    // Set message to nil clears the oneof.
    msg.oneof = nil
    XCTAssertEqual(msg.oneof.optionalInt32, Int32(0))  // Default
    XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase.gpbUnsetOneOfCase)
  }
  func testProto3OneOfSupport() {
    let msg = Message3()
    XCTAssertEqual(msg.oOneOfCase, Message3_O_OneOfCase.gpbUnsetOneOfCase)
    XCTAssertEqual(msg.oneofInt32, Int32(0))  // Default
    XCTAssertEqual(msg.oneofFloat, Float(0.0))  // Default
    XCTAssertEqual(msg.oneofEnum, Message3_Enum.foo)  // Default
    let autoCreated = msg.oneof  // Default create one.
    XCTAssertNotNil(autoCreated)
    XCTAssertEqual(msg.oOneOfCase, Message3_O_OneOfCase.gpbUnsetOneOfCase)
    msg.oneofInt32 = 10
    XCTAssertEqual(msg.oneofInt32, Int32(10))
    XCTAssertEqual(msg.oneofFloat, Float(0.0))  // Default
    XCTAssertEqual(msg.oneofEnum, Message3_Enum.foo)  // Default
    XCTAssertTrue(msg.oneof === autoCreated)  // Still the same
    XCTAssertEqual(msg.oOneOfCase, Message3_O_OneOfCase.oneofInt32)
    msg.oneofFloat = 20.0
    XCTAssertEqual(msg.oneofInt32, Int32(0))  // Default
    XCTAssertEqual(msg.oneofFloat, Float(20.0))
    XCTAssertEqual(msg.oneofEnum, Message3_Enum.foo)  // Default
    XCTAssertTrue(msg.oneof === autoCreated)  // Still the same
    XCTAssertEqual(msg.oOneOfCase, Message3_O_OneOfCase.oneofFloat)
    msg.oneofEnum = .bar
    XCTAssertEqual(msg.oneofInt32, Int32(0))  // Default
    XCTAssertEqual(msg.oneofFloat, Float(0.0))  // Default
    XCTAssertEqual(msg.oneofEnum, Message3_Enum.bar)
    XCTAssertTrue(msg.oneof === autoCreated)  // Still the same
    XCTAssertEqual(msg.oOneOfCase, Message3_O_OneOfCase.oneofEnum)
    // Sets via the autocreated instance.
    msg.oneof.optionalInt32 = 200
    XCTAssertEqual(msg.oneofInt32, Int32(0))  // Default
    XCTAssertEqual(msg.oneofFloat, Float(0.0))  // Default
    XCTAssertEqual(msg.oneofEnum, Message3_Enum.foo)  // Default
    XCTAssertTrue(msg.oneof === autoCreated)  // Still the same
    XCTAssertEqual(msg.oneof.optionalInt32, Int32(200))
    XCTAssertEqual(msg.oOneOfCase, Message3_O_OneOfCase.oneofMessage)
    // Clear the oneof.
    Message3_ClearOOneOfCase(msg)
    XCTAssertEqual(msg.oneofInt32, Int32(0))  // Default
    XCTAssertEqual(msg.oneofFloat, Float(0.0))  // Default
    XCTAssertEqual(msg.oneofEnum, Message3_Enum.foo)  // Default
    let autoCreated2 = msg.oneof  // Default create one
    XCTAssertNotNil(autoCreated2)
    XCTAssertTrue(autoCreated2 !== autoCreated)  // New instance
    XCTAssertEqual(msg.oneof.optionalInt32, Int32(0))  // Default
    XCTAssertEqual(msg.oOneOfCase, Message3_O_OneOfCase.gpbUnsetOneOfCase)
    msg.oneofInt32 = 10
    XCTAssertEqual(msg.oneofInt32, Int32(10))
    XCTAssertEqual(msg.oOneOfCase, Message3_O_OneOfCase.oneofInt32)
    // Confirm Message.clear() handles the oneof correctly.
    msg.clear()
    XCTAssertEqual(msg.oneofInt32, Int32(0))  // Default
    XCTAssertEqual(msg.oOneOfCase, Message3_O_OneOfCase.gpbUnsetOneOfCase)
    // Sets via the autocreated instance.
    msg.oneof.optionalInt32 = 300
    XCTAssertTrue(msg.oneof !== autoCreated)  // New instance
    XCTAssertTrue(msg.oneof !== autoCreated2)  // New instance
    XCTAssertEqual(msg.oneof.optionalInt32, Int32(300))
    XCTAssertEqual(msg.oOneOfCase, Message3_O_OneOfCase.oneofMessage)
    // Set message to nil clears the oneof.
    msg.oneof = nil
    XCTAssertEqual(msg.oneof.optionalInt32, Int32(0))  // Default
    XCTAssertEqual(msg.oOneOfCase, Message3_O_OneOfCase.gpbUnsetOneOfCase)
  }
  func testSerialization() {
    let msg = Message2()
    msg.optionalInt32 = 100
    msg.optionalInt64 = 101
    msg.optionalGroup.a = 102
    msg.repeatedStringArray.add("abc")
    msg.repeatedStringArray.add("def")
    msg.mapInt32Int32.setInt32(200, forKey:300)
    msg.mapInt32Int32.setInt32(201, forKey:201)
    msg.mapStringString.setObject("foo", forKey:"bar" as NSString)
    msg.mapStringString.setObject("abc", forKey:"xyz" as NSString)
    let data = msg.data()
    let msg2 = try! Message2(data: data!)
    XCTAssertTrue(msg2 !== msg)  // New instance
    XCTAssertEqual(msg.optionalInt32, Int32(100))
    XCTAssertEqual(msg.optionalInt64, Int64(101))
    XCTAssertEqual(msg.optionalGroup.a, Int32(102))
    XCTAssertEqual(msg.repeatedStringArray.count, Int(2))
    XCTAssertEqual(msg.mapInt32Int32.count, UInt(2))
    XCTAssertEqual(msg.mapStringString.count, Int(2))
    XCTAssertEqual(msg2, msg)
  }
}


TFLBertTokenizerTest

class TFLBertTokenizerTest: XCTestCase {
  static let bundle = Bundle(for: TFLBertTokenizerTest.self)
  static let mobileBertVocabPath = bundle.path(forResource: "mobilebert_vocab", ofType: "txt")!
  func testInitBertTokenizerFromPath() {
    let bertTokenizer = TFLBertTokenizer(vocabPath: TFLBertTokenizerTest.mobileBertVocabPath)
    XCTAssertNotNil(bertTokenizer)
    let tokens = bertTokenizer.tokens(fromInput: "i'm questionansweraskask")
    XCTAssertEqual(tokens, ["i", "'", "m", "question", "##ans", "##wer", "##ask", "##ask"])
    let ids = bertTokenizer.ids(fromTokens: tokens)
    XCTAssertEqual(ids, [1045, 1005, 1049, 3160, 6962, 13777, 19895, 19895])
  }
  func testInitBertTokenizerFromVocab() {
    let bertTokenizer = TFLBertTokenizer(vocab: ["hell", "##o", "wor", "##ld", "there"])
    XCTAssertNotNil(bertTokenizer)
    let tokens = bertTokenizer.tokens(fromInput: "hello there hello world")
    XCTAssertEqual(tokens, ["hell", "##o", "there", "hell", "##o", "wor", "##ld"])
    let ids = bertTokenizer.ids(fromTokens: tokens)
    XCTAssertEqual(ids, [0, 1, 4, 0, 1, 2, 3])
  }
}


TFLSentencepieceTokenizerTest

class TFLSentencepieceTokenizerTest: XCTestCase {
  static let bundle = Bundle(for: TFLSentencepieceTokenizerTest.self)
  static let spModelPath = bundle.path(forResource: "30k-clean", ofType: "model")!
  func testInitSentenpieceTokenizerFromPath() {
    let spTokenizer = TFLSentencepieceTokenizer(
      modelPath: TFLSentencepieceTokenizerTest.spModelPath)
    XCTAssertNotNil(spTokenizer)
    let tokens = spTokenizer.tokens(fromInput: "good morning, i'm your teacher.\n")
    XCTAssertEqual(tokens, ["▁good", "▁morning", ",", "▁i", "'", "m", "▁your", "▁teacher", "."])
    let ids = spTokenizer.ids(fromTokens: tokens)
    XCTAssertEqual(ids, [254, 959, 15, 31, 22, 79, 154, 2197, 9])
  }
}


TFLAudioClassifierTests

class AudioClassifierTests: XCTestCase {
  static let bundle = Bundle(for: AudioClassifierTests.self)
  static let modelPath = bundle.path(
    forResource: "yamnet_audio_classifier_with_metadata",
    ofType: "tflite")
  func verifyError(
    _ error: Error,
    expectedLocalizedDescription: String
  ) {
    XCTAssertEqual(
      error.localizedDescription,
      expectedLocalizedDescription)
  }
  func verifyCategory(
    _ category: ClassificationCategory,
    expectedIndex: NSInteger,
    expectedScore: Float,
    expectedLabel: String,
    expectedDisplayName: String?
  ) {
    XCTAssertEqual(
      category.index,
      expectedIndex)
    XCTAssertEqual(
      category.score,
      expectedScore,
      accuracy: 1e-6)
    XCTAssertEqual(
      category.label,
      expectedLabel)
    XCTAssertEqual(
      category.displayName,
      expectedDisplayName)
  }
  func verifyClassifications(
    _ classifications: Classifications,
    expectedHeadIndex: NSInteger,
    expectedCategoryCount: NSInteger
  ) {
    XCTAssertEqual(
      classifications.headIndex,
      expectedHeadIndex)
    XCTAssertEqual(
      classifications.categories.count,
      expectedCategoryCount)
  }
  func verifyClassificationResult(
    _ classificationResult: ClassificationResult,
    expectedClassificationsCount: NSInteger
  ) {
    XCTAssertEqual(
      classificationResult.classifications.count,
      expectedClassificationsCount)
  }
  func bufferFromFile(
    withName name: String,
    fileExtension: String,
    audioFormat: AudioFormat
  ) -> AVAudioPCMBuffer? {
    guard
      let filePath = AudioClassifierTests.bundle.path(
        forResource: name,
        ofType: fileExtension)
    else {
      return nil
    }
    return AVAudioPCMBuffer.loadPCMBufferFromFile(
      withPath: filePath,
      audioFormat: audioFormat)
  }
  func createAudioClassifierOptions(
    modelPath: String?
  ) throws -> AudioClassifierOptions? {
    let modelPath = try XCTUnwrap(modelPath)
    let options = AudioClassifierOptions(modelPath: modelPath)
    return options
  }
  func createAudioClassifier(
    withModelPath modelPath: String?
  ) throws -> AudioClassifier? {
    let options = try XCTUnwrap(
      self.createAudioClassifierOptions(
        modelPath: AudioClassifierTests.modelPath))
    let audioClassifier = try XCTUnwrap(
      AudioClassifier.classifier(
        options: options))
    return audioClassifier
  }
  func createAudioTensor(
    withAudioClassifier audioClassifier: AudioClassifier
  ) -> AudioTensor {
    let audioTensor = audioClassifier.createInputAudioTensor()
    return audioTensor
  }
  func loadAudioTensor(
    _ audioTensor: AudioTensor,
    fromWavFileWithName fileName: String
  ) throws {
    // Load pcm buffer from file.
    let buffer = try XCTUnwrap(
      self.bufferFromFile(
        withName: fileName,
        fileExtension: "wav",
        audioFormat: audioTensor.audioFormat))
    // Get float buffer from pcm buffer.
    let floatBuffer = try XCTUnwrap(buffer.floatBuffer)
    // Load float buffer into the audio tensor.
    try audioTensor.load(
      buffer: floatBuffer,
      offset: 0,
      size: floatBuffer.size)
  }
  func classify(
    audioTensor: AudioTensor,
    usingAudioClassifier audioClassifier: AudioClassifier
  ) throws -> ClassificationResult? {
    let classificationResult = try XCTUnwrap(
      audioClassifier.classify(
        audioTensor: audioTensor))
    let expectedClassificationsCount = 1
    self.verifyClassificationResult(
      classificationResult,
      expectedClassificationsCount: expectedClassificationsCount)
    let expectedCategoryCount = 521
    let expectedHeadIndex = 0
    self.verifyClassifications(
      classificationResult.classifications[0],
      expectedHeadIndex: expectedHeadIndex,
      expectedCategoryCount: expectedCategoryCount)
    return classificationResult
  }
  func validateForInferenceWithFloatBuffer(
    categories: [ClassificationCategory]
  ) {
    self.verifyCategory(
      categories[0],
      expectedIndex: 0,
      expectedScore: 0.957031,
      expectedLabel: "Speech",
      expectedDisplayName: nil)
    self.verifyCategory(
      categories[1],
      expectedIndex: 500,
      0.019531,               // expectedScore
      expectedLabel: "Inside, small room",
      expectedDisplayName: nil)
  }
  func testInferenceWithFloatBufferSucceeds() throws {
    let audioClassifier = try XCTUnwrap(
      self.createAudioClassifier(
        withModelPath: AudioClassifierTests.modelPath))
    let audioTensor = self.createAudioTensor(
      withAudioClassifier: audioClassifier)
    try self.loadAudioTensor(
      audioTensor, fromWavFileWithName: "speech")
    let classificationResult = try XCTUnwrap(
      self.classify(
        audioTensor: audioTensor,
        usingAudioClassifier: audioClassifier))
    self.validateForInferenceWithFloatBuffer(
      categories: classificationResult.classifications[0].categories)
  }
  func testInferenceWithNoModelPathFails() throws {
    let options = AudioClassifierOptions()
    do {
      let audioClassifier = try AudioClassifier.classifier(
        options: options)
      XCTAssertNil(audioClassifier)
    } catch {
      self.verifyError(
        error,
        expectedLocalizedDescription:
          "INVALID_ARGUMENT: Missing mandatory `model_file` field in `base_options`")
    }
  }
}


TFLImageSegmenterTests

class ImageSegmenterTests: XCTestCase {
  static let bundle = Bundle(for: ImageSegmenterTests.self)
  static let modelPath = bundle.path(
    forResource: "deeplabv3",
    ofType: "tflite")
  // The maximum fraction of pixels in the candidate mask that can have a
  // different class than the golden mask for the test to pass.
  let kGoldenMaskTolerance: Float = 1e-2
  // Magnification factor used when creating the golden category masks to make
  // them more human-friendly. Each pixel in the golden masks has its value
  // multiplied by this factor, i.e. a value of 10 means class index 1, a value of
  // 20 means class index 2, etc.
  let kGoldenMaskMagnificationFactor: UInt8 = 10
  let deepLabV3SegmentationWidth = 257
  let deepLabV3SegmentationHeight = 257
  func verifyDeeplabV3PartialSegmentationResult(_ coloredLabels: [ColoredLabel]) {
    self.verifyColoredLabel(
      coloredLabels[0],
      expectedR: 0,
      expectedG: 0,
      expectedB: 0,
      expectedLabel: "background")
    self.verifyColoredLabel(
      coloredLabels[1],
      expectedR: 128,
      expectedG: 0,
      expectedB: 0,
      expectedLabel: "aeroplane")
    self.verifyColoredLabel(
      coloredLabels[2],
      expectedR: 0,
      expectedG: 128,
      expectedB: 0,
      expectedLabel: "bicycle")
    self.verifyColoredLabel(
      coloredLabels[3],
      expectedR: 128,
      expectedG: 128,
      expectedB: 0,
      expectedLabel: "bird")
    self.verifyColoredLabel(
      coloredLabels[4],
      expectedR: 0,
      expectedG: 0,
      expectedB: 128,
      expectedLabel: "boat")
    self.verifyColoredLabel(
      coloredLabels[5],
      expectedR: 128,
      expectedG: 0,
      expectedB: 128,
      expectedLabel: "bottle")
    self.verifyColoredLabel(
      coloredLabels[6],
      expectedR: 0,
      expectedG: 128,
      expectedB: 128,
      expectedLabel: "bus")
    self.verifyColoredLabel(
      coloredLabels[7],
      expectedR: 128,
      expectedG: 128,
      expectedB: 128,
      expectedLabel: "car")
    self.verifyColoredLabel(
      coloredLabels[8],
      expectedR: 64,
      expectedG: 0,
      expectedB: 0,
      expectedLabel: "cat")
    self.verifyColoredLabel(
      coloredLabels[9],
      expectedR: 192,
      expectedG: 0,
      expectedB: 0,
      expectedLabel: "chair")
    self.verifyColoredLabel(
      coloredLabels[10],
      expectedR: 64,
      expectedG: 128,
      expectedB: 0,
      expectedLabel: "cow")
    self.verifyColoredLabel(
      coloredLabels[11],
      expectedR: 192,
      expectedG: 128,
      expectedB: 0,
      expectedLabel: "dining table")
    self.verifyColoredLabel(
      coloredLabels[12],
      expectedR: 64,
      expectedG: 0,
      expectedB: 128,
      expectedLabel: "dog")
    self.verifyColoredLabel(
      coloredLabels[13],
      expectedR: 192,
      expectedG: 0,
      expectedB: 128,
      expectedLabel: "horse")
    self.verifyColoredLabel(
      coloredLabels[14],
      expectedR: 64,
      expectedG: 128,
      expectedB: 128,
      expectedLabel: "motorbike")
    self.verifyColoredLabel(
      coloredLabels[15],
      expectedR: 192,
      expectedG: 128,
      expectedB: 128,
      expectedLabel: "person")
    self.verifyColoredLabel(
      coloredLabels[16],
      expectedR: 0,
      expectedG: 64,
      expectedB: 0,
      expectedLabel: "potted plant")
    self.verifyColoredLabel(
      coloredLabels[17],
      expectedR: 128,
      expectedG: 64,
      expectedB: 0,
      expectedLabel: "sheep")
    self.verifyColoredLabel(
      coloredLabels[18],
      expectedR: 0,
      expectedG: 192,
      expectedB: 0,
      expectedLabel: "sofa")
    self.verifyColoredLabel(
      coloredLabels[19],
      expectedR: 128,
      expectedG: 192,
      expectedB: 0,
      expectedLabel: "train")
    self.verifyColoredLabel(
      coloredLabels[20],
      expectedR: 0,
      expectedG: 64,
      expectedB: 128,
      expectedLabel: "tv")
  }
  func verifyColoredLabel(
    _ coloredLabel: ColoredLabel,
    expectedR: UInt,
    expectedG: UInt,
    expectedB: UInt,
    expectedLabel: String
  ) {
    XCTAssertEqual(
      coloredLabel.r,
      expectedR)
    XCTAssertEqual(
      coloredLabel.g,
      expectedG)
    XCTAssertEqual(
      coloredLabel.b,
      expectedB)
    XCTAssertEqual(
      coloredLabel.label,
      expectedLabel)
  }
  func testSuccessfullInferenceOnMLImageWithUIImage() throws {
    let modelPath = try XCTUnwrap(ImageSegmenterTests.modelPath)
    let imageSegmenterOptions = ImageSegmenterOptions(modelPath: modelPath)
    let imageSegmenter =
      try ImageSegmenter.segmenter(options: imageSegmenterOptions)
    let gmlImage = try XCTUnwrap(
      MLImage.imageFromBundle(
        class: type(of: self),
        filename: "segmentation_input_rotation0",
        type: "jpg"))
    let segmentationResult: SegmentationResult =
      try XCTUnwrap(imageSegmenter.segment(mlImage: gmlImage))
    XCTAssertEqual(segmentationResult.segmentations.count, 1)
    let coloredLabels = try XCTUnwrap(segmentationResult.segmentations[0].coloredLabels)
    verifyDeeplabV3PartialSegmentationResult(coloredLabels)
    let categoryMask = try XCTUnwrap(segmentationResult.segmentations[0].categoryMask)
    XCTAssertEqual(deepLabV3SegmentationWidth, categoryMask.width)
    XCTAssertEqual(deepLabV3SegmentationHeight, categoryMask.height)
    let goldenMaskImage = try XCTUnwrap(
      MLImage.imageFromBundle(
        class: type(of: self),
        filename: "segmentation_golden_rotation0",
        type: "png"))
    let pixelBuffer = goldenMaskImage.grayScalePixelBuffer().takeRetainedValue()
    CVPixelBufferLockBaseAddress(pixelBuffer, CVPixelBufferLockFlags.readOnly)
    let pixelBufferBaseAddress = (try XCTUnwrap(CVPixelBufferGetBaseAddress(pixelBuffer)))
      .assumingMemoryBound(to: UInt8.self)
    let numPixels = deepLabV3SegmentationWidth * deepLabV3SegmentationHeight
    let mask = try XCTUnwrap(categoryMask.mask)
    var inconsistentPixels: Float = 0.0
    for i in 0..<numPixels {
      if mask[i] * kGoldenMaskMagnificationFactor != pixelBufferBaseAddress[i] {
        inconsistentPixels += 1
      }
    }
    CVPixelBufferUnlockBaseAddress(pixelBuffer, CVPixelBufferLockFlags.readOnly)
    XCTAssertLessThan(inconsistentPixels / Float(numPixels), kGoldenMaskTolerance)
  }
}


TFLObjectDetectorTests

class ObjectDetectorTests: XCTestCase {
  static let bundle = Bundle(for: ObjectDetectorTests.self)
  static let modelPath = bundle.path(
    forResource: "coco_ssd_mobilenet_v1_1.0_quant_2018_06_29",
    ofType: "tflite")
  func verifyDetectionResult(_ detectionResult: DetectionResult) {
    XCTAssertGreaterThan(detectionResult.detections.count, 0)
    self.verifyDetection(
      detectionResult.detections[0],
      expectedBoundingBox: CGRect(x: 54, y: 396, width: 393, height: 199),
      expectedFirstScore: 0.632812,
      expectedFirstLabel: "cat")
    self.verifyDetection(
      detectionResult.detections[1],
      expectedBoundingBox: CGRect(x: 602, y: 157, width: 394, height: 447),
      expectedFirstScore: 0.609375,
      expectedFirstLabel: "cat")
    self.verifyDetection(
      detectionResult.detections[2],
      expectedBoundingBox: CGRect(x: 260, y: 394, width: 179, height: 209),
      expectedFirstScore: 0.5625,
      expectedFirstLabel: "cat")
    self.verifyDetection(
      detectionResult.detections[3],
      expectedBoundingBox: CGRect(x: 387, y: 197, width: 281, height: 409),
      expectedFirstScore: 0.488281,
      expectedFirstLabel: "dog")
  }
  func verifyDetection(
    _ detection: Detection, expectedBoundingBox: CGRect,
    expectedFirstScore: Float,
    expectedFirstLabel: String
  ) {
    XCTAssertGreaterThan(detection.categories.count, 0)
    XCTAssertEqual(
      detection.boundingBox.origin.x,
      expectedBoundingBox.origin.x)
    XCTAssertEqual(
      detection.boundingBox.origin.y,
      expectedBoundingBox.origin.y)
    XCTAssertEqual(
      detection.boundingBox.size.width,
      expectedBoundingBox.size.width)
    XCTAssertEqual(
      detection.boundingBox.size.height,
      expectedBoundingBox.size.height)
    XCTAssertEqual(
      detection.categories[0].label,
      expectedFirstLabel)
    XCTAssertEqual(
      detection.categories[0].score,
      expectedFirstScore, accuracy: 0.001)
  }
  func testSuccessfullInferenceOnMLImageWithUIImage() throws {
    let modelPath = try XCTUnwrap(ObjectDetectorTests.modelPath)
    let objectDetectorOptions = ObjectDetectorOptions(modelPath: modelPath)
    let objectDetector =
      try ObjectDetector.detector(options: objectDetectorOptions)
    let gmlImage = try XCTUnwrap(
      MLImage.imageFromBundle(
        class: type(of: self),
        filename: "cats_and_dogs",
        type: "jpg"))
    let detectionResults: DetectionResult =
      try objectDetector.detect(mlImage: gmlImage)
    self.verifyDetectionResult(detectionResults)
  }
  func testModelOptionsWithMaxResults() throws {
    let modelPath = try XCTUnwrap(ObjectDetectorTests.modelPath)
    let objectDetectorOptions = ObjectDetectorOptions(modelPath: modelPath)
    let maxResults = 3
    objectDetectorOptions.classificationOptions.maxResults = maxResults
    let objectDetector =
      try ObjectDetector.detector(options: objectDetectorOptions)
    let gmlImage = try XCTUnwrap(
      MLImage.imageFromBundle(
        class: type(of: self),
        filename: "cats_and_dogs",
        type: "jpg"))
    let detectionResult: DetectionResult = try objectDetector.detect(
      mlImage: gmlImage)
    XCTAssertLessThanOrEqual(detectionResult.detections.count, maxResults)
    self.verifyDetection(
      detectionResult.detections[0],
      expectedBoundingBox: CGRect(x: 54, y: 396, width: 393, height: 199),
      expectedFirstScore: 0.632812,
      expectedFirstLabel: "cat")
    self.verifyDetection(
      detectionResult.detections[1],
      expectedBoundingBox: CGRect(x: 602, y: 157, width: 394, height: 447),
      expectedFirstScore: 0.609375,
      expectedFirstLabel: "cat")
    self.verifyDetection(
      detectionResult.detections[2],
      expectedBoundingBox: CGRect(x: 260, y: 394, width: 179, height: 209),
      expectedFirstScore: 0.5625,
      expectedFirstLabel: "cat")
  }
}


TFLImageClassifierTests

class ImageClassifierTests: XCTestCase {
  static let bundle = Bundle(for: ImageClassifierTests.self)
  static let modelPath = bundle.path(
    forResource: "mobilenet_v2_1.0_224",
    ofType: "tflite")
  func testSuccessfullInferenceOnMLImageWithUIImage() throws {
    let modelPath = try XCTUnwrap(ImageClassifierTests.modelPath)
    let imageClassifierOptions = ImageClassifierOptions(modelPath: modelPath)
    let imageClassifier =
      try ImageClassifier.classifier(options: imageClassifierOptions)
    let gmlImage = try XCTUnwrap(
      MLImage.imageFromBundle(
        class: type(of: self),
        filename: "burger",
        type: "jpg"))
    let classificationResults: ClassificationResult =
      try imageClassifier.classify(mlImage: gmlImage)
    XCTAssertNotNil(classificationResults)
    XCTAssertEqual(classificationResults.classifications.count, 1)
    XCTAssertGreaterThan(classificationResults.classifications[0].categories.count, 0)
    // TODO: match the score as image_classifier_test.cc
    let category = classificationResults.classifications[0].categories[0]
    XCTAssertEqual(category.label, "cheeseburger")
    XCTAssertEqual(category.score, 0.748976, accuracy: 0.001)
  }
  func testModelOptionsWithMaxResults() throws {
    let modelPath = try XCTUnwrap(ImageClassifierTests.modelPath)
    let imageClassifierOptions = ImageClassifierOptions(modelPath: modelPath)
    let maxResults = 3
    imageClassifierOptions.classificationOptions.maxResults = maxResults
    let imageClassifier =
      try ImageClassifier.classifier(options: imageClassifierOptions)
    let gmlImage = try XCTUnwrap(
      MLImage.imageFromBundle(
        class: type(of: self),
        filename: "burger",
        type: "jpg"))
    let classificationResults: ClassificationResult = try imageClassifier.classify(
      mlImage: gmlImage)
    XCTAssertNotNil(classificationResults)
    XCTAssertEqual(classificationResults.classifications.count, 1)
    XCTAssertLessThanOrEqual(classificationResults.classifications[0].categories.count, maxResults)
    // TODO: match the score as image_classifier_test.cc
    let category = classificationResults.classifications[0].categories[0]
    XCTAssertEqual(category.label, "cheeseburger")
    XCTAssertEqual(category.score, 0.748976, accuracy: 0.001)
  }
  func testInferenceWithBoundingBox() throws {
    let modelPath = try XCTUnwrap(ImageClassifierTests.modelPath)
    let imageClassifierOptions = ImageClassifierOptions(modelPath: modelPath)
    let imageClassifier =
      try ImageClassifier.classifier(options: imageClassifierOptions)
    let gmlImage = try XCTUnwrap(
      MLImage.imageFromBundle(
        class: type(of: self),
        filename: "multi_objects",
        type: "jpg"))
    let roi = CGRect(x: 406, y: 110, width: 148, height: 153)
    let classificationResults =
      try imageClassifier.classify(mlImage: gmlImage, regionOfInterest: roi)
    XCTAssertNotNil(classificationResults)
    XCTAssertEqual(classificationResults.classifications.count, 1)
    XCTAssertGreaterThan(classificationResults.classifications[0].categories.count, 0)
    // TODO: match the label and score as image_classifier_test.cc
    // let category = classificationResults.classifications[0].categories[0]
    // XCTAssertEqual(category.label, "soccer ball")
    // XCTAssertEqual(category.score, 0.256512, accuracy:0.001);
  }
  func testInferenceWithRGBAImage() throws {
    let modelPath = try XCTUnwrap(ImageClassifierTests.modelPath)
    let imageClassifierOptions = ImageClassifierOptions(modelPath: modelPath)
    let imageClassifier =
      try ImageClassifier.classifier(options: imageClassifierOptions)
    let gmlImage = try XCTUnwrap(
      MLImage.imageFromBundle(
        class: type(of: self),
        filename: "sparrow",
        type: "png"))
    let classificationResults =
      try imageClassifier.classify(mlImage: gmlImage)
    XCTAssertNotNil(classificationResults)
    XCTAssertEqual(classificationResults.classifications.count, 1)
    XCTAssertGreaterThan(classificationResults.classifications[0].categories.count, 0)
    let category = classificationResults.classifications[0].categories[0]
    XCTAssertEqual(category.label, "junco")
    XCTAssertEqual(category.score, 0.253016, accuracy: 0.001)
  }
}


TFLNLClassifierTest

class TFLNLClassifierTest: XCTestCase {
  static let bundle = Bundle(for: TFLNLClassifierTest.self)
  static let modelPath = bundle.path(
    forResource: "test_model_nl_classifier_with_regex_tokenizer",
    ofType: "tflite")!
  var modelOptions:TFLNLClassifierOptions!;
  override func setUp() {
    modelOptions = TFLNLClassifierOptions()
    modelOptions.inputTensorName = "input_text"
    modelOptions.outputScoreTensorName = "probability"
  }
  func testClassifyPositiveResult() {
    let nlClassifier = TFLNLClassifier.nlClassifier(
      modelPath: TFLNLClassifierTest.modelPath,
      options: modelOptions)
    XCTAssertNotNil(nlClassifier)
    let categories = nlClassifier.classify(
      text: "This is the best movie I’ve seen in recent years. Strongly recommend it!")
    XCTAssertGreaterThan(categories["Positive"]!.doubleValue, categories["Negative"]!.doubleValue)
  }
   func testClassifyNegativeResult() {
     let nlClassifier = TFLNLClassifier.nlClassifier(
       modelPath: TFLNLClassifierTest.modelPath,
       options: modelOptions)
     XCTAssertNotNil(nlClassifier)
     let categories = nlClassifier.classify(text: "What a waste of my time.")
     XCTAssertGreaterThan(categories["Negative"]!.doubleValue, categories["Positive"]!.doubleValue)
   }
}


TFLBertNLClassifierTest

class TFLBertNLClassifierTest: XCTestCase {
  static let bundle = Bundle(for: TFLBertNLClassifierTest.self)
  static let bertModelPath = bundle.path(forResource: "bert_nl_classifier", ofType: "tflite")!
  func testClassifyPositiveResult() {
    let bertNLClassifier = TFLBertNLClassifier.bertNLClassifier(
      modelPath: TFLBertNLClassifierTest.bertModelPath)
    XCTAssertNotNil(bertNLClassifier)
    let categories = bertNLClassifier.classify(text: "it's a charming and often affecting journey")
    XCTAssertGreaterThan(categories["positive"]!.doubleValue, categories["negative"]!.doubleValue)
  }
  func testClassifyNegativeResult() {
    let bertNLClassifier = TFLBertNLClassifier.bertNLClassifier(
      modelPath: TFLBertNLClassifierTest.bertModelPath)
    XCTAssertNotNil(bertNLClassifier)
    let categories = bertNLClassifier.classify(text: "unflinchingly bleak and desperate")
    XCTAssertGreaterThan(categories["negative"]!.doubleValue, categories["positive"]!.doubleValue)
  }
  func testCreateFromOptionsClassifyPositiveResult() {
    let modelOptions = TFLBertNLClassifierOptions()
    modelOptions.maxSeqLen = 128
    let bertNLClassifier = TFLBertNLClassifier.bertNLClassifier(
      modelPath: TFLBertNLClassifierTest.bertModelPath,
      options: modelOptions)
    XCTAssertNotNil(bertNLClassifier)
    let categories = bertNLClassifier.classify(text: "it's a charming and often affecting journey")
    XCTAssertGreaterThan(categories["positive"]!.doubleValue, categories["negative"]!.doubleValue)
  }
  func testCreateFromOptionsClassifyNegativeResult() {
    let modelOptions = TFLBertNLClassifierOptions()
    modelOptions.maxSeqLen = 128
    let bertNLClassifier = TFLBertNLClassifier.bertNLClassifier(
      modelPath: TFLBertNLClassifierTest.bertModelPath,
      options: modelOptions)
    XCTAssertNotNil(bertNLClassifier)
    let categories = bertNLClassifier.classify(text: "unflinchingly bleak and desperate")
    XCTAssertGreaterThan(categories["negative"]!.doubleValue, categories["positive"]!.doubleValue)
  }
}


TFLBertQuestionAnswererTest

class TFLBertQuestionAnswererTest: XCTestCase {
  static let bundle = Bundle(for: TFLBertQuestionAnswererTest.self)
  static let mobileBertModelPath = bundle.path(forResource: "mobilebert_with_metadata", ofType: "tflite")!
  static let albertModelPath = bundle.path(forResource: "albert_with_metadata", ofType: "tflite")!
  static let context = """
    The role of teacher is often formal and ongoing, carried out at a school or other place of
    formal education. In many countries, a person who wishes to become a teacher must first obtain
     specified professional qualifications or credentials from a university or college. These
    professional qualifications may include the study of pedagogy, the science of teaching.
    Teachers, like other professionals, may have to continue their education after they qualify,
    a process known as continuing professional development. Teachers may use a lesson plan to
    facilitate student learning, providing a course of study which is called the curriculum.
    """
  static let question = "What is a course of study called?"
  static let answer = "the curriculum."
  func testInitMobileBert() {
    let mobileBertAnswerer = TFLBertQuestionAnswerer.questionAnswerer(
      modelPath: TFLBertQuestionAnswererTest.mobileBertModelPath)
    XCTAssertNotNil(mobileBertAnswerer)
    let answers = mobileBertAnswerer.answer(
      context: TFLBertQuestionAnswererTest.context, question: TFLBertQuestionAnswererTest.question)
    XCTAssertNotNil(answers)
    XCTAssertEqual(answers[0].text, TFLBertQuestionAnswererTest.answer)
  }
  func testInitAlbert() {
    let albertAnswerer = TFLBertQuestionAnswerer.questionAnswerer(
      modelPath: TFLBertQuestionAnswererTest.albertModelPath)
    XCTAssertNotNil(albertAnswerer)
    let answers = albertAnswerer.answer(
      context: TFLBertQuestionAnswererTest.context, question: TFLBertQuestionAnswererTest.question)
    XCTAssertNotNil(answers)
    XCTAssertEqual(answers[0].text, TFLBertQuestionAnswererTest.answer)
  }
}


s

infix operator •: AdditionPrecedence
func • (a: Int, b: Int) -> Int {
  return a * b
}
func g(a: Int) -> Int{
  return a + 1
}
let i = g(a: 1 • 2)


import Swift

var s: [Int] = [1, 2, 3, 4]

func f() {
  for i in 0..<4 {
    print(s[i])
  }
}

f()


WebKitSwiftOverlay

/*
 * THE POSSIBILITY OF SUCH DAMAGE.
 */
        __createPDF(with: configuration, completionHandler: ObjCBlockConversion.exclusive(completionHandler))
    }
extension WKWebView {
    public func callAsyncJavaScript(_ functionBody: String, arguments: [String:Any] = [:], in frame: WKFrameInfo? = nil, contentWorld: WKContentWorld) async throws -> Any? {
        return try await __callAsyncJavaScript(functionBody, arguments: arguments, inFrame: frame, in: contentWorld)
    }
    public func pdf(configuration: WKPDFConfiguration = .init()) async throws -> Data {
        try await __createPDF(with: configuration)
    }
    public func evaluateJavaScript(_ javaScript: String, in frame: WKFrameInfo? = nil, contentWorld: WKContentWorld) async throws -> Any? {
        try await __evaluateJavaScript(javaScript, inFrame: frame, in: contentWorld)
    }
    public func find(_ string: String, configuration: WKFindConfiguration = .init()) async throws -> WKFindResult {
        await __find(string, with: configuration)
    }
}
#endif


WKGroupSession

// THE POSSIBILITY OF SUCH DAMAGE.
#if canImport(GroupActivities)
import AVFoundation
import Combine
public final class WKURLActivityWrapper : NSObject {
    private var urlActivity: URLActivity
    init(activity: URLActivity) {
        self.urlActivity = activity
        super.init()
    }
public final class WKGroupSessionWrapper : NSObject {
    private var groupSession: GroupSession<URLActivity>
    private var activityWrapper : WKURLActivityWrapper
    private var cancellables: Set<AnyCancellable> = []
    init(groupSession: GroupSession<URLActivity>) {
        self.groupSession = groupSession
        self.activityWrapper = WKURLActivityWrapper(activity: groupSession.activity)
        super.init()
        self.groupSession.$activity
            .sink { [unowned self] in self.activityChanged(activity: $0) }
            .store(in: &cancellables)
        self.groupSession.$state
            .sink { [unowned self] in self.stateChanged(state: $0) }
            .store(in: &cancellables)
    }
            // Unanticipated state value.
            assertionFailure()
            return WKGroupSessionState.invalidated
        }
    }
    public func coordinate(coordinator: AVPlaybackCoordinator) {
        coordinator.coordinateWithSession(self.groupSession)
    }
    private func activityChanged(activity: URLActivity) {
        self.activityWrapper = WKURLActivityWrapper(activity: groupSession.activity)
        guard let callback = newActivityCallback else {
            return
        }
        callback(activityWrapper)
    }
    private func stateChanged(state: GroupSession<URLActivity>.State) {
        guard let callback = stateChangedCallback else {
            return
        }
        callback(WKGroupSessionWrapper.wrapperSessionState(state: state))
    }
}
        super.init()
        incomingSessionsTask = detach { [weak self] in
            for await newSession in URLActivity.self.sessions() {
                DispatchQueue.main.async { [weak self] in
                    self?.receivedSession(newSession)
                }
            }
        }
    }
    deinit {
        incomingSessionsTask?.cancel()
    }
    private func receivedSession(_ session: GroupSession<URLActivity>) {
        guard let callback = newSessionCallback else {
            return
        }
        let sessionWrapper = WKGroupSessionWrapper(groupSession: session)
        callback(sessionWrapper)
    }
}
#endif


ObjectiveCBlockConversions

/*
 * THE POSSIBILITY OF SUCH DAMAGE.
 */
/// A family of conversions for translating between Swift blocks expecting a `Result<V, Error>` and
/// Objective-C callbacks of the form `(T?, Error?)`.
///
/// Depending on the semantics of the Objective-C API, only one of these conversions is appropriate.
/// - If the Objective-C block expects to be called with exactly one non-null argument, use the
/// `exclusive(_:)` conversion.
/// - If the Objective-C block can be called with one or zero non-null arguments, use the
/// `treatNilAsSuccess` conversion.
/// - If the Objective-C block can be called with two non-null values, it is ineligible for
/// conversion to a `Result`.
/// - `boxingNilAsAnyForCompatibility` exists as a workaround for http://webkit.org/b/216198, and
/// should not be used by new code.
enum ObjCBlockConversion {
    /// Converts a block from `(Result<Value, Error>) -> Void` to `(Value?, Error?) -> Void`.
    ///
    /// The result block must be called with exactly one non-null argument. If both arguments are
    /// non-null then `handler` will be called with `.success(T)`. If both arguments are `nil`
    /// the conversion will trap.
        return { value, error in
            if let value = value {
                handler(.success(value))
            } else if let error = error {
                handler(.failure(error))
            } else {
                preconditionFailure("Bug in WebKit: Received neither result or failure.")
            }
        }
    }
    /// Converts a block from `(Result<Value?, Error>) -> Void` to `(Value?, Error) -> Void`.
    ///
    /// This performs the same conversion as `Self.exclusive(_:)`, but if the result block is called
    /// with `(nil, nil)` then `handler` is called with `.success(nil)`.
        return { value, error in
            if let error = error {
                handler(.failure(error))
            } else {
                handler(.success(value))
            }
        }
    }
    /// Converts a block from `(Result<Value, Error>) -> Void` to `(Value?, Error) -> Void`.
    ///
    /// This performs the same conversion as `Self.exclusive(_:)`, but if the result block is called
    /// with `(nil, nil)` then `handler` is called with `.success(Optional<Any>.none as Any)`. This
    /// is a compatibility behavior for http://webkit.org/b/216198, and should not be adopted by
    /// new code.
        return { value, error in
            if let error = error {
                handler(.failure(error))
            } else if let success = value {
                handler(.success(success))
            } else {
                handler(.success(Optional<Any>.none as Any))
            }
        }
    }
}


JavaScriptToSwiftTypeConversions

/*
 * THE POSSIBILITY OF SUCH DAMAGE.
 */
import XCTest
import WebKit
final class JavaScriptToSwiftConversions : XCTestCase {
    #if os(macOS)
    let window = NSWindow()
    #elseif os(iOS)
    let window = UIWindow()
    #endif
    let webView = WKWebView(frame: CGRect(x: 0, y: 0, width: 320, height: 480))
    override func setUp() {
        #if os(macOS)
        window.contentView?.addSubview(webView)
        #else
        window.isHidden = false
        window.addSubview(webView)
        #endif
        webView.load(URLRequest(url: URL(string: "about:blank")!))
    }
    override func tearDown() {
        #if os(macOS)
        window.orderOut(nil)
        #else
        window.isHidden = true
        #endif
    }
    func evaluateJavaScript<T : Equatable>(_ javaScript: String, andExpect expectedValue: T) {
        let evaluationExpectation = self.expectation(description: "Evaluation of \(javaScript.debugDescription)")
        webView.evaluateJavaScript(javaScript, in: nil, in: .defaultClient) { result in
            do {
                let actualValue = try result.get() as? T
                XCTAssertEqual(actualValue, expectedValue)
                evaluationExpectation.fulfill()
            } catch {
                XCTFail("Evaluating \(javaScript.debugDescription) failed with error: \(error)")
            }
        }
        wait(for: [evaluationExpectation], timeout: 30)
    }
    func testNull() {
        evaluateJavaScript("null", andExpect: NSNull())
    }
    func testInteger() {
        evaluateJavaScript("12", andExpect: 12 as Int)
    }
    func testDecimal() {
        evaluateJavaScript("12.0", andExpect: 12 as Float)
    }
    func testBoolean() {
        evaluateJavaScript("true", andExpect: true)
        evaluateJavaScript("false", andExpect: false)
    }
    func testString() {
        evaluateJavaScript(#""Hello, world!""#, andExpect: "Hello, world!")
    }
    func testArray() {
        // This uses [AnyHashable], instead of [Any], so we can perform an equality check for testing.
        evaluateJavaScript(#"[ 1, 2, "cat" ]"#, andExpect: [1, 2, "cat"] as [AnyHashable])
    }
    func testDictionary() {
        // This uses [AnyHashable:AnyHashable], instead of [AnyHashable:Any], so we can perform an
        // equality check for testing. An object’s keys are always converted to strings, so even
        // though we input `1:` we expect `"1":` back.
        let result: [AnyHashable:AnyHashable] = ["1": 2, "cat": "dog"]
        evaluateJavaScript(#"const value = { 1: 2, "cat": "dog" }; value"#, andExpect: result)
    }
    func testUndefined() {
        let evaluationExpectation = self.expectation(description: "Evaluation of \"undefined\" using deprecated API")
        webView.evaluateJavaScript("undefined", in: nil, in: .defaultClient) { (result: Result<Any, Error>) in
            do {
                let value = try result.get()
                if let optionalValue = value as? Any?, optionalValue == nil {
                    evaluationExpectation.fulfill()
                } else {
                    XCTFail("Value did not contain nil")
                }
            } catch {
                XCTFail("Evaluating \"undefined\" failed with error: \(error)")
            }
        }
        wait(for: [evaluationExpectation], timeout: 30)
    }
    #if swift(>=5.5)
    func testUsingSwiftAsync() async throws {
        guard let result = try await webView.evaluateJavaScript(#""Hello, world!""#) as? String else {
            XCTFail("Unexpected result from evaluating JavaScript.")
            return
        }
        XCTAssertEqual(result, "Hello, world!")
    }
    #endif
}


WebKitTests

/*
 * THE POSSIBILITY OF SUCH DAMAGE.
 */
import XCTest
import WebKit
class WebKitTests: XCTestCase {
    /// This is a compile-time test that ensures the function names are what we expect.
    func testAPI() {
        _ = WKContentWorld.world(name:)
        _ = WKWebView.callAsyncJavaScript(_:arguments:in:in:completionHandler:)
        _ = WKWebView.createPDF(configuration:completionHandler:)
        _ = WKWebView.createWebArchiveData(completionHandler:)
        _ = WKWebView.evaluateJavaScript(_:in:in:completionHandler:)
        _ = WKWebView.find(_:configuration:completionHandler:)
    }
    func testWKPDFConfigurationRect() {
        let configuration = WKPDFConfiguration()
        XCTAssert(type(of: configuration.rect) == Optional<CGRect>.self)
        configuration.rect = nil
        XCTAssertEqual(configuration.rect, nil)
        configuration.rect = .null
        XCTAssertEqual(configuration.rect, nil)
        configuration.rect = CGRect.zero
        XCTAssertEqual(configuration.rect, .zero)
        let originalPhoneBounds = CGRect(x: 0, y: 0, width: 320, height: 480)
        configuration.rect = originalPhoneBounds
        XCTAssertEqual(configuration.rect, originalPhoneBounds)
    }
}


import UIKit
/// Globally maps reference views to layout guides.
///
/// Usage:
/// 1.  Reference a view from a part of the UI under a specific name.
/// 2.  In a different part of the UI, request a layout guide under that name. Add the synced layout
///     guide to the UI. It will track the reference view automatically.
///
/// Details:
/// -   Referenced views and created layout guides are only weakly stored by the layout guide
///     center.
/// -   Referenced views don't have to be laid out by AutoLayout.
/// -   Referenced views and layout guides don't have to be in the same window.
  public func reference(view referenceView: UIView?, under name: String) {
    let oldReferenceView = referencedView(under: name)
    // Early return if `referenceView` is already set.
    if referenceView == oldReferenceView {
      return
    }
    oldReferenceView?.cr_onWindowCoordinatesChanged = nil
    if let referenceView = referenceView {
      referenceViews.setObject(referenceView, forKey: name as NSString)
    } else {
      referenceViews.removeObject(forKey: name as NSString)
    }
    updateGuides(named: name)
    // Schedule updates to the matching layout guides when the reference view
    // moves in its window.
    referenceView?.cr_onWindowCoordinatesChanged = { [weak self] _ in
      self?.updateGuides(named: name)
    }
  }
  /// Returns the referenced view under `name`.
  public func makeLayoutGuide(named name: String) -> UILayoutGuide {
    let layoutGuide = FrameLayoutGuide()
    layoutGuide.onDidMoveToWindow = { [weak self] _ in
      self?.updateGuides(named: name)
    }
    // Keep a weak reference to the layout guide.
    if layoutGuides[name] == nil {
      layoutGuides[name] = NSHashTable<FrameLayoutGuide>.weakObjects()
    }
    layoutGuides[name]?.add(layoutGuide)
    return layoutGuide
  }
  /// MARK: Private
  /// Weakly stores the reference views.
  private var referenceViews = NSMapTable<NSString, UIView>.strongToWeakObjects()
  /// Weakly stores the layout guides.
  private var layoutGuides = [String: NSHashTable<FrameLayoutGuide>]()
  /// Updates all available guides for the given `name`.
  private func updateGuides(named name: String) {
    // Early return if there is no reference window.
    guard let referenceView = referencedView(under: name) else { return }
    guard let referenceWindow = referenceView.window else { return }
    // Early return if there are no layout guides.
    guard let layoutGuidesForName = layoutGuides[name] else { return }
    for layoutGuide in layoutGuidesForName.allObjects {
      // Skip if there is no owning window.
      guard let owningView = layoutGuide.owningView else { continue }
      guard let owningWindow = owningView.window else { continue }
      let frameInReferenceWindow = referenceView.convert(referenceView.bounds, to: nil)
      let frameInOwningWindow = referenceWindow.convert(frameInReferenceWindow, to: owningWindow)
      let frameInOwningView = owningView.convert(frameInOwningWindow, from: nil)
      layoutGuide.constrainedFrame = frameInOwningView
    }
  }
}


UIView+WindowCoordinates

import UIKit
/// Lets any UIView communicate when its coordinates in its window change.
///
/// This is useful to know when a view moved on the screen, even when it didn't change frame in its
/// own parent.
///
/// To get notified when the view moved in its window:
///
///     let myView = UIView()
///     myView.cr_onWindowCoordinatesChanged = { view in
///       // Print the window coordinates.
///       print("\(view) moved to \(view convertRect:view.bounds toView:nil)")
///     }
///     let parentView = UIView()
///     parentView.addSubview(myView)
///     let window = UIWindow()
///     window.addSubview(parentView)  // → Calls the closure a first time.
///     parentView.frame = CGRect(x: 10, y: 20, width: 30, height: 40)  // → Calls the closure.
///
///  Even though `myView`'s frame itself was not modified in `parentView`, the closure is called, as
///  actually, `myView` moved transitively in its window.
///
    get {
      objc_getAssociatedObject(self, &UIView.OnWindowCoordinatesChangedKey) as? (UIView) -> Void
    }
    set {
      objc_setAssociatedObject(
        self, &UIView.OnWindowCoordinatesChangedKey, newValue, .OBJC_ASSOCIATION_COPY)
      if newValue != nil {
        // Make sure UIView supports window observing.
        Self.cr_supportsWindowObserving = true
        observation = observe(\.window, options: [.initial]) { [weak self] _, _ in
          guard let self = self else { return }
          if self.window != nil {
            self.addMirrorViewInWindow()
            // Additionally, call the closure here as the view moved to a window.
            self.cr_onWindowCoordinatesChanged?(self)
          } else {
            self.removeMirrorViewInWindow()
          }
        }
      } else {
        observation = nil
      }
    }
  }
  /// MARK: Private
  /// The currently set observation of the window property.
  private var observation: NSKeyValueObservation? {
    get {
      objc_getAssociatedObject(self, &UIView.ObservationKey) as? NSKeyValueObservation
    }
    set {
      objc_setAssociatedObject(
        self, &UIView.ObservationKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    }
  }
  /// Inserts a direct subview to the receiver's window, with constraints such that the mirror view
  /// always has the same window coordinates as the receiver. The mirror view calls the
  /// `onWindowCoordinatesChanged` closure when its bounds change.
  private func addMirrorViewInWindow() {
    let mirrorViewInWindow = NotifyingView()
    mirrorViewInWindow.backgroundColor = .clear
    mirrorViewInWindow.isUserInteractionEnabled = false
    mirrorViewInWindow.translatesAutoresizingMaskIntoConstraints = false
    mirrorViewInWindow.onLayoutChanged = { [weak self] _ in
      // Callback on the next turn of the run loop to wait for AutoLayout to have updated the entire
      // hierarchy. (It can happen that AutoLayout updates the mirror view before the mirrored
      // view.)
      DispatchQueue.main.async {
        guard let self = self else { return }
        self.cr_onWindowCoordinatesChanged?(self)
      }
    }
    guard let window = window else { fatalError() }
    window.insertSubview(mirrorViewInWindow, at: 0)
    NSLayoutConstraint.activate([
      mirrorViewInWindow.topAnchor.constraint(equalTo: topAnchor),
      mirrorViewInWindow.bottomAnchor.constraint(equalTo: bottomAnchor),
      mirrorViewInWindow.leadingAnchor.constraint(equalTo: leadingAnchor),
      mirrorViewInWindow.trailingAnchor.constraint(equalTo: trailingAnchor),
    ])
    self.mirrorViewInWindow = mirrorViewInWindow
  }
  /// Removes the mirror view added by a call to `addMirrorViewInWindow`.
  private func removeMirrorViewInWindow() {
    mirrorViewInWindow?.onLayoutChanged = nil
    mirrorViewInWindow?.removeFromSuperview()
    mirrorViewInWindow = nil
  }
  /// The currently set mirror view.
  private var mirrorViewInWindow: NotifyingView? {
    get {
      objc_getAssociatedObject(self, &UIView.MirrorViewInWindowKey) as? NotifyingView
    }
    set {
      objc_setAssociatedObject(
        self, &UIView.MirrorViewInWindowKey, newValue, .OBJC_ASSOCIATION_ASSIGN)
    }
  }
  /// A simple view that calls a closure when its bounds and center changed.
  private class NotifyingView: UIView {
    var onLayoutChanged: ((UIView) -> Void)?
    override var bounds: CGRect {
      didSet {
        onLayoutChanged?(self)
      }
    }
    override var center: CGPoint {
      didSet {
        onLayoutChanged?(self)
      }
    }
  }
  /// Keys for storing associated objects.
  private static var OnWindowCoordinatesChangedKey = ""
  private static var ObservationKey = ""
  private static var MirrorViewInWindowKey = ""
}


import UIKit
/// A layout guide with two distinct additions:
/// 1.  encapsulates the KVO on its owning view's window and triggers a callback closure;
/// 2.  can be constrained to a fixed frame in its owning view coordinates.
///
/// 1. To get notified when the layout guide is added to a new window:
///
///     let layoutGuide = FrameLayoutGuide()
///     layoutGuide.onDidMoveToWindow = { guide in
///       if let window = guide.owningView?.window {
///         print("\(guide) moved to \(guide.owningView?.window)")
///       } else {
///         print("\(guide) was removed from its window")
///       }
///     }
///
///  2. To constrain a frame to the layout guide:
///
///      let layoutGuide = FrameLayoutGuide()
///      layoutGuide.constrainedFrame = CGRect(x: 10, y: 20, width: 30, height: 40)
///
///  The layout guide can then be used as an anchor to place elements related to its position.
    get {
      constrainedFrameView.frame
    }
    set {
      constrainedFrameView.frame = newValue
    }
  }
  /// MARK: Private
  /// When `owningView` changes, remove `constrainedFrameView` from the old to the new view, then
  /// reset the constraints anchoring the layout guide on `constrainedFrameView`.
  override open var owningView: UIView? {
    willSet {
      constrainedFrameView.removeFromSuperview()
    }
    didSet {
      if let owningView = owningView {
        owningView.addSubview(constrainedFrameView)
        NSLayoutConstraint.activate([
          leadingAnchor.constraint(equalTo: constrainedFrameView.leadingAnchor),
          trailingAnchor.constraint(equalTo: constrainedFrameView.trailingAnchor),
          topAnchor.constraint(equalTo: constrainedFrameView.topAnchor),
          bottomAnchor.constraint(equalTo: constrainedFrameView.bottomAnchor),
        ])
      }
    }
  }
  /// The observation of the owning view's window property. It's an optional variable because it
  /// can't be initialized before `self` is. See `init`.
  private var observation: NSKeyValueObservation?
  /// The view to inject in the owning view. Its frame will be set to `constrainedFrame`. The layout
  /// guide will anchor itself on that view.
  private let constrainedFrameView: UIView
  public override init() {
    constrainedFrameView = UIView()
    constrainedFrameView.backgroundColor = .clear
    constrainedFrameView.isUserInteractionEnabled = false
    super.init()
    // Make sure UIView supports window observing.
    UIView.cr_supportsWindowObserving = true
    // Start observing. It is not possible to initialize `observation` before `super.init()` because
    // the call to `observe(_:changeHandler:)` requires `self` to be initialized.
    // https://developer.apple.com/documentation/swift/cocoa_design_patterns/using_key-value_observing_in_swift
    observation = observe(\.owningView?.window, options: [.old, .new]) { layoutGuide, change in
      // Filter out changes where owning view changed but not the window. This can happen if a
      // layout guide is moved to a different owning view whose window is the same.
      if change.oldValue != change.newValue {
        layoutGuide.onDidMoveToWindow?(layoutGuide)
      }
    }
  }
  required init(coder aDecoder: NSCoder) {
    fatalError("Not using storyboards")
  }
}


UIView+WindowObserving

    get {
      return swizzled
    }
    set {
      if swizzled != newValue {
        // Swap implementations.
        guard
          let willMove1 = class_getInstanceMethod(UIView.self, #selector(willMove(toWindow:))),
          let willMove2 = class_getInstanceMethod(UIView.self, #selector(cr_willMove(toWindow:))),
          let didMove1 = class_getInstanceMethod(UIView.self, #selector(didMoveToWindow)),
          let didMove2 = class_getInstanceMethod(UIView.self, #selector(cr_didMoveToWindow)),
          // UITextField's original implementations don't call the super class implementations,
          // which breaks the interposition at the UIView level. Handle UITextField explicitly.
          let textFieldWillMove1 = class_getInstanceMethod(
            UITextField.self, #selector(willMove(toWindow:))),
          let textFieldWillMove2 = class_getInstanceMethod(
            UITextField.self, #selector(cr_willMove(toWindow:))),
          let textFieldDidMove1 = class_getInstanceMethod(
            UITextField.self, #selector(didMoveToWindow)),
          let textFieldDidMove2 = class_getInstanceMethod(
            UITextField.self, #selector(cr_didMoveToWindow))
        else {
          // If it failed here, don't change the `swizzled` state.
          return
        }
        method_exchangeImplementations(willMove1, willMove2)
        method_exchangeImplementations(didMove1, didMove2)
        method_exchangeImplementations(textFieldWillMove1, textFieldWillMove2)
        method_exchangeImplementations(textFieldDidMove1, textFieldDidMove2)
        // Change the `swizzled` state.
        swizzled = newValue
      }
    }
  }
  /// Signals that the `window` key is supported via Manual Change Notification.
  /// https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/KeyValueObserving/Articles/KVOCompliance.html#//apple_ref/doc/uid/20002178-SW3
    cr_willMove(toWindow: newWindow)
    willChangeValue(forKey: "window")
  }
  /// Adds a call to the KVO `didChangeValue(forKey:)` method.
    cr_willMove(toWindow: newWindow)
    willChangeValue(forKey: "window")
  }
  /// Adds a call to the KVO `didChangeValue(forKey:)` method.
  ///
  /// This version is necessary, as UITextField's original implementation doesn't call its
  /// superclass implementation, hence escapes the interposition at the UIView level.
    cr_didMoveToWindow()
    didChangeValue(forKey: "window")
  }
}


import Foundation
import WidgetKit
/// Helper class to update timelines for WidgetKit widgets.
public final class WidgetTimelinesUpdater: NSObject {
  /// The queue onto which time-consuming work is dispatched.
  private static let queue = DispatchQueue(label: "com.google.chrome.ios.WidgetTimelinesUpdater")
  /// Updates timelines for all shared widgets.
  ///
  /// Whether or not widgets are actually updated depends on many factors
  /// governed by an OS level budgeting system. Widgets are guaranteed
  /// to be updated if the main app is in the foreground.
  ///
  /// This method is safe to call from any thread.
  public static func reloadAllTimelines() {
    queue.async {
      WidgetCenter.shared.reloadAllTimelines()
    }
  }
}


import Foundation
/// An object that stores and retrieves the widget configurations from user defaults.
public struct UserDefaultsWidgetStore {
  /// The key that the current widget configurations are stored under in the user defaults.
  private let widgetInfoKey = "com.google.chrome.ios.widgets.widget-info"
  // Use UserDefaults.standard because this information only needs to be stored and retrieved from
  // the main app.
  private let userDefaults = UserDefaults.standard
  public func storeWidgetInfo(_ info: [String]) throws {
    let encoder = JSONEncoder()
    let data = try encoder.encode(info)
    userDefaults.set(data, forKey: widgetInfoKey)
  }
  public func retrieveStoredWidgetInfo() -> Result<[String], Error> {
    guard let data = userDefaults.data(forKey: widgetInfoKey) else {
      // If there is no stored data, return an empty array.
      return .success([])
    }
    let decoder = JSONDecoder()
    return Result {
      return try decoder.decode(Array<String>.self, from: data)
    }
  }
}


import Foundation
import WidgetKit
/// Logs metrics associated with iOS 14 home screen widgets.
public final class WidgetsMetricLogger: NSObject {
  /// The queue onto which time-consuming work is dispatched.
  private static let queue = DispatchQueue(label: "com.google.chrome.ios.WidgetMetricLogger")
  // MARK: Public
  /// A callback to be called when a widget install is detected. This callback is passed the
  /// information about which widget was installed.
  ///
  /// This property must be set before the `logInstalledWidgets` method is called.
  /// Logs metrics if the user has installed or uninstalled a widget since the last check.
  ///
  /// This method should be called once per application foreground, for example in the
  /// `applicationWillEnterForeground` method.
  ///
  /// This method is safe to call from any thread.
  public static func logInstalledWidgets() {
    if #available(iOS 14, *) {
      // To avoid blocking startup, perform work on a background queue.
      queue.async {
        logInstalledWidgets(fetcher: WidgetCenter.shared, store: UserDefaultsWidgetStore())
      }
    }
  }
  // MARK: Private
  /// Logs metrics if the user has installed or uninstalled a widget since the last app launch.
  static func logInstalledWidgets(fetcher: WidgetCenter, store: UserDefaultsWidgetStore) {
    fetcher.getCurrentConfigurations { result in
      // If fetching either current or previous info fails, avoid logging anything. The next time
      // this is called, metrics will be logged.
      guard let currentWidgets = try? result.get().map({ $0.kind }) else {
        return
      }
      // Log current widgets.
      for widget in currentWidgets {
        widgetCurrentCallback?(widget)
      }
      guard let storedWidgets = try? store.retrieveStoredWidgetInfo().get() else {
        return
      }
      // Attempt to store the new configurations and verify that it is successful to avoid double
      // logging. If metrics were logged when storage failed, they would be double-logged the next
      // time this method is called.
      do {
        try store.storeWidgetInfo(currentWidgets)
      } catch {
        return
      }
      // Current widgets minus stored widgets are installations.
      var installedWidgets = currentWidgets
      for storedWidget in storedWidgets {
        if let index = installedWidgets.firstIndex(of: storedWidget) {
          installedWidgets.remove(at: index)
        }
      }
      for installedWidget in installedWidgets {
        widgetInstalledCallback?(installedWidget)
      }
      // Stored widgets minus current widgets are uninstallations.
      var uninstalledWidgets = storedWidgets
      for currentWidget in currentWidgets {
        if let index = uninstalledWidgets.firstIndex(of: currentWidget) {
          uninstalledWidgets.remove(at: index)
        }
      }
      for uninstalledWidget in uninstalledWidgets {
        widgetUninstalledCallback?(uninstalledWidget)
      }
    }
  }
}


import CoreSpotlight
import UIKit
/// Displays details of a single CSSearchableItem.
class SearchableItemDetailViewController: UIViewController {
  let item: CSSearchableItem
  init(with item: CSSearchableItem) {
    self.item = item
    super.init(nibName: nil, bundle: nil)
  }
  required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }
  enum Section: CaseIterable {
    case main
  }
  struct SearchableItemDetail: Hashable {
    let title: String  // Used as unique ID
    let value: String?
    func hash(into hasher: inout Hasher) {
      hasher.combine(title)
    }
    static func == (lhs: SearchableItemDetail, rhs: SearchableItemDetail) -> Bool {
      return lhs.title == rhs.title
    }
  }
  var dataSource: UICollectionViewDiffableDataSource<Section, SearchableItemDetail>! = nil
  var collectionView: UICollectionView! = nil
  override func viewDidLoad() {
    super.viewDidLoad()
    navigationItem.title = item.attributeSet.title ?? item.uniqueIdentifier
    configureHierarchy()
    configureDataSource()
  }
  func createLayout() -> UICollectionViewLayout {
    let config = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
    return UICollectionViewCompositionalLayout.list(using: config)
  }
  private func configureHierarchy() {
    collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: createLayout())
    collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    view.addSubview(collectionView)
  }
  private func configureDataSource() {
    let cellRegistration = UICollectionView.CellRegistration<
      UICollectionViewListCell, SearchableItemDetail
    > { (cell, indexPath, detail) in
      var content = cell.defaultContentConfiguration()
      content.text = detail.title
      content.secondaryText = detail.value
      cell.contentConfiguration = content
    }
    dataSource = UICollectionViewDiffableDataSource<Section, SearchableItemDetail>(
      collectionView: collectionView
    ) {
      (collectionView: UICollectionView, indexPath: IndexPath, identifier: SearchableItemDetail)
        -> UICollectionViewCell? in
      return collectionView.dequeueConfiguredReusableCell(
        using: cellRegistration, for: indexPath, item: identifier)
    }
    var snapshot = NSDiffableDataSourceSnapshot<Section, SearchableItemDetail>()
    snapshot.appendSections([SearchableItemDetailViewController.Section.main])
    snapshot.appendItems([
      SearchableItemDetail(title: "displayName", value: item.attributeSet.displayName),
      SearchableItemDetail(title: "domain", value: item.domainIdentifier),
      SearchableItemDetail(title: "title", value: item.attributeSet.title),
      SearchableItemDetail(title: "id", value: item.uniqueIdentifier),
      SearchableItemDetail(title: "URL", value: item.attributeSet.url?.absoluteString),
      SearchableItemDetail(title: "description", value: item.attributeSet.contentDescription),
      SearchableItemDetail(title: "thumbnail data", value: thumbnailDescription()),
    ])
    dataSource.apply(snapshot, animatingDifferences: false)
  }
  private func thumbnailDescription() -> String {
    guard let data = item.attributeSet.thumbnailData else {
      return "not available"
    }
    guard let image = UIImage(data: data) else {
      return "corrupt image data"
    }
  }
}


import CoreSpotlight
import UIKit
/// Controller backed by SpotlightLogger known items, with filter function.
class ItemsController {
  func filteredItems(with filter: String? = nil, limit: Int? = nil) -> [CSSearchableItem] {
    let filtered = knownItems.filter { $0.contains(filter) }
    if let limit = limit {
      return Array(filtered.prefix(through: limit))
    } else {
      return filtered
    }
  }
  private lazy var knownItems: [CSSearchableItem] = {
    return SpotlightLogger.shared().knownIndexedItems() ?? []
  }()
}
/// Displays a list of all searchable items from the spotlight logger.
class SpotlightDebuggerAllItemsViewController: UIViewController {
  enum Section: CaseIterable {
    case main
  }
  let itemsController = ItemsController()
  let searchBar = UISearchBar(frame: .zero)
  var collectionView: UICollectionView!
  var dataSource: UICollectionViewDiffableDataSource<Section, CSSearchableItem>!
  var nameFilter: String?
  override func viewDidLoad() {
    super.viewDidLoad()
    navigationItem.title = "Donated Items"
    configureHierarchy()
    configureDataSource()
    performQuery(with: nil)
  }
  override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    collectionView.deselectAllItems(animated: animated)
  }
}
extension UICollectionView {
  func deselectAllItems(animated: Bool) {
    guard let selectedItems = indexPathsForSelectedItems else { return }
    for indexPath in selectedItems { deselectItem(at: indexPath, animated: animated) }
  }
}
extension SpotlightDebuggerAllItemsViewController {
  func configureDataSource() {
    let cellRegistration = UICollectionView.CellRegistration<
      UICollectionViewListCell, CSSearchableItem
    > { (cell, indexPath, item) in
      var content = cell.defaultContentConfiguration()
      content.text = item.attributeSet.title
      if let data = item.attributeSet.thumbnailData {
        content.image = UIImage(data: data)
        content.imageProperties.maximumSize = CGSize(width: 40, height: 40)
      } else {
        content.image = UIImage(systemName: "questionmark.diamond")
      }
      cell.accessories = [.disclosureIndicator()]
      cell.contentConfiguration = content
    }
    dataSource = UICollectionViewDiffableDataSource<Section, CSSearchableItem>(
      collectionView: collectionView
    ) {
      (collectionView: UICollectionView, indexPath: IndexPath, identifier: CSSearchableItem)
        -> UICollectionViewCell? in
      // Return the cell.
      return collectionView.dequeueConfiguredReusableCell(
        using: cellRegistration, for: indexPath, item: identifier)
    }
  }
  func performQuery(with filter: String?) {
    let items = itemsController.filteredItems(with: filter).sorted {
      $0.uniqueIdentifier < $1.uniqueIdentifier
    }
    var snapshot = NSDiffableDataSourceSnapshot<Section, CSSearchableItem>()
    snapshot.appendSections([.main])
    snapshot.appendItems(items)
    dataSource.apply(snapshot, animatingDifferences: true)
  }
}
extension SpotlightDebuggerAllItemsViewController {
  func createLayout() -> UICollectionViewLayout {
    let config = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
    return UICollectionViewCompositionalLayout.list(using: config)
  }
  func configureHierarchy() {
    view.backgroundColor = .systemBackground
    let layout = createLayout()
    let collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
    collectionView.translatesAutoresizingMaskIntoConstraints = false
    searchBar.translatesAutoresizingMaskIntoConstraints = false
    collectionView.backgroundColor = .systemBackground
    collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    collectionView.delegate = self
    view.addSubview(collectionView)
    view.addSubview(searchBar)
    let views = ["cv": collectionView, "searchBar": searchBar]
    var constraints = [NSLayoutConstraint]()
    constraints.append(
      contentsOf: NSLayoutConstraint.constraints(
        withVisualFormat: "H:|[cv]|", options: [], metrics: nil, views: views))
    constraints.append(
      contentsOf: NSLayoutConstraint.constraints(
        withVisualFormat: "H:|[searchBar]|", options: [], metrics: nil, views: views))
    constraints.append(
      contentsOf: NSLayoutConstraint.constraints(
        withVisualFormat: "V:[searchBar]-20-[cv]|", options: [], metrics: nil, views: views))
    constraints.append(
      searchBar.topAnchor.constraint(
        equalToSystemSpacingBelow: view.safeAreaLayoutGuide.topAnchor, multiplier: 1.0))
    NSLayoutConstraint.activate(constraints)
    self.collectionView = collectionView
    searchBar.delegate = self
  }
}
extension SpotlightDebuggerAllItemsViewController: UICollectionViewDelegate {
  func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    guard let item = self.dataSource.itemIdentifier(for: indexPath) else {
      collectionView.deselectItem(at: indexPath, animated: true)
      return
    }
    let detailViewController = SearchableItemDetailViewController(with: item)
    self.navigationController?.pushViewController(detailViewController, animated: true)
  }
}
extension SpotlightDebuggerAllItemsViewController: UISearchBarDelegate {
  func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
    performQuery(with: searchText)
  }
}
extension CSSearchableItem {
  func contains(_ filter: String?) -> Bool {
    guard let filterText = filter else { return true }
    if filterText.isEmpty { return true }
    let lowercasedFilter = filterText.lowercased()
    return (attributeSet.title?.lowercased().contains(lowercasedFilter) ?? false)
      || (attributeSet.url?.absoluteString.lowercased().contains(lowercasedFilter) ?? false)
      || (domainIdentifier?.lowercased().contains(lowercasedFilter) ?? false)
      || uniqueIdentifier.lowercased().contains(lowercasedFilter)
  }
}


  /// Pedal title, as seen in the pedal button/row.
  public let title: String
  /// Pedal subtitle, e.g. "Settings -> Passwords"
  public let subtitle: String
  /// Describes the action performed; can be used for voiceover.
  public let hint: String
  /// The image for the Pedal.
  public let image: UIImage
  public let imageTintColor: UIColor?
  public let backgroundColor: UIColor?
  public let imageBorderColor: UIColor?
  /// Action to run when the pedal is executed.
  public let action: () -> Void
  /// Action type for metrics collection. Int-casted OmniboxPedalId
  public let type: Int
  public init(
    title: String, subtitle: String,
    accessibilityHint: String,
    image: UIImage,
    imageTintColor: UIColor?,
    backgroundColor: UIColor?,
    imageBorderColor: UIColor?,
    type: Int,
  ) {
    self.title = title
    self.subtitle = subtitle
    self.hint = accessibilityHint
    self.image = image
    self.imageTintColor = imageTintColor
    self.backgroundColor = backgroundColor
    self.imageBorderColor = imageBorderColor
    self.type = type
    self.action = action
  }
}
extension OmniboxPedalData: OmniboxIcon {
  public var iconType: OmniboxIconType {
    return .suggestionIcon
  }
  public var iconImage: UIImage? {
    return image
  }
  public var imageURL: CrURL? { return nil }
  public var iconImageTintColor: UIColor? {
    return imageTintColor
  }
  public var backgroundImageTintColor: UIColor? {
    return backgroundColor
  }
  public var borderColor: UIColor? {
    return imageBorderColor
  }
}


  }
  var body: some View {
    Button {
      state.action?()
    } label: {
      if sizeCategory < .accessibilityMedium {
        regularLayout()
      } else {
        xxlLayout()
      }
    }
    .buttonStyle(InactiveTabsButtonStyle())
  }
  /// MARK - Layouts
  /// Displays the elements mostly horizontally, for when the size category is small.
  private func xxlLayout() -> some View {
    HStack {
      leadingTextVStack {
        title()
        counter()
        subtitle()
      }
      Spacer()
      disclosure()
    }
  }
  /// MARK - Elements
  /// Displays the button title.
  private func subtitle() -> some View {
    if let daysThreshold = state.daysThreshold {
      Text(
        L10nUtils.formatString(
          messageId: IDS_IOS_INACTIVE_TABS_BUTTON_SUBTITLE,
          argument: String(daysThreshold))
      )
      .font(.footnote)
      .foregroundColor(.textSecondary)
    }
  }
  /// Displays the tabs count.
  ///
  /// If the count is larger than 99, "99+" is displayed.
  /// If the count is not set, this returns nothing.
  private func disclosure() -> some View {
    if #available(iOS 16.0, *) {
      Image(systemName: kChevronForwardSymbol)
        .foregroundColor(.textTertiary)
        .fontWeight(.semibold)
    } else {
      // fontWeight is not available on Image. Wrap it in a Text.
      Text(Image(systemName: kChevronForwardSymbol))
        .foregroundColor(.textTertiary)
        .fontWeight(.semibold)
    }
  }
  /// VStack displaying its contents from the leading edge. Textual content is also leading aligned
  /// when displayed on multiple lines.
    VStack(alignment: .leading, spacing: Dimensions.spacing) {
      content()
    }
    .multilineTextAlignment(.leading)
  }
  /// MARK - Styles
  /// Style for the main button, i.e. the top-level view.
  private struct InactiveTabsButtonStyle: ButtonStyle {
    func makeBody(configuration: Configuration) -> some View {
      configuration.label
        .padding([.top, .bottom], Dimensions.verticalPadding)
        .padding([.leading, .trailing], Dimensions.horizontalPadding)
        .background(
          configuration.isPressed ? Color(.systemGray4) : Color.groupedSecondaryBackground
        )
        .cornerRadius(Dimensions.cornerRadius)
        .environment(\.colorScheme, .dark)
    }
  }
}


public class InactiveTabsButtonHeader: UICollectionReusableView {
  private enum Dimensions {
    /// The margin at the top of the header.
    static let topMargin: CGFloat = 12
    /// The margin on the other edges of the header.
    static let margin: CGFloat = 16
  }
  /// The state driving the SwiftUI button.
  private let buttonState = InactiveTabsButton.State()
  /// The hosting controller to map to the SwiftUI button.
  private lazy var hostingController = {
    UIHostingController(rootView: InactiveTabsButton(state: buttonState))
  }()
  /// Installs the hosting controller in the parent view controller.
    didSet {
      guard hostingController.parent != parent else { return }
      if let parent {
        parent.addChild(hostingController)
        hostingController.view.backgroundColor = .clear
        hostingController.view.translatesAutoresizingMaskIntoConstraints = false
        addSubview(hostingController.view)
        hostingController.didMove(toParent: parent)
        NSLayoutConstraint.activate([
          hostingController.view.topAnchor.constraint(
            equalTo: topAnchor, constant: Dimensions.topMargin),
          hostingController.view.leadingAnchor.constraint(
            equalTo: leadingAnchor, constant: Dimensions.margin),
          hostingController.view.bottomAnchor.constraint(
            equalTo: bottomAnchor, constant: -Dimensions.margin),
          hostingController.view.trailingAnchor.constraint(
            equalTo: trailingAnchor, constant: -Dimensions.margin),
        ])
      } else {
        hostingController.willMove(toParent: nil)
        hostingController.view.removeFromSuperview()
        hostingController.removeFromParent()
      }
    }
  }
  /// The callback when the Inactive Tabs button is pressed.
    buttonState.count = count
  }
}


import SwiftUI
import ios_chrome_common_ui_colors_swift
private let kPromptInnerPaddingHorizontal: CGFloat = 12
private let kPromptInnerPaddingVertical: CGFloat = 8
private let kPromptOuterPaddingHorizontal: CGFloat = 6
private let kPromptOuterPaddingVertical: CGFloat = 8
private let kPromptCornerRadius: CGFloat = 20
private let kTextVerticalSpacing: CGFloat = 12
private let kTextMinimumScaleFactor: CGFloat = 0.5
private let kSeparatorHeight: CGFloat = 1
// The view object for "Bring Android Tabs" bottom message prompt.
struct BringAndroidTabsPromptBottomMessageView: View {
  // Number of tabs brought from Android.
  let tabsCount: Int
  // Provider object for the view.
  weak var provider: BringAndroidTabsPromptBottomMessageProvider?
  var body: some View {
    HStack(alignment: .top) {
      Image(systemName: kTabsSymbol)
        .imageScale(.large)
        .padding(.top, kTextVerticalSpacing)
      VStack(alignment: .leading, spacing: kTextVerticalSpacing) {
        HStack(alignment: .top) {
          VStack(
            alignment: .leading,
            spacing: kTextVerticalSpacing - kPromptInnerPaddingVertical
          ) {
            Text(
              L10nUtils.pluralString(
                messageId: IDS_IOS_BRING_ANDROID_TABS_PROMPT_TITLE,
                number: self.tabsCount)
            )
            .font(.body)
            .minimumScaleFactor(kTextMinimumScaleFactor)
            Text(
              L10nUtils.pluralString(
                messageId: IDS_IOS_BRING_ANDROID_TABS_PROMPT_SUBTITLE,
                number: self.tabsCount)
            )
            .font(.footnote)
            .foregroundColor(.textSecondary)
            .minimumScaleFactor(kTextMinimumScaleFactor)
          }
          .padding(.top, kTextVerticalSpacing - kPromptInnerPaddingVertical)
          Spacer()
          Button(action: self.close) {
            Image(systemName: kXMarkCircleFillSymbol)
              .foregroundColor(.textQuaternary)
          }
          .accessibilityIdentifier(
            kBringAndroidTabsPromptBottomMessageCloseButtonAXId)
        }
        .padding(0)
        Divider().overlay(Color.separator).frame(height: kSeparatorHeight)
        Button(action: self.review) {
          HStack {
            Text(
              L10nUtils.pluralString(
                messageId:
                  IDS_IOS_BRING_ANDROID_TABS_PROMPT_REVIEW_TABS_BUTTON_BOTTOM_MESSAGE,
                number: self.tabsCount)
            )
            .minimumScaleFactor(kTextMinimumScaleFactor)
            Spacer()
            Image(systemName: kChevronForwardSymbol)
              .foregroundColor(.textSecondary)
          }
        }
        .foregroundColor(.chromeBlue)
        .accessibilityIdentifier(
          kBringAndroidTabsPromptBottomMessageReviewButtonAXId)
      }
      .padding(.bottom, kTextVerticalSpacing)
    }
    .padding(.horizontal, kPromptInnerPaddingHorizontal)
    .padding(.vertical, kPromptInnerPaddingVertical)
    .background(Color.primaryBackground)
    .clipShape(RoundedRectangle(cornerRadius: kPromptCornerRadius))
    .environment(\.colorScheme, .dark)
    .onAppear { self.onAppear() }
    .padding(.horizontal, kPromptOuterPaddingHorizontal)
    .padding(.vertical, kPromptOuterPaddingVertical)
  }
  // MARK: - Action handlers.
  // Invoked when the view is displayed.
  func onAppear() {
    self.provider?.delegate?.bringAndroidTabsPromptViewControllerDidShow()
  }
  // Review button action handler.
  func review() {
    self.provider?.delegate?
      .bringAndroidTabsPromptViewControllerDidTapReviewButton()
    self.provider?.commandHandler?.reviewAllBringAndroidTabs()
  }
  // Close button action handler.
  func close() {
    self.provider?.delegate?.bringAndroidTabsPromptViewControllerDidDismiss(
      swiped: false)
    self.provider?.commandHandler?.dismissBringAndroidTabsPrompt()
  }
}


class BringAndroidTabsPromptBottomMessageProvider: NSObject {
  // Delegate to handle user actions and update browser models accordingly.
  weak var delegate: BringAndroidTabsPromptViewControllerDelegate?
  // Command handler to continue Bring Android Tabs user journey based on
  // command invocation.
  weak var commandHandler: BringAndroidTabsCommands?
  // Number of active tabs from Android.
  let tabsCount: Int
  init(tabsCount: Int) {
    self.tabsCount = tabsCount
  }
  // View controller that manages this view. Should be added to the view
  // hierarchy when the view is displayed or removed.
  lazy var viewController: UIViewController = {
    var viewController = UIHostingController(
      rootView: BringAndroidTabsPromptBottomMessageView(
        tabsCount: self.tabsCount, provider: self))
    // Workaround of SwiftUI/UIKit interoperality limitation that the topmost
    // view always have an opaque background.
    viewController.view.backgroundColor = UIColor.clear
    return viewController
  }()
}


class BringAndroidTabsPromptConfirmationAlertViewController:
  ConfirmationAlertViewController,  // super class
  ConfirmationAlertActionHandler,  // protocol
  UIAdaptivePresentationControllerDelegate
{
  // Delegate to handle user actions.
  weak var delegate: BringAndroidTabsPromptViewControllerDelegate?
  // Command handler to continue Bring Android Tabs user journey base on command
  // invocation.
  weak var commandHandler: BringAndroidTabsCommands?
  // Number of active tabs from Android.
  private let tabsCount: Int
  init(tabsCount: Int) {
    self.tabsCount = tabsCount
  }
  override func viewDidLoad() {
    // Configure confirmation alert properties. These properties need to be set
    // before calling the confirmation alert's `viewDidLoad`.
    self.image = UIImage(named: "bring_android_tabs_icon")
    self.imageHasFixedSize = true
    self.customSpacingBeforeImageIfNoNavigationBar = kSpacingBeforeImage
    self.customSpacingAfterImage = kSpacingAfterImage
    self.helpButtonAvailable = false
    self.showDismissBarButton = false
    self.titleString = L10nUtils.pluralString(
      messageId: IDS_IOS_BRING_ANDROID_TABS_PROMPT_TITLE,
      number: self.tabsCount)
    self.titleTextStyle = .title2
    self.subtitleString = L10nUtils.pluralString(
      messageId: IDS_IOS_BRING_ANDROID_TABS_PROMPT_SUBTITLE,
      number: self.tabsCount)
    self.primaryActionString = L10nUtils.pluralString(
      messageId: IDS_IOS_BRING_ANDROID_TABS_PROMPT_OPEN_TABS_BUTTON,
      number: self.tabsCount)
    self.secondaryActionString = L10nUtils.pluralString(
      messageId: IDS_IOS_BRING_ANDROID_TABS_CANCEL_BUTTON,
      number: self.tabsCount)
    self.tertiaryActionString = L10nUtils.pluralString(
      messageId:
        IDS_IOS_BRING_ANDROID_TABS_PROMPT_REVIEW_TABS_BUTTON_CONFIRMATION_ALERT,
      number: self.tabsCount)
    super.viewDidLoad()
    // Configure properties specific to the "Bring Android Tabs" prompt.
    self.actionHandler = self
    self.presentationController?.delegate = self
    self.overrideUserInterfaceStyle = .dark
    self.view.accessibilityIdentifier =
      kBringAndroidTabsPromptConfirmationAlertAXId
  }
  override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    self.delegate?.bringAndroidTabsPromptViewControllerDidShow()
  }
  // MARK: - UIAdaptivePresentationControllerDelegate
  func presentationControllerDidDismiss(_ controller: UIPresentationController) {
    self.delegate?.bringAndroidTabsPromptViewControllerDidDismiss(swiped: true)
    self.commandHandler?.dismissBringAndroidTabsPrompt()
  }
  // MARK: - ConfirmationAlertActionHandler
  func confirmationAlertPrimaryAction() {
    self.delegate?.bringAndroidTabsPromptViewControllerDidTapOpenAllButton()
    self.commandHandler?.dismissBringAndroidTabsPrompt()
  }
  func confirmationAlertSecondaryAction() {
    self.delegate?.bringAndroidTabsPromptViewControllerDidDismiss(swiped: false)
    self.commandHandler?.dismissBringAndroidTabsPrompt()
  }
  func confirmationAlertTertiaryAction() {
    self.delegate?.bringAndroidTabsPromptViewControllerDidTapReviewButton()
    self.commandHandler?.reviewAllBringAndroidTabs()
  }
}