Skip to content

Commit

Permalink
Merge pull request #96 from overpas/#70/image-type-prediction
Browse files Browse the repository at this point in the history
VectorImageType detection
  • Loading branch information
overpas authored Dec 31, 2023
2 parents db67450 + 99e9d49 commit 6f37d49
Show file tree
Hide file tree
Showing 11 changed files with 153 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package by.overpass.svgtocomposeintellij

import by.overpass.svgtocomposeintellij.data.DataProcessingSvgIconsSvgIconsGenerator
import by.overpass.svgtocomposeintellij.domain.SvgToComposeDataProcessor
import by.overpass.svgtocomposeintellij.domain.VectorImageTypeDetector
import by.overpass.svgtocomposeintellij.presentation.SvgToComposeViewModelImpl
import by.overpass.svgtocomposeintellij.presentation.validation.CantBeEmptyStringValidator
import by.overpass.svgtocomposeintellij.presentation.validation.ProperDirValidator
Expand All @@ -28,6 +29,7 @@ class SvgToComposeAction : AnAction() {
SvgToComposeViewModelImpl(
targetDir = event.targetDir ?: File(""),
svgIconsGenerator = DataProcessingSvgIconsSvgIconsGenerator(SvgToComposeDataProcessor),
vectorImageTypeDetector = VectorImageTypeDetector,
nonStringEmptyValidator = CantBeEmptyStringValidator,
directoryValidator = ProperDirValidator,
dispatcher = Dispatchers.Default,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package by.overpass.svgtocomposeintellij.domain

import java.io.File

interface VectorImageTypeDetector {

fun detect(path: String): VectorImageType?

companion object : VectorImageTypeDetector {

override fun detect(path: String): VectorImageType? {
val files = File(path)
.listFiles { file -> !file.isHidden }
?: return null
return if (files.all { file -> file.extension == "svg" }) {
VectorImageType.SVG
} else if (files.all { file -> file.extension == "xml" }) {
VectorImageType.DRAWABLE
} else {
null
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package by.overpass.svgtocomposeintellij.presentation
import by.overpass.svgtocomposeintellij.domain.SvgIconsGenerator
import by.overpass.svgtocomposeintellij.domain.SvgToComposeData
import by.overpass.svgtocomposeintellij.domain.VectorImageType
import by.overpass.svgtocomposeintellij.domain.VectorImageTypeDetector
import by.overpass.svgtocomposeintellij.presentation.validation.DirError
import by.overpass.svgtocomposeintellij.presentation.validation.Validatable
import by.overpass.svgtocomposeintellij.presentation.validation.ValidationResult
Expand Down Expand Up @@ -39,6 +40,7 @@ interface SvgToComposeViewModel {
class SvgToComposeViewModelImpl(
targetDir: File,
private val svgIconsGenerator: SvgIconsGenerator,
private val vectorImageTypeDetector: VectorImageTypeDetector,
private val nonStringEmptyValidator: ValueValidator<String, Unit>,
private val directoryValidator: ValueValidator<String, DirError>,
dispatcher: CoroutineDispatcher,
Expand Down Expand Up @@ -92,14 +94,22 @@ class SvgToComposeViewModelImpl(
value = vectorImagesDir,
isValid = validationResult == ValidationResult.Ok,
error = when (validationResult) {
is ValidationResult.Ok -> null
is ValidationResult.Ok -> {
detectVectorImagesType(vectorImagesDir)
null
}
is ValidationResult.Error -> validationResult.error
},
),
)
}
}

private fun detectVectorImagesType(vectorImagesDir: String) {
vectorImageTypeDetector.detect(vectorImagesDir)
?.let(::onVectorImageTypeChanged)
}

override fun onVectorImageTypeChanged(vectorImageType: VectorImageType) {
updateInput { old ->
old.copy(vectorImageType = vectorImageType)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package by.overpass.svgtocomposeintellij.domain

import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized

@RunWith(Parameterized::class)
class VectorImageTypeDetectorTest(
private val path: String,
private val expected: VectorImageType?,
) {

companion object {

@JvmStatic
@Parameterized.Parameters
fun data(): Collection<Array<Any?>> = listOf(
arrayOf(
"src/test/resources/svg/",
VectorImageType.SVG,
),
arrayOf(
"src/test/resources/xml/",
VectorImageType.DRAWABLE,
),
arrayOf(
"src/test/resources/mixed/",
null,
),
arrayOf(
"src/test/resources/svg-with-hidden-files/",
VectorImageType.SVG,
),
)
}

private val detector = VectorImageTypeDetector

@Test
fun test() {
val actual = detector.detect(path)

assertEquals(expected, actual)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,19 @@ package by.overpass.svgtocomposeintellij.presentation
import app.cash.turbine.test
import by.overpass.svgtocomposeintellij.domain.SvgIconsGenerator
import by.overpass.svgtocomposeintellij.domain.VectorImageType
import by.overpass.svgtocomposeintellij.domain.VectorImageTypeDetector
import by.overpass.svgtocomposeintellij.presentation.validation.DirError
import by.overpass.svgtocomposeintellij.presentation.validation.Validatable
import by.overpass.svgtocomposeintellij.presentation.validation.ValidationResult
import by.overpass.svgtocomposeintellij.presentation.validation.ValueValidator
import java.io.File
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyNoInteractions
import org.mockito.kotlin.whenever
import org.mockito.kotlin.wheneverBlocking
Expand All @@ -23,18 +24,19 @@ class SvgToComposeViewModelImplTest {

private val targetDir = File("/dir")
private val svgIconsGenerator = mock<SvgIconsGenerator>()
private val vectorImageTypeDetector = mock<VectorImageTypeDetector>()
private val nonStringEmptyValidator = mock<ValueValidator<String, Unit>>()
private val directoryValidator = mock<ValueValidator<String, DirError>>()

private val testDispatcher = StandardTestDispatcher()
private val testScope = TestScope(testDispatcher)

private val viewModel = SvgToComposeViewModelImpl(
targetDir,
svgIconsGenerator,
nonStringEmptyValidator,
directoryValidator,
testDispatcher,
targetDir = targetDir,
svgIconsGenerator = svgIconsGenerator,
vectorImageTypeDetector = vectorImageTypeDetector,
nonStringEmptyValidator = nonStringEmptyValidator,
directoryValidator = directoryValidator,
dispatcher = testDispatcher,
)

@Test
Expand Down Expand Up @@ -119,6 +121,7 @@ class SvgToComposeViewModelImplTest {
assertEquals(expected, actual)
}

@Test
fun `vectorImagesDirChanged updated to a valid value`() {
val expected = DataInput(
outputDir = Validatable("/dir"),
Expand All @@ -132,6 +135,31 @@ class SvgToComposeViewModelImplTest {
assertEquals(expected, actual)
}

@Test
fun `vectorImageTypeDetector is triggered when vectorImagesDirChanged updated to a valid value`() {
whenever(directoryValidator.validate(any())).thenReturn(ValidationResult.Ok)

viewModel.onVectorImagesDirChanged("/new/dir")

verify(vectorImageTypeDetector).detect("/new/dir")
}

@Test
fun `vectorImageType is updated when vectorImagesDirChanged updated to a valid value`() {
val expected = DataInput(
outputDir = Validatable("/dir"),
vectorImagesDir = Validatable("/new/dir"),
vectorImageType = VectorImageType.DRAWABLE,
)
whenever(directoryValidator.validate(any())).thenReturn(ValidationResult.Ok)
whenever(vectorImageTypeDetector.detect(any())).thenReturn(VectorImageType.DRAWABLE)

viewModel.onVectorImagesDirChanged("/new/dir")
val actual = viewModel.state.value

assertEquals(expected, actual)
}

@Test
fun `vectorImagesDirChanged updated to an invalid value`() {
val expected = DataInput(
Expand All @@ -146,6 +174,16 @@ class SvgToComposeViewModelImplTest {
assertEquals(expected, actual)
}

@Test
fun `vectorImageTypeDetector is NOT triggered when vectorImagesDirChanged updated to an invalid value`() {

whenever(directoryValidator.validate(any())).thenReturn(ValidationResult.Error(DirError.Empty))

viewModel.onVectorImagesDirChanged("")

verifyNoInteractions(vectorImageTypeDetector)
}

@Test
fun `vectorImageType updated`() {
val expected = DataInput(
Expand Down
11 changes: 11 additions & 0 deletions src/test/resources/mixed/Note.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal"
android:autoMirrored="true">
<path
android:fillColor="@android:color/white"
android:pathData="M120,720L120,660L600,660L600,720L120,720ZM120,510L120,450L840,450L840,510L120,510ZM120,300L120,240L840,240L840,300L120,300Z"/>
</vector>
1 change: 1 addition & 0 deletions src/test/resources/mixed/ab-testing.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file.
1 change: 1 addition & 0 deletions src/test/resources/svg-with-hidden-files/ab-testing.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/test/resources/svg/ab-testing.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions src/test/resources/xml/Note.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal"
android:autoMirrored="true">
<path
android:fillColor="@android:color/white"
android:pathData="M120,720L120,660L600,660L600,720L120,720ZM120,510L120,450L840,450L840,510L120,510ZM120,300L120,240L840,240L840,300L120,300Z"/>
</vector>

0 comments on commit 6f37d49

Please sign in to comment.