Repository: linkchecker/linkchecker (language: python)

Issue:
Unable to run tests on macos due to missing GeoIP.h
<!-- Verify first that your issue is not already reported. -->
<!-- If possible, test if the latest release is affected too. -->

## Summary

While trying to run tests on macos, via `tox -e py`, I got an error complaining about missing some headers.

<!-- general description of the problem -->

<!-- Reporting a minimal example that reproduces the problem in the text of
the issue can increase the chance of a solution significantly. -->

## Actual result

```
      py_GeoIP.c:23:10: fatal error: 'GeoIP.h' file not found
      #include "GeoIP.h"
```

## Expected result

Be able to run tets.
## Environment


 * Operating system: Darwin m1 23.4.0 Darwin Kernel Version 23.4.0: Wed Feb 21 21:44:43 PST 2024; root:xnu-10063.101.15~2/RELEASE_ARM64_T6000 arm64
 * Linkchecker version: main branch
 * Python version: 3.12
 * Install method: pip

## Configuration

<!-- Include the output of `linkchecker -Dcmdline -q -f <FILENAME>` inline, not as an attachment, -->
<!-- you may want to substitute any sensitive strings, make sure any substitutions are consistent -->
<!-- in the reported result and logs. -->
<!-- (if you don't use -f/--config when reproducing the problem omit that option here). -->

## Logs

<!-- The exact command line used -->
<!-- Rerun with -Dall and include all relevant lines -->
<!-- If a Traceback happened include it in full as well -->

## Other notes

I installed the currently deprecated geoip from https://formulae.brew.sh/formula/geoip but that was not enough.

I think that it would be very useful to add a github actions pipeline that also runts the tests on macos, to catch such issues.

Interestingly I did not had problems using linkchecker itself on macos, only running the tests.


Discussion / hints:
As tox deps can't install this dependency (or the geoip database) let's add a geoip tox factor to control the installation of the GeoIP Python package, included in #831.

Fix patch:
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index afb35ffc3..d5e7b51c3 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -105,7 +105,7 @@ jobs:
       - name: Run tests
         run: |
             python -m hatchling build -t sdist --hooks-only
-            python -m tox -e ${{ matrix.toxenv }}
+            python -m tox -e ${{ matrix.toxenv }}-geoip
 
       - name: Report to coveralls
         uses: coverallsapp/github-action@f350da2c033043742f89e8c0b7b5145a1616da6d  # v2.1.2
diff --git a/tox.ini b/tox.ini
index 2878d0826..90b356cc1 100644
--- a/tox.ini
+++ b/tox.ini
@@ -9,7 +9,7 @@ deps =
     pyopenssl
     pytest-xdist
     pytest-cov
-    GeoIP
+    geoip: GeoIP
 
 [testenv]
 usedevelop = true


Test patch:
diff --git a/tests/__init__.py b/tests/__init__.py
index b99a563ab..0bfe671e5 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -38,6 +38,18 @@ def setUp(self):
         init_i18n()
 
 
+@lru_cache(1)
+def running_in_ci():
+    return "CI" in os.environ
+
+
+def skip(reason, strict=True):
+    if strict and running_in_ci():
+        pytest.fail(reason)
+    else:
+        pytest.skip(reason)
+
+
 def run(cmd, verbosity=0, **kwargs):
     """Run command without error checking.
     @return: command return code"""
@@ -65,14 +77,14 @@ def run_silent(cmd):
         null.close()
 
 
-def _need_func(testfunc, name):
+def _need_func(testfunc, name, strict=True):
     """Decorator skipping test if given testfunc fails."""
 
     def check_func(func):
         @wraps(func)
         def newfunc(*args, **kwargs):
             if not testfunc():
-                pytest.skip("%s is not available" % name)
+                skip("%s is not available" % name, strict)
             return func(*args, **kwargs)
 
         return newfunc
@@ -111,7 +123,7 @@ def has_posix():
     return os.name == "posix"
 
 
-need_posix = _need_func(has_posix, "POSIX system")
+need_posix = _need_func(has_posix, "POSIX system", False)
 
 
 @lru_cache(1)
@@ -120,7 +132,7 @@ def has_windows():
     return os.name == "nt"
 
 
-need_windows = _need_func(has_windows, "Windows system")
+need_windows = _need_func(has_windows, "Windows system", False)
 
 
 @lru_cache(1)
@@ -129,7 +141,7 @@ def has_linux():
     return sys.platform.startswith("linux")
 
 
-need_linux = _need_func(has_linux, "Linux system")
+need_linux = _need_func(has_linux, "Linux system", False)
 
 
 @lru_cache(1)
@@ -207,7 +219,7 @@ def has_word():
     return parseword.has_word()
 
 
-need_word = _need_func(has_word, "Word")
+need_word = _need_func(has_word, "Word", False)
 
 
 @lru_cache(1)
diff --git a/tests/checker/ftpserver.py b/tests/checker/ftpserver.py
index 8643f6160..e3e01379b 100644
--- a/tests/checker/ftpserver.py
+++ b/tests/checker/ftpserver.py
@@ -19,8 +19,9 @@
 import os
 import time
 import threading
-import pytest
 from ftplib import FTP
+
+from tests import skip
 from . import LinkCheckTest
 
 
@@ -61,7 +62,7 @@ def line_logger(self, msg):
         from pyftpdlib.servers import FTPServer
         from pyftpdlib import __ver__ as pyftpdlib_version
     except ImportError:
-        pytest.skip("pyftpdlib is not available")
+        skip("pyftpdlib is not available")
         return
     authorizer = DummyAuthorizer()
     datadir = os.path.join(os.path.dirname(__file__), "data")
diff --git a/tests/checker/test_file.py b/tests/checker/test_file.py
index b8098d633..f2a129359 100644
--- a/tests/checker/test_file.py
+++ b/tests/checker/test_file.py
@@ -135,7 +135,7 @@ def test_bad_file(self):
             # Fails on NT platforms and I am too lazy to fix
             # Cause: url get quoted %7C which gets lowercased to
             # %7c and this fails.
-            pytest.skip("Not running on NT")
+            pytest.skip("Running on Windows")
         url = "file:/%(curdir)s/%(datadir)s/file.txt" % self.get_attrs()
         nurl = self.norm(url)
         resultlines = [
diff --git a/tests/checker/test_httpbin.py b/tests/checker/test_httpbin.py
index db17f6c99..6c4d6128d 100644
--- a/tests/checker/test_httpbin.py
+++ b/tests/checker/test_httpbin.py
@@ -16,15 +16,14 @@
 """
 Test http stuff with httpbin.org.
 """
-import os
 import re
-from tests import need_network
+from tests import need_network, running_in_ci
 from . import LinkCheckTest
 
 
 def get_httpbin_url(path):
     """Get httpbin URL."""
-    if "CI" in os.environ:
+    if running_in_ci():
         host = "localhost:8080"
     else:
         host = "httpbin.org"