# Avoiding check mate when testing
<!-- Put the link to this slide here so people can follow -->
slide: https://hackmd.io/@thanh/S1I8Qr-G6
---
## Introduction
- Thanh Le
- Software Engineer at Recorded Future
- Developer at Lichess.org
---
### Lichess
- Second largest chess online in the world
- Probably the biggest open source server in the wolrd
- Non profit organization
- Most of contributors (dev and non dev) are volunteers
---
### Scenario
- You have to work with an evil colleague to implement the addition function
- The evil task is writing implement
- Your task is writing tests
- How do you make sure the implmentation is correct.
```scala
def add(a: Int, b: Int): Int
```
---
### Unit Test

---
### Unit Test
- Example based testing
- Choosing input/output
- False sense of security
- Cannot cover all cases
- Hard to invole
- Hard to read
---
### Property Based Test (PBT)
- Define properties of your functions
- Write your tests to verify those properties
---
### Advantages of PBT
- It helps us understand more about our functions
- It can generate many more examples than we would likely have done manually
- It makes tests significantly cleaner and more robust
- It makes fewer implicit assumptions about fixture data
---
### 1st example
Propeties for addtion function:
- Commutative Property: `a + b == b + a
- Associative Property: `(a + b) + c == a + (b + c)`
- Identity Property: `a + 0 == 0 + a == a`
- Distributive property: `(a + b) * c = a*c + b*c`
---
### Chess Bitboards
Bitboards (or better Square Set), are in essence, finite sets of up to 64 elements - all the squares of a chessboard, one bit per square.
```scala
opaque type Bitboard = Long
object Bitboard:
extension (a: Bitboard)
def isEmpty: Boolean
def nonEmpty: Boolean
def contains(square: Square): Boolean
def add(square: Square): Bitboard
def remove(square: Square): Bitboard
def intersects(o: Bitboard): Boolean
def isDisjoint(o: Bitboard): Boolean
def squares: List[Square]
```
---
### Bitboards Testing
```scala
test("the result of add should contain the added square"):
forAll: (bb: Bitboard, square: Square) =>
bb.add(square).contains(square) == true
```
---
### Bitboards Testing
```scala
test("intersects should be true when the two bitboards have at least one common square"):
forAll: (b1: Bitboard, b2: Bitboard, p: Square) =>
b1.add(p).intersects(b2.add(p))
test("isDisjoint should be false when the two bitboards have at least common square"):
forAll: (b1: Bitboard, b2: Bitboard, p: Square) =>
b1.add(p).isDisjoint(b2.add(p)) == false
test("isDisjoint and intersects always return the opposite value"):
forAll: (b1: Bitboard, b2: Bitboard) =>
b1.isDisjoint(b2) != b1.intersects(b2)
```
---
### A Service
The requirement is getting data from `Remote`, and compare with data from `Local` and return the different if there is any and save the latest data to Local.
```scala
trait DiffService[F[_]]:
def diff: F[Option[DiffResult]]
object DiffService:
def apply[F[_]](local: Local[F], remote: Remote[F]): DiffService
trait Remote[F[_]]:
def fetch: F[Data]
trait Local[F[_]]:
def save(data: Data): F[Unit]
def fetch: F[Option[Data]]
```
---
### Preparation
```scala
case class TestEnv(local: Option[Data], remote: Data)
type Test[A] = State[TestEnv, A]
val remote: Remote[Test] = new Remote[Test]:
State.inspect(_.remote)
val local: Local[Test] = new Local[Test]:
override def fetch: Test[Option[Data]] =
State.inspect(_.local)
override def save(data: Data): Test[Unit] =
State.modify(_.copy(local = data.some))
private val diffService = DiffService.instance(local, remote)
```
---
### Tests
```scala
test("save the latest data"):
forAll: env: TestEnv =>
diffService.diff.runS(env).value.local == env.remote.some
test("compare only if there is previous data"):
forAll: env: TestEnv =>
diffService.diff.runS(env).value.isDefined == env.local.isDefined
test("return correct result"):
forAll: env: TestEnv =>
diffService.diff.runA(env).value == env.local.map(Diff.compare(_, env.remote))
```
---
### Thank you
{"description":"View the slide with \"Slide Mode\".","title":"Testing","contributors":"[{\"id\":\"28f9d763-8570-42fd-946f-90c08e4ae219\",\"add\":6680,\"del\":2195}]"}