콘솔로 이동

Firebase Test Lab 게임 루프 테스트

게임 개발에는 엔진에 종속될 수도 있는 다양한 UI 프레임워크가 사용되고 게임 내 UI를 자동으로 탐색하는 것은 어렵기 때문에 게임 테스트 자동화는 결코 쉬운 과제가 아닙니다. 이제 게임 앱 테스트를 지원하기 위해 게임 앱을 실행하면서 플레이어의 행동을 시뮬레이션하는 '데모 모드' 사용 지원이 Test Lab에 포함되었습니다. 이 모드는 여러 루프 또는 시나리오를 포함할 수 있으며, 서로 관련된 루프를 함께 실행할 수 있도록 라벨을 사용하여 루프를 논리적으로 구성할 수 있습니다.

이 문서에서 제공하는 가이드라인에 따라 게임 루프를 구현하면 루프를 사용하여 개발 중인 게임을 손쉽게 테스트할 수 있습니다. 이 가이드라인은 단일 테스트 기기, 테스트 기기 팜, Test Lab 중 어떤 것을 사용하든 테스트에는 모두 적용됩니다.

시작하기

Test Lab에서 게임 루프 테스트를 사용하려면 다음을 수행하도록 게임을 수정해야 합니다.

  1. 루프 시작
  2. 루프 실행
  3. (선택사항) 게임 앱 종료

루프 시작

게임 루프 테스트를 시작하면 게임은 특정 인텐트를 통해 트리거되므로 다음 예시 코드와 같이 매니페스트를 수정하여 액티비티에 새 인텐트 필터를 추가해야 합니다.

<activity android:name=".MyActivity">
   <intent-filter>
       <action android:name="com.google.intent.action.TEST_LOOP"/>
       <category android:name="android.intent.category.DEFAULT"/>
       <data android:mimeType="application/javascript"/>
   </intent-filter>
   <intent-filter>
      ... (other intent filters here)
   </intent-filter>
</activity>

루프 실행

액티비티가 시작되면 다음 예시 코드와 같이 액티비티를 실행한 인텐트를 확인합니다.

자바

Intent launchIntent = getIntent();
if(launchIntent.getAction().equals("com.google.intent.action.TEST_LOOP")) {
    int scenario = launchIntent.getIntExtra("scenario", 0);
    // Code to handle your game loop here
}

Kotlin

val launchIntent = intent
if (launchIntent.action == "com.google.intent.action.TEST_LOOP") {
    val scenario = launchIntent.getIntExtra("scenario", 0)
    // Code to handle your game loop here
}

onCreate 메서드에서 이 코드를 실행하는 것이 좋지만 필요에 따라 나중에 실행할 수도 있습니다(예: 게임 엔진을 처음 로드한 후에 실행). 그런 다음 게임 엔진에 따라 원하는 대로 게임 루프를 구현하거나 다중 게임 루프의 설명대로 여러 루프를 구현할 수 있습니다. 예를 들어 게임 루프로 다음을 수행할 수 있습니다.

  • 최종 사용자가 플레이하는 방식과 동일한 방식으로 게임 레벨을 진행합니다. 사용자 입력을 스크립트로 작성하거나, 사용자를 자리 비움 상태로 두거나, 게임에 따라서는 사용자를 AI로 대체할 수 있습니다. 예를 들어 이미 구현된 AI가 존재하는 자동차 경주 게임의 경우 사용자 입력을 AI 운전자로 손쉽게 대체할 수 있습니다.
  • 최고 품질 설정으로 게임을 실행하여 기기에서 해당 설정을 지원하는지 확인합니다.
  • 여러 셰이더를 컴파일하고, 실행하고, 예상대로 출력되는지 확인하는 등 기술 테스트를 실행합니다.

Test Loop Manager

Google은 게임 루프를 통합하고, 로컬 기기에서 실행하고, 품질보증팀이 기기에서 게임 루프를 실행하는 데 도움이 되도록 Test Loop Manager라는 오픈소스 앱을 제공합니다.

