<commit_before>// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';
import 'dart:io';

import 'package:path/path.dart' as path;
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/utils.dart';

final List<String> flutterAssets = <String>[
  'assets/flutter_assets/AssetManifest.json',
  'assets/flutter_assets/NOTICES',
  'assets/flutter_assets/fonts/MaterialIcons-Regular.otf',
  'assets/flutter_assets/packages/cupertino_icons/assets/CupertinoIcons.ttf',
];

final List<String> debugAssets = <String>[
  'assets/flutter_assets/isolate_snapshot_data',
  'assets/flutter_assets/kernel_blob.bin',
  'assets/flutter_assets/vm_snapshot_data',
];

final List<String> baseApkFiles = <String> [
  'classes.dex',
  'AndroidManifest.xml',
];

/// Runs the given [testFunction] on a freshly generated Flutter project.
Future<void> runProjectTest(Future<void> testFunction(FlutterProject project)) async {
  final Directory tempDir = Directory.systemTemp.createTempSync('flutter_devicelab_gradle_plugin_test.');
  final FlutterProject project = await FlutterProject.create(tempDir, 'hello');

  try {
    await testFunction(project);
  } finally {
    rmTree(tempDir);
  }
}

/// Runs the given [testFunction] on a freshly generated Flutter plugin project.
Future<void> runPluginProjectTest(Future<void> testFunction(FlutterPluginProject pluginProject)) async {
  final Directory tempDir = Directory.systemTemp.createTempSync('flutter_devicelab_gradle_plugin_test.');
  final FlutterPluginProject pluginProject = await FlutterPluginProject.create(tempDir, 'aaa');

  try {
    await testFunction(pluginProject);
  } finally {
    rmTree(tempDir);
  }
}

/// Runs the given [testFunction] on a freshly generated Flutter module project.
Future<void> runModuleProjectTest(Future<void> testFunction(FlutterModuleProject moduleProject)) async {
  final Directory tempDir = Directory.systemTemp.createTempSync('flutter_devicelab_gradle_module_test.');
  final FlutterModuleProject moduleProject = await FlutterModuleProject.create(tempDir, 'hello_module');

  try {
    await testFunction(moduleProject);
  } finally {
    rmTree(tempDir);
  }
}

/// Returns the list of files inside an Android Package Kit.
Future<Iterable<String>> getFilesInApk(String apk) async {
  if (!File(apk).existsSync()) {
    throw TaskResult.failure(
        'Gradle did not produce an output artifact file at: $apk');
  }
  final String files = await _evalApkAnalyzer(
    <String>[
      'files',
      'list',
      apk,
    ]
  );
  return files.split('\n').map((String file) => file.substring(1).trim());
}
/// Returns the list of files inside an Android App Bundle.
Future<Iterable<String>> getFilesInAppBundle(String bundle) {
  return getFilesInApk(bundle);
}

/// Returns the list of files inside an Android Archive.
Future<Iterable<String>> getFilesInAar(String aar) {
  return getFilesInApk(aar);
}

TaskResult failure(String message, ProcessResult result) {
  print('Unexpected process result:');
  print('Exit code: ${result.exitCode}');
  print('Std out  :\n${result.stdout}');
  print('Std err  :\n${result.stderr}');
  return TaskResult.failure(message);
}

bool hasMultipleOccurrences(String text, Pattern pattern) {
  return text.indexOf(pattern) != text.lastIndexOf(pattern);
}

/// The Android home directory.
String get _androidHome {
  final String androidHome = Platform.environment['ANDROID_HOME'] ??
      Platform.environment['ANDROID_SDK_ROOT'];
  if (androidHome == null || androidHome.isEmpty) {
    throw Exception('Environment variable `ANDROID_SDK_ROOT` is not set.');
  }
  return androidHome;
}

