Added Test Cases for CommandExecutor

Updated kotlin_lint.yml
Fixed Lint Errors
This commit is contained in:
Bitmap
2024-03-10 19:42:35 +05:30
parent ab3e4c2624
commit cd82d882ff
20 changed files with 189 additions and 78 deletions
+1 -2
View File
@@ -2,7 +2,7 @@ name: kotlin_lint
on:
push:
branches:
branches-ignore:
- "*"
pull_request:
paths:
@@ -12,7 +12,6 @@ on:
jobs:
ktlint:
runs-on: ubuntu-latest
steps:
- name: "checkout"
uses: actions/checkout@v2
+61 -29
View File
@@ -1,8 +1,28 @@
import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@@ -16,15 +36,28 @@ import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.*
import androidx.compose.ui.window.AwtWindow
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.WindowPosition
import androidx.compose.ui.window.application
import androidx.compose.ui.window.rememberWindowState
import command.CommandBuilder
import command.CommandExecutor
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import local.FileStorageHelper
import ui.Styles
import ui.components.*
import utils.*
import ui.components.ButtonWithToolTip
import ui.components.CheckboxWithText
import ui.components.ChooseFileTextField
import ui.components.CustomTextField
import ui.components.LoadingDialog
import utils.Constant
import utils.DBConstants
import utils.FileDialogType
import utils.FileHelper
import utils.Log
import utils.SigningMode
import java.awt.Desktop
import java.awt.FileDialog
import java.awt.Frame
@@ -61,13 +94,13 @@ fun App(fileStorageHelper: FileStorageHelper, savedPath: String?, adbSavedPath:
var adbPath by remember { mutableStateOf("") }
var showLoadingDialog by remember { mutableStateOf(Pair("", false)) }
//TODO: (Fixed this issue need to test more!) - Can't update file path once saved, For now Delete path.kb file inside storage directory.
// TODO: (Fixed this issue need to test more!) - Can't update file path once saved, For now Delete path.kb file inside storage directory.
savedJarPath?.let {
bundletoolPath = it
saveJarPath = true
}
//Check if ADB Setup is Done or Not
// Check if ADB Setup is Done or Not
adbSavedPath?.let {
adbPath = it
isAdbSetupDone = true
@@ -80,12 +113,12 @@ fun App(fileStorageHelper: FileStorageHelper, savedPath: String?, adbSavedPath:
return@FileDialog
}
when (fileDialogType) {
FileDialogType.BUNDLETOOL -> bundletoolPath = "${directory}$fileName"
FileDialogType.AAPT2 -> aapt2Path = "${directory}$fileName"
FileDialogType.KEY_STORE_PATH -> keyStorePath = "${directory}$fileName"
FileDialogType.BUNDLETOOL -> bundletoolPath = "$directory$fileName"
FileDialogType.AAPT2 -> aapt2Path = "$directory$fileName"
FileDialogType.KEY_STORE_PATH -> keyStorePath = "$directory$fileName"
FileDialogType.ADB_PATH -> {
adbPath = "${directory}${fileName}"
//Show Loading Here
adbPath = "$directory$fileName"
// Show Loading Here
showLoadingDialog = Pair(Strings.VERIFYING_ADB_PATH, true)
CommandExecutor().executeCommand(
CommandBuilder()
@@ -96,14 +129,14 @@ fun App(fileStorageHelper: FileStorageHelper, savedPath: String?, adbSavedPath:
isAdbSetupDone = true
Log.i("Saving Path in DB $adbPath")
fileStorageHelper.save(DBConstants.ADB_PATH, adbPath)
//Hide Loading
// Hide Loading
Thread.sleep(1000L)
showLoadingDialog = Pair(Strings.VERIFYING_ADB_PATH, false)
},
onFailure = {
logs += it
isAdbSetupDone = false
//Hide Loading
// Hide Loading
showLoadingDialog = Pair(Strings.VERIFYING_ADB_PATH, false)
}
)
@@ -122,7 +155,7 @@ fun App(fileStorageHelper: FileStorageHelper, savedPath: String?, adbSavedPath:
if (isExecute) {
isExecute = false
//Get Command to Execute
// Get Command to Execute
val (cmd, isValid) = CommandBuilder()
.bundletoolPath(bundletoolPath)
.aabFilePath(aabFilePath)
@@ -143,7 +176,7 @@ fun App(fileStorageHelper: FileStorageHelper, savedPath: String?, adbSavedPath:
val runtime = Runtime.getRuntime()
val startTime = System.currentTimeMillis()
try {
//Launch Runtime to execute command
// Launch Runtime to execute command
val process = runtime.exec(cmd)
// Read and log error output
val errorReader = BufferedReader(InputStreamReader(process.errorStream))
@@ -169,8 +202,8 @@ fun App(fileStorageHelper: FileStorageHelper, savedPath: String?, adbSavedPath:
isLoading = false
}
//Save Path in Storage
//If We don't have any saved path in file storage and save jar path option is checked.Then,we can save new value in storage.
// Save Path in Storage
// If We don't have any saved path in file storage and save jar path option is checked.Then,we can save new value in storage.
if (savedJarPath == null && saveJarPath) {
fileStorageHelper.save("path", bundletoolPath)
}
@@ -216,7 +249,7 @@ fun App(fileStorageHelper: FileStorageHelper, savedPath: String?, adbSavedPath:
isOpen = true
},
Strings.SETUP_ADB_INFO,
icon = if (isAdbSetupDone) "done" else "info",
icon = if (isAdbSetupDone) "done" else "info"
)
}
Spacer(modifier = Modifier.padding(8.dp))
@@ -225,7 +258,7 @@ fun App(fileStorageHelper: FileStorageHelper, savedPath: String?, adbSavedPath:
modifier = Modifier.wrapContentSize(),
verticalAlignment = Alignment.CenterVertically
) {
//Bundle tool select flow
// Bundle tool select flow
ChooseFileTextField(
bundletoolPath,
Strings.SELECT_BUNDLETOOL_JAR,
@@ -415,13 +448,13 @@ fun App(fileStorageHelper: FileStorageHelper, savedPath: String?, adbSavedPath:
isExecute = true
},
modifier = Modifier.padding(start = 16.dp, top = 8.dp, end = 16.dp, bottom = 8.dp)
.wrapContentWidth(),
.wrapContentWidth()
) {
Text(
text = Strings.EXECUTE,
style = Styles.TextStyleMedium(16.sp),
color = Color.White,
fontWeight = FontWeight.Medium,
fontWeight = FontWeight.Medium
)
}
}
@@ -439,17 +472,17 @@ fun App(fileStorageHelper: FileStorageHelper, savedPath: String?, adbSavedPath:
modifier = Modifier.padding(end = 0.dp, bottom = 8.dp),
onClick = {
logs = ""
},
}
) {
Text(
text = Strings.CLEAR_LOGS,
style = Styles.TextStyleBold(13.sp),
style = Styles.TextStyleBold(13.sp)
)
Spacer(modifier = Modifier.size(ButtonDefaults.IconSpacing))
Icon(
painter = useResource("clear.svg") { loadSvgPainter(it, density) },
contentDescription = "Clear",
modifier = Modifier.size(ButtonDefaults.IconSize),
modifier = Modifier.size(ButtonDefaults.IconSize)
)
}
}
@@ -457,7 +490,7 @@ fun App(fileStorageHelper: FileStorageHelper, savedPath: String?, adbSavedPath:
modifier = Modifier.fillMaxSize().padding(start = 16.dp, end = 16.dp, bottom = 16.dp),
value = logs,
textStyle = Styles.TextStyleMedium(16.sp),
onValueChange = {},
onValueChange = {}
)
}
}
@@ -481,10 +514,9 @@ private fun FileDialog(
dispose = FileDialog::dispose
)
fun main() = application {
val fileStorageHelper = FileStorageHelper()
//Check if path for bundletool exists in local storage
// Check if path for bundletool exists in local storage
val path = fileStorageHelper.read(DBConstants.BUNDLETOOL_PATH) as String?
val adbPath = fileStorageHelper.read(DBConstants.ADB_PATH) as String?
Log.showLogs = true
+3 -3
View File
@@ -29,11 +29,11 @@ class CommandBuilder {
fun keyAlias(alias: String) = apply { this.keyAlias = alias }
fun keyPassword(password: String) = apply { this.keyPassword = password }
fun verifyAdbPath(value: Boolean, path: String) = apply { this.adbVerifyCommandExecute = Pair(value,path) }
fun verifyAdbPath(value: Boolean, path: String) = apply { this.adbVerifyCommandExecute = Pair(value, path) }
fun getAdbVerifyCommand(): String {
val (forVerify,path) = adbVerifyCommandExecute
if (forVerify){
val (forVerify, path) = adbVerifyCommandExecute
if (forVerify) {
return "\"${path}\" version"
}
return ""
@@ -14,7 +14,7 @@ class CommandExecutor {
onSuccess: (String) -> Unit,
onFailure: (Throwable) -> Unit
) {
coroutineScope.launch(Dispatchers.IO){
coroutineScope.launch(Dispatchers.IO) {
try {
val runtime = Runtime.getRuntime()
val startTime = System.currentTimeMillis()
@@ -51,5 +51,4 @@ class CommandExecutor {
}
}
}
}
@@ -17,6 +17,7 @@ class FileStorageHelper {
init {
initializeDir()
}
private fun getKryo(): Kryo {
return if (this::kryo.isInitialized) {
kryo
@@ -71,5 +72,4 @@ class FileStorageHelper {
false
}
}
}
@@ -5,5 +5,4 @@ class KiteDbException : RuntimeException {
constructor(detailMessage: String?) : super(detailMessage)
constructor(detailMessage: String?, throwable: Throwable?) : super(detailMessage, throwable)
}
+4 -5
View File
@@ -10,28 +10,27 @@ object Styles {
TextStyle(
fontWeight = FontWeight.Normal,
fontFamily = codeFontFamily,
fontSize = size,
fontSize = size
)
fun TextStyleMedium(size: TextUnit) =
TextStyle(
fontWeight = FontWeight.Medium,
fontFamily = codeFontFamily,
fontSize = size,
fontSize = size
)
fun TextStyleSemiBold(size: TextUnit) =
TextStyle(
fontWeight = FontWeight.SemiBold,
fontFamily = codeFontFamily,
fontSize = size,
fontSize = size
)
fun TextStyleBold(size: TextUnit) =
TextStyle(
fontWeight = FontWeight.Bold,
fontFamily = codeFontFamily,
fontSize = size,
fontSize = size
)
}
+3 -3
View File
@@ -9,9 +9,9 @@ import androidx.compose.ui.unit.sp
val codeFontFamily = FontFamily(
Font(resource = "fonts/sans_regular.ttf", weight = FontWeight.Light),
Font( resource = "fonts/sans_thin.ttf", weight = FontWeight.Normal),
Font( resource = "fonts/sans_medium.ttf", weight = FontWeight.Medium),
Font( resource = "fonts/sans_bold.ttf", weight = FontWeight.Bold)
Font(resource = "fonts/sans_thin.ttf", weight = FontWeight.Normal),
Font(resource = "fonts/sans_medium.ttf", weight = FontWeight.Medium),
Font(resource = "fonts/sans_bold.ttf", weight = FontWeight.Bold)
)
val typography = Typography(
@@ -6,7 +6,12 @@ import androidx.compose.foundation.TooltipPlacement
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Icon
import androidx.compose.material.Surface
import androidx.compose.material.Button
import androidx.compose.material.ButtonColors
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -23,7 +28,13 @@ import ui.Styles
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun ButtonWithToolTip(label: String, onClick: () -> Unit, toolTipText: String = "", icon: String = "info", buttonColors: ButtonColors = ButtonDefaults.buttonColors()) {
fun ButtonWithToolTip(
label: String,
onClick: () -> Unit,
toolTipText: String = "",
icon: String = "info",
buttonColors: ButtonColors = ButtonDefaults.buttonColors()
) {
val density = LocalDensity.current // to calculate the intrinsic size of vector images (SVG, XML)
Button(
onClick = {
@@ -31,13 +42,13 @@ fun ButtonWithToolTip(label: String, onClick: () -> Unit, toolTipText: String =
},
colors = buttonColors,
modifier = Modifier.padding(start = 16.dp, top = 8.dp, end = 16.dp, bottom = 8.dp)
.wrapContentWidth(),
.wrapContentWidth()
) {
Text(
text = label,
style = Styles.TextStyleMedium(16.sp),
color = Color.White,
fontWeight = FontWeight.Medium,
fontWeight = FontWeight.Medium
)
if (toolTipText.isNotEmpty()) {
TooltipArea(
@@ -36,12 +36,12 @@ fun CheckboxWithText(label: String, isChecked: Boolean, onCheckedChange: (Boolea
Checkbox(
modifier = Modifier.testTag(TestTags.CHECKBOX_TAG),
checked = isChecked,
onCheckedChange = { onCheckedChange.invoke(it) },
onCheckedChange = { onCheckedChange.invoke(it) }
)
Text(
modifier = Modifier.testTag(TestTags.TEXT_TAG),
text = label,
style = Styles.TextStyleMedium(14.sp),
style = Styles.TextStyleMedium(14.sp)
)
if (toolTipText.isNotEmpty()) {
TooltipArea(
@@ -2,9 +2,18 @@ package ui.components
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.Button
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.material.MaterialTheme
import androidx.compose.material.TextField
import androidx.compose.material.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -42,11 +51,11 @@ fun ChooseFileTextField(value: String, label: String, onSelect: () -> Unit) {
Text(
text = label,
style = Styles.TextStyleMedium(16.sp),
fontWeight = FontWeight.Medium,
fontWeight = FontWeight.Medium
)
},
textStyle = Styles.TextStyleMedium(16.sp),
onValueChange = {},
onValueChange = {}
)
Button(
onClick = {
@@ -54,11 +63,11 @@ fun ChooseFileTextField(value: String, label: String, onSelect: () -> Unit) {
},
modifier = Modifier
.height(50.dp)
.wrapContentWidth(),
.wrapContentWidth()
) {
Icon(
painter = useResource("open_folder.svg") { loadSvgPainter(it, density) },
contentDescription = "",
contentDescription = ""
)
}
}
@@ -49,13 +49,13 @@ fun CustomTextField(value: String, label: String, forPassword: Boolean = true, o
Text(
text = label,
style = Styles.TextStyleMedium(16.sp),
fontWeight = FontWeight.Medium,
fontWeight = FontWeight.Medium
)
},
textStyle = Styles.TextStyleMedium(16.sp),
onValueChange = {
onValueChange.invoke(it)
},
}
)
}
}
@@ -2,7 +2,12 @@ package ui.components
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.Surface
@@ -45,13 +50,11 @@ fun LoadingDialog(text: String) {
Text(
text = text,
style = Styles.TextStyleBold(20.sp),
modifier = Modifier.padding(start = 8.dp,end = 8.dp, bottom = 8.dp)
modifier = Modifier.padding(start = 8.dp, end = 8.dp, bottom = 8.dp)
)
CircularProgressIndicator()
}
}
}
}
}
@@ -24,13 +24,13 @@ fun TextWithIcon(label: String, onIconClick: () -> Unit) {
text = label,
style = Styles.TextStyleMedium(16.sp),
color = Color.Black,
fontWeight = FontWeight.Medium,
fontWeight = FontWeight.Medium
)
Icon(
painter = useResource("info.svg") { loadSvgPainter(it, density) },
contentDescription = "Info",
modifier = Modifier.padding(start = 8.dp)
.clickable { onIconClick.invoke() },
.clickable { onIconClick.invoke() }
)
}
}
+1 -2
View File
@@ -22,5 +22,4 @@ object FileDialogType {
object SigningMode {
const val DEBUG = 1
const val RELEASE = 2
}
}
+4 -2
View File
@@ -12,7 +12,10 @@ object FileHelper {
Log.i("CHANGED NAME\n Unzipping...")
try {
FileUtils.unzip(newFile, directory)
fileStatus.invoke(Constant.SUCCESS, "Rename and Unzip Successful\nFile Saved at ${directory.removeSuffix("\\")}.")
fileStatus.invoke(
Constant.SUCCESS,
"Rename and Unzip Successful\nFile Saved at ${directory.removeSuffix("\\")}."
)
Log.i("TRYING DELETING FILE")
val value = FileUtils.deleteFile(newFile)
Log.i("DELETE STATUS : $value")
@@ -25,5 +28,4 @@ object FileHelper {
fileStatus.invoke(Constant.FAILURE, "FAILED RENAMING THE FILE!!")
}
}
}
+5 -3
View File
@@ -1,6 +1,10 @@
package utils
import java.io.*
import java.io.BufferedOutputStream
import java.io.File
import java.io.IOException
import java.io.InputStream
import java.io.FileOutputStream
import java.util.zip.ZipFile
object FileUtils {
@@ -13,7 +17,6 @@ object FileUtils {
fun deleteFile(file: File) =
file.delete()
/**
* @param oldFile
* @param newFile
@@ -72,5 +75,4 @@ object FileUtils {
}
bos.close()
}
}
+2 -3
View File
@@ -1,11 +1,10 @@
package utils
import java.util.*
import java.util.Locale
object Utils {
fun isWindowsOS(): Boolean{
fun isWindowsOS(): Boolean {
return System.getProperty("os.name").lowercase(Locale.getDefault()).contains("windows")
}
}
@@ -0,0 +1,55 @@
package command
import junit.framework.TestCase.assertTrue
import kotlinx.coroutines.runBlocking
import org.hamcrest.MatcherAssert.assertThat
import org.junit.Before
import org.junit.Test
class CommandExecutorTest {
private lateinit var commandExecutor: CommandExecutor
@Before
fun setUp() {
commandExecutor = CommandExecutor()
}
// These test cases may not work for MAC and Linux need to verify.
@Test
fun `execute valid command successfully`() {
val expectedOutput = "openjdk 11.0.18 2023-01-17"
val cmd = "java --version"
runBlocking {
commandExecutor.executeCommand(
cmd,
this,
onSuccess = {
println("SUCCESS -> $it")
assertThat(expectedOutput, it.contains(expectedOutput))
},
onFailure = {
println("ERROR -> ${it.message}")
assertThat(expectedOutput, it.message?.contains(expectedOutput) ?: false)
}
)
}
}
@Test
fun `execute invalid command and handle failure`() {
val cmd = "invalid_command"
runBlocking {
commandExecutor.executeCommand(
cmd,
this,
onSuccess = {
assertTrue(it.isEmpty())
},
onFailure = {
assertTrue(it.message?.contains("The system cannot find the file specified") ?: false)
}
)
}
}
}
@@ -4,8 +4,12 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.test.*
import androidx.compose.ui.test.assertIsOff
import androidx.compose.ui.test.assertIsOn
import androidx.compose.ui.test.assertTextEquals
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import org.junit.Rule
import org.junit.Test
import utils.TestTags
@@ -57,5 +61,4 @@ class CheckboxWithTextTest {
checkbox.performClick()
checkbox.assertIsOff()
}
}