|
1 | 1 | package com.shopify.testify |
2 | 2 |
|
3 | 3 | import android.graphics.Bitmap |
| 4 | +import com.nhaarman.mockitokotlin2.any |
4 | 5 | import com.nhaarman.mockitokotlin2.doReturn |
5 | 6 | import com.nhaarman.mockitokotlin2.mock |
6 | 7 | import com.nhaarman.mockitokotlin2.whenever |
7 | 8 | import com.shopify.testify.internal.processor.ParallelPixelProcessor |
8 | | -import com.shopify.testify.internal.processor.numberOfCores |
| 9 | +import com.shopify.testify.internal.processor._executorDispatcher |
| 10 | +import com.shopify.testify.internal.processor.maxNumberOfChunkThreads |
| 11 | +import kotlinx.coroutines.Dispatchers |
| 12 | +import kotlinx.coroutines.ExperimentalCoroutinesApi |
| 13 | +import kotlinx.coroutines.ObsoleteCoroutinesApi |
| 14 | +import kotlinx.coroutines.newSingleThreadContext |
| 15 | +import kotlinx.coroutines.test.resetMain |
| 16 | +import kotlinx.coroutines.test.setMain |
| 17 | +import org.junit.After |
9 | 18 | import org.junit.Assert.assertEquals |
| 19 | +import org.junit.Assert.assertTrue |
10 | 20 | import org.junit.Before |
11 | 21 | import org.junit.Test |
| 22 | +import java.util.concurrent.atomic.AtomicInteger |
12 | 23 |
|
| 24 | +@ObsoleteCoroutinesApi |
| 25 | +@ExperimentalCoroutinesApi |
13 | 26 | class ParallelPixelProcessorTest { |
14 | 27 |
|
| 28 | + private val mainThreadSurrogate = newSingleThreadContext("UI thread") |
| 29 | + |
15 | 30 | companion object { |
16 | 31 | const val WIDTH = 1080 |
17 | 32 | const val HEIGHT = 2220 |
18 | 33 | } |
19 | 34 |
|
20 | | - private fun mockBitmap(): Bitmap { |
| 35 | + private fun mockBitmap(width: Int = WIDTH, height: Int = HEIGHT): Bitmap { |
21 | 36 | return mock<Bitmap>().apply { |
22 | | - doReturn(WIDTH).whenever(this).height |
23 | | - doReturn(HEIGHT).whenever(this).width |
| 37 | + doReturn(width).whenever(this).height |
| 38 | + doReturn(height).whenever(this).width |
| 39 | + doReturn(0xffffffff.toInt()).whenever(this).getPixel(any(), any()) |
24 | 40 | } |
25 | 41 | } |
26 | 42 |
|
27 | | - private val expectedX = (0 until WIDTH).flatMap { (0 until HEIGHT).toList() } |
28 | | - private val expectedY = (0 until WIDTH).flatMap { y -> (0 until HEIGHT).map { y } } |
29 | 43 | private lateinit var pixelProcessor: ParallelPixelProcessor |
30 | 44 |
|
| 45 | + private fun forceSingleThreadedExecution() { |
| 46 | + Dispatchers.setMain(mainThreadSurrogate) |
| 47 | + _executorDispatcher = Dispatchers.Main |
| 48 | + } |
| 49 | + |
31 | 50 | @Before |
32 | 51 | fun setUp() { |
| 52 | + forceSingleThreadedExecution() |
33 | 53 | pixelProcessor = ParallelPixelProcessor |
34 | 54 | .create() |
35 | 55 | .baseline(mockBitmap()) |
36 | 56 | .current(mockBitmap()) |
37 | 57 | } |
38 | 58 |
|
| 59 | + @After |
| 60 | + fun tearDown() { |
| 61 | + Dispatchers.resetMain() |
| 62 | + mainThreadSurrogate.close() |
| 63 | + } |
| 64 | + |
39 | 65 | @Test |
40 | 66 | fun default() { |
41 | | - numberOfCores = 1 |
| 67 | + maxNumberOfChunkThreads = 1 |
| 68 | + |
| 69 | + val index = AtomicInteger(0) |
| 70 | + pixelProcessor.analyze { _, _, _ -> |
| 71 | + index.incrementAndGet() |
| 72 | + true |
| 73 | + } |
| 74 | + assertEquals(WIDTH * HEIGHT, index.get()) |
| 75 | + } |
| 76 | + |
| 77 | + @Test |
| 78 | + fun twoCores() { |
| 79 | + maxNumberOfChunkThreads = 2 |
| 80 | + |
| 81 | + val index = AtomicInteger(0) |
| 82 | + pixelProcessor.analyze { _, _, _ -> |
| 83 | + index.incrementAndGet() |
| 84 | + true |
| 85 | + } |
| 86 | + |
| 87 | + assertEquals(WIDTH * HEIGHT, index.get()) |
| 88 | + } |
| 89 | + |
| 90 | + @Test |
| 91 | + fun oddNumberOfCores() { |
| 92 | + maxNumberOfChunkThreads = 7 |
| 93 | + |
| 94 | + val index = AtomicInteger(0) |
| 95 | + pixelProcessor.analyze { _, _, _ -> |
| 96 | + index.incrementAndGet() |
| 97 | + true |
| 98 | + } |
| 99 | + |
| 100 | + assertEquals(WIDTH * HEIGHT, index.get()) |
| 101 | + } |
| 102 | + |
| 103 | + @Test |
| 104 | + fun oddNumberOfPixels() { |
| 105 | + maxNumberOfChunkThreads = 2 |
| 106 | + |
| 107 | + pixelProcessor = ParallelPixelProcessor |
| 108 | + .create() |
| 109 | + .baseline(mockBitmap(3, 3)) |
| 110 | + .current(mockBitmap(3, 3)) |
| 111 | + |
| 112 | + val expected = mutableSetOf( |
| 113 | + 0 to 0, 1 to 0, 2 to 0, |
| 114 | + 0 to 1, 1 to 1, 2 to 1, |
| 115 | + 0 to 2, 1 to 2, 2 to 2 |
| 116 | + ) |
42 | 117 |
|
43 | | - var index = 0 |
| 118 | + val index = AtomicInteger(0) |
44 | 119 | pixelProcessor.analyze { _, _, (x, y) -> |
45 | | - assertEquals(expectedX[index], x) |
46 | | - assertEquals(expectedY[index], y) |
47 | | - index++ |
| 120 | + assertTrue(expected.remove(x to y)) |
| 121 | + index.incrementAndGet() |
48 | 122 | true |
49 | 123 | } |
| 124 | + assertEquals(9, index.get()) |
| 125 | + assertTrue(expected.isEmpty()) |
50 | 126 | } |
51 | 127 |
|
52 | 128 | private fun assertPosition(index: Int, position: Pair<Int, Int>) { |
53 | | - val width = 1080 |
54 | | - val (x, y) = pixelProcessor.getPosition(index, width) |
| 129 | + val (x, y) = pixelProcessor.getPosition(index, WIDTH) |
55 | 130 | assertEquals(position, x to y) |
56 | 131 | } |
57 | 132 |
|
58 | 133 | @Test |
59 | 134 | fun multicoreChunks() { |
60 | | - numberOfCores = 2 |
| 135 | + maxNumberOfChunkThreads = 2 |
61 | 136 | assertPosition(7, 7 to 0) |
62 | 137 | assertPosition(500, 500 to 0) |
63 | 138 | assertPosition(1500, 420 to 1) |
|
0 commit comments