| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- // Package test provides utility drivers for running UI tests without rendering to a screen.
- package test // import "fyne.io/fyne/v2/test"
- import (
- "net/url"
- "sync"
- "fyne.io/fyne/v2"
- "fyne.io/fyne/v2/internal"
- "fyne.io/fyne/v2/internal/app"
- "fyne.io/fyne/v2/internal/cache"
- "fyne.io/fyne/v2/internal/painter"
- "fyne.io/fyne/v2/theme"
- )
- // ensure we have a dummy app loaded and ready to test
- func init() {
- NewApp()
- }
- type testApp struct {
- driver *testDriver
- settings *testSettings
- prefs fyne.Preferences
- propertyLock sync.RWMutex
- storage fyne.Storage
- lifecycle fyne.Lifecycle
- cloud fyne.CloudProvider
- // user action variables
- appliedTheme fyne.Theme
- lastNotification *fyne.Notification
- }
- func (a *testApp) CloudProvider() fyne.CloudProvider {
- return a.cloud
- }
- func (a *testApp) Icon() fyne.Resource {
- return nil
- }
- func (a *testApp) SetIcon(fyne.Resource) {
- // no-op
- }
- func (a *testApp) NewWindow(title string) fyne.Window {
- return a.driver.CreateWindow(title)
- }
- func (a *testApp) OpenURL(url *url.URL) error {
- // no-op
- return nil
- }
- func (a *testApp) Run() {
- // no-op
- }
- func (a *testApp) Quit() {
- // no-op
- }
- func (a *testApp) UniqueID() string {
- return "testApp" // TODO should this be randomised?
- }
- func (a *testApp) Driver() fyne.Driver {
- return a.driver
- }
- func (a *testApp) SendNotification(notify *fyne.Notification) {
- a.propertyLock.Lock()
- defer a.propertyLock.Unlock()
- a.lastNotification = notify
- }
- func (a *testApp) SetCloudProvider(p fyne.CloudProvider) {
- if p == nil {
- a.cloud = nil
- return
- }
- a.transitionCloud(p)
- }
- func (a *testApp) Settings() fyne.Settings {
- return a.settings
- }
- func (a *testApp) Preferences() fyne.Preferences {
- return a.prefs
- }
- func (a *testApp) Storage() fyne.Storage {
- return a.storage
- }
- func (a *testApp) Lifecycle() fyne.Lifecycle {
- return a.lifecycle
- }
- func (a *testApp) Metadata() fyne.AppMetadata {
- return fyne.AppMetadata{} // just dummy data
- }
- func (a *testApp) lastAppliedTheme() fyne.Theme {
- a.propertyLock.Lock()
- defer a.propertyLock.Unlock()
- return a.appliedTheme
- }
- func (a *testApp) transitionCloud(p fyne.CloudProvider) {
- if a.cloud != nil {
- a.cloud.Cleanup(a)
- }
- err := p.Setup(a)
- if err != nil {
- fyne.LogError("Failed to set up cloud provider "+p.ProviderName(), err)
- return
- }
- a.cloud = p
- listeners := a.prefs.ChangeListeners()
- if pp, ok := p.(fyne.CloudProviderPreferences); ok {
- a.prefs = pp.CloudPreferences(a)
- } else {
- a.prefs = internal.NewInMemoryPreferences()
- }
- if store, ok := p.(fyne.CloudProviderStorage); ok {
- a.storage = store.CloudStorage(a)
- } else {
- a.storage = &testStorage{}
- }
- for _, l := range listeners {
- a.prefs.AddChangeListener(l)
- l() // assume that preferences have changed because we replaced the provider
- }
- // after transition ensure settings listener is fired
- a.settings.apply()
- }
- // NewApp returns a new dummy app used for testing.
- // It loads a test driver which creates a virtual window in memory for testing.
- func NewApp() fyne.App {
- settings := &testSettings{scale: 1.0, theme: Theme()}
- prefs := internal.NewInMemoryPreferences()
- store := &testStorage{}
- test := &testApp{settings: settings, prefs: prefs, storage: store, driver: NewDriver().(*testDriver),
- lifecycle: &app.Lifecycle{}}
- root, _ := store.docRootURI()
- store.Docs = &internal.Docs{RootDocURI: root}
- painter.ClearFontCache()
- cache.ResetThemeCaches()
- fyne.SetCurrentApp(test)
- listener := make(chan fyne.Settings)
- test.Settings().AddChangeListener(listener)
- go func() {
- for {
- <-listener
- test.propertyLock.Lock()
- painter.ClearFontCache()
- cache.ResetThemeCaches()
- app.ApplySettings(test.Settings(), test)
- test.appliedTheme = test.Settings().Theme()
- test.propertyLock.Unlock()
- }
- }()
- return test
- }
- type testSettings struct {
- theme fyne.Theme
- scale float32
- changeListeners []chan fyne.Settings
- propertyLock sync.RWMutex
- }
- func (s *testSettings) AddChangeListener(listener chan fyne.Settings) {
- s.propertyLock.Lock()
- defer s.propertyLock.Unlock()
- s.changeListeners = append(s.changeListeners, listener)
- }
- func (s *testSettings) BuildType() fyne.BuildType {
- return fyne.BuildStandard
- }
- func (s *testSettings) PrimaryColor() string {
- return theme.ColorBlue
- }
- func (s *testSettings) SetTheme(theme fyne.Theme) {
- s.propertyLock.Lock()
- s.theme = theme
- s.propertyLock.Unlock()
- s.apply()
- }
- func (s *testSettings) ShowAnimations() bool {
- return true
- }
- func (s *testSettings) Theme() fyne.Theme {
- s.propertyLock.RLock()
- defer s.propertyLock.RUnlock()
- if s.theme == nil {
- return theme.DarkTheme()
- }
- return s.theme
- }
- func (s *testSettings) ThemeVariant() fyne.ThemeVariant {
- return 2 // not a preference
- }
- func (s *testSettings) Scale() float32 {
- s.propertyLock.RLock()
- defer s.propertyLock.RUnlock()
- return s.scale
- }
- func (s *testSettings) apply() {
- s.propertyLock.RLock()
- listeners := s.changeListeners
- s.propertyLock.RUnlock()
- for _, listener := range listeners {
- select {
- case listener <- s:
- default:
- l := listener
- go func() { l <- s }()
- }
- }
- }
|