Test Android packaging configuration
This commit is contained in:
@@ -45,6 +45,16 @@ go build ./...
|
||||
|
||||
KeePassGO uses Gio, so Android packaging is done with `gogio`.
|
||||
|
||||
The repo now has automated tests for the packaging contract:
|
||||
- default APK build arguments
|
||||
- required Android SDK / NDK / JDK layout checks
|
||||
|
||||
Those are covered by normal test runs:
|
||||
|
||||
```bash
|
||||
go test ./...
|
||||
```
|
||||
|
||||
Install:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
package buildapk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultSDKRoot = "/opt/android-sdk"
|
||||
DefaultNDKRoot = "/opt/android-ndk"
|
||||
DefaultJavaHome = "/usr/lib/jvm/java-25-openjdk"
|
||||
DefaultAppID = "org.julianfamily.keepassgo"
|
||||
DefaultAPKOut = "build/keepassgo.apk"
|
||||
DefaultVersion = "0.1.0.1"
|
||||
DefaultMinSDK = "28"
|
||||
DefaultTargetSDK = "35"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
SDKRoot string
|
||||
NDKRoot string
|
||||
JavaHome string
|
||||
AppID string
|
||||
APKOut string
|
||||
Version string
|
||||
MinSDK string
|
||||
TargetSDK string
|
||||
}
|
||||
|
||||
func DefaultConfig() Config {
|
||||
return Config{
|
||||
SDKRoot: DefaultSDKRoot,
|
||||
NDKRoot: DefaultNDKRoot,
|
||||
JavaHome: DefaultJavaHome,
|
||||
AppID: DefaultAppID,
|
||||
APKOut: DefaultAPKOut,
|
||||
Version: DefaultVersion,
|
||||
MinSDK: DefaultMinSDK,
|
||||
TargetSDK: DefaultTargetSDK,
|
||||
}
|
||||
}
|
||||
|
||||
func (c Config) GogioArgs() []string {
|
||||
return []string{
|
||||
"-target", "android",
|
||||
"-buildmode", "exe",
|
||||
"-appid", c.AppID,
|
||||
"-o", c.APKOut,
|
||||
"-version", c.Version,
|
||||
"-minsdk", c.MinSDK,
|
||||
"-targetsdk", c.TargetSDK,
|
||||
".",
|
||||
}
|
||||
}
|
||||
|
||||
func (c Config) Validate() error {
|
||||
if !isExecutable(filepath.Join(c.JavaHome, "bin", "java")) {
|
||||
return fmt.Errorf("JAVA_HOME must point to a JDK 17+ install")
|
||||
}
|
||||
if !isDir(c.SDKRoot) {
|
||||
return fmt.Errorf("ANDROID_SDK_ROOT must point to an Android SDK install")
|
||||
}
|
||||
if !isDir(c.NDKRoot) {
|
||||
return fmt.Errorf("ANDROID_NDK_ROOT must point to an Android NDK install")
|
||||
}
|
||||
if !isExecutable(filepath.Join(c.SDKRoot, "cmdline-tools", "latest", "bin", "sdkmanager")) {
|
||||
return fmt.Errorf("Android SDK cmdline-tools are missing")
|
||||
}
|
||||
if !isDir(filepath.Join(c.SDKRoot, "platforms", "android-"+c.TargetSDK)) {
|
||||
return fmt.Errorf("Android platform android-%s is missing", c.TargetSDK)
|
||||
}
|
||||
if !isDir(filepath.Join(c.SDKRoot, "build-tools")) {
|
||||
return fmt.Errorf("Android build-tools are missing")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isDir(path string) bool {
|
||||
info, err := os.Stat(path)
|
||||
return err == nil && info.IsDir()
|
||||
}
|
||||
|
||||
func isExecutable(path string) bool {
|
||||
info, err := os.Stat(path)
|
||||
if err != nil || info.IsDir() {
|
||||
return false
|
||||
}
|
||||
return info.Mode()&0o111 != 0
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package buildapk
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDefaultConfigGogioArgs(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg := DefaultConfig()
|
||||
want := []string{
|
||||
"-target", "android",
|
||||
"-buildmode", "exe",
|
||||
"-appid", DefaultAppID,
|
||||
"-o", DefaultAPKOut,
|
||||
"-version", DefaultVersion,
|
||||
"-minsdk", DefaultMinSDK,
|
||||
"-targetsdk", DefaultTargetSDK,
|
||||
".",
|
||||
}
|
||||
|
||||
if got := cfg.GogioArgs(); !slices.Equal(got, want) {
|
||||
t.Fatalf("GogioArgs() = %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateAcceptsCompleteAndroidToolchainLayout(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
root := t.TempDir()
|
||||
sdkRoot := filepath.Join(root, "sdk")
|
||||
ndkRoot := filepath.Join(root, "ndk")
|
||||
javaHome := filepath.Join(root, "java")
|
||||
|
||||
mkdirAll(t, filepath.Join(javaHome, "bin"))
|
||||
mkdirAll(t, filepath.Join(sdkRoot, "cmdline-tools", "latest", "bin"))
|
||||
mkdirAll(t, filepath.Join(sdkRoot, "platforms", "android-"+DefaultTargetSDK))
|
||||
mkdirAll(t, filepath.Join(sdkRoot, "build-tools"))
|
||||
mkdirAll(t, ndkRoot)
|
||||
|
||||
makeExecutable(t, filepath.Join(javaHome, "bin", "java"))
|
||||
makeExecutable(t, filepath.Join(sdkRoot, "cmdline-tools", "latest", "bin", "sdkmanager"))
|
||||
|
||||
cfg := Config{
|
||||
SDKRoot: sdkRoot,
|
||||
NDKRoot: ndkRoot,
|
||||
JavaHome: javaHome,
|
||||
AppID: DefaultAppID,
|
||||
APKOut: DefaultAPKOut,
|
||||
Version: DefaultVersion,
|
||||
MinSDK: DefaultMinSDK,
|
||||
TargetSDK: DefaultTargetSDK,
|
||||
}
|
||||
|
||||
if err := cfg.Validate(); err != nil {
|
||||
t.Fatalf("Validate() error = %v, want nil", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateRejectsMissingPrerequisites(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
root := t.TempDir()
|
||||
cfg := Config{
|
||||
SDKRoot: filepath.Join(root, "missing-sdk"),
|
||||
NDKRoot: filepath.Join(root, "missing-ndk"),
|
||||
JavaHome: filepath.Join(root, "missing-java"),
|
||||
AppID: DefaultAppID,
|
||||
APKOut: DefaultAPKOut,
|
||||
Version: DefaultVersion,
|
||||
MinSDK: DefaultMinSDK,
|
||||
TargetSDK: DefaultTargetSDK,
|
||||
}
|
||||
|
||||
if err := cfg.Validate(); err == nil {
|
||||
t.Fatal("Validate() error = nil, want missing prerequisite error")
|
||||
}
|
||||
}
|
||||
|
||||
func mkdirAll(t *testing.T, path string) {
|
||||
t.Helper()
|
||||
if err := os.MkdirAll(path, 0o755); err != nil {
|
||||
t.Fatalf("MkdirAll(%q) error = %v", path, err)
|
||||
}
|
||||
}
|
||||
|
||||
func makeExecutable(t *testing.T, path string) {
|
||||
t.Helper()
|
||||
if err := os.WriteFile(path, []byte("#!/bin/sh\n"), 0o755); err != nil {
|
||||
t.Fatalf("WriteFile(%q) error = %v", path, err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user