#!/bin/bash

#
# Copyright 2015 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}/android_helper.sh" \
  || { echo "android_helper.sh not found!" >&2; exit 1; }
fail_if_no_android_sdk

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

if [[ "$1" = '--with_platforms' ]]; then
  # TODO(b/161709111): With platforms, the below fails with
  # "no attribute `$android_sdk_toolchain_type`" on AspectAwareAttributeMapper.
  echo "android_local_test_integration_test.sh does not support --with_platforms!" >&2
  exit 0
fi

function setup_android_local_test_env() {
  mkdir -p java/com/bin/res/values
  mkdir -p javatests/com/bin

  # Targets for android_local_test to depend on
  cat > java/com/bin/BUILD <<EOF
android_library(
  name = 'lib',
  manifest = 'AndroidManifest.xml',
  exports_manifest = 0,
  srcs = ['Collatz.java'],
  visibility = ["//visibility:public"],
)
EOF
  cat > java/com/bin/AndroidManifest.xml <<EOF
<?xml version="1.0" encoding="utf-8"?>
<manifest package='com.bin' xmlns:android="http://schemas.android.com/apk/res/android" />
EOF
  cat > java/com/bin/Collatz.java <<EOF
package com.bin;

public class Collatz {

  public static int getCollatzFinal(int n) {
    if (n == 1) {
      return 1;
    }
    if (n % 2 == 0) {
      return getCollatzFinal(n / 2);
    } else {
      return getCollatzFinal(n * 3 + 1);
    }
  }

}
EOF
  cat > java/com/bin/res/values/values.xml <<EOF
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">
</resources>
EOF

  # android_local_test targets
  cat > javatests/com/bin/robolectric-deps.properties <<EOF
EOF

  cat > javatests/com/bin/BUILD <<EOF
java_library(
  name = 'robolectric-deps',
  data = ['robolectric-deps.properties'],
)
android_local_test(
  name = 'test',
  srcs = ['TestCollatz.java'],
  manifest = 'AndroidManifest.xml',
  test_class = "com.bin.TestCollatz",
  deps = ['//java/com/bin:lib', ':robolectric-deps'],
)
EOF
  cat > javatests/com/bin/AndroidManifest.xml <<EOF
<?xml version="1.0" encoding="utf-8"?>
<manifest package='com.bin.test' xmlns:android='http://schemas.android.com/apk/res/android'>
</manifest>
EOF
  cat > javatests/com/bin/TestCollatz.java <<EOF
package com.bin;

import static org.junit.Assert.assertEquals;
import org.junit.Test;

public class TestCollatz {

  @Test
  public void testGetCollatzFinal() {
    assertEquals(Collatz.getCollatzFinal(1), 1);
    assertEquals(Collatz.getCollatzFinal(5), 1);
    assertEquals(Collatz.getCollatzFinal(10), 1);
    assertEquals(Collatz.getCollatzFinal(21), 1);
  }

}
EOF
}

# Returns the path of the code coverage report that was generated by Bazel by
# looking at the current $TEST_log. The method fails if TEST_log does not
# contain any coverage report for a passed test.
function get_coverage_file_path_from_test_log() {
  local ending_part="$(sed -n -e '/PASSED/,$p' "$TEST_log")"

  local coverage_file_path=$(grep -Eo "/[/a-zA-Z0-9\.\_\-]+\.dat$" <<< "$ending_part")
  [[ -e "$coverage_file_path" ]] || fail "Coverage output file does not exist!"
  echo "$coverage_file_path"
}

# Asserts if the given expected coverage result is included in the given output
# file.
#
# - expected_coverage The expected result that must be included in the output.
# - output_file       The location of the coverage output file.
function assert_coverage_result() {
  local expected_coverage="${1}"; shift
  local output_file="${1}"; shift

  # Replace newlines with commas to facilitate the assertion.
  local expected_coverage_no_newlines="$( echo "$expected_coverage" | tr '\n' ',' )"
  local output_file_no_newlines="$( cat "$output_file" | tr '\n' ',' )"

  (echo "$output_file_no_newlines" \
    | grep -F "$expected_coverage_no_newlines") \
    || fail "Expected coverage result
<$expected_coverage>
was not found in actual coverage report:
<$( cat "$output_file" )>"
}

function test_android_local_test() {
  create_new_workspace
  setup_android_sdk_support
  setup_android_local_test_env

  bazel clean
  bazel test --test_output=all \
    //javatests/com/bin:test &>$TEST_log || fail "Tests for //javatests/com/bin:test failed"
}

function test_android_local_test_with_coverage() {
  create_new_workspace
  setup_android_sdk_support
  setup_android_local_test_env

  bazel clean
  bazel coverage --test_output=all \
    //javatests/com/bin:test &>$TEST_log || fail "Test with coverage for //javatests/com/bin:test failed"

  local coverage_file_path="$( get_coverage_file_path_from_test_log )"
  local expected_result="SF:java/com/bin/Collatz.java
FN:3,com/bin/Collatz::<init> ()V
FN:6,com/bin/Collatz::getCollatzFinal (I)I
FNDA:0,com/bin/Collatz::<init> ()V
FNDA:1,com/bin/Collatz::getCollatzFinal (I)I
FNF:2
FNH:1
BRDA:6,0,0,1
BRDA:6,0,1,1
BRDA:9,0,0,1
BRDA:9,0,1,1
BRF:4
BRH:4
DA:3,0
DA:6,1
DA:7,1
DA:9,1
DA:10,1
DA:12,1
LH:5
LF:6
end_of_record"

  assert_coverage_result "$expected_result" "$coverage_file_path"
}

function test_android_local_test_with_combined_coverage() {
  create_new_workspace
  setup_android_sdk_support
  setup_android_local_test_env

  bazel clean
  bazel coverage --test_output=all --coverage_report_generator=@bazel_tools//tools/test:coverage_report_generator --combined_report=lcov \
    //javatests/com/bin:test &>$TEST_log || fail "Test with coverage for //javatests/com/bin:test failed"

  local coverage_file_path="./bazel-out/_coverage/_coverage_report.dat"
  local expected_result="SF:java/com/bin/Collatz.java
FN:3,com/bin/Collatz::<init> ()V
FN:6,com/bin/Collatz::getCollatzFinal (I)I
FNDA:0,com/bin/Collatz::<init> ()V
FNDA:1,com/bin/Collatz::getCollatzFinal (I)I
FNF:2
FNH:1
BRDA:6,0,0,1
BRDA:6,0,1,1
BRDA:9,0,0,1
BRDA:9,0,1,1
BRF:4
BRH:4
DA:3,0
DA:6,1
DA:7,1
DA:9,1
DA:10,1
DA:12,1
LH:5
LF:6
end_of_record"

  assert_coverage_result "$expected_result" "$coverage_file_path"
}

run_suite "android_local_test integration tests"
