mesh/merge-gate pass: builds new: modules/betterbird, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery delivered
A composer window whose class is not Betterbird's own was not seen, so a restart could lose its message. Each step of a restart now has a bound short enough that all of them together, taken in full, stay under the runtime's 30 s: 3 s for the window tree, 8 s to quit, 3 and 5 s to start, 4 s to see it.
183 lines
6.4 KiB
Go
183 lines
6.4 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// The review of pull request 155 (hq issue 345): a restart never loses an unsent message unasked, a
|
|
// preference's value never carries an address, a log never a credential, the reminder test never
|
|
// counts as the operator's Betterbird and leaves nothing behind, and a restart answers within the
|
|
// runtime's call limit.
|
|
|
|
const composerTree = `
|
|
0x2a00003 "Inbox - Betterbird": ("Mail" "eu.betterbird.Betterbird") 2560x1388+0+0 +0+26
|
|
0x2a01234 "Write: hello - Betterbird": ("Msgcompose" "eu.betterbird.Betterbird") 900x700+10+10 +10+10
|
|
`
|
|
|
|
func TestARestartRefusesWhileAComposerIsOpenUnlessTheUnsentMayGo(t *testing.T) {
|
|
f := newFake(t)
|
|
f.desktopSession()
|
|
f.betterbird()
|
|
links(t, nil)
|
|
f.declare("// none")
|
|
f.answer = func(name string, args []string) Output {
|
|
if name == "xwininfo" {
|
|
return Output{Stdout: composerTree}
|
|
}
|
|
return Output{}
|
|
}
|
|
if _, err := f.Restart(false); err == nil || !strings.Contains(err.Error(), "composer") {
|
|
t.Fatalf("refused while a message is being written: %v", err)
|
|
}
|
|
if len(f.signals) != 0 {
|
|
t.Fatalf("nothing was ended: %v", f.signals)
|
|
}
|
|
f.onStart = func([]string) {
|
|
f.proc(5000, 1000, "betterbird-bin", []string{"betterbird"}, "mesh-betterbird.service")
|
|
}
|
|
a, err := f.Restart(true)
|
|
if err != nil || len(a.Ended) != 1 || a.Composers != 1 {
|
|
t.Fatalf("with discard_unsent it goes ahead and says what was open: %+v %v", a, err)
|
|
}
|
|
}
|
|
|
|
func TestARestartAnswersWithinTheCallLimitEvenWhenNothingCooperates(t *testing.T) {
|
|
f := newFake(t)
|
|
f.desktopSession()
|
|
f.betterbird()
|
|
links(t, nil)
|
|
f.stubborn[4000] = true
|
|
slept := time.Duration(0)
|
|
f.Sleep = func(d time.Duration) { slept += d }
|
|
a, err := f.Restart(false)
|
|
if err != nil {
|
|
t.Fatalf("a start that has not shown yet is said, not failed: %v", err)
|
|
}
|
|
if slept > 18*time.Second || len(a.Killed) != 1 || a.Note == "" {
|
|
t.Fatalf("waited %v of the runtime's 30 s: %+v", slept, a)
|
|
}
|
|
}
|
|
|
|
func TestAPreferencesValueCarriesNoAddressHostOrHome(t *testing.T) {
|
|
f := newFake(t)
|
|
f.betterbird()
|
|
links(t, nil)
|
|
f.write(profileDir+"/prefs.js", strings.Join([]string{
|
|
`user_pref("mail.last_msg_movecopy_target_uri", "imap://someone%40example.org@mail.example.org/INBOX");`,
|
|
`user_pref("messenger.account.a1.server", "x");`,
|
|
`user_pref("mail.cloud_files.accounts.a", "y");`,
|
|
`user_pref("editor.lastFileLocation", "/home/operator/Documents/a.pdf");`,
|
|
`user_pref("network.proxy.http", "proxy.corp.example.com");`,
|
|
`user_pref("mail.compose.default_to", "imap://someone%40example.org@mail.example.org/x and bob@example.org");`,
|
|
`user_pref("extensions.activeThemeID", "thunderbird-compact-dark@mozilla.org");`,
|
|
`user_pref("extensions.ui.lastCategory", "addons://list/theme");`,
|
|
`user_pref("font.size.variable.x-western", 12);`,
|
|
}, "\n")+"\n")
|
|
got, err := f.Prefs([]string{"mail.last_msg*", "messenger.account.", "mail.cloud_files.", "editor.", "network.proxy.",
|
|
"mail.compose.default_to", "extensions.", "font.size."})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
by := map[string]Pref{}
|
|
for _, p := range got["prefs"].([]Pref) {
|
|
by[p.Name] = p
|
|
}
|
|
for _, n := range []string{"mail.last_msg_movecopy_target_uri", "messenger.account.a1.server", "mail.cloud_files.accounts.a", "extensions.ui.lastCategory"} {
|
|
if p := by[n]; p.Value != "" || p.Withheld == "" {
|
|
t.Errorf("withheld: %s %+v", n, p)
|
|
}
|
|
}
|
|
if v := by["editor.lastFileLocation"].Value; v != `"~/Documents/a.pdf"` {
|
|
t.Errorf("home as ~: %s", v)
|
|
}
|
|
if v := by["network.proxy.http"].Value; strings.Contains(v, "example") {
|
|
t.Errorf("a host: %s", v)
|
|
}
|
|
if v := by["mail.compose.default_to"].Value; strings.Contains(v, "example") || strings.Contains(v, "someone") {
|
|
t.Errorf("an address: %s", v)
|
|
}
|
|
if v := by["extensions.activeThemeID"].Value; v != `"thunderbird-compact-dark@mozilla.org"` {
|
|
t.Errorf("a theme's id is answered as it is: %s", v)
|
|
}
|
|
if v := by["font.size.variable.x-western"].Value; v != "12" {
|
|
t.Errorf("%s", v)
|
|
}
|
|
for _, bad := range [][]string{{""}, {"*"}, {"."}, {" "}} {
|
|
if _, err := f.Prefs(bad); err == nil {
|
|
t.Errorf("%q is refused: it asks for everything", bad)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestALogLineKeepsNoBearerOrBasicCredential(t *testing.T) {
|
|
for _, l := range []string{"Authorization: Bearer eyJhbGciOi.abc.def", "auth header basic dXNlcjpwYXNz sent", "token=abc123"} {
|
|
got := mask(l)
|
|
for _, secret := range []string{"eyJhbGciOi", "dXNlcjpwYXNz", "abc123"} {
|
|
if strings.Contains(got, secret) {
|
|
t.Errorf("%q → %q", l, got)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestTheReminderTestIsNeverTheOperatorsBetterbirdAndCleansUpWhenItEnds(t *testing.T) {
|
|
f := newFake(t)
|
|
f.desktopSession()
|
|
f.betterbird()
|
|
links(t, nil)
|
|
f.proc(6000, 1000, "betterbird-bin", []string{"/usr/bin/betterbird", "--no-remote", "--profile", "/run/user/1000/" + testUnit + "/profile"}, testUnit+".service")
|
|
if ps := f.mainProcs(); len(ps) != 1 || ps[0].PID != 4000 {
|
|
t.Fatalf("only the operator's: %+v", ps)
|
|
}
|
|
f.declare("// none")
|
|
if _, err := f.ReminderTest(40*time.Second, nil, false); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !f.called("systemd-run --user --collect --quiet --unit=" + testUnit + " -p RuntimeMaxSec=600 -p ExecStopPost=/usr/bin/rm -rf /run/user/1000/" + testUnit) {
|
|
t.Fatalf("the unit removes its profile when it ends: %v", f.calls)
|
|
}
|
|
}
|
|
|
|
func TestAComposerIsKnownByItsInstanceWhateverItsClass(t *testing.T) {
|
|
f := newFake(t)
|
|
f.desktopSession()
|
|
f.betterbird()
|
|
links(t, nil)
|
|
f.answer = func(name string, args []string) Output {
|
|
if name == "xwininfo" {
|
|
return Output{Stdout: ` 0x2a01234 "Write: x": ("Msgcompose" "Betterbird") 900x700+10+10 +10+10` + "\n"}
|
|
}
|
|
return Output{}
|
|
}
|
|
if _, err := f.Restart(false); err == nil || !strings.Contains(err.Error(), "composer") {
|
|
t.Fatalf("a composer of another class is still a composer: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestARestartsWorstCaseStaysUnderTheRuntimesThirtySeconds(t *testing.T) {
|
|
f := newFake(t)
|
|
f.desktopSession()
|
|
f.betterbird()
|
|
links(t, nil)
|
|
f.stubborn[4000] = true
|
|
total := time.Duration(0)
|
|
run := f.Run
|
|
f.Run = func(ctx context.Context, env []string, name string, args ...string) Output {
|
|
// Every command takes its whole timeout: the worst case.
|
|
if d, ok := ctx.Deadline(); ok {
|
|
total += time.Until(d).Round(time.Second)
|
|
}
|
|
return run(ctx, env, name, args...)
|
|
}
|
|
f.Sleep = func(d time.Duration) { total += d }
|
|
if _, err := f.Restart(true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if total > 26*time.Second {
|
|
t.Fatalf("the worst case is %v, over the runtime's 30 s less a margin", total)
|
|
}
|
|
}
|