mirror of
https://github.com/httprunner/httprunner.git
synced 2026-07-12 16:01:27 +08:00
feat: add LianLianKan (连连看) game bot implementation
- Add complete LianLianKan game bot with AI-powered interface analysis - Implement static analysis solver with 0-2 turn connection algorithms - Support cross-platform game automation (Android, iOS, HarmonyOS, Browser) - Include comprehensive test suite with real game data - Add command line tool and documentation - Integrate with HttpRunner @/uixt module and Doubao AI models
This commit is contained in:
139
examples/game/llk/main_test.go
Normal file
139
examples/game/llk/main_test.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package llk
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/httprunner/httprunner/v5/internal/builtin"
|
||||
"github.com/httprunner/httprunner/v5/uixt/ai"
|
||||
"github.com/httprunner/httprunner/v5/uixt/option"
|
||||
"github.com/httprunner/httprunner/v5/uixt/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// hasRequiredEnvVars checks if the required environment variables are set for testing
|
||||
func hasRequiredEnvVars() bool {
|
||||
// Check for OpenAI environment variables
|
||||
if os.Getenv("OPENAI_BASE_URL") != "" && os.Getenv("OPENAI_API_KEY") != "" {
|
||||
return true
|
||||
}
|
||||
// Check for GPT-4O specific environment variables
|
||||
if os.Getenv("OPENAI_GPT_4O_BASE_URL") != "" && os.Getenv("OPENAI_GPT_4O_API_KEY") != "" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// loadTestImage loads the test image from testdata
|
||||
func loadTestImage(t *testing.T) (string, types.Size) {
|
||||
screenshot, size, err := builtin.LoadImage("../../../uixt/ai/testdata/llk_1.png")
|
||||
require.NoError(t, err)
|
||||
return screenshot, size
|
||||
}
|
||||
|
||||
// createAIQueryer creates a AI queryer with AI analysis capability
|
||||
func createAIQueryer(t *testing.T) *ai.Querier {
|
||||
ctx := context.Background()
|
||||
modelConfig, err := ai.GetModelConfig(option.DOUBAO_SEED_1_6_250615)
|
||||
require.NoError(t, err)
|
||||
querier, err := ai.NewQuerier(ctx, modelConfig)
|
||||
require.NoError(t, err)
|
||||
return querier
|
||||
}
|
||||
|
||||
// TestLLKGameBot_AnalyzeGameInterface comprehensive test for game interface analysis
|
||||
func TestLLKGameBot_AnalyzeGameInterface(t *testing.T) {
|
||||
if !hasRequiredEnvVars() {
|
||||
t.Skip("Skipping test: required environment variables not set")
|
||||
}
|
||||
|
||||
t.Run("AnalyzeWithTestImage", func(t *testing.T) {
|
||||
// Create test bot and load test image
|
||||
querier := createAIQueryer(t)
|
||||
screenshot, size := loadTestImage(t)
|
||||
t.Logf("Loaded test image with size: %dx%d", size.Width, size.Height)
|
||||
|
||||
// Prepare query options for AI analysis
|
||||
opts := &ai.QueryOptions{
|
||||
Query: `Analyze this LianLianKan (连连看) game interface and provide CONCISE structured information:
|
||||
|
||||
1. Game type: "LianLianKan"
|
||||
2. Grid dimensions (rows x columns) - CRITICAL: rows are horizontal lines, columns are vertical lines
|
||||
3. Game elements with positions and types - LIMIT to essential info only
|
||||
4. Bounding boxes - use approximate coordinates
|
||||
|
||||
REQUIREMENTS:
|
||||
- Count ROWS as horizontal lines (top to bottom)
|
||||
- Count COLUMNS as vertical lines (left to right)
|
||||
- Position: row=0 is top, col=0 is left
|
||||
- Keep response SHORT to avoid truncation
|
||||
- Use simple element type names (max 10 chars)
|
||||
- Omit detailed descriptions
|
||||
|
||||
Return JSON with: content, dimensions{rows,cols}, elements[{type,position{row,col},boundBox{x,y,width,height}}], statistics{totalElements,uniqueTypes}.`,
|
||||
Screenshot: screenshot,
|
||||
Size: size,
|
||||
OutputSchema: GameElement{},
|
||||
}
|
||||
|
||||
// Query AI model and convert result
|
||||
result, err := querier.Query(context.Background(), opts)
|
||||
require.NoError(t, err, "Failed to query AI model")
|
||||
|
||||
// Convert result using enhanced compatibility logic
|
||||
gameElement, err := convertToGameElement(result)
|
||||
require.NoError(t, err, "Failed to convert query result to GameElement")
|
||||
require.NotNil(t, gameElement, "GameElement should not be nil")
|
||||
|
||||
// Log analysis results
|
||||
t.Logf("\n=== Game Interface Analysis Results ===")
|
||||
t.Logf("Dimensions: %dx%d", gameElement.Dimensions.Rows, gameElement.Dimensions.Cols)
|
||||
|
||||
// Basic validations
|
||||
assert.NotEmpty(t, gameElement.Content, "Content should not be empty")
|
||||
assert.Greater(t, gameElement.Dimensions.Rows, 0, "Rows should be greater than 0")
|
||||
assert.Greater(t, gameElement.Dimensions.Cols, 0, "Cols should be greater than 0")
|
||||
assert.Greater(t, len(gameElement.Elements), 0, "Should have detected elements")
|
||||
|
||||
// Test solver integration
|
||||
t.Logf("\n=== Solver Integration Test ===")
|
||||
solver := NewLLKSolver(gameElement)
|
||||
require.NotNil(t, solver, "Solver should be created successfully")
|
||||
|
||||
pairs := solver.FindAllPairs()
|
||||
t.Logf("Solver found %d valid matching pairs", len(pairs))
|
||||
|
||||
// Log sample element details
|
||||
t.Logf("\n=== Sample Elements ===")
|
||||
for i, element := range gameElement.Elements {
|
||||
if i < 5 { // Show first 5 elements
|
||||
t.Logf("Element %d: %s at grid(%d,%d)",
|
||||
i+1, element.Type,
|
||||
element.Position.Row, element.Position.Col)
|
||||
}
|
||||
}
|
||||
if len(gameElement.Elements) > 5 {
|
||||
t.Logf("... and %d more elements", len(gameElement.Elements)-5)
|
||||
}
|
||||
|
||||
t.Logf("\n=== Analysis Test Completed Successfully ===")
|
||||
})
|
||||
}
|
||||
|
||||
// TestLLKGameBot_RealDevice test with real Android device
|
||||
func TestLLKGameBot_RealDevice(t *testing.T) {
|
||||
t.Run("CreateAndAnalyze", func(t *testing.T) {
|
||||
// Create game bot with real device
|
||||
bot, err := NewLLKGameBot("android", "")
|
||||
require.NoError(t, err, "Failed to create LLKGameBot")
|
||||
defer bot.Close()
|
||||
|
||||
// err = bot.EnterGame(context.Background())
|
||||
// require.NoError(t, err, "Failed to enter game")
|
||||
|
||||
err = bot.Play()
|
||||
require.NoError(t, err, "Failed to play game")
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user