mirror of
https://github.com/blacktop/ipsw.git
synced 2026-06-07 12:27:36 +00:00
Refactor diff jobs into task-owned renderers with persistent cache support, root-confined walkers, and quieter Mach-O/Firmware output.
311 lines
10 KiB
Go
311 lines
10 KiB
Go
package diff
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/blacktop/go-plist"
|
|
)
|
|
|
|
func TestDiffLocalizedResourcesReportsUpdatedLoctableKeys(t *testing.T) {
|
|
path := "FileSystem/System/Library/PrivateFrameworks/CommunicationsFilter.framework/CommunicationsFilter.loctable"
|
|
oldResources := map[string]string{
|
|
path: `en.%d_ADDITIONAL_CONTACTS_BLOCKED_ALERT_TITLE.value.one = "%d other contact was unblocked."`,
|
|
}
|
|
newResources := map[string]string{
|
|
path: strings.Join([]string{
|
|
`en.%d_ADDITIONAL_CONTACTS_BLOCKED_ALERT_TITLE.value.one = "%d other contact was unblocked."`,
|
|
`en.BLOCKLIST_LIMIT_ALERT_TITLE = "Blocked Contacts Limit Reached"`,
|
|
}, "\n"),
|
|
}
|
|
out := &PlistDiff{
|
|
New: make(map[string]string),
|
|
Updated: make(map[string]string),
|
|
}
|
|
|
|
if err := diffLocalizedResources(out, oldResources, newResources); err != nil {
|
|
t.Fatalf("diffLocalizedResources returned error: %v", err)
|
|
}
|
|
|
|
got := out.Updated[path]
|
|
if !strings.Contains(got, `+en.BLOCKLIST_LIMIT_ALERT_TITLE = "Blocked Contacts Limit Reached"`) {
|
|
t.Fatalf("updated localization diff missing blocklist alert title:\n%s", got)
|
|
}
|
|
}
|
|
|
|
func TestLocalizationMarkdownFilenameUsesFullPath(t *testing.T) {
|
|
first := plistMarkdownFilename("FileSystem/Applications/Phone.app/en.lproj/Localizable.strings", localizationDisplayName)
|
|
second := plistMarkdownFilename("FileSystem/System/Library/Frameworks/Test.framework/en.lproj/Localizable.strings", localizationDisplayName)
|
|
|
|
if first == second {
|
|
t.Fatalf("localizationMarkdownFilename collided for same basename: %q", first)
|
|
}
|
|
if !strings.HasSuffix(first, "_Phone.md") {
|
|
t.Fatalf("plistMarkdownFilename(%q) should use containing bundle name", first)
|
|
}
|
|
if !strings.HasSuffix(second, "_Test.md") {
|
|
t.Fatalf("plistMarkdownFilename(%q) should use containing bundle name", second)
|
|
}
|
|
}
|
|
|
|
func TestLocalizationDisplayNameUsesContainingBundle(t *testing.T) {
|
|
tests := []struct {
|
|
path string
|
|
want string
|
|
}{
|
|
{
|
|
path: "FileSystem/Applications/CarPlaySettings.app/Localizable.loctable",
|
|
want: "CarPlaySettings",
|
|
},
|
|
{
|
|
path: "FileSystem/System/Library/PrivateFrameworks/ActionKit.framework/Localizable.loctable",
|
|
want: "ActionKit",
|
|
},
|
|
{
|
|
path: "FileSystem/System/Library/PrivateFrameworks/NearFieldPrivateServices.framework/XPCServices/NFLocationService.xpc/InfoPlist.loctable",
|
|
want: "NFLocationService",
|
|
},
|
|
{
|
|
path: "FileSystem/System/Library/OnBoardingBundles/com.apple.onboarding.analyticsdevice.bundle/AnalyticsDevice.loctable",
|
|
want: "com.apple.onboarding.analyticsdevice",
|
|
},
|
|
{
|
|
path: "FileSystem/usr/share/Localizable.loctable",
|
|
want: "Localizable",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
if got := localizationDisplayName(tt.path); got != tt.want {
|
|
t.Fatalf("localizationDisplayName(%q) = %q, want %q", tt.path, got, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMarkdownUsesLocalizationDisplayName(t *testing.T) {
|
|
path := "FileSystem/System/Library/PrivateFrameworks/ActionKit.framework/Localizable.loctable"
|
|
output := t.TempDir()
|
|
d := New(&Config{
|
|
Title: "Localization Test",
|
|
IpswOld: "old.ipsw",
|
|
IpswNew: "new.ipsw",
|
|
Output: output,
|
|
})
|
|
d.Localizations = map[string]*PlistDiff{
|
|
"SystemOS": {
|
|
New: map[string]string{
|
|
path: `en.KEY = "value"`,
|
|
},
|
|
},
|
|
}
|
|
|
|
if err := d.Markdown(); err != nil {
|
|
t.Fatalf("Markdown returned error: %v", err)
|
|
}
|
|
|
|
readme, err := os.ReadFile(filepath.Join(output, d.TitleToFilename(), "README.md"))
|
|
if err != nil {
|
|
t.Fatalf("failed to read Markdown output: %v", err)
|
|
}
|
|
rendered := string(readme)
|
|
if !strings.Contains(rendered, "##### ActionKit") {
|
|
t.Fatalf("rendered Markdown should use localization owner name:\n%s", rendered)
|
|
}
|
|
if strings.Contains(rendered, "##### Localizable.loctable") {
|
|
t.Fatalf("rendered Markdown should not use generic localization resource basename:\n%s", rendered)
|
|
}
|
|
}
|
|
|
|
func TestMarkdownNewLocalizationOverflowFilesAreFenced(t *testing.T) {
|
|
output := t.TempDir()
|
|
d := New(&Config{
|
|
Title: "Localization Overflow Test",
|
|
IpswOld: "old.ipsw",
|
|
IpswNew: "new.ipsw",
|
|
Output: output,
|
|
})
|
|
|
|
// >= 20 new resources forces the overflow path that writes per-resource
|
|
// Markdown files instead of inlining the content.
|
|
volumeDiff := &PlistDiff{New: make(map[string]string)}
|
|
for i := range 25 {
|
|
path := fmt.Sprintf("FileSystem/System/Library/PrivateFrameworks/Frame%02d.framework/Localizable.loctable", i)
|
|
volumeDiff.New[path] = fmt.Sprintf("en.KEY_A = \"value %d\"\nen.KEY_B = \"other %d\"", i, i)
|
|
}
|
|
d.Localizations = map[string]*PlistDiff{"SystemOS": volumeDiff}
|
|
|
|
if err := d.Markdown(); err != nil {
|
|
t.Fatalf("Markdown returned error: %v", err)
|
|
}
|
|
|
|
locDir := filepath.Join(output, d.TitleToFilename(), "LOCALIZATIONS")
|
|
entries, err := os.ReadDir(locDir)
|
|
if err != nil {
|
|
t.Fatalf("failed to read LOCALIZATIONS dir: %v", err)
|
|
}
|
|
if len(entries) == 0 {
|
|
t.Fatalf("expected overflow localization files in %s", locDir)
|
|
}
|
|
|
|
body, err := os.ReadFile(filepath.Join(locDir, entries[0].Name()))
|
|
if err != nil {
|
|
t.Fatalf("failed to read overflow file: %v", err)
|
|
}
|
|
if !strings.Contains(string(body), "```text\n") {
|
|
t.Fatalf("overflow localization file should fence flattened content as a code block:\n%s", body)
|
|
}
|
|
}
|
|
|
|
func TestNormalizeLocalizedResourceFlattensLoctable(t *testing.T) {
|
|
resource := map[string]any{
|
|
"ar": map[string]any{
|
|
"BLOCKLIST_LIMIT_ALERT_TITLE": "Arabic title",
|
|
},
|
|
"en": map[string]any{
|
|
"BLOCKLIST_LIMIT_ALERT_TITLE": "Blocked Contacts Limit Reached",
|
|
"%d_ADDITIONAL_CONTACTS_BLOCKED_ALERT_TITLE": map[string]any{
|
|
"NSStringLocalizedFormatKey": "%#@value@",
|
|
"value": map[string]any{
|
|
"one": "%d other contact who shares the same phone number or email address was unblocked.",
|
|
"other": "%d other contacts who share the same phone number or email address were unblocked.",
|
|
},
|
|
},
|
|
},
|
|
"en_GB": map[string]any{
|
|
"BLOCKLIST_LIMIT_ALERT_TITLE": "Blocked Contacts Limit Reached",
|
|
},
|
|
}
|
|
|
|
data, err := plist.Marshal(resource, plist.BinaryFormat)
|
|
if err != nil {
|
|
t.Fatalf("failed to marshal test plist: %v", err)
|
|
}
|
|
|
|
path := t.TempDir() + "/CommunicationsFilter.loctable"
|
|
if err := os.WriteFile(path, data, 0o644); err != nil {
|
|
t.Fatalf("failed to write test loctable: %v", err)
|
|
}
|
|
|
|
normalized, err := normalizeLocalizedResource(path)
|
|
if err != nil {
|
|
t.Fatalf("normalizeLocalizedResource returned error: %v", err)
|
|
}
|
|
|
|
for _, needle := range []string{
|
|
`en.BLOCKLIST_LIMIT_ALERT_TITLE = "Blocked Contacts Limit Reached"`,
|
|
`en.%d_ADDITIONAL_CONTACTS_BLOCKED_ALERT_TITLE.value.one = "%d other contact who shares the same phone number or email address was unblocked."`,
|
|
} {
|
|
if !strings.Contains(normalized, needle) {
|
|
t.Fatalf("normalized resource missing %q:\n%s", needle, normalized)
|
|
}
|
|
}
|
|
for _, needle := range []string{`ar.`, `en_GB.`} {
|
|
if strings.Contains(normalized, needle) {
|
|
t.Fatalf("normalized resource should only include US English, found %q:\n%s", needle, normalized)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNormalizeLocalizedResourceFallsBackToAvailableEnglishLoctable(t *testing.T) {
|
|
resource := map[string]any{
|
|
"en_AU": map[string]any{
|
|
"CFBundleName": "NFLocationService",
|
|
},
|
|
"en_GB": map[string]any{
|
|
"CFBundleName": "NFLocationService",
|
|
},
|
|
"fr": map[string]any{
|
|
"CFBundleName": "Service NFC",
|
|
},
|
|
"LocProvenance": map[string]any{
|
|
"en_AU": uint64(1),
|
|
},
|
|
}
|
|
|
|
data, err := plist.Marshal(resource, plist.BinaryFormat)
|
|
if err != nil {
|
|
t.Fatalf("failed to marshal test plist: %v", err)
|
|
}
|
|
|
|
path := t.TempDir() + "/InfoPlist.loctable"
|
|
if err := os.WriteFile(path, data, 0o644); err != nil {
|
|
t.Fatalf("failed to write test loctable: %v", err)
|
|
}
|
|
|
|
normalized, err := normalizeLocalizedResource(path)
|
|
if err != nil {
|
|
t.Fatalf("normalizeLocalizedResource returned error: %v", err)
|
|
}
|
|
|
|
if !strings.Contains(normalized, `en.CFBundleName = "NFLocationService"`) {
|
|
t.Fatalf("normalized resource missing fallback English locale:\n%s", normalized)
|
|
}
|
|
for _, needle := range []string{"en_AU.", "en_GB.", "fr.", "LocProvenance"} {
|
|
if strings.Contains(normalized, needle) {
|
|
t.Fatalf("normalized resource should include one English fallback, found %q:\n%s", needle, normalized)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNormalizeLocalizedResourcePrefersUSEnglishLoctable(t *testing.T) {
|
|
resource := map[string]any{
|
|
"en": map[string]any{
|
|
"BLOCKLIST_LIMIT_ALERT_TITLE": "Generic English title",
|
|
},
|
|
"en_US": map[string]any{
|
|
"BLOCKLIST_LIMIT_ALERT_TITLE": "Blocked Contacts Limit Reached",
|
|
},
|
|
"en_GB": map[string]any{
|
|
"BLOCKLIST_LIMIT_ALERT_TITLE": "Blocked Contacts Limit Reached",
|
|
},
|
|
}
|
|
|
|
data, err := plist.Marshal(resource, plist.BinaryFormat)
|
|
if err != nil {
|
|
t.Fatalf("failed to marshal test plist: %v", err)
|
|
}
|
|
|
|
path := t.TempDir() + "/CommunicationsFilter.loctable"
|
|
if err := os.WriteFile(path, data, 0o644); err != nil {
|
|
t.Fatalf("failed to write test loctable: %v", err)
|
|
}
|
|
|
|
normalized, err := normalizeLocalizedResource(path)
|
|
if err != nil {
|
|
t.Fatalf("normalizeLocalizedResource returned error: %v", err)
|
|
}
|
|
|
|
if !strings.Contains(normalized, `en.BLOCKLIST_LIMIT_ALERT_TITLE = "Blocked Contacts Limit Reached"`) {
|
|
t.Fatalf("normalized resource should prefer US English:\n%s", normalized)
|
|
}
|
|
for _, needle := range []string{`Generic English title`, `en_US.`, `en_GB.`} {
|
|
if strings.Contains(normalized, needle) {
|
|
t.Fatalf("normalized resource should include one US English lane, found %q:\n%s", needle, normalized)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestUSEnglishLocalizedResourcePathFilter(t *testing.T) {
|
|
tests := []struct {
|
|
path string
|
|
want bool
|
|
}{
|
|
{path: "System/Library/Foo.framework/en.lproj/Localizable.strings", want: true},
|
|
{path: "System/Library/Foo.framework/en_US.lproj/Localizable.strings", want: true},
|
|
{path: "System/Library/Foo.framework/en-US.lproj/Localizable.strings", want: true},
|
|
{path: "System/Library/Foo.framework/Base.lproj/Localizable.strings", want: true},
|
|
{path: "System/Library/Foo.framework/en_GB.lproj/Localizable.strings", want: false},
|
|
{path: "System/Library/Foo.framework/fr.lproj/Localizable.strings", want: false},
|
|
{path: "System/Library/Foo.framework/CommunicationsFilter.loctable", want: true},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
if got := isUSEnglishLocalizedResourcePath(tt.path); got != tt.want {
|
|
t.Fatalf("isUSEnglishLocalizedResourcePath(%q) = %t, want %t", tt.path, got, tt.want)
|
|
}
|
|
}
|
|
}
|