Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7d867c29 authored by Jorim Jaggi's avatar Jorim Jaggi Committed by android-build-merger
Browse files

Merge changes from topic "flicker-move" am: 7943776f am: 7f0716ff

am: ec64dab4

Change-Id: I9ea102b35bd08bde0710ccab322fe1bf6749a31d
parents f3161212 ec64dab4
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <!-- Capture screen contents -->
    <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
    <!-- Enable / Disable tracing !-->
    <uses-permission android:name="android.permission.DUMP" />
    <!-- Run layers trace -->
    <uses-permission android:name="android.permission.HARDWARE_TEST"/>
    <application>
+1 −0
Original line number Diff line number Diff line
@@ -25,5 +25,6 @@
    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
        <option name="directory-keys" value="/sdcard/flicker" />
        <option name="collect-on-run-ended-only" value="true" />
        <option name="clean-up" value="false" />
    </metrics_collector>
</configuration>

tests/FlickerTests/lib/Android.bp

deleted100644 → 0
+0 −57
Original line number Diff line number Diff line
//
// Copyright (C) 2018 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

java_test {
    name: "flickerlib",
    platform_apis: true,
    srcs: ["src/**/*.java"],
    static_libs: [
        "androidx.test.janktesthelper",
        "cts-wm-util",
        "platformprotosnano",
        "layersprotosnano",
        "truth-prebuilt",
        "sysui-helper",
        "launcher-helper-lib",
    ],
}

java_library {
    name: "flickerlib_without_helpers",
    platform_apis: true,
    srcs: ["src/**/*.java"],
    exclude_srcs: ["src/**/helpers/*.java"],
    static_libs: [
        "cts-wm-util",
        "platformprotosnano",
        "layersprotosnano",
        "truth-prebuilt"
    ],
}

java_library {
    name: "flickerautomationhelperlib",
    sdk_version: "test_current",
    srcs: [
        "src/com/android/server/wm/flicker/helpers/AutomationUtils.java",
        "src/com/android/server/wm/flicker/WindowUtils.java",
    ],
    static_libs: [
        "sysui-helper",
        "launcher-helper-lib",
        "compatibility-device-util-axt",
    ],
}
+0 −134
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.wm.flicker;

import java.util.concurrent.TimeUnit;
import java.util.function.Function;

/**
 * Collection of functional interfaces and classes representing assertions and their associated
 * results. Assertions are functions that are applied over a single trace entry and returns a
 * result which includes a detailed reason if the assertion fails.
 */
public class Assertions {
    /**
     * Checks assertion on a single trace entry.
     *
     * @param <T> trace entry type to perform the assertion on.
     */
    @FunctionalInterface
    public interface TraceAssertion<T> extends Function<T, Result> {
        /**
         * Returns an assertion that represents the logical negation of this assertion.
         *
         * @return a assertion that represents the logical negation of this assertion
         */
        default TraceAssertion<T> negate() {
            return (T t) -> apply(t).negate();
        }
    }

    /**
     * Checks assertion on a single layers trace entry.
     */
    @FunctionalInterface
    public interface LayersTraceAssertion extends TraceAssertion<LayersTrace.Entry> {

    }

    /**
     * Utility class to store assertions with an identifier to help generate more useful debug
     * data when dealing with multiple assertions.
     */
    public static class NamedAssertion<T> {
        public final TraceAssertion<T> assertion;
        public final String name;

        public NamedAssertion(TraceAssertion<T> assertion, String name) {
            this.assertion = assertion;
            this.name = name;
        }
    }

    /**
     * Contains the result of an assertion including the reason for failed assertions.
     */
    public static class Result {
        public static final String NEGATION_PREFIX = "!";
        public final boolean success;
        public final long timestamp;
        public final String assertionName;
        public final String reason;

        public Result(boolean success, long timestamp, String assertionName, String reason) {
            this.success = success;
            this.timestamp = timestamp;
            this.assertionName = assertionName;
            this.reason = reason;
        }

        public Result(boolean success, String reason) {
            this.success = success;
            this.reason = reason;
            this.assertionName = "";
            this.timestamp = 0;
        }

        /**
         * Returns the negated {@code Result} and adds a negation prefix to the assertion name.
         */
        public Result negate() {
            String negatedAssertionName;
            if (this.assertionName.startsWith(NEGATION_PREFIX)) {
                negatedAssertionName = this.assertionName.substring(NEGATION_PREFIX.length() + 1);
            } else {
                negatedAssertionName = NEGATION_PREFIX + this.assertionName;
            }
            return new Result(!this.success, this.timestamp, negatedAssertionName, this.reason);
        }

        public boolean passed() {
            return this.success;
        }

        public boolean failed() {
            return !this.success;
        }

        @Override
        public String toString() {
            return "Timestamp: " + prettyTimestamp(timestamp)
                    + "\nAssertion: " + assertionName
                    + "\nReason:   " + reason;
        }

        private String prettyTimestamp(long timestamp_ns) {
            StringBuilder prettyTimestamp = new StringBuilder();
            TimeUnit[] timeUnits = {TimeUnit.HOURS, TimeUnit.MINUTES, TimeUnit.SECONDS, TimeUnit
                    .MILLISECONDS};
            String[] unitSuffixes = {"h", "m", "s", "ms"};

            for (int i = 0; i < timeUnits.length; i++) {
                long convertedTime = timeUnits[i].convert(timestamp_ns, TimeUnit.NANOSECONDS);
                timestamp_ns -= TimeUnit.NANOSECONDS.convert(convertedTime, timeUnits[i]);
                prettyTimestamp.append(convertedTime).append(unitSuffixes[i]);
            }

            return prettyTimestamp.toString();
        }
    }
}
+0 −183
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.wm.flicker;

