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>

루프 실행

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

자바
Android

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
Android

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. 테스트 실행을 클릭하고 시나리오가 실행되는 과정을 확인합니다.

게임 앱 종료

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

자바
Android

yourActivity.finish();

Kotlin
Android

yourActivity.finish()

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

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

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

Firebase 콘솔 사용

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

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

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

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

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

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

gcloud 명령줄 환경 사용

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

  1. Firebase 프로젝트가 없으면 Firebase 콘솔로 이동한 후 프로젝트 추가를 클릭하여 프로젝트를 만듭니다.
  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() 메소드에서 인텐트를 가져올 때 다음 코드를 사용하여 해당 파일을 확인할 수도 있습니다.

자바
Android

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

Kotlin
Android

val launchIntent = intent
val logFile = launchIntent.data
if (logFile != null) {
    Log.i(TAG, "Log file " + logFile.encodedPath!!)
    // ...
}

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

자바
Android

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
Android

val launchIntent = intent
val logFile = launchIntent.data
var fd = -1
if (logFile != null) {
    Log.i(TAG, "Log file " + logFile.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);
}

다중 게임 루프

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

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

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

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

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

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

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

자바
Android

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

Kotlin
Android

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 콘솔에 게임 루프 테스트 결과를 표시할 수 있습니다. /.../로 표시된 영역에는 필요한 맞춤 필드를 모두 포함할 수 있지만, 이 파일에 사용된 다른 필드의 이름과 충돌하지 않아야 합니다.

{
  "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
}

다음에 대한 의견 보내기...

도움이 필요하시나요? 지원 페이지를 방문하세요.