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`.
|
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:
|
Install:
|
||||||
|
|
||||||
```bash
|
```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