Repository: beeware/toga

Issue:
tests.test_image.ImageTests cannot pass
## Expected Behavior

all tests pass

## Current Behavior

```
======================================================================
ERROR: test_object_created (tests.test_image.ImageTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/elelay/devel/gpodder-toga/toga/src/core/tests/test_image.py", line 20, in test_object_created
    self.image.bind(factory=toga_dummy.factory)
  File "/home/elelay/devel/gpodder-toga/toga/src/core/toga/images.py", line 29, in bind
    full_path = factory.paths.app / factory.paths.Path(self.path)
  File "/home/elelay/devel/gpodder-toga/toga/src/dummy/toga_dummy/paths.py", line 14, in app
    return Path(sys.modules[App.app.module_name].__file__).parent
AttributeError: 'NoneType' object has no attribute 'module_name'
```

## Steps to reproduce
follow contribute steps


## Your Environment

* Python 3.8.2

* Operating System and Version 

  - [ ] macOS - version:
  - [x] Linux - ArchLinux
  - [ ] Windows - version:
  - [ ] Other - name:  - version:

* Toga Version (list the specific version number or git hash)
c940d61c

* Toga Target (the type of app you are trying to generate)

  - [ ] android
  - [ ] cocoa
  - [ ] django
  - [x] gtk
  - [ ] iOS
  - [ ] tvOS
  - [ ] watchOS
  - [ ] winforms
  - [ ] win32
  - [ ] Other (please specify)


## Resolution

This testcase doesn't create an App, so `paths.Paths.app()` allways encounters a None in `App.app.module_name` in [toga_dummy/paths.py](https://github.com/beeware/toga/blob/master/src/dummy/toga_dummy/paths.py#L14).

This could be fixed in different ways:

 - change `Paths.app()` in dummy/paths.py to return `Path.cwd()` when not `App.app`

 - or change toga/images.py for bind to not go for `Paths.app` if the image's path is absolute.
   Also change the test case to use an absolute path (won't exist anyway)
tests.test_image.ImageTests cannot pass
## Expected Behavior

all tests pass

## Current Behavior

```
======================================================================
ERROR: test_object_created (tests.test_image.ImageTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/elelay/devel/gpodder-toga/toga/src/core/tests/test_image.py", line 20, in test_object_created
    self.image.bind(factory=toga_dummy.factory)
  File "/home/elelay/devel/gpodder-toga/toga/src/core/toga/images.py", line 29, in bind
    full_path = factory.paths.app / factory.paths.Path(self.path)
  File "/home/elelay/devel/gpodder-toga/toga/src/dummy/toga_dummy/paths.py", line 14, in app
    return Path(sys.modules[App.app.module_name].__file__).parent
AttributeError: 'NoneType' object has no attribute 'module_name'
```

## Steps to reproduce
follow contribute steps


## Your Environment

* Python 3.8.2

* Operating System and Version 

  - [ ] macOS - version:
  - [x] Linux - ArchLinux
  - [ ] Windows - version:
  - [ ] Other - name:  - version:

* Toga Version (list the specific version number or git hash)
c940d61c

* Toga Target (the type of app you are trying to generate)

  - [ ] android
  - [ ] cocoa
  - [ ] django
  - [x] gtk
  - [ ] iOS
  - [ ] tvOS
  - [ ] watchOS
  - [ ] winforms
  - [ ] win32
  - [ ] Other (please specify)


## Resolution

This testcase doesn't create an App, so `paths.Paths.app()` allways encounters a None in `App.app.module_name` in [toga_dummy/paths.py](https://github.com/beeware/toga/blob/master/src/dummy/toga_dummy/paths.py#L14).

This could be fixed in different ways:

 - change `Paths.app()` in dummy/paths.py to return `Path.cwd()` when not `App.app`

 - or change toga/images.py for bind to not go for `Paths.app` if the image's path is absolute.
   Also change the test case to use an absolute path (won't exist anyway)


Discussion:
It's highly unusual that you're seeing this failure, because CI runs the full test suite multiple times, on multiple platforms before we merge any patch, and we haven't see any test failures of the kind you describe.

That said, the bug you're reporting *is* familiar to me. We saw an analogous failure in the icon tests a while back; the fix is to [introduce a dummy app](https://github.com/beeware/toga/commit/0c2696169641bbf8769dd215aec5025f9303996a).

I'm going to close this on the basis that the tests *are* passing in CI, so from the perspective of Toga as a project, this is a local configuration issue. However, if you want to submit a PR that introduces a dummy app to avoid this issue, we'll gladly merge it.

It's highly unusual that you're seeing this failure, because CI runs the full test suite multiple times, on multiple platforms before we merge any patch, and we haven't see any test failures of the kind you describe.

That said, the bug you're reporting *is* familiar to me. We saw an analogous failure in the icon tests a while back; the fix is to [introduce a dummy app](https://github.com/beeware/toga/commit/0c2696169641bbf8769dd215aec5025f9303996a).

I'm going to close this on the basis that the tests *are* passing in CI, so from the perspective of Toga as a project, this is a local configuration issue. However, if you want to submit a PR that introduces a dummy app to avoid this issue, we'll gladly merge it.


Resolving patch:
diff --git a/src/core/toga/app.py b/src/core/toga/app.py
--- a/src/core/toga/app.py
+++ b/src/core/toga/app.py
@@ -51,7 +51,7 @@ class App:
 
     The absolute minimum App would be::
 
-        >>> app = toga.App(name='Empty App', app_id='org.beeware.empty')
+        >>> app = toga.App(formal_name='Empty App', app_id='org.beeware.empty')
         >>> app.main_loop()
 
     :param formal_name: The formal name of the application. Will be derived from


Test patch:
diff --git a/src/core/tests/test_image.py b/src/core/tests/test_image.py
--- a/src/core/tests/test_image.py
+++ b/src/core/tests/test_image.py
@@ -8,6 +8,11 @@ class ImageTests(TestCase):
     def setUp(self):
         super().setUp()
 
+        # an App must have been created before calling image.bind
+        # because it tries to resolve the image path against the app path.
+        toga.App(formal_name='Image Test App',
+                 app_id='org.beeware.test_image',
+                 factory=toga_dummy.factory)
         self.path = 'path/to/image.jpg'
         self.image = toga.Image(path=self.path)