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

Commit 36a0c7c4 authored by Makoto Onuki's avatar Makoto Onuki
Browse files

[ravenwood dashboard] @RavenwoodSupported is not boring

- If a public method has @RavenwoodSupported, make sure it shows up
  on the dashboard by not marking it as "boring".
  (even if it's abstract)

- Also clean up ravenwood-stats-collector.sh

- Add "csv-flattener" for "flattening" CSV -- this will print each
  column in a separate line, for a better diff result.

- Add "-d DIFF_BASE" option to ravenwood-stats-collector.sh to
  print diff against another CSV file.

Test: Manual test with comparing against the old CSV
Flag: EXEMPT script change only
Bug: 414821464

Change-Id: I1837222df6ae775e5804cd0d850891c76c0f5ed7
parent b664a9c9
Loading
Loading
Loading
Loading
+57 −0
Original line number Diff line number Diff line
#!/usr/bin/env python3
#
# Copyright (C) 2025 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.

"""
Reads lines from CSV files and print each colum in a separate line,
which helps taking a per-field diff of CSV files. Columns that are
not the first in each line are indented.

Example:
$ (echo "a,b,c"; echo "d,e,f") | csv-flattener
a
  b
  c
d
  e
  f
"""

import csv
import sys
from signal import signal, SIGPIPE, SIG_DFL

def flatten(infile):
    rd = csv.reader(infile, delimiter=',', quotechar='"')

    for cols in rd:
        prefix = ""
        for col in cols:
            print(prefix + col)
            prefix = "  "

def main(files):
    signal(SIGPIPE, SIG_DFL) # Don't show stacktrace on SIGPIPE

    if len(files) == 0:
        flatten(sys.stdin)
        return

    for f in files:
        with open(f, newline='') as csvfile:
            flatten(csvfile)

if __name__ == "__main__":
    main(sys.argv[1:])
+127 −42
Original line number Diff line number Diff line
@@ -17,6 +17,25 @@

set -e

# This script's directory.
SCRIPT_DIR="$(realpath ${0%/*})"

# Parse the options.

diff_base=""
while getopts "d:" opt; do
case "$opt" in
    d)
        # If this flag is provided, diff the newly generated CSV against this file.
        diff_base="$OPTARG"
        ;;
    '?')
        exit 1
        ;;
esac
done
shift $(($OPTIND - 1))

# Output files
out_dir=/tmp/ravenwood
apis=$out_dir/ravenwood-apis-all.csv
@@ -28,78 +47,144 @@ mkdir -p $out_dir
mkdir -p $keep_all_dir
mkdir -p $dump_dir


stats_checker_module="ravenwood-stats-checker"
minfo=$OUT/module-info.json

# Current time.
timestamp="$(date --iso-8601=seconds)"

# First, use jq to get the output files from the checker module. This will be something like this:
# The input CSV files are generated by different build modules, so collecting
# them would be painful.
#
# So instead, we use a dummy sh_test_host "ravenwood-stats-checker" with
# all the input files as its data files. So if we build this module, soong
# will copy this script to its test output directory, along with all the data files,
# so we can now just copy all the *.csv, *.txt files from this single directory.
#
# So first, we need to find this output directory, which is in the module-info.json
# file. So we use JQ to find it.
#
# The json contains something like the following, so we need to find the script path.
#
# ---
# out/host/linux-x86/nativetest64/ravenwood-stats-checker/framework-configinfrastructure_apis.csv
# out/host/linux-x86/nativetest64/ravenwood-stats-checker/framework-configinfrastructure_dump.txt
# "ravenwood-stats-checker" : {
#  :
# out/host/linux-x86/nativetest64/ravenwood-stats-checker/hoststubgen_services.core_stats.csv
# out/host/linux-x86/nativetest64/ravenwood-stats-checker/ravenwood-stats-checker
#  "installed": [
#    "out/host/linux-x86/nativetest64/ravenwood-stats-checker/framework-configinfrastructure_apis.csv",
#    "out/host/linux-x86/nativetest64/ravenwood-stats-checker/framework-configinfrastructure_dump.txt",
#      :
#    "out/host/linux-x86/nativetest64/ravenwood-stats-checker/ravenwood-stats-checker" <-- need to find this.
#  ],
#  :
#},
# ---
# Then, use grep to find the script's path (the last line in the above examle)
script_path="$(
    jq -r ".\"$stats_checker_module\".installed | .[]" $minfo |
    grep '/ravenwood-stats-checker$'
checker=ravenwood-stats-checker
minfo=$OUT/module-info.json

checker_path="$(
    jq -r ".\"$checker\".installed.[] | select(endswith(\"/$checker\"))" \
        $minfo
)"

if [[ "$script_path" == "" ]] ; then
    echo "Error: $stats_checker_module script not found from $minfo"
if [[ "$checker_path" == "" ]] ; then
    echo "Error: '$checker' script not found in $minfo"
    exit 1
fi