Test Loop Manager를 사용하려면 다음 안내를 따르세요.

  1. Test Loop Manager를 다운로드합니다.
  2. 다음 명령어를 사용하여 사용 중인 기기에 Test Loop Manager를 설치합니다.
    adb install testloopmanager.apk
  3. 스마트폰 또는 태블릿에서 'Test Loop Apps'라는 앱을 엽니다. 기기에서 게임 루프가 포함된 앱 목록이 표시됩니다. 여기에 앱이 표시되지 않으면 인텐트 필터가 루프 시작에서 설명하는 필터와 일치하는지 확인하세요.
  4. 테스트할 게임 앱을 선택합니다.
    1. 목록에서 게임 앱 이름을 클릭한 후 앱에서 구현하는 시나리오(즉, 게임 루프)를 선택합니다. 앱에서 여러 루프를 구현하는 경우 다중 게임 루프를 참조하세요.
    2. 테스트 실행을 클릭하고 시나리오가 실행되는 과정을 확인합니다.

게임 앱 종료

테스트가 끝나면 다음을 사용하여 앱을 종료해야 합니다.

자바

yourActivity.finish();

Kotlin

yourActivity.finish()

일반적으로 UI 프레임워크는 게임 루프 테스트를 통해 다음 루프를 시작할 수 있습니다. 앱을 종료하지 않으면 루프를 실행하는 UI 프레임워크에서 테스트 완료 사실을 알 수 없으며 일정한 시간이 지난 후에 앱을 종료할 가능성이 있습니다.

Test Lab에서 게임 루프 테스트 실행

Firebase Console로 이동하고 아직 프로젝트가 없으면 프로젝트를 만듭니다. Test Lab을 일일 사용량 한도 내에서 무료로 사용하려면 Spark 요금제를 사용하세요. 사용 한도 없이 Test Lab을 사용하는 방법은 Firebase 요금제를 참조하세요.

Firebase Console 사용

Firebase Console을 사용하여 게임 루프 테스트를 실행하는 방법은 다음과 같습니다.

  1. Firebase Console의 왼쪽 탐색 패널에 있는 Test Lab을 클릭합니다.
  2. 첫 번째 테스트 실행을 클릭합니다. 프로젝트에서 이전에 테스트를 실행한 적이 있다면 테스트 실행을 클릭합니다.
  3. 테스트 유형으로 게임 루프를 선택하고 계속을 클릭합니다.
  4. 찾아보기를 클릭한 후 앱의 .apk 파일을 찾습니다.
  5. (선택사항) 사용 가능한 시나리오(게임 루프) 중 일부를 선택하려면 다음 중 하나를 수행합니다.

    • 특정 시나리오를 선택하려면 시나리오 필드에 시나리오 번호 목록 또는 범위를 입력합니다.
    • 특정 라벨이 적용된 모든 시나리오를 선택하려면 라벨 필드에 시나리오 라벨을 한 개 이상 입력합니다.
  6. 계속을 클릭합니다.

  7. 앱을 테스트하는 데 사용할 실제 기기를 선택합니다.

  8. 테스트 시작을 클릭합니다.

Firebase Console에 대한 자세한 내용은 Firebase Console에서 Firebase Test Lab 사용을 참조하세요.

gcloud 명령줄 환경 사용

gcloud 명령줄 환경(CLI)에서 게임 루프 테스트를 실행하는 방법은 다음과 같습니다.

  1. Firebase 프로젝트가 없으면 Firebase Console로 이동하고 프로젝트 추가를 클릭하여 프로젝트를 만듭니다.
  2. gcloud CLI가 포함된 Google Cloud SDK를 설치합니다.
  3. Google 계정으로 로그인합니다.

    gcloud auth login

  4. 다음 명령어를 사용하여 Firebase 프로젝트를 설정합니다. 여기서 PROJECT_ID는 1단계에서 만든 Firebase 프로젝트입니다.

    gcloud config set project PROJECT_ID
    
  5. 다음과 같이 첫 번째 테스트를 실행합니다.

    gcloud firebase test android run \
    --type=game-loop --app=<path-to-apk> \
    --device model=herolte,version=23
  6. (선택사항) 사용 가능한 시나리오(게임 루프) 중 일부를 선택하려면 게임 루프 테스트를 실행할 때 다음 중 하나를 수행합니다.

    • 특정 시나리오를 선택하려면 --scenario-numbers 플래그(예: --scenario-numbers=1,3,5)를 사용하여 시나리오 번호 목록을 지정합니다.
    • 특정 라벨이 적용된 모든 시나리오를 선택하려면 --scenario-labels 플래그(예: --scenario-labels=performance,gpu)를 사용하여 시나리오 라벨을 한 개 이상 지정합니다.

Test Lab과 함께 gcloud CLI를 사용하는 방법에 대한 자세한 내용은 gcloud 명령줄에서 Android용 Firebase Test Lab 사용을 참조하세요.

기능 선택사항

