// Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

// This test ensures that the AOT compiler can generate stripped versions of
// ELF and assembly output. This test is currently very weak, in that it just
// checks that the stripped version is strictly smaller than the unstripped one.

// OtherResources=use_save_debugging_info_flag_program.dart

import "dart:io";

import 'package:expect/expect.dart';
import 'package:path/path.dart' as path;

import 'use_flag_test_helper.dart';

main(List<String> args) async {
  if (!isAOTRuntime) {
    return; // Running in JIT: AOT binaries not available.
  }

  if (Platform.isAndroid) {
    return; // SDK tree not available on the test device.
  }

  // These are the tools we need to be available to run on a given platform:
  if (!File(platformDill).existsSync()) {
    throw "Cannot run test as $platformDill does not exist";
  }
  if (!await testExecutable(genSnapshot)) {
    throw "Cannot run test as $genSnapshot not available";
  }

  await withTempDir('strip-flag-test', (String tempDir) async {
    final cwDir = path.dirname(Platform.script.toFilePath());
    // We can just reuse the program for the use_save_debugging_info_flag test.
    final script = path.join(
      cwDir,
      'use_save_debugging_info_flag_program.dart',
    );
    final scriptDill = path.join(tempDir, 'flag_program.dill');

    // Compile script to Kernel IR.
    await run(genKernel, <String>[
      '--aot',
      '--platform=$platformDill',
      '-o',
      scriptDill,
      script,
    ]);

    // Run the AOT compiler to generate stripped and unstripped ELF snapshots.
    final unstrippedSnapshot = path.join(tempDir, 'whole.so');
    await run(genSnapshot, <String>[
      if (Platform.isMacOS) ...[
        '--snapshot-kind=app-aot-macho-dylib',
        '--macho=$unstrippedSnapshot',
      ] else ...[
        '--snapshot-kind=app-aot-elf',
        '--elf=$unstrippedSnapshot',
      ],
      scriptDill,
    ]);

    final strippedSnapshot = path.join(tempDir, 'stripped.so');
    await run(genSnapshot, <String>[
      if (Platform.isMacOS) ...[
        '--snapshot-kind=app-aot-macho-dylib',
        '--macho=$strippedSnapshot',
      ] else ...[
        '--snapshot-kind=app-aot-elf',
        '--elf=$strippedSnapshot',
      ],
      '--strip',
      scriptDill,
    ]);

    compareStrippedAndUnstripped(
      stripped: strippedSnapshot,
      unstripped: unstrippedSnapshot,
    );

    if (Platform.isWindows) {
      return; // No assembly generation on Windows.
    }

    final unstrippedCode = path.join(tempDir, 'whole.S');
    await run(genSnapshot, <String>[
      '--snapshot-kind=app-aot-assembly',
      '--assembly=$unstrippedCode',
      scriptDill,
    ]);

    final strippedCode = path.join(tempDir, 'stripped.S');
    await run(genSnapshot, <String>[
      '--snapshot-kind=app-aot-assembly',
      '--assembly=$strippedCode',
      '--strip',
      scriptDill,
    ]);

    compareStrippedAndUnstripped(
      stripped: strippedCode,
      unstripped: unstrippedCode,
    );
  });
}

void compareStrippedAndUnstripped({
  required String stripped,
  required String unstripped,
}) {
  final strippedSize = File(stripped).lengthSync();
  final unstrippedSize = File(unstripped).lengthSync();
  print("File size for stripped file $stripped: $strippedSize");
  print("File size for stripped file $unstripped: $unstrippedSize");
  Expect.isTrue(strippedSize < unstrippedSize);
}