# This is the directory where our input files are.
script_dir="$ANDROID_BUILD_TOP/$(dirname "$script_path")"
files_dir="$ANDROID_BUILD_TOP/$(dirname "$checker_path")/"

# Clear it before (re-)buildign the script, to make sure we won't have stale files.
rm -fr "$script_dir"
# Clear the directory before (re-)building the script, to make sure we won't have stale files.
rm -fr "$files_dir"

# Then build it, which will also collect the input files in the same dir.
echo "Collecting the input files..."
m "$stats_checker_module"
# Then build it, which will also collect the input files in $files_dir.
echo "Files directory is: $files_dir"

# Start...
echo "Collecting the input files..."
m $checker

echo "Files directory is: $script_dir"
cd "$script_dir"
# Run a command after printing it.
run() {
    echo "Running: $*" 1>&2
    "$@"
}

# Just cat a CSV file, but with two extra columns prepended:
# - Jar module name (first argument)
# - Timestamp
dump() {
    local jar=$1
    local file=$2
    local jar="$1"
    local file="$2"

    # Remove the header row, and prepend the columns.
    sed -e '1d' -e "s/^/$jar,$timestamp,/" $file
    sed -e '1d' -e "s/^/$jar,$timestamp,/" "$file"
}

collect_apis() {
    local out="$1"
    local desc="$2"
    {
        # Copy the header, with the first column appended.
        # Copy the header, with the first two columns appended.
        echo -n "Jar,Generated Date,"
        head -n 1 hoststubgen_framework-minus-apex_apis.csv
        head -n 1 "$files_dir"/hoststubgen_framework-minus-apex_apis.csv

        dump "framework-minus-apex"  hoststubgen_framework-minus-apex_apis.csv
        dump "service.core"  hoststubgen_services.core_apis.csv
        dump "framework-configinfrastructure"  framework-configinfrastructure_apis.csv
        dump "framework-statsd"  framework-statsd_apis.csv
        # Dump each module.
        dump "framework-minus-apex"  "$files_dir"/hoststubgen_framework-minus-apex_apis.csv
        dump "service.core"  "$files_dir"/hoststubgen_services.core_apis.csv
        dump "framework-configinfrastructure"  "$files_dir"/framework-configinfrastructure_apis.csv
        dump "framework-statsd" "$files_dir"/framework-statsd_apis.csv
    } > "$out"

    echo "API CVS created at $out$desc"
    echo "API CVS created at $out (import it as 'ravenwood_supported_apis3')"
}

collect_apis $apis " (import it as 'ravenwood_supported_apis3')"
do_main() {
    collect_apis $apis

cp *keep_all.txt $keep_all_dir
    # Collect keep_all files.
    cp "$files_dir"/*keep_all.txt $keep_all_dir
    echo "Keep all files created at:"
    find $keep_all_dir -type f

cp *dump.txt $dump_dir
    # Collect dump files.
    cp "$files_dir"/*dump.txt $dump_dir
    echo "Dump files created at:"
    find $dump_dir -type f
}

# Run the main routine.
do_main

# Main routine done.

#---------------------------------------------------------------------
# Next, if -d (diff base) is provided, take a diff against that file.

if [[ "$diff_base" == "" ]] ; then
    exit 0
fi

echo
echo "Taking a diff against $diff_base ..."

# Flatten a CSV (expand each column in a separate line) for a better diff.
# (Also remove the timestamp.)
flatten() {
    local csv="$1"
    local to="$2"

    echo "Flattening $csv to $to ..."
    # "grep -v" to remove timestamps.
    "$SCRIPT_DIR"/csv-flattener "$csv" | grep -v "^  20[0-9][0-9]-[0-9][0-9]" > "$to"
}

do_diff() {
    local f1="$1"
    local f2="$2"

    # Generate flattened text files from the old and new CSVs.
    flatten "$f1" "$f1.txt"
    flatten "$f2" "$f2.txt"

    # Then take a diff.
    # ("|| true" for ignoring the status code)
    run ${DIFF_CMD:-diff} -U 10 "$f1.txt" "$f2.txt" || true
}

do_diff "$diff_base" "$apis"
 No newline at end of file
+15 −1
Original line number Diff line number Diff line
@@ -127,7 +127,21 @@ class ApiDumper(
        }

        // Use heuristics to override the label.
        if (!mn.isPublic() || mn.isAbstract()) {
        if (!mn.isPublic()) {
            return StatsLabel.SupportedButBoring
        }

        // If the policy is "not supported" but the statsLabel explicitly says "supported"
        // then that's "throw-but-supported". In that case, let's not mark it as "boring"
        // even if it's abstract, and show it. (if the statsLabel doesn't say "supported",
        // we'd be returning earlier already.)
        if (!methodPolicy.policy.isSupported) {
            return StatsLabel.Supported
        }

        if (mn.isAbstract()) {
            // An abstract method is normally boring -- but if it's a throw-but-interesting,
            // that's actually not boring.
            return StatsLabel.SupportedButBoring
        }