-
Notifications
You must be signed in to change notification settings - Fork 205
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
DPP-726 Add string interning unit tests (#11841)
* DPP-726 Add string interning unit tests changelog_begin changelog_end * Add more test cases
- Loading branch information
1 parent
59eb0d2
commit 25b476f
Showing
2 changed files
with
188 additions
and
0 deletions.
There are no files selected for viewing
93 changes: 93 additions & 0 deletions
93
...ntegration-api/src/test/suite/scala/platform/store/interning/RawStringInterningSpec.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
|
||
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") | ||
} | ||
} |
95 changes: 95 additions & 0 deletions
95
...gration-api/src/test/suite/scala/platform/store/interning/StringInterningDomainSpec.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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")) | ||
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" | ||
} | ||
} |