/// Executes an APK analyzer subcommand.
Future<String> _evalApkAnalyzer(
  List<String> args, {
  bool printStdout = true,
  String workingDirectory,
}) async {
  final String javaHome = await findJavaHome();
  final String javaBinary = path.join(javaHome, 'bin', 'java');
  assert(canRun(javaBinary));

  final String androidTools = path.join(_androidHome, 'tools');
  final String libs = path.join(androidTools, 'lib');
  assert(Directory(libs).existsSync());

  final String classSeparator =  Platform.isWindows ? ';' : ':';
  return eval(
    javaBinary,
    <String>[
      '-Dcom.android.sdklib.toolsdir=$androidTools',
      '-classpath',
      '.$classSeparator$libs${Platform.pathSeparator}*',
      'com.android.tools.apk.analyzer.ApkAnalyzerCli',
      ...args,
    ],
    printStdout: printStdout,
    workingDirectory: workingDirectory,
  );
}

/// Utility class to analyze the content inside an APK using the APK analyzer.
class ApkExtractor {
  ApkExtractor(this.apkFile);

  /// The APK.
  final File apkFile;

  bool _extracted = false;

  Set<String> _classes = const <String>{};

  Future<void> _extractDex() async {
    if (_extracted) {
      return;
    }
    final String packages = await _evalApkAnalyzer(
      <String>[
        'dex',
        'packages',
        apkFile.path,
      ],
      printStdout: false,
    );
    _classes = Set<String>.from(
      packages
        .split('\n')
        .where((String line) => line.startsWith('C'))
        .map<String>((String line) => line.split('\t').last),
    );
    assert(_classes.isNotEmpty);
    _extracted = true;
  }

  // Removes any temporary directory.
  void dispose() {
    if (!_extracted) {
      return;
    }
    _classes = const <String>{};
    _extracted = true;
  }

