Skip to content

Commit

Permalink
Merge branch 'main' of github.com:gee-community/pytest-gee into main
Browse files Browse the repository at this point in the history
  • Loading branch information
12rambau committed Dec 18, 2023
2 parents 14d5396 + 0a632a0 commit ebc923c
Show file tree
Hide file tree
Showing 7 changed files with 412 additions and 2 deletions.
1 change: 1 addition & 0 deletions .github/workflows/unit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,4 @@ jobs:
file: ./coverage.xml
token: ${{ secrets.CODECOV_TOKEN }}
verbose: true
files: ./coverage.xml
96 changes: 94 additions & 2 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -85,23 +85,115 @@ Finally you need to configure the ``pytest`` execution environment itself. Add t
You are now ready to make API calls within your tests!

Generate a test file tree in GEE
--------------------------------

Using the ``pytest_gee`` plugin, you can easily generate a test file tree in GEE that will be used to run your tests.
This tree will start in a folder named with the ``gee_hash`` fixture and will be deleted at the end of the test session.

By using this method you will ensure that the folder you are using for your test is unique and that it will not interfere with other tests (e.g. parallel tests).

.. code-block:: python
# test_something.py
def test_something(gee_hash, gee_folder_root, gee_test_folder):
# this folder is existing within your GEE account and will be deleted at the end of the test session
print(gee_folder_root)
Customize the test folder tree
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

By default the test folder tree is empty and will be deleted at the end of the test session.
You can decide to populate it with some assets that will be used in your tests.

To do so customize the ``gee_folder_structure`` fixture in your ``conftest.py`` file.
This fixture is a ``dict`` that will be used to create the folder tree in GEE. As shown in the following example you can add subfolder and assets to this tree.
assets need to be ``ee.Image`` or ``ee.FeatureCollection`` objects and remain small as the creation operation is taken care of by the plugin.
Specifically for ``ee.Image`` objects, please use the ``clipToBoundsAndScale`` method to make sure the asset has a geometry and a scale.

.. code-block:: python
# conftest.py
import pytest
@pytest.fixture(scope="session")
def gee_folder_structure():
"""Override the default test folder structure."""
point = ee.Geometry.Point([0, 0])
return {
"folder": {
"image": ee.Image(1).clipToBoundsAndScale(point.buffer(100), scale=30),
"fc": ee.FeatureCollection(point),
}
}
Which will render in your GEE account as:

.. code-block::
8d98a5be574041a6a54d6def9d915c67/
└── folder/
├── fc (FeatureCollection)
└── image (ImageCollection)
Customize the root folder
^^^^^^^^^^^^^^^^^^^^^^^^^

By default the test folder will be created at the root of the user account. There are situation where one could prefer to store it in a specific folder.

To do so customize the ``gee_folder_root`` fixture in your ``conftest.py`` file, simply return the asset id of the folder you want to use as root.

.. code-block:: python
# conftest.py
import pytest
@pytest.fixture(scope="session")
def gee_folder_root():
"""Override the default test folder root."""
return "users/username/my_root_folder"
.. note::

This is compulsory if you use a service account to connect to GEE as the service account has no associated root folder.

Create assets
-------------

Most of tests pipelines are checking different python versions in parallel which can create multiple issues from a GEE perspective:

- The assets names need to be unique
- the tasks names need also to be unique
- The tasks names need also to be unique

To avoid this issue, the plugin is shipped with a session wise unique hex fixture that can be used to suffix or prefix your assets and tasks names.
To avoid this issue, the plugin is shipped with a session wise unique hex fixture ``gee_hash`` that can be used to suffix or prefix your assets and tasks names.
To make sure the asset exist when you run your tests, you can use the ``pytest_gee.wait`` method to wait until the asset is effectively generated.

.. code-block:: python
# test.py
import pytest
import pytest_gee
def test_create_asset(gee_hash):
# create an asset name
asset_name = f"asset_{gee_hash}"
# export the an object to this asset
task = ee.batch.Export.image.toAsset(
image=ee.Image(1),
description=asset_name,
assetId=asset_name,
scale=1,
maxPixels=1e9,
)
task.start()
# wait for the asset to be created
pytest_gee.wait(task)
# Do something with the asset name
20 changes: 20 additions & 0 deletions pytest_gee/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
"""The init file of the package."""
from __future__ import annotations

import os
from pathlib import Path
from typing import Union

import ee
import httplib2

from pytest_gee import utils

__version__ = "0.2.0"
__author__ = "Pierrick Rambaud"
__email__ = "[email protected]"
Expand Down Expand Up @@ -35,3 +40,18 @@ def init_ee_from_token():
# if the user is in local development the authentication should
# already be available
ee.Initialize(http_transport=httplib2.Http())


def wait(task: Union[ee.batch.Task, str], timeout: int = 5 * 60) -> str:
"""Wait until the selected process is finished or we reached timeout value.
Args:
task: name of the running task or the Task object itself.
timeout: timeout in seconds. if set to 0 the parameter is ignored. default to 5 minutes.
Returns:
the final state of the task
"""
# just expose the utils function
# this is compulsory as wait is also needed in the utils module
return utils.wait(task, timeout)
28 changes: 28 additions & 0 deletions pytest_gee/plugin.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,38 @@
"""A pytest plugin to build a GEE environment for a test session."""
from __future__ import annotations

import uuid
from pathlib import Path

import ee
import pytest

from . import utils


@pytest.fixture(scope="session")
def gee_hash():
"""Generate a unique hash for the test session."""
return uuid.uuid4().hex


@pytest.fixture(scope="session")
def gee_folder_root():
"""Link to the root folder of the connected account."""
return Path(ee.data.getAssetRoots()[0]["id"])


@pytest.fixture(scope="session")
def gee_folder_structure():
"""The structure of the generated test folder."""
return {}


@pytest.fixture(scope="session")
def gee_test_folder(gee_hash, gee_folder_root, gee_folder_structure):
"""Create a test folder for the duration of the test session."""
folder = utils.init_tree(gee_folder_structure, gee_hash, gee_folder_root)

yield folder

utils.delete_assets(folder, False)
Loading

0 comments on commit ebc923c

Please sign in to comment.