[libgweather] tests: Check locations against METAR database



commit a3028c23f6b4daec6612ec951b3ce4fa5954fd90
Author: Bastien Nocera <hadess hadess net>
Date:   Fri Dec 8 13:21:06 2017 +0100

    tests: Check locations against METAR database
    
    Check whether all our airports/METAR stations:
    - exist in the METAR database
    - have a weather station
    - and whether that station isn't obsolete

 libgweather/test_libgweather.c |  130 ++++++++++++++++++++++++++++++++++++++++
 1 files changed, 130 insertions(+), 0 deletions(-)
---
diff --git a/libgweather/test_libgweather.c b/libgweather/test_libgweather.c
index b189d81..9d04e6d 100644
--- a/libgweather/test_libgweather.c
+++ b/libgweather/test_libgweather.c
@@ -20,9 +20,15 @@
 #include "config.h"
 
 #include <locale.h>
+#include <string.h>
+#include <libsoup/soup.h>
+
 #include <gweather-version.h>
 #include "gweather-location.h"
 
+/* For test_metar_weather_stations */
+#define METAR_SOURCES "https://www.aviationweather.gov/docs/metar/stations.txt";
+
 /* Maximum for test_airport_distance_sanity() */
 #define TOO_FAR 100.0
 static double max_distance = 0.0;
@@ -159,6 +165,129 @@ test_airport_distance_sanity (void)
         g_warning ("Maximum city to airport distance is %.1f km", max_distance);
 }
 
+static GHashTable *
+parse_metar_stations (const char *contents)
+{
+    GHashTable *stations_ht;
+    char **lines;
+    guint i, num_stations;
+
+    stations_ht = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+    lines = g_strsplit (contents, "\n", -1);
+
+    for (i = 0; lines[i] != NULL; i++) {
+        char *line = lines[i];
+        char *station;
+
+        if (line[0] == '!')
+            continue;
+
+        if (strlen (line) != 83)
+            continue;
+
+        station = g_strndup (line + 20, 4);
+        /* Skip stations with no ICAO code */
+        if (g_str_equal (station, "    ")) {
+            g_free (station);
+            continue;
+        }
+
+        if (g_hash_table_lookup (stations_ht, station)) {
+            if (g_str_equal (station, "VOGO") ||
+                g_str_equal (station, "KHQG")) {
+                g_free (station);
+                continue;
+            }
+            g_test_message ("Weather station '%s' already defined", station);
+        }
+
+        g_hash_table_insert (stations_ht, station, g_strdup (line));
+        num_stations++;
+    }
+
+    g_strfreev (lines);
+
+    /* Duplicates? */
+    g_assert_cmpuint (num_stations, ==, g_hash_table_size (stations_ht));
+
+    g_test_message ("Parsed %u weather stations", num_stations);
+
+    return stations_ht;
+}
+
+static void
+test_metar_weather_station (GWeatherLocation *location,
+                            GHashTable       *stations_ht)
+{
+    const char *code, *line;
+
+    code = gweather_location_get_code (location);
+    g_assert_nonnull (code);
+
+    line = g_hash_table_lookup (stations_ht, code);
+    if (!line) {
+        g_test_message ("Could not find airport for '%s'", code);
+        g_test_fail ();
+    } else {
+        char *has_metar;
+
+        has_metar = g_strndup (line + 62, 1);
+        if (*has_metar == 'Z') {
+            g_test_message ("Airport weather station '%s' is obsolete", code);
+            g_test_fail ();
+        } else if (*has_metar == ' ') {
+            g_test_message ("Could not find weather station for '%s'", code);
+            g_test_fail ();
+        }
+        g_free (has_metar);
+    }
+}
+
+static void
+test_metar_weather_stations_children (GWeatherLocation *location,
+                                      GHashTable       *stations_ht)
+{
+    GWeatherLocation **children;
+    guint i;
+
+    children = gweather_location_get_children (location);
+    for (i = 0; children[i] != NULL; i++) {
+        if (gweather_location_get_level (children[i]) == GWEATHER_LOCATION_WEATHER_STATION)
+            test_metar_weather_station (children[i], stations_ht);
+        else
+            test_metar_weather_stations_children (children[i], stations_ht);
+    }
+}
+
+static void
+test_metar_weather_stations (void)
+{
+    GWeatherLocation *world;
+    SoupMessage *msg;
+    SoupSession *session;
+    GHashTable *stations_ht;
+    char *contents;
+
+    world = gweather_location_get_world ();
+    g_assert (world);
+
+    msg = soup_message_new ("GET", METAR_SOURCES);
+    session = soup_session_new ();
+    soup_session_send_message (session, msg);
+    g_assert (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code));
+    g_object_unref (session);
+    g_assert_nonnull (msg->response_body);
+
+    contents = g_strndup (msg->response_body->data, msg->response_body->length);
+    g_object_unref (msg);
+
+    stations_ht = parse_metar_stations (contents);
+    g_assert_nonnull (stations_ht);
+    g_free (contents);
+
+    test_metar_weather_stations_children (world, stations_ht);
+}
+
 static void
 log_handler (const char *log_domain, GLogLevelFlags log_level, const char *message, gpointer user_data)
 {
@@ -183,6 +312,7 @@ main (int argc, char *argv[])
        g_test_add_func ("/weather/named-timezones", test_named_timezones);
        g_test_add_func ("/weather/timezones", test_timezones);
        g_test_add_func ("/weather/airport_distance_sanity", test_airport_distance_sanity);
+       g_test_add_func ("/weather/metar_weather_stations", test_metar_weather_stations);
 
        return g_test_run ();
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]