Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DPP-726 Add string interning unit tests #11841

Merged
merged 2 commits into from
Nov 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Copyright (c) 2021 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.platform.store.interning

import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

class RawStringInterningSpec extends AnyFlatSpec with Matchers {

behavior of "RawStringInterning.from"

it should "start empty" in {
val current = RawStringInterning.from(Nil)
current.map shouldBe empty
current.idMap shouldBe empty
current.lastId shouldBe 0
}

it should "append empty entries to existing cache" in {
val previous = RawStringInterning(Map("one" -> 1), Map(1 -> "one"), 1)
val current = RawStringInterning.from(Nil, previous)
current.map shouldBe previous.map
current.idMap shouldBe previous.idMap
current.lastId shouldBe previous.lastId
}

it should "append non-empty entries to empty cache" in {
val current = RawStringInterning.from(List(1 -> "one"))
current.map shouldBe Map("one" -> 1)
current.idMap shouldBe Map(1 -> "one")
current.lastId shouldBe 1
}

it should "append non-empty entries to non-empty cache" in {
val previous = RawStringInterning(
Map("one" -> 1, "two" -> 2),
Map(1 -> "one", 2 -> "two"),
2,
)
val current = RawStringInterning.from(List(3 -> "three"), previous)
current.map shouldBe Map("one" -> 1, "two" -> 2, "three" -> 3)
current.idMap shouldBe Map(1 -> "one", 2 -> "two", 3 -> "three")
current.lastId shouldBe 3
}

behavior of "RawStringInterning.newEntries"
rautenrieth-da marked this conversation as resolved.
Show resolved Hide resolved

it should "return an empty result if the input and previous state is empty" in {
val current = RawStringInterning.from(Nil)
val newEntries = RawStringInterning.newEntries(Iterator.empty, current)
newEntries shouldBe empty
}

it should "return an empty result if the input is empty" in {
val current = RawStringInterning(Map("one" -> 1), Map(1 -> "one"), 1)
val newEntries = RawStringInterning.newEntries(Iterator.empty, current)
newEntries shouldBe empty
}

it should "return an empty result if the input only contains duplicates" in {
val current = RawStringInterning(Map("one" -> 1), Map(1 -> "one"), 1)
val newEntries = RawStringInterning.newEntries(List("one").iterator, current)
newEntries shouldBe empty
}

it should "return a new entry if the input is an unknown string" in {
val current = RawStringInterning(Map("one" -> 1), Map(1 -> "one"), 1)
val newEntries = RawStringInterning.newEntries(List("two").iterator, current)
newEntries shouldBe Vector(2 -> "two")
}

it should "not return a new entry for known strings" in {
val current = RawStringInterning(Map("one" -> 1), Map(1 -> "one"), 1)
val newEntries = RawStringInterning.newEntries(List("one", "two").iterator, current)
newEntries shouldBe Vector(2 -> "two")
}

it should "handle duplicate unknown strings" in {
val current = RawStringInterning(Map("one" -> 1), Map(1 -> "one"), 1)
val newEntries = RawStringInterning.newEntries(List("two", "two", "two").iterator, current)
newEntries shouldBe Vector(2 -> "two")
}

it should "handle mixed input" in {
val current = RawStringInterning(Map("one" -> 1, "two" -> 2), Map(1 -> "one", 2 -> "two"), 2)
val newEntries = RawStringInterning.newEntries(
List("one", "three", "two", "four", "two", "four").iterator,
current,
)
newEntries shouldBe Vector(3 -> "three", 4 -> "four")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright (c) 2021 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.platform.store.interning

import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

class StringInterningDomainSpec extends AnyFlatSpec with Matchers {

behavior of "StringInterningDomain.prefixing"

case class StringBox(value: String)
object StringBox {
def from(raw: String): StringBox = StringBox(raw)
def to(boxed: StringBox): String = boxed.value
}

class StaticStringInterningAccessor(
idToString: Map[Int, String],
stringToId: Map[String, Int],
) extends StringInterningAccessor[String] {
override def internalize(t: String): Int = tryInternalize(t).get
override def tryInternalize(t: String): Option[Int] = stringToId.get(t)
override def externalize(id: Int): String = tryExternalize(id).get
override def tryExternalize(id: Int): Option[String] = idToString.get(id)
}

object StaticStringInterningAccessor {
def apply(entries: Seq[(Int, String)]): StaticStringInterningAccessor = {
new StaticStringInterningAccessor(
idToString = entries.toMap,
stringToId = entries.map(_.swap).toMap,
)
}
}

it should "handle a known string " in {
val accessor = StaticStringInterningAccessor(List(1 -> ".one", 2 -> ".two"))
val domain = StringInterningDomain.prefixing(".", accessor, StringBox.from, StringBox.to)

domain.tryExternalize(2) shouldBe Some(StringBox("two"))
rautenrieth-da marked this conversation as resolved.
Show resolved Hide resolved
domain.externalize(2) shouldBe StringBox("two")
domain.tryInternalize(StringBox("two")) shouldBe Some(2)
domain.internalize(StringBox("two")) shouldBe 2

domain.unsafe.tryExternalize(2) shouldBe Some("two")
domain.unsafe.externalize(2) shouldBe "two"
domain.unsafe.tryInternalize("two") shouldBe Some(2)
domain.unsafe.internalize("two") shouldBe 2
}

it should "handle an unknown string" in {
val accessor = StaticStringInterningAccessor(List(1 -> ".one", 2 -> ".two"))
val domain = StringInterningDomain.prefixing(".", accessor, StringBox.from, StringBox.to)

domain.tryExternalize(3) shouldBe empty
domain.tryInternalize(StringBox("three")) shouldBe empty

domain.unsafe.tryExternalize(3) shouldBe empty
domain.unsafe.tryInternalize("three") shouldBe empty
}

it should "work when two different domains share an accessor" in {
val accessor = StaticStringInterningAccessor(List(1 -> "aX", 2 -> "bX"))
val domainA = StringInterningDomain.prefixing("a", accessor, StringBox.from, StringBox.to)
val domainB = StringInterningDomain.prefixing("b", accessor, StringBox.from, StringBox.to)

domainA.internalize(StringBox("X")) shouldBe 1
domainB.internalize(StringBox("X")) shouldBe 2
domainA.externalize(1) shouldBe StringBox("X")
domainB.externalize(2) shouldBe StringBox("X")

domainA.unsafe.internalize("X") shouldBe 1
domainB.unsafe.internalize("X") shouldBe 2
domainA.unsafe.externalize(1) shouldBe "X"
domainB.unsafe.externalize(2) shouldBe "X"
}

it should "work when two identical domains share an accessor" in {
val accessor = StaticStringInterningAccessor(List(1 -> ".one", 2 -> ".two"))
val domainA = StringInterningDomain.prefixing(".", accessor, StringBox.from, StringBox.to)
val domainB = StringInterningDomain.prefixing(".", accessor, StringBox.from, StringBox.to)

domainA.internalize(StringBox("one")) shouldBe 1
domainB.internalize(StringBox("one")) shouldBe 1
domainA.externalize(2) shouldBe StringBox("two")
domainB.externalize(2) shouldBe StringBox("two")

domainA.unsafe.internalize("one") shouldBe 1
domainB.unsafe.internalize("one") shouldBe 1
domainA.unsafe.externalize(2) shouldBe "two"
domainB.unsafe.externalize(2) shouldBe "two"
}
}