For 1), I have mostly this figured out, in pytest: Run gunittest-based tests using a fixture to create a temporary copy of data directory by echoix · Pull Request #380 · echoix/grass · GitHub (I limited myself to vector), but there’s some annoying issues that I can’t figure out how to solve correctly.
First, is that in order to have all the needed grass session env vars set up and the test dataset available, I have to run pytest inside a grass - -exec call, that is inside the test project dataset. That means a bad test, or that overwrites a layer with a name from the sample dataset will impact others. How to solve? Either redo something like gunittest does for launching a new process and copying stuff (we would have the same overhead as before), or reimplement the parts needed to bootstrap launching grass that the C parts needs, a bit like the init script.
Another annoying issue, is that when running gunittest/unittest tests with pytest, the setUp, setUpClass etc are implemented as automatically generated autouse fixtures, but since they can’t be changed to use other fixtures, they are ran before I can define another autouse fixture to « fix » the problems they have when running (and copy the locally defined data folder to a temporary folder). Reading the pytest scopes docs attentively, I could only make it work by having gunittest tests avoid setUpClass calling grass functions, and use a per-test setup only when using pytest, that would be correctly run, since the autouse fixture would catch on correctly there. That means that creating datasets are redone for each test (slower than needed, especially on Windows).
While finding out how to solve that, I observed a known behaviour of setUpClass/tearDownClass (and all the other variants for per-test, per-module, etc.): the teardown only happens on success of the paired setUp function. When I encountered an error somewhere in a setUp, or stopped the test when a setUp function was executing, then the sample dataset was left broken, and next tests couldn’t run. Since Python 3.8, there’s a solution for this, with addCleanup and other scoped variants unittest — Unit testing framework — Python 3.13.2 documentation. I tried it in the PR I linked above, it totally replaces any tearDown functions and I think it’s a better pattern for us. You add functions to call with all arguments needed, and they’ll be executed in reverse order (LIFO), which is exactly what we want. On unsuccessful setUp, the calls already added will be executed. It works quite well.
Another issue, (more pytest related, or when using grass_setup.init() for a session) that I often end up needing to do a monkeypatch env magic, is related to how env vars are handled by python. I fell multiple times in that rabbit hole. Briefly, there’s currently no way to have the env vars changed to be loaded back/acknowledged by Python, if it was not changed by that Python process. Since any first call to a grass C-based module/function will change some env vars, that’s the root cause of [Bug] [help-needed] gs.create_project doesn't seem enough to get a working session on Windows · Issue #4480 · OSGeo/grass · GitHub. I encountered this again, and shuffling the order of tests ran is useful to point this out. Some problems are hidden just because we always run tests in the same order, and the first call to a C-based module isn’t failing. The entire need for so many env vars to make grass (C based) work is kinda hard. Some parts of g.gisenv are also problematic for Python work.