#!/bin/bash
#
# Copyright 2023 The Bazel Authors. All rights reserved.
#
# 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.

# For these tests to run do the following:
#
#   1. Install an Android SDK from https://developer.android.com
#   2. Set the $ANDROID_HOME environment variable
#   3. Uncomment the line in WORKSPACE containing android_sdk_repository
#
# Note that if the environment is not set up as above android_integration_test
# will silently be ignored and will be shown as passing.

# Load the test setup defined in the parent directory
CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

source "${CURRENT_DIR}/../../integration_test_setup.sh" \
  || { echo "integration_test_setup.sh not found!" >&2; exit 1; }

source "${CURRENT_DIR}/android_helper.sh" \
  || { echo "android_helper.sh not found!" >&2; exit 1; }
fail_if_no_android_sdk

resolve_android_toolchains "$1"

function create_test_app() {

  inner_class_count="$1"
  lambda_count="$2"
  dex_shard_count="$3"

  mkdir -p java/com/testapp

  cat > java/com/testapp/AndroidManifest.xml <<EOF
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.testapp"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="30"
        android:targetSdkVersion="30" />

    <application android:label="Test App" >
        <activity
            android:name="com.testapp.MainActivity"
            android:label="App"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>
EOF

  cat > java/com/testapp/MainActivity.java <<EOF
package com.testapp;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends Activity {
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Log.i("tag", "info");
  }
}
EOF

  generate_java_file_with_many_synthetic_classes "$1" "$2" > java/com/testapp/BigLib.java

  cat > java/com/testapp/BUILD <<EOF
android_binary(
    name = "testapp",
    srcs = [
        "MainActivity.java",
        ":BigLib.java",
    ],
    dex_shards = $dex_shard_count,
    manifest = "AndroidManifest.xml",
)
EOF
}

function generate_java_file_with_many_synthetic_classes() {

  inner_class_count="$1"
  lambda_count="$2"

  echo "package com.testapp;"
  echo "public class BigLib {"

  # First generate enough inner classes to fill up most of the dex
  for (( i = 0; i < $inner_class_count; i++ )) do

    echo "  public static class Bar$i {"
    echo "    public int bar() {"
    echo "      return $i;"
    echo "    }"
    echo "  }"

  done

  # Then create enough synethetic classes via lambdas to fill up the rest of the
  # dex and into another dex file.
  echo "  public interface IntSupplier {"
  echo "    int supply();"
  echo "  }"

  echo "  public static class Foo {"
  echo "    public IntSupplier[] foo() {"
  echo "      return new IntSupplier[] {"

  for ((i = 0; i < $lambda_count; i++ )) do
    echo "        () -> $i,"
  done

  echo "      };"
  echo "    }"
  echo "  }"
  echo "}"
}

function test_DexFileSplitter_synthetic_classes_crossing_dexfiles() {
  create_new_workspace
  setup_android_sdk_support

  # dex_shards default is 1
  create_test_app 21400 6000 1

  bazel build java/com/testapp || fail "Test app should have built succesfully"

  dex_file_count=$(unzip -l bazel-bin/java/com/testapp/testapp.apk | grep "classes[0-9]*.dex" | wc -l)
  if [[ ! "$dex_file_count" -ge "2" ]]; then
    echo "Expected at least 2 dexes in app, found: $dex_file_count"
    exit 1
  fi
}

function test_DexMapper_synthetic_classes_crossing_dexfiles() {
  create_new_workspace
  setup_android_sdk_support

  # 3 inner classes, 6 lambdas (and thus 6 synthetics from D8) and 5 dex_shards
  # is one magic combination to repro synthetics being separated from their
  # context / enclosing classes.
  create_test_app 3 6 5

  echo here2
  echo $TEST_TMPDIR/bazelrc
  cat $TEST_TMPDIR/bazelrc

  bazel build java/com/testapp || fail "Test app should have built succesfully"

  dex_file_count=$(unzip -l bazel-bin/java/com/testapp/testapp.apk | grep "classes[0-9]*.dex" | wc -l)
  if [[ ! "$dex_file_count" -eq "5" ]]; then
    echo "Expected 5 dexes in app, found: $dex_file_count"
    exit 1
  fi
}

run_suite "Tests for DexFileSplitter with synthetic classes crossing dexfiles"