#!/usr/bin/env python3
# Copyright (C) 2023 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.

from python.generators.diff_tests.testing import Path, DataPath, Metric
from python.generators.diff_tests.testing import Csv, Json, TextProto
from python.generators.diff_tests.testing import DiffTestBlueprint
from python.generators.diff_tests.testing import TestSuite


class GraphicsMetrics(TestSuite):

  def test_frame_missed_metrics(self):
    return DiffTestBlueprint(
        trace=Path('frame_missed.py'),
        query=Metric('android_surfaceflinger'),
        out=TextProto(r"""
        android_surfaceflinger {
          missed_frames: 3
          missed_hwc_frames: 0
          missed_gpu_frames: 0
          missed_frame_rate: 0.42857142857142855 # = 3/7
          gpu_invocations: 0
          metrics_per_display: {
            display_id: "101"
            missed_frames: 2
            missed_hwc_frames: 0
            missed_gpu_frames: 0
            missed_frame_rate: 0.5
          }
          metrics_per_display: {
            display_id: "102"
            missed_frames: 1
            missed_hwc_frames: 0
            missed_gpu_frames: 0
            missed_frame_rate: 0.33333333333333333
          }
        }
        """))

  def test_surfaceflinger_gpu_invocation(self):
    return DiffTestBlueprint(
        trace=Path('surfaceflinger_gpu_invocation.py'),
        query=Metric('android_surfaceflinger'),
        out=TextProto(r"""
        android_surfaceflinger {
          missed_frames: 0
          missed_hwc_frames: 0
          missed_gpu_frames: 0
          gpu_invocations: 4
          avg_gpu_waiting_dur_ms: 4
          total_non_empty_gpu_waiting_dur_ms: 11
        }
        """))

  # GPU metrics
  def test_gpu_metric(self):
    return DiffTestBlueprint(
        trace=Path('gpu_metric.py'),
        query=Metric('android_gpu'),
        out=TextProto(r"""
        android_gpu {
          processes {
            name: "app_1"
            mem_max: 8
            mem_min: 2
            mem_avg: 3
          }
          processes {
            name: "app_2"
            mem_max: 10
            mem_min: 6
            mem_avg: 8
          }
          mem_max: 4
          mem_min: 1
          mem_avg: 1
        }
        """))

  def test_gpu_frequency_metric(self):
    return DiffTestBlueprint(
        trace=Path('gpu_frequency_metric.textproto'),
        query=Metric('android_gpu'),
        out=Path('gpu_frequency_metric.out'))

  # Android Jank CUJ metric
  def test_android_jank_cuj(self):
    return DiffTestBlueprint(
        trace=Path('android_jank_cuj.py'),
        query=Metric('android_jank_cuj'),
        out=Path('android_jank_cuj.out'))

  def test_android_jank_cuj_query(self):
    return DiffTestBlueprint(
        trace=Path('android_jank_cuj.py'),
        query=Path('android_jank_cuj_query_test.sql'),
        out=Path('android_jank_cuj_query.out'))

  # Composition layer
  def test_composition_layer_count(self):
    return DiffTestBlueprint(
        trace=Path('composition_layer.py'),
        query="""
        SELECT RUN_METRIC('android/android_hwcomposer.sql');

        SELECT display_id, AVG(value)
        FROM total_layers
        GROUP BY display_id;
        """,
        out=Csv("""
        "display_id","AVG(value)"
        "0",3.000000
        "1",5.000000
        """))

  # G2D metrics TODO(rsavitski): find a real trace and double-check that the
  # is realistic. One kernel's source I checked had tgid=0 for all counter
  # Initial support was added/discussed in b/171296908.
  def test_g2d_metrics(self):
    return DiffTestBlueprint(
        trace=Path('g2d_metrics.textproto'),
        query=Metric('g2d'),
        out=Path('g2d_metrics.out'))

  # Composer execution
  def test_composer_execution(self):
    return DiffTestBlueprint(
        trace=Path('composer_execution.py'),
        query="""
        SELECT RUN_METRIC('android/composer_execution.sql',
          'output', 'hwc_execution_spans');

        SELECT
          validation_type,
          display_id,
          COUNT(*) AS count,
          SUM(execution_time_ns) AS total
        FROM hwc_execution_spans
        GROUP BY validation_type, display_id
        ORDER BY validation_type, display_id;
        """,
        out=Csv("""
        "validation_type","display_id","count","total"
        "separated_validation","1",1,200
        "skipped_validation","0",2,200
        "skipped_validation","1",1,100
        "unknown","1",1,0
        "unskipped_validation","0",1,200
        """))

  # Display metrics
  def test_display_metrics(self):
    return DiffTestBlueprint(
        trace=Path('display_metrics.py'),
        query=Metric('display_metrics'),
        out=TextProto(r"""
        display_metrics {
          total_duplicate_frames: 0
          duplicate_frames_logged: 0
          dpu_state {
            total_dpu_underrun_count: 0
          }
          panel_state {
            total_ddic_underrun_count: 0
          }
          refresh_rate_switches: 5
          refresh_rate_stats {
            refresh_rate_fps: 60
            count: 2
            total_dur_ms: 2
            avg_dur_ms: 1
          }
          refresh_rate_stats {
            refresh_rate_fps: 90
            count: 2
            total_dur_ms: 2
            avg_dur_ms: 1
          }
          refresh_rate_stats {
            refresh_rate_fps: 120
            count: 1
            total_dur_ms: 2
            avg_dur_ms: 2
          }
          update_power_state {
            avg_runtime_micro_secs: 4000
          }
        }
        """))

  def test_dpu_state(self):
    return DiffTestBlueprint(
        trace=DataPath('dpu_state.textproto'),
        query=Metric("display_metrics"),
        out=TextProto(r"""
        display_metrics {
          total_duplicate_frames: 0
          duplicate_frames_logged: 0
          dpu_state {
            total_dpu_underrun_count: 336
            dpu_underrun_detail {
              name: "underrun[0]"
              dpu_underrun_count: 167
            }
            dpu_underrun_detail {
              name: "underrun[1]"
              dpu_underrun_count: 169
            }
          }
          panel_state {
            total_ddic_underrun_count: 0
          }
          refresh_rate_switches: 0
          update_power_state {
          }
        }
        """))

  def test_panel_state(self):
    return DiffTestBlueprint(
        trace=DataPath('panel_state.textproto'),
        query=Metric("display_metrics"),
        out=TextProto(r"""
        display_metrics {
          total_duplicate_frames: 0
          duplicate_frames_logged: 0
          dpu_state {
            total_dpu_underrun_count:8
            dpu_underrun_detail {
              name: "underrun[0]"
              dpu_underrun_count: 8
            }
          }
          panel_state {
            total_ddic_underrun_count: 5
            ddic_underrun_detail {
              name: "ddic_underrun[0]"
              ddic_underrun_count: 4
            }
            ddic_underrun_detail {
              name: "ddic_underrun[1]"
              ddic_underrun_count: 1
            }
          }
          refresh_rate_switches: 0
          update_power_state {
          }
        }
        """))

  # DPU vote clock and bandwidth
  def test_dpu_vote_clock_bw(self):
    return DiffTestBlueprint(
        trace=Path('dpu_vote_clock_bw.textproto'),
        query=Metric('android_hwcomposer'),
        out=TextProto(r"""
        android_hwcomposer {
          skipped_validation_count: 0
          unskipped_validation_count: 0
          separated_validation_count: 0
          unknown_validation_count: 0
          dpu_vote_metrics {
            tid: 237
            avg_dpu_vote_clock: 206250
            avg_dpu_vote_avg_bw: 210000
            avg_dpu_vote_peak_bw: 205000
            avg_dpu_vote_rt_bw: 271000
          }
          dpu_vote_metrics {
            tid: 299
            avg_dpu_vote_clock: 250000
          }
        }
        """))