이 섹션에서는 단일 테스트 매트릭스에서 서로 관련된 루프를 쉽게 테스트할 수 있도록 출력 파일에 데이터 쓰기, 다중 게임 루프 지원, 관련 루프 라벨 지정과 같은 기능 선택사항을 사용하는 방법에 관해 설명합니다.

출력 데이터 쓰기

게임 루프는 launchIntent.getData() 메서드를 사용하여 제공된 파일에 출력을 작성할 수 있습니다. Test Lab은 테스트 결과 페이지에 이 출력 데이터를 표시할 수 있습니다. 데이터 출력 파일 예시는 게임 루프 테스트 출력 파일 예시를 참조하세요.

Test Lab은 파일 공유에서 설명된 앱 간 파일 공유에 대한 권장사항을 따릅니다. 액티비티의 onCreate() 메서드에서 인텐트를 가져올 때 다음 코드를 사용하여 파일을 확인할 수도 있습니다.

자바

Intent launchIntent = getIntent();
Uri logFile = launchIntent.getData();
if (logFile != null) {
    Log.i(TAG, "Log file " + logFile.getEncodedPath());
    // ...
}

Kotlin

val launchIntent = intent
val logFile = launchIntent.data
logFile?.let {
    Log.i(TAG, "Log file ${it.encodedPath}")
    // ...
}

게임 앱의 C++ 부분에서 이 파일에 쓰려면 다음 예시 코드와 같이 파일 경로가 아닌 파일 설명자를 전달합니다.

자바

Intent launchIntent = getIntent();
Uri logFile = launchIntent.getData();
int fd = -1;
if (logFile != null) {
    Log.i(TAG, "Log file " + logFile.getEncodedPath());
    try {
        fd = getContentResolver()
                .openAssetFileDescriptor(logFile, "w")
                .getParcelFileDescriptor()
                .getFd();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
        fd = -1;
    } catch (NullPointerException e) {
        e.printStackTrace();
        fd = -1;
    }
}

// C++ code invoked here.
// native_function(fd);

Kotlin

val launchIntent = intent
val logFile = launchIntent.data
var fd = -1
logFile?.let {
    Log.i(TAG, "Log file ${it.encodedPath}")
    fd = try {
        contentResolver
                .openAssetFileDescriptor(logFile, "w")!!
                .parcelFileDescriptor
                .fd
    } catch (e: FileNotFoundException) {
        e.printStackTrace()
        -1
    } catch (e: NullPointerException) {
        e.printStackTrace()
        -1
    }
}

// C++ code invoked here.
// native_function(fd);

C++

#include <unistd.h>
JNIEXPORT void JNICALL
Java_my_package_name_MyActivity_native_function(JNIEnv *env, jclass type, jint log_file_descriptor) {
// The file descriptor needs to be duplicated.
int my_file_descriptor = dup(log_file_descriptor);
}

여러 게임 루프

게임 앱에서 여러 게임 루프를 지원할 수도 있습니다. 예를 들어 게임에 여러 레벨이 있는 경우 루프 1개로 모든 레벨을 반복하는 대신 게임 루프를 레벨마다 1개씩 시작할 수 있습니다.

이렇게 하면 레벨 32에서 앱이 비정상 종료된 경우 게임 루프를 바로 시작하여 비정상 종료를 재현하고 버그가 수정되었는지 테스트할 수 있습니다.

예를 들어 앱에 게임 루프가 5개 있다면 앱의 매니페스트 파일에서 <application> 요소 안에 한 줄만 추가하면 됩니다.

<meta-data
  android:name="com.google.test.loops"
  android:value="5" />

그런 다음 Test Loop Manager를 시작할 때 선택 화면에서 시작할 루프를 선택할 수 있습니다. 실행할 루프를 여러 개 선택하면 각 루프가 순서대로 하나씩 시작됩니다. Test Lab을 사용하면 실행할 루프를 선택할 수 있습니다.

시작 인텐트는 타겟 루프를 정수 매개변수로 포함하며, 이 매개변수의 범위는 1부터 지원되는 루프의 최대 개수까지입니다.

자바에서 다음 예시 코드와 같이 인텐트 부가 정보를 읽을 수 있습니다.

자바

Intent launchIntent = getIntent();
int scenario = launchIntent.getIntExtra("scenario", 0);

Kotlin

val launchIntent = intent
val scenario = launchIntent.getIntExtra("scenario", 0)

그런 다음 이 부가 정보를 네이티브 C++ 코드에 전달하여 결과 int 값에 따라 루프 동작을 변경할 수 있습니다.

