1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.cts.verifier.sensors.sixdof.Utils.TestPhase;
18 
19 import com.android.cts.verifier.sensors.sixdof.Dialogs.BaseResultsDialog;
20 import com.android.cts.verifier.sensors.sixdof.Utils.Manager;
21 import com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils;
22 import com.android.cts.verifier.sensors.sixdof.Utils.TestReport;
23 import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointAreaCoveredException;
24 import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointDistanceException;
25 import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointRingNotEnteredException;
26 import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointStartPointException;
27 import com.android.cts.verifier.sensors.sixdof.Utils.Path.AccuracyPath;
28 import com.android.cts.verifier.sensors.sixdof.Utils.Path.Path;
29 import com.android.cts.verifier.sensors.sixdof.Utils.Path.ReferencePath;
30 import com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses.Waypoint;
31 
32 import android.util.Log;
33 
34 import java.util.ArrayList;
35 import java.util.HashMap;
36 
37 /**
38  * TestPhase generic class will be inherited by the other tests.
39  */
40 public abstract class Test {
41     public static final int MAX_MARKER_NUMBER = 5;
42     private static final float FAILURE_TOLERANCE_PERCENTAGE = 0.025f; // 2.5%
43     private String mTestPhaseName;
44 
45     protected ArrayList<Float> mMarkerAccuracy = new ArrayList<>();
46     protected ArrayList<Float> mPathAccuracy = new ArrayList<>();
47     protected ArrayList<Float> mReferencePathDistances = new ArrayList<>();
48     private ArrayList<Float> mTestPathDistances = new ArrayList<>();
49 
50     protected ReferencePath mReferencePath;
51     protected Path mTestPath;
52     protected TestReport mTestReport;
53     protected Manager mManager;
54 
55     /**
56      * Constructor for this class.
57      *
58      * @param referencePath Reference the the reference path.
59      * @param testReport    The test report object to record the tests.
60      * @param manager       The manager to call when the test is done.
61      */
Test(ReferencePath referencePath, TestReport testReport, Manager manager, String testPhase)62     public Test(ReferencePath referencePath, TestReport testReport, Manager manager, String testPhase) {
63         if (referencePath != null) {
64             mReferencePath = referencePath;
65         } else {
66             throw new AssertionError("TestPhase received a null referencePath", null);
67         }
68         mTestPhaseName = testPhase;
69         mTestReport = testReport;
70         mManager = manager;
71         mTestPath = new AccuracyPath();
72         mReferencePathDistances = calculatePathDistance(mReferencePath.getCurrentPath(), mReferencePath.getPathMarkers());
73     }
74 
75     /**
76      * Adds the current waypoint to the test path.
77      *
78      * @param coordinates   the coordinates to use for the waypoint.
79      * @param userGenerated indicates whether the data was user created or system created.
80      * @param currentLap    the lap the data was created in.
81      * @throws WaypointDistanceException    if the location is too close to another.
82      * @throws WaypointAreaCoveredException if the area covered by the user is too little.
83      * @throws WaypointStartPointException  if the location is not close enough to the start.
84      */
addWaypointDataToPath( float[] coordinates, boolean userGenerated, Manager.Lap currentLap)85     public void addWaypointDataToPath(
86             float[] coordinates, boolean userGenerated, Manager.Lap currentLap)
87             throws WaypointAreaCoveredException, WaypointDistanceException,
88             WaypointStartPointException, WaypointRingNotEnteredException {
89         mTestPath.createWaypointAndAddToPath(coordinates, userGenerated, currentLap);
90         runAdditionalMethods();
91     }
92 
93     /**
94      * Abstract method that is used but subclasses.
95      */
runAdditionalMethods()96     protected abstract void runAdditionalMethods();
97 
98     /**
99      * Removes the last marker from the chosen lap.
100      *
101      * @return true of the first marker false if any other marker
102      */
removeLastAddedMarker()103     public boolean removeLastAddedMarker() {
104         return mTestPath.removeLastMarker();
105     }
106 
107     /**
108      * Performs the tests for this test phase.
109      *
110      * @return the state of the tests, true if they pass false if they fail.
111      */
executeTests(boolean includeMarkerTest, boolean includePathTest)112     protected HashMap<BaseResultsDialog.ResultType, Boolean> executeTests(boolean includeMarkerTest, boolean includePathTest) {
113         HashMap<BaseResultsDialog.ResultType, Boolean> testResults = new HashMap<>();
114         if (includePathTest) {
115             testResults.put(BaseResultsDialog.ResultType.PATH, pathTest());
116         }
117         if (includeMarkerTest) {
118             testResults.put(BaseResultsDialog.ResultType.WAYPOINT, markerTest());
119         }
120         return testResults;
121     }
122 
123     /**
124      * Calculates the difference between the markers of the laps and executes the marker related
125      * test.
126      *
127      * @return true if the test passes and false if the rest fails.
128      */
markerTest()129     private boolean markerTest() {
130         float distance;
131         for (int i = 0; i < mReferencePath.getPathMarkersSize(); i++) {
132             distance = MathsUtils.distanceCalculationInXYZSpace(
133                     mReferencePath.getPathMarkers().get(i).getCoordinates(),
134                     mTestPath.getPathMarkers().get(i).getCoordinates());
135             mMarkerAccuracy.add(distance);
136         }
137         return markerAccuracyTest();
138     }
139 
140     /**
141      * Runs a check to find any markers that have failed the test and adds them to the test report.
142      *
143      * @return true if the test passes and false if the rest fails
144      */
markerAccuracyTest()145     private boolean markerAccuracyTest() {
146         boolean testState = true;
147         for (float markerDifference : mMarkerAccuracy) {
148             if (markerDifference > mReferencePath.getFailureTolerance()) {
149                 recordMarkerTestResults(markerDifference);
150                 testState = false;
151             }
152         }
153         return testState;
154     }
155 
156     /**
157      * Formats the failed markers into a string to add it to the test report.
158      *
159      * @param markerDifference the difference which caused the marker to fail
160      */
recordMarkerTestResults(float markerDifference)161     private void recordMarkerTestResults(float markerDifference) {
162         int markerNumber = mMarkerAccuracy.indexOf(markerDifference);
163         String referenceMarker = MathsUtils.coordinatesToString(
164                 mReferencePath.getPathMarkers().get(markerNumber).getCoordinates());
165         String testMarker = MathsUtils.coordinatesToString(
166                 mTestPath.getPathMarkers().get(markerNumber).getCoordinates());
167         String testDetails = mTestPhaseName +
168                 " Marker Accuracy: Distance between the markers too great. Marker: " + markerNumber +
169                 " Difference: " + markerDifference +
170                 " Coordinates " + referenceMarker + " " + testMarker + "\n";
171         Log.e("Marker Result", testDetails);
172         mTestReport.setFailDetails(testDetails);
173     }
174 
175     /**
176      * Executes the the path related tests.
177      *
178      * @return true if the test passes, false if the test fails
179      */
pathTest()180     private boolean pathTest() {
181         mTestPathDistances = calculatePathDistance(mTestPath.getCurrentPath(), mTestPath.getPathMarkers());
182         calculatePathDifferences();
183         return pathAccuracyTest();
184     }
185 
186     /**
187      * Calculates the distance between the markers for the given path.
188      *
189      * @param pathToCalculate The path that we want to calculate the distances for
190      * @param markers         The locations of the user generated markers in that path
191      * @return the list of distances for that path
192      */
calculatePathDistance(ArrayList<Waypoint> pathToCalculate, ArrayList<Waypoint> markers)193     protected ArrayList<Float> calculatePathDistance(ArrayList<Waypoint> pathToCalculate,
194                                                      ArrayList<Waypoint> markers) {
195         ArrayList<Float> pathDistances = new ArrayList<>();
196         float totalDistance, distance;
197         int currentLocation = pathToCalculate.indexOf(markers.get(0));
198 
199         while (currentLocation < pathToCalculate.size() - 1) {
200             totalDistance = 0;
201             do {
202                 distance = MathsUtils.distanceCalculationOnXYPlane(
203                         pathToCalculate.get(currentLocation).getCoordinates(),
204                         pathToCalculate.get(currentLocation + 1).getCoordinates());
205                 totalDistance += distance;
206                 currentLocation++;
207             } while (!pathToCalculate.get(currentLocation).isUserGenerated());
208             pathDistances.add(Math.abs(totalDistance));
209             if (currentLocation == markers.size() - 1) {
210                 break;
211             }
212         }
213         return pathDistances;
214     }
215 
216     /**
217      * Calculates the difference between paths on different laps.
218      */
calculatePathDifferences()219     private void calculatePathDifferences() {
220         float difference;
221 
222         if (!mReferencePathDistances.isEmpty() && !mTestPathDistances.isEmpty()) {
223             for (int i = 0; i < mReferencePathDistances.size(); i++) {
224                 difference = mReferencePathDistances.get(i) - mTestPathDistances.get(i);
225                 mPathAccuracy.add(Math.abs(difference));
226             }
227         } else {
228             throw new AssertionError("calculatePathDifference has one of the arrays empty", null);
229         }
230     }
231 
232     /**
233      * Checks to see if any of the path differences have failed the test and adds them to the test
234      * report.
235      *
236      * @return True if the test passes and false if there is a fail
237      */
pathAccuracyTest()238     private boolean pathAccuracyTest() {
239         boolean testState = true;
240         for (float path : mPathAccuracy) {
241             if (path > mReferencePath.getFailureTolerance()) {
242                 recordPathTestResults(path);
243                 testState = false;
244             }
245         }
246         return testState;
247     }
248 
249     /**
250      * Formats the failed paths into a string to add it to the test report.
251      *
252      * @param difference The distance that failed the test
253      */
recordPathTestResults(float difference)254     private void recordPathTestResults(float difference) {
255         int pathNumber = mPathAccuracy.indexOf(difference);
256         String referencePath = String.valueOf(mReferencePathDistances.get(pathNumber));
257         String testPath = String.valueOf(mTestPathDistances.get(pathNumber));
258         String testDetails = mTestPhaseName +
259                 " Path Length: Path length difference was too great. Path: " + pathNumber +
260                 " Difference: " + difference +
261                 " Paths: " + referencePath + " " + testPath + "\n";
262         Log.e("Path Result", testDetails);
263         mTestReport.setFailDetails(testDetails);
264     }
265 
266     /**
267      * Returns the makers in the test path.
268      */
getTestPathMarkers()269     public ArrayList<Waypoint> getTestPathMarkers() {
270         return mTestPath.getPathMarkers();
271     }
272 
273     /**
274      * Returns the size of the current path.
275      */
getTestPathMarkersSize()276     public int getTestPathMarkersSize() {
277         return mTestPath.getPathMarkers().size();
278     }
279 }
280