Skip to content

Commit

Permalink
Initial Library Release
Browse files Browse the repository at this point in the history
  • Loading branch information
consuelita committed Jul 28, 2022
1 parent 01dd2ea commit eb8dd67
Show file tree
Hide file tree
Showing 28 changed files with 543 additions and 373 deletions.
27 changes: 21 additions & 6 deletions CanaryLibrary/build.gradle
Original file line number Diff line number Diff line change
@@ -1,42 +1,57 @@
plugins {
id 'com.android.library'
id 'org.jetbrains.kotlin.android'
id 'org.jetbrains.kotlin.plugin.serialization'
}

android {
compileSdk 32

defaultConfig {
minSdk 21
minSdk 17
targetSdk 32

multiDexEnabled = true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = '1.8'
freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
}

}

dependencies {
androidTestImplementation 'org.testng:testng:7.4.0'
def coroutines_version = '1.6.4'

implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.core:core-ktx:1.8.0'
implementation 'androidx.appcompat:appcompat:1.4.2'
implementation "androidx.multidex:multidex:2.0.1"

implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"

implementation 'com.google.android.material:material:1.6.1'
implementation 'com.github.OperatorFoundation:ShapeshifterAndroidKotlin:3.1.0'
implementation 'com.beust:klaxon:5.5'

testImplementation 'junit:junit:4.13.2'

androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.example.CanaryLibrary

import android.os.Environment
import androidx.test.platform.app.InstrumentationRegistry
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.Assert.*
import org.operatorfoundation.shapeshifter.shadow.kotlin.ShadowConfig

class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("CanaryLibrary.test", appContext.packageName)
}

@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun createCanaryInstance() = runTest {
// ***** This is the actual API for the Canary Library ******
val configDirectory = Environment.getStorageDirectory()
val canary = Canary(configDirectory)
assertNotNull(canary)
canary.runTest()
}

@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun createCanaryTest() = runTest {
val configDirectory = Environment.getStorageDirectory()
val chirp = CanaryTest(configDirectory, 1)

chirp.begin()
}

@Test
fun checkSetupEmptyConfigDir()
{
val configDirectory = Environment.getStorageDirectory()
val chirp = CanaryTest(configDirectory, 1)

assertFalse(chirp.checkSetup())
}

@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testController() = runTest {
val testController = TestController()
val shadowConfig = ShadowConfig("", "DarkStar")
val canaryConfig = CanaryConfig<ShadowConfig>("", 1234, shadowConfig)
val transport = Transport("ShadowExample", TransportType.shadow, canaryConfig)

val result = testController.runTransportTest(transport)
assertNotNull(result)
}
}

This file was deleted.

5 changes: 3 additions & 2 deletions CanaryLibrary/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.canarylibrary">

package="CanaryLibrary">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
</manifest>
23 changes: 23 additions & 0 deletions CanaryLibrary/src/main/java/com/example/CanaryLibrary/Canary.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.example.CanaryLibrary

import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import java.io.File

class Canary(configDirectoryFile: File, timesToRun: Int = 1)
{
private var chirp: CanaryTest

init
{
chirp = CanaryTest(configDirectoryFile, timesToRun)
}

fun runTest()
{
// TODO: Better coroutines
MainScope().launch {
chirp.begin()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.example.CanaryLibrary

import kotlinx.serialization.Serializable

@Serializable
data class CanaryConfig<out T : Any>(val serverIP: String, val serverPort: Int, val transportConfig: T)
{

}
117 changes: 117 additions & 0 deletions CanaryLibrary/src/main/java/com/example/CanaryLibrary/CanaryTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package com.example.CanaryLibrary

import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import org.operatorfoundation.shapeshifter.shadow.kotlin.ShadowConfig
import java.io.File

class CanaryTest(val configDirectory: File, val timesToRun: Int = 1, var saveDirectory: File? = null)
{
suspend fun begin()
{
println("\n attempting to run tests...\n")

// Make sure we have everything we need first
if (!checkSetup())
{
return
}

runAllTests()
}

private suspend fun runAllTests()
{
val testController = TestController()

for (i in 1..timesToRun)
{
println("\n***************************\nRunning test batch $i of $timesToRun\n***************************\n")

for (transport in testingTransports)
{
println("\n 🧪 Starting test for ${transport.name} 🧪")
testController.test(transport)
}
}
}

fun checkSetup(): Boolean
{
if (saveDirectory != null)
{
// Does the save directory exist?
if (!saveDirectory!!.exists())
{
println("\n‼️ The selected save directory does not exist at ${saveDirectory!!.path}.\n")
return false
}
else if (!saveDirectory!!.isDirectory)
{
println("\n‼️ The selected save directory is not a directory. Please select a directory for saving your results. \nSelected path: ${saveDirectory!!.path}.\n")
return false
}

println("\n✔️ User selected save directory: ${saveDirectory!!.path}\n")
}

// Does the Config Directory Exist?
if (!configDirectory.exists())
{
println("\n‼️ The selected config directory does not exist at ${configDirectory.path}.\n")
return false
}
else if (!configDirectory.isDirectory)
{
println("\n‼️ The selected config directory is not a directory. Please select the directory where your transport config files are located. \nSelected path: ${configDirectory.path}.\n")
return false
}

println("\n✔️ Config directory: ${configDirectory.path}\n")

if (!prepareTransports())
{
return false
}

println("✔️ Check setup complete")
return true
}

private fun prepareTransports(): Boolean
{
// Get a list of all of the files in the config directory
// return false if we are unable to retrieve a list of files
val configFiles = configDirectory.listFiles() ?: return false

if (configFiles.isEmpty())
{
println("\n ‼️ There are no config files in the selected directory: ${configDirectory.path}")
return false
}

configFiles.forEach { configFile ->
for (transportType in possibleTransportTypes)
{
// Check each file name to see if it contains the name of a supported transport
if (configFile.name.contains(transportType.name, true))
{
val configString = configFile.readText()
val canaryConfig: CanaryConfig<ShadowConfig> = Json.decodeFromString(configString)

val maybeNewTransport = Transport(configFile.name, transportType, canaryConfig)
testingTransports += maybeNewTransport
println("\n✔️ ${maybeNewTransport.name} test is ready\n")
}
}
}

if (testingTransports.isEmpty())
{
println("‼️ There were no valid transport configs in the provided directory. Ending test.\nConfig Directory: $configDirectory.path")
return false
}

return true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.example.CanaryLibrary

var testingTransports = arrayOf<Transport>()

val possibleTransportTypes = arrayOf(TransportType.shadow)
val httpRequestString = "GET / HTTP/1.0\r\nConnection: close\r\n\r\n"
val canaryString = "Yeah!\n"
val resultsFileName = "CanaryResults.csv"

Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.example.CanaryLibrary

import android.os.Environment
import android.os.Environment.MEDIA_MOUNTED
import java.io.File
import java.util.*


class TestController()
{
suspend fun runTransportTest(transport: Transport): TestResult?
{
// Connection test
val connectionTest = TransportConnectionTest(transport)
val success = connectionTest.run()

// Save the result to a file
val hostString = transport.serverIP + ":${transport.port}"
val result = TestResult(hostString, Date(), transport.name, success)
save(result, transport.name)

return null
}

// Saves the provided test results to a csv file with a filename that contains a timestamp.
// If a file with this name already exists it will append the results to the end of the file.
// - Parameter result: The test result information to be saved. The type is a TestResult struct.
// - Returns: A boolean value indicating whether or not the results were saved successfully.
fun save(result: TestResult, testName: String): Boolean
{
if (Environment.getExternalStorageState() != MEDIA_MOUNTED)
{
println("Unable to save the results file: external storage is not available for reading/writing")
return false
}

val extDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)
val saveFile = File(extDir, resultsFileName)

// Make sure the Documents directory exists.
extDir.mkdirs()

if (!saveFile.exists())
{
// Make a new csv file for our test results
saveFile.createNewFile()

// The first row should be our labels
val labelRow = "TestDate, ServerIP, Transport, Success\n"
saveFile.appendText(labelRow)
}

// Add out newest test results to the file
val resultString = "${result.testDate}, ${result.hostString}, $testName, ${result.success}\n"
saveFile.appendText(resultString)

return saveFile.exists()
}

suspend fun test(transport: Transport) {
println("Testing ${transport.name} transport...")

val transportTestResult = runTransportTest(transport)

if (transportTestResult == null)
{
println("\n Received a null result when testing ${transport.name} transport. \n")
}
}
}
Loading

0 comments on commit eb8dd67

Please sign in to comment.