import com.android.server.wm.flicker.Assertions.NamedAssertion;
import com.android.server.wm.flicker.Assertions.Result;
import com.android.server.wm.flicker.Assertions.TraceAssertion;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Captures some of the common logic in {@link LayersTraceSubject} and {@link WmTraceSubject}
 * used to filter trace entries and combine multiple assertions.
 *
 * @param <T> trace entry type
 */
public class AssertionsChecker<T extends ITraceEntry> {
    private boolean mFilterEntriesByRange = false;
    private long mFilterStartTime = 0;
    private long mFilterEndTime = 0;
    private AssertionOption mOption = AssertionOption.NONE;
    private List<NamedAssertion<T>> mAssertions = new LinkedList<>();

    public void add(Assertions.TraceAssertion<T> assertion, String name) {
        mAssertions.add(new NamedAssertion<>(assertion, name));
    }

    public void filterByRange(long startTime, long endTime) {
        mFilterEntriesByRange = true;
        mFilterStartTime = startTime;
        mFilterEndTime = endTime;
    }

    private void setOption(AssertionOption option) {
        if (mOption != AssertionOption.NONE && option != mOption) {
            throw new IllegalArgumentException("Cannot use " + mOption + " option with "
                    + option + " option.");
        }
        mOption = option;
    }

    public void checkFirstEntry() {
        setOption(AssertionOption.CHECK_FIRST_ENTRY);
    }

    public void checkLastEntry() {
        setOption(AssertionOption.CHECK_LAST_ENTRY);
    }

    public void checkChangingAssertions() {
        setOption(AssertionOption.CHECK_CHANGING_ASSERTIONS);
    }


    /**
     * Filters trace entries then runs assertions returning a list of failures.
     *
     * @param entries list of entries to perform assertions on
     * @return list of failed assertion results
     */
    public List<Result> test(List<T> entries) {
        List<T> filteredEntries;
        List<Result> failures;

        if (mFilterEntriesByRange) {
            filteredEntries = entries.stream()
                    .filter(e -> ((e.getTimestamp() >= mFilterStartTime)
                            && (e.getTimestamp() <= mFilterEndTime)))
                    .collect(Collectors.toList());
        } else {
            filteredEntries = entries;
        }

        switch (mOption) {
            case CHECK_CHANGING_ASSERTIONS:
                return assertChanges(filteredEntries);
            case CHECK_FIRST_ENTRY:
                return assertEntry(filteredEntries.get(0));
            case CHECK_LAST_ENTRY:
                return assertEntry(filteredEntries.get(filteredEntries.size() - 1));
        }
        return assertAll(filteredEntries);
    }

    /**
     * Steps through each trace entry checking if provided assertions are true in the order they
     * are added. Each assertion must be true for at least a single trace entry.
     *
     * This can be used to check for asserting a change in property over a trace. Such as visibility
     * for a window changes from true to false or top-most window changes from A to Bb and back to A
     * again.
     */
    private List<Result> assertChanges(List<T> entries) {
        List<Result> failures = new ArrayList<>();
        int entryIndex = 0;
        int assertionIndex = 0;
        int lastPassedAssertionIndex = -1;

        if (mAssertions.size() == 0) {
            return failures;
        }

        while (assertionIndex < mAssertions.size() && entryIndex < entries.size()) {
            TraceAssertion<T> currentAssertion = mAssertions.get(assertionIndex).assertion;
            Result result = currentAssertion.apply(entries.get(entryIndex));
            if (result.passed()) {
                lastPassedAssertionIndex = assertionIndex;
                entryIndex++;
                continue;
            }

            if (lastPassedAssertionIndex != assertionIndex) {
                failures.add(result);
                break;
            }
            assertionIndex++;

            if (assertionIndex == mAssertions.size()) {
                failures.add(result);
                break;
            }
        }

        if (failures.isEmpty()) {
            if (assertionIndex != mAssertions.size() - 1) {
                String reason = "\nAssertion " + mAssertions.get(assertionIndex).name
                        + " never became false";
                reason += "\nPassed assertions: " + mAssertions.stream().limit(assertionIndex)
                        .map(assertion -> assertion.name).collect(Collectors.joining(","));
                reason += "\nUntested assertions: " + mAssertions.stream().skip(assertionIndex + 1)
                        .map(assertion -> assertion.name).collect(Collectors.joining(","));

                Result result = new Result(false /* success */, 0 /* timestamp */,
                        "assertChanges", "Not all assertions passed." + reason);
                failures.add(result);
            }
        }
        return failures;
    }

    private List<Result> assertEntry(T entry) {
        List<Result> failures = new ArrayList<>();
        for (NamedAssertion<T> assertion : mAssertions) {
            Result result = assertion.assertion.apply(entry);
            if (result.failed()) {
                failures.add(result);
            }
        }
        return failures;
    }

    private List<Result> assertAll(List<T> entries) {
        return mAssertions.stream().flatMap(
                assertion -> entries.stream()
                        .map(assertion.assertion)
                        .filter(Result::failed))
                .collect(Collectors.toList());
    }

    private enum AssertionOption {
        NONE,
        CHECK_CHANGING_ASSERTIONS,
        CHECK_FIRST_ENTRY,
        CHECK_LAST_ENTRY,
    }
}
Loading