  /// Returns true if the APK contains a given class.
  Future<bool> containsClass(String className) async {
    await _extractDex();
    return _classes.contains(className);<commit_msg>Print dex when analyzing the APK (#63538)<commit_after>// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';
import 'dart:io';

import 'package:path/path.dart' as path;
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/utils.dart';

final List<String> flutterAssets = <String>[
  'assets/flutter_assets/AssetManifest.json',
  'assets/flutter_assets/NOTICES',
  'assets/flutter_assets/fonts/MaterialIcons-Regular.otf',
  'assets/flutter_assets/packages/cupertino_icons/assets/CupertinoIcons.ttf',
];

final List<String> debugAssets = <String>[
  'assets/flutter_assets/isolate_snapshot_data',
  'assets/flutter_assets/kernel_blob.bin',
  'assets/flutter_assets/vm_snapshot_data',
];

final List<String> baseApkFiles = <String> [
  'classes.dex',
  'AndroidManifest.xml',
];

/// Runs the given [testFunction] on a freshly generated Flutter project.
Future<void> runProjectTest(Future<void> testFunction(FlutterProject project)) async {
  final Directory tempDir = Directory.systemTemp.createTempSync('flutter_devicelab_gradle_plugin_test.');
  final FlutterProject project = await FlutterProject.create(tempDir, 'hello');

  try {
    await testFunction(project);
  } finally {
    rmTree(tempDir);
  }
}

/// Runs the given [testFunction] on a freshly generated Flutter plugin project.
Future<void> runPluginProjectTest(Future<void> testFunction(FlutterPluginProject pluginProject)) async {
  final Directory tempDir = Directory.systemTemp.createTempSync('flutter_devicelab_gradle_plugin_test.');
  final FlutterPluginProject pluginProject = await FlutterPluginProject.create(tempDir, 'aaa');

  try {
    await testFunction(pluginProject);
  } finally {
    rmTree(tempDir);
  }
}

/// Runs the given [testFunction] on a freshly generated Flutter module project.
Future<void> runModuleProjectTest(Future<void> testFunction(FlutterModuleProject moduleProject)) async {
  final Directory tempDir = Directory.systemTemp.createTempSync('flutter_devicelab_gradle_module_test.');
  final FlutterModuleProject moduleProject = await FlutterModuleProject.create(tempDir, 'hello_module');

  try {
    await testFunction(moduleProject);
  } finally {
    rmTree(tempDir);
  }
}

/// Returns the list of files inside an Android Package Kit.
Future<Iterable<String>> getFilesInApk(String apk) async {
  if (!File(apk).existsSync()) {
    throw TaskResult.failure(
        'Gradle did not produce an output artifact file at: $apk');
  }
  final String files = await _evalApkAnalyzer(
    <String>[
      'files',
      'list',
      apk,
    ]
  );
  return files.split('\n').map((String file) => file.substring(1).trim());
}
/// Returns the list of files inside an Android App Bundle.
Future<Iterable<String>> getFilesInAppBundle(String bundle) {
  return getFilesInApk(bundle);
}

/// Returns the list of files inside an Android Archive.
Future<Iterable<String>> getFilesInAar(String aar) {
  return getFilesInApk(aar);
}

TaskResult failure(String message, ProcessResult result) {
  print('Unexpected process result:');
  print('Exit code: ${result.exitCode}');
  print('Std out  :\n${result.stdout}');
  print('Std err  :\n${result.stderr}');
  return TaskResult.failure(message);
}

bool hasMultipleOccurrences(String text, Pattern pattern) {
  return text.indexOf(pattern) != text.lastIndexOf(pattern);
}

/// The Android home directory.
String get _androidHome {
  final String androidHome = Platform.environment['ANDROID_HOME'] ??
      Platform.environment['ANDROID_SDK_ROOT'];
  if (androidHome == null || androidHome.isEmpty) {
    throw Exception('Environment variable `ANDROID_SDK_ROOT` is not set.');
  }
  return androidHome;
}

/// Executes an APK analyzer subcommand.
Future<String> _evalApkAnalyzer(
  List<String> args, {
  bool printStdout = true,
  String workingDirectory,
}) async {
  final String javaHome = await findJavaHome();
  final String javaBinary = path.join(javaHome, 'bin', 'java');
  assert(canRun(javaBinary));

  final String androidTools = path.join(_androidHome, 'tools');
  final String libs = path.join(androidTools, 'lib');
  assert(Directory(libs).existsSync());

  final String classSeparator =  Platform.isWindows ? ';' : ':';
  return eval(
    javaBinary,
    <String>[
      '-Dcom.android.sdklib.toolsdir=$androidTools',
      '-classpath',
      '.$classSeparator$libs${Platform.pathSeparator}*',
      'com.android.tools.apk.analyzer.ApkAnalyzerCli',
      ...args,
    ],
    printStdout: printStdout,
    workingDirectory: workingDirectory,
  );
}

/// Utility class to analyze the content inside an APK using the APK analyzer.
class ApkExtractor {
  ApkExtractor(this.apkFile);

  /// The APK.
  final File apkFile;

  bool _extracted = false;

  Set<String> _classes = const <String>{};

  Future<void> _extractDex() async {
    if (_extracted) {
      return;
    }
    final String packages = await _evalApkAnalyzer(
      <String>[
        'dex',
        'packages',
        apkFile.path,
      ],
    );
    _classes = Set<String>.from(
      packages
        .split('\n')
        .where((String line) => line.startsWith('C'))
        .map<String>((String line) => line.split('\t').last),
    );
    assert(_classes.isNotEmpty);
    _extracted = true;
  }

  // Removes any temporary directory.
  void dispose() {
    if (!_extracted) {
      return;
    }
    _classes = const <String>{};
    _extracted = true;
  }

  /// Returns true if the APK contains a given class.
  Future<bool> containsClass(String className) async {
    await _extractDex();
    return _classes.contains(className);
{'fraction_non_alphanumeric': 0.12251135172895564, 'fraction_numerical': 0.001309814879497031, 'mean_word_length': 4.203543843707406, 'pattern_counts': {'":': 0, '<': 63, '<?xml version=': 0, '>': 69, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5617388', 'n_tokens_mistral': 3587, 'n_tokens_neox': 3501, 'n_words': 841}