게임 루프 라벨 지정

게임 루프에 하나 이상의 시나리오 라벨을 지정하면 QA 팀에서 서로 관련된 게임 루프 세트(예: '모든 호환성 게임 루프')를 손쉽게 시작할 수 있습니다. 이렇게 하려면 앱의 매니페스트 파일에 다음 예시와 비슷한 메타데이터 줄을 추가해야 합니다.

<meta-data
  android:name="com.google.test.loops.LABEL_NAME"
  android:value="1,3-5" />

라벨을 직접 만들 수 있으며 다음과 같이 사전 정의된 4가지 라벨도 제공됩니다.

  • com.google.test.loops.player_experience: 실제 사용자의 게임 플레이 환경을 재현하는 데 사용되는 루프입니다. 이러한 루프로 테스트하는 목적은 실제 사용자가 게임을 플레이하는 중에 발생할 수 있는 문제를 찾기 위함입니다.
  • com.google.test.loops.gpu_compatibility: GPU 관련 문제를 테스트하는 데 사용되는 루프입니다. 이러한 루프로 테스트하는 목적은 프로덕션 환경에서 제대로 실행되지 않을 수 있는 GPU 코드를 실행하여 하드웨어 및 드라이버 관련 문제를 확인하기 위함입니다.
  • com.google.test.loops.compatibility: I/O 문제 및 OpenSSL 문제를 포함하여 다양한 호환성 문제를 테스트하는 데 사용되는 루프입니다.
  • com.google.test.loops.performance: 기기 성능을 테스트하는 데 사용되는 루프입니다. 예를 들어 가장 복잡한 그래픽 설정으로 게임을 실행하여 새 기기의 동작을 확인할 수 있습니다.

앱 라이선스 지원

Test Lab은 Google Play에서 제공하는 앱 라이선스 서비스를 사용하는 앱을 지원합니다. Test Lab으로 앱을 테스트할 때 라이선스를 정상적으로 확인하려면 Play 스토어의 프로덕션 채널에 앱을 게시해야 합니다. Test Lab으로 알파 또는 베타 채널의 앱을 테스트하려면 Test Lab에 앱을 업로드하기 전에 라이선스 확인을 삭제하세요.

알려진 문제

Test Lab의 게임 루프 테스트에는 다음과 같은 알려진 문제가 있습니다.

  • Khronos Vulkan API에 대한 지원이 제한됩니다. Test Lab에서 Vulkan API를 지원하는 기기는 Samsung Galaxy S7(API 수준 24), Google Pixel(API 수준 25) 밖에 없습니다.
  • 일부 장애는 역추적되지 않습니다. 예를 들어 일부 출시 빌드에서는 prctl(PR_SET_DUMPABLE, 0)을 사용하여 debuggerd 프로세스 출력을 억제할 수 있습니다. 자세한 내용은 debuggerd를 참조하세요.
  • 파일 권한 오류로 인해 현재 API 수준 19는 지원되지 않습니다.

게임 루프 테스트 출력 파일 예시

다음 예시와 같은 형식의 출력 데이터 파일을 사용하여 Firebase Console에 게임 루프 테스트 결과를 표시할 수 있습니다. /.../로 표시된 영역에는 필요한 모든 커스텀 필드가 포함될 수 있지만 이 파일에서 사용된 다른 필드의 이름과 충돌하지 않아야 합니다.

{
  "name": "test name",
  "start_timestamp": 0, // Timestamp of the test start (in us).
                           Can be absolute or relative
  "driver_info": "...",
  "frame_stats": [
    {
      "timestamp": 1200000, // Timestamp at which this section was written
                               It contains value regarding the period
                               start_timestamp(0) -> this timestamp (1200000 us)
      "avg_frame_time": 15320, // Average time to render a frame in ns
      "nb_swap": 52, // Number of frame rendered
      "threads": [
        {
          "name": "physics",
          "Avg_time": 8030 // Average time spent in this thread per frame in us
        },
        {
          "name": "AI",
          "Avg_time": 2030 // Average time spent in this thread per frame in us
        }
      ],
      /.../ // Any custom field you want (vertices display on the screen, nb units …)
    },
    {
      // Next frame data here, same format as above
    }
  ],
  "loading_stats": [
    {
      "name": "assets_level_1",
      "total_time": 7850, // in us
      /.../
    },
    {
      "name": "victory_screen",
      "total_time": 554, // in us
      /.../
    }

  ],
  /.../, // You can add custom fields here
}