Skip to content

Commit 6d8fe54

Browse files
committed
Added Roboelectric tests to test the new DesiredYawCalculator, and applying it to the DroneService. Thanks @nenick for your Roboelectric project!
1 parent f0ed3db commit 6d8fe54

File tree

8 files changed

+207
-15
lines changed

8 files changed

+207
-15
lines changed

app/build.gradle

+7
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,18 @@ android {
2020
}
2121
}
2222

23+
apply from: 'build.workaround-missing-resource.gradle'
24+
2325
dependencies {
2426
compile fileTree(dir: 'libs', include: ['*.jar'])
2527
compile 'com.android.support:appcompat-v7:21.0.3'
2628
compile 'de.greenrobot:eventbus:2.4.0'
2729
compile 'com.google.code.gson:gson:2.3.1'
2830
compile 'com.squareup.phrase:phrase:1.1.0'
2931
compile 'com.android.support:design:22.2.0'
32+
33+
testCompile 'junit:junit:4.12'
34+
testCompile "org.mockito:mockito-core:1.9.5"
35+
testCompile 'org.apache.maven:maven-ant-tasks:2.1.3'
36+
testCompile "org.robolectric:robolectric:3.0-rc3"
3037
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Workaround for missing test resources when run unit tests within android studio.
2+
// This copy the test resources next to the test classes for each variant.
3+
// Tracked at https://github.com/nenick/AndroidStudioAndRobolectric/issues/7
4+
// Original solution comes from https://code.google.com/p/android/issues/detail?id=136013#c10
5+
6+
gradle.projectsEvaluated {
7+
// Base path which is recognized by android studio.
8+
def testClassesPath = "${buildDir}/intermediates/classes/test/"
9+
// Copy must be done for each variant.
10+
def variants = android.applicationVariants.collect()
11+
12+
variants.each { variant ->
13+
def variationName = variant.name.capitalize()
14+
15+
// Get the flavor and also merge flavor groups.
16+
def productFlavorNames = variant.productFlavors.collect { it.name.capitalize() }
17+
if (productFlavorNames.isEmpty()) {
18+
productFlavorNames = [""]
19+
}
20+
productFlavorNames = productFlavorNames.join('')
21+
22+
// Base path addition for this specific variant.
23+
def variationPath = variant.buildType.name;
24+
if (productFlavorNames != null && !productFlavorNames.isEmpty()) {
25+
variationPath = uncapitalize(productFlavorNames) + "/${variationPath}"
26+
}
27+
28+
// Specific copy task for each variant
29+
def copyTestResourcesTask = project.tasks.create("copyTest${variationName}Resources", Copy)
30+
copyTestResourcesTask.from("${projectDir}/src/test/resources")
31+
copyTestResourcesTask.into("${testClassesPath}/${variationPath}")
32+
copyTestResourcesTask.execute()
33+
}
34+
}
35+
36+
def uncapitalize(String str) {
37+
int strLen;
38+
if (str == null || (strLen = str.length()) == 0) {
39+
return str;
40+
}
41+
return new StringBuffer(strLen)
42+
.append(Character.toLowerCase(str.charAt(0)))
43+
.append(str.substring(1))
44+
.toString();
45+
}

app/src/androidTest/java/com/fewlaps/flone/ApplicationTest.java

-13
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.fewlaps.flone;
2+
3+
/**
4+
* A tool to get the yaw the user wants to send to the drone. The idea is that
5+
* droneHeading and desiresHeading are values from -180 to 180.
6+
*
7+
* @author Roc Boronat ([email protected])
8+
* @date 05/07/2015
9+
*/
10+
public class DesiredYawCalculator {
11+
public double getYaw(double droneHeading, double phoneHeading) {
12+
double difference = phoneHeading - droneHeading;
13+
14+
if (difference > 180) {
15+
difference = difference - 180 * 2;
16+
} else if (difference < -180) {
17+
difference = difference + 180 * 2;
18+
}
19+
20+
return difference;
21+
}
22+
}

app/src/main/java/com/fewlaps/flone/service/DroneService.java

+6-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import android.os.Handler;
55
import android.util.Log;
66

7+
import com.fewlaps.flone.DesiredYawCalculator;
78
import com.fewlaps.flone.data.KnownDronesDatabase;
89
import com.fewlaps.flone.data.bean.Drone;
910
import com.fewlaps.flone.io.bean.DroneConnectionStatusChanged;
@@ -50,6 +51,7 @@ public class DroneService extends BaseService {
5051
private PhoneOutputData phoneOutputData = new PhoneOutputData();
5152

5253
public static final RCSignals rc = new RCSignals(); //Created at startup, never changed, never destroyed, totally reused at every request
54+
DesiredYawCalculator yawCalculator = new DesiredYawCalculator();
5355

5456
@Override
5557
public int onStartCommand(Intent intent, int flags, int startId) {
@@ -92,8 +94,10 @@ public void onEventMainThread(DroneConnectionStatusChanged status) {
9294
* to do before sending the RC to the drone, to make it fly as the user excepts
9395
*/
9496
private void updateRCWithInputData() {
95-
int yaw = (int) (userInput.getHeading() - droneInput.getHeading());
96-
int pitch = (int) (userInput.getPitch());
97+
Log.i("HEADING", "phone: " + userInput.getHeading() + " drone: " + droneInput.getHeading());
98+
99+
int yaw = (int) yawCalculator.getYaw(droneInput.getHeading(), userInput.getHeading());
100+
int pitch = (int) userInput.getPitch();
97101
int roll = (int) userInput.getRoll();
98102

99103
rc.setThrottle(userInput.getThrottle());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.fewlaps.flone;
2+
3+
4+
import org.junit.runners.model.InitializationError;
5+
import org.robolectric.RobolectricGradleTestRunner;
6+
import org.robolectric.annotation.Config;
7+
import org.robolectric.manifest.AndroidManifest;
8+
import org.robolectric.res.FileFsFile;
9+
import org.robolectric.res.FsFile;
10+
11+
12+
/**
13+
* More dynamic path resolution.
14+
*
15+
* This workaround is only for Mac Users necessary and only if they don't use the $MODULE_DIR$
16+
* workaround mentioned at http://robolectric.org/getting-started/.
17+
*
18+
* Follow this issue at https://code.google.com/p/android/issues/detail?id=158015
19+
*/
20+
public class CustomRobolectricRunner extends RobolectricGradleTestRunner {
21+
22+
23+
public CustomRobolectricRunner(Class<?> klass) throws InitializationError {
24+
super(klass);
25+
}
26+
27+
28+
protected AndroidManifest getAppManifest(Config config) {
29+
AndroidManifest appManifest = super.getAppManifest(config);
30+
FsFile androidManifestFile = appManifest.getAndroidManifestFile();
31+
32+
33+
if (androidManifestFile.exists()) {
34+
return appManifest;
35+
} else {
36+
String moduleRoot = getModuleRootPath(config);
37+
androidManifestFile = FileFsFile.from(moduleRoot, appManifest.getAndroidManifestFile().getPath().replace("bundles", "manifests/full"));
38+
FsFile resDirectory = FileFsFile.from(moduleRoot, appManifest.getResDirectory().getPath().replace("/res", "").replace("bundles", "res"));
39+
FsFile assetsDirectory = FileFsFile.from(moduleRoot, appManifest.getAssetsDirectory().getPath().replace("/assets", "").replace("bundles", "assets"));
40+
return new AndroidManifest(androidManifestFile, resDirectory, assetsDirectory);
41+
}
42+
}
43+
44+
45+
private String getModuleRootPath(Config config) {
46+
String moduleRoot = config.constants().getResource("").toString().replace("file:", "");
47+
return moduleRoot.substring(0, moduleRoot.indexOf("/build"));
48+
}
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package com.fewlaps.flone;
2+
3+
import org.junit.Before;
4+
import org.junit.Test;
5+
6+
import static junit.framework.Assert.assertEquals;
7+
8+
/**
9+
* @author Roc Boronat ([email protected])
10+
* @date 05/07/2015
11+
*/
12+
public class DesiredYawTest {
13+
14+
DesiredYawCalculator desiredYaw;
15+
16+
@Before
17+
public void init(){
18+
desiredYaw = new DesiredYawCalculator();
19+
}
20+
21+
@Test
22+
public void shouldReturn0ForSameValues() {
23+
double sameValue = 42;
24+
double yaw = desiredYaw.getYaw(sameValue, sameValue);
25+
26+
assertEquals(0d, yaw);
27+
}
28+
29+
@Test
30+
public void shouldReturn20ForDroneAt0AndPhoneAt20() {
31+
double yaw = desiredYaw.getYaw(0, 20);
32+
33+
assertEquals(20d, yaw);
34+
}
35+
36+
@Test
37+
public void shouldReturn30ForDroneAt50AndPhoneAt80() {
38+
double yaw = desiredYaw.getYaw(50, 80);
39+
40+
assertEquals(30d, yaw);
41+
}
42+
43+
@Test
44+
public void shouldReturn20ForDroneAtMinus10AndPhoneAt10() {
45+
double yaw = desiredYaw.getYaw(-10, 10);
46+
47+
assertEquals(20d, yaw);
48+
}
49+
50+
@Test
51+
public void shouldReturn20ForDroneAtMinus170AndPhoneAtMinus150() {
52+
double yaw = desiredYaw.getYaw(-170, -150);
53+
54+
assertEquals(20d, yaw);
55+
}
56+
57+
@Test
58+
public void shouldReturnMinus20ForDroneAtMinus170AndPhoneAt170() {
59+
double yaw = desiredYaw.getYaw(-170, 170);
60+
61+
assertEquals(-20d, yaw);
62+
}
63+
64+
@Test
65+
public void shouldReturn20ForDroneAt170AndPhoneAtMinus170() {
66+
double yaw = desiredYaw.getYaw(170, -170);
67+
68+
assertEquals(20d, yaw);
69+
}
70+
71+
@Test
72+
public void shouldReturn170ForDroneAtMinus20AndPhoneAt150() {
73+
double yaw = desiredYaw.getYaw(-20, 150);
74+
75+
assertEquals(170d, yaw);
76+
}
77+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
constants=com.fewlaps.flone.BuildConfig

0 commit comments

Comments
 (0)