226 lines
10 KiB
C++
226 lines
10 KiB
C++
#include "TestHarness.h"
|
|
|
|
#include "src/weather/WeatherParser.h"
|
|
#include "src/weather/WeatherLocationResolver.h"
|
|
#include "src/weather/WeatherSummaryFormatter.h"
|
|
|
|
namespace
|
|
{
|
|
WeatherLocationCandidate candidate(
|
|
const QString &city,
|
|
const QString &admin,
|
|
const QString &country,
|
|
const QString &countryCode,
|
|
const QString &featureCode,
|
|
qint64 population,
|
|
double latitude,
|
|
double longitude)
|
|
{
|
|
WeatherLocationCandidate value;
|
|
value.cityName = city;
|
|
value.adminName = admin;
|
|
value.countryName = country;
|
|
value.countryCode = countryCode;
|
|
value.featureCode = featureCode;
|
|
value.population = population;
|
|
value.latitude = latitude;
|
|
value.longitude = longitude;
|
|
return value;
|
|
}
|
|
}
|
|
|
|
QA_TEST(WeatherParser, ExtractsExplicitCitiesAndDates)
|
|
{
|
|
const WeatherParser parser;
|
|
const WeatherQuery current = parser.parse(QStringLiteral("西安天气怎么样"));
|
|
const WeatherQuery tomorrow = parser.parse(QStringLiteral("明天西安天气怎么样"));
|
|
const WeatherQuery afterTomorrow = parser.parse(QStringLiteral("后天纽约天气"));
|
|
|
|
QA_EXPECT_EQ(current.cityName, QStringLiteral("西安"));
|
|
QA_EXPECT_EQ(current.kind, WeatherQueryKind::Current);
|
|
QA_EXPECT_EQ(tomorrow.cityName, QStringLiteral("西安"));
|
|
QA_EXPECT_EQ(tomorrow.dateOffset, 1);
|
|
QA_EXPECT_EQ(afterTomorrow.cityName, QStringLiteral("纽约"));
|
|
QA_EXPECT_EQ(afterTomorrow.dateOffset, 2);
|
|
}
|
|
|
|
QA_TEST(WeatherParser, ParsesRangeAndDefaultCityRequest)
|
|
{
|
|
const WeatherParser parser;
|
|
const WeatherQuery range = parser.parse(QStringLiteral("未来三天北京天气"));
|
|
const WeatherQuery defaultCity = parser.parse(QStringLiteral("今天天气怎么样"));
|
|
|
|
QA_EXPECT_EQ(range.kind, WeatherQueryKind::Range);
|
|
QA_EXPECT_EQ(range.forecastDays, 3);
|
|
QA_EXPECT_EQ(range.cityName, QStringLiteral("北京"));
|
|
QA_EXPECT(defaultCity.cityName.isEmpty());
|
|
}
|
|
|
|
QA_TEST(WeatherParser, RejectsUnsupportedQueries)
|
|
{
|
|
const WeatherParser parser;
|
|
QA_EXPECT(!parser.parse(QStringLiteral("西安空气质量")).unsupportedReason.isEmpty());
|
|
QA_EXPECT(!parser.parse(QStringLiteral("北京天气预警")).unsupportedReason.isEmpty());
|
|
QA_EXPECT(!parser.parse(QStringLiteral("明天穿什么")).unsupportedReason.isEmpty());
|
|
}
|
|
|
|
QA_TEST(WeatherLocationResolver, ResolvesChineseInternationalCities)
|
|
{
|
|
const WeatherLocationSearch newYork = WeatherLocationResolver::resolveSearch(QStringLiteral("纽约"));
|
|
const WeatherLocationSearch tokyo = WeatherLocationResolver::resolveSearch(QStringLiteral("东京"));
|
|
const WeatherLocationSearch japanTokyo = WeatherLocationResolver::resolveSearch(QStringLiteral("日本东京"));
|
|
const WeatherLocationSearch qualifiedTokyo = WeatherLocationResolver::resolveSearch(QStringLiteral("东京,日本"));
|
|
|
|
QA_EXPECT_EQ(newYork.queryName, QStringLiteral("New York"));
|
|
QA_EXPECT_EQ(newYork.countryCode, QStringLiteral("US"));
|
|
QA_EXPECT_EQ(tokyo.queryName, QStringLiteral("Tokyo"));
|
|
QA_EXPECT_EQ(tokyo.countryCode, QStringLiteral("JP"));
|
|
QA_EXPECT_EQ(japanTokyo.queryName, QStringLiteral("Tokyo"));
|
|
QA_EXPECT_EQ(japanTokyo.countryCode, QStringLiteral("JP"));
|
|
QA_EXPECT_EQ(qualifiedTokyo.queryName, QStringLiteral("Tokyo"));
|
|
QA_EXPECT_EQ(qualifiedTokyo.countryCode, QStringLiteral("JP"));
|
|
}
|
|
|
|
QA_TEST(WeatherLocationResolver, PreservesDomesticCityAndCountryQualifier)
|
|
{
|
|
const WeatherLocationSearch xian = WeatherLocationResolver::resolveSearch(QStringLiteral("西安市"));
|
|
const WeatherLocationSearch chinaXian = WeatherLocationResolver::resolveSearch(QStringLiteral("中国西安"));
|
|
|
|
QA_EXPECT_EQ(xian.queryName, QStringLiteral("西安"));
|
|
QA_EXPECT(xian.countryCode.isEmpty());
|
|
QA_EXPECT_EQ(chinaXian.queryName, QStringLiteral("西安"));
|
|
QA_EXPECT_EQ(chinaXian.countryCode, QStringLiteral("CN"));
|
|
}
|
|
|
|
QA_TEST(WeatherLocationResolver, SuppressesLowValueAlternatives)
|
|
{
|
|
const QVector<WeatherLocationCandidate> candidates = {
|
|
candidate(QStringLiteral("西安"), QStringLiteral("陕西"), QStringLiteral("中国"), QStringLiteral("CN"), QStringLiteral("PPLA"), 6500000, 34.26, 108.93),
|
|
candidate(QStringLiteral("西安"), QStringLiteral("湖南"), QStringLiteral("中国"), QStringLiteral("CN"), QStringLiteral("PPL"), 1200, 27.10, 111.20),
|
|
candidate(QStringLiteral("西安"), QStringLiteral("广东"), QStringLiteral("中国"), QStringLiteral("CN"), QStringLiteral("PPL"), -1, 23.10, 113.20),
|
|
};
|
|
|
|
const QVector<WeatherLocationCandidate> selected = WeatherLocationResolver::selectRelevantCandidates(
|
|
candidates,
|
|
WeatherLocationResolver::resolveSearch(QStringLiteral("西安")));
|
|
QA_EXPECT_EQ(selected.size(), 1);
|
|
QA_EXPECT(!WeatherLocationResolver::hasAmbiguity(selected));
|
|
}
|
|
|
|
QA_TEST(WeatherLocationResolver, KeepsPlausibleAmbiguity)
|
|
{
|
|
const QVector<WeatherLocationCandidate> candidates = {
|
|
candidate(QStringLiteral("Springfield"), QStringLiteral("Missouri"), QStringLiteral("United States"), QStringLiteral("US"), QStringLiteral("PPLA2"), 170000, 37.21, -93.29),
|
|
candidate(QStringLiteral("Springfield"), QStringLiteral("Illinois"), QStringLiteral("United States"), QStringLiteral("US"), QStringLiteral("PPLA"), 114000, 39.80, -89.64),
|
|
candidate(QStringLiteral("Springfield"), QStringLiteral("Georgia"), QStringLiteral("United States"), QStringLiteral("US"), QStringLiteral("PPL"), 2700, 32.37, -81.31),
|
|
};
|
|
|
|
const QVector<WeatherLocationCandidate> selected = WeatherLocationResolver::selectRelevantCandidates(
|
|
candidates,
|
|
WeatherLocationResolver::resolveSearch(QStringLiteral("Springfield")));
|
|
QA_EXPECT_EQ(selected.size(), 2);
|
|
QA_EXPECT(WeatherLocationResolver::hasAmbiguity(selected));
|
|
}
|
|
|
|
QA_TEST(WeatherLocationResolver, AppliesExplicitCountryFilter)
|
|
{
|
|
const QVector<WeatherLocationCandidate> candidates = {
|
|
candidate(QStringLiteral("Tokyo"), QStringLiteral("Tokyo"), QStringLiteral("Japan"), QStringLiteral("JP"), QStringLiteral("PPLC"), 8300000, 35.68, 139.69),
|
|
candidate(QStringLiteral("Tokyo"), QStringLiteral("Texas"), QStringLiteral("United States"), QStringLiteral("US"), QStringLiteral("PPL"), 1000, 33.90, -102.50),
|
|
};
|
|
|
|
const QVector<WeatherLocationCandidate> selected = WeatherLocationResolver::selectRelevantCandidates(
|
|
candidates,
|
|
WeatherLocationResolver::resolveSearch(QStringLiteral("日本东京")));
|
|
QA_EXPECT_EQ(selected.size(), 1);
|
|
QA_EXPECT_EQ(selected.first().countryCode, QStringLiteral("JP"));
|
|
}
|
|
|
|
QA_TEST(WeatherLocationResolver, ParsesBothIpLocationSchemas)
|
|
{
|
|
const QByteArray ipApiPayload = QByteArrayLiteral(
|
|
R"({"city":"Xi'an","region":"Shaanxi","country_name":"China","latitude":34.26,"longitude":108.94,"timezone":"Asia/Shanghai"})");
|
|
const QByteArray ipWhoIsPayload = QByteArrayLiteral(
|
|
R"({"success":true,"city":"Tokyo","region":"Tokyo","country":"Japan","latitude":35.68,"longitude":139.69,"timezone":{"id":"Asia/Tokyo"}})");
|
|
|
|
WeatherLocation ipApiLocation;
|
|
WeatherLocation ipWhoIsLocation;
|
|
QA_EXPECT(WeatherLocationResolver::parseIpLocationResponse(
|
|
ipApiPayload,
|
|
WeatherIpLocationProvider::IpApiCo,
|
|
&ipApiLocation));
|
|
QA_EXPECT(WeatherLocationResolver::parseIpLocationResponse(
|
|
ipWhoIsPayload,
|
|
WeatherIpLocationProvider::IpWhoIs,
|
|
&ipWhoIsLocation));
|
|
QA_EXPECT_EQ(ipApiLocation.cityName, QStringLiteral("Xi'an"));
|
|
QA_EXPECT_EQ(ipApiLocation.countryName, QStringLiteral("China"));
|
|
QA_EXPECT_EQ(ipApiLocation.timezone, QStringLiteral("Asia/Shanghai"));
|
|
QA_EXPECT_EQ(ipWhoIsLocation.cityName, QStringLiteral("Tokyo"));
|
|
QA_EXPECT_EQ(ipWhoIsLocation.countryName, QStringLiteral("Japan"));
|
|
QA_EXPECT_EQ(ipWhoIsLocation.timezone, QStringLiteral("Asia/Tokyo"));
|
|
}
|
|
|
|
QA_TEST(WeatherLocationResolver, RejectsBrokenIpLocationResponses)
|
|
{
|
|
WeatherLocation location;
|
|
QA_EXPECT(!WeatherLocationResolver::parseIpLocationResponse(
|
|
QByteArrayLiteral("{broken"),
|
|
WeatherIpLocationProvider::IpApiCo,
|
|
&location));
|
|
QA_EXPECT(!WeatherLocationResolver::parseIpLocationResponse(
|
|
QByteArrayLiteral(R"({"success":false,"message":"rate limited"})"),
|
|
WeatherIpLocationProvider::IpWhoIs,
|
|
&location));
|
|
QA_EXPECT(!WeatherLocationResolver::parseIpLocationResponse(
|
|
QByteArrayLiteral(R"({"city":"Tokyo","latitude":35.68})"),
|
|
WeatherIpLocationProvider::IpWhoIs,
|
|
&location));
|
|
}
|
|
|
|
QA_TEST(WeatherFormatter, MapsWeatherCodesAndWindDirections)
|
|
{
|
|
QA_EXPECT_EQ(WeatherSummaryFormatter::weatherCodeText(0), QStringLiteral("晴"));
|
|
QA_EXPECT_EQ(WeatherSummaryFormatter::weatherCodeText(95), QStringLiteral("雷暴"));
|
|
QA_EXPECT_EQ(WeatherSummaryFormatter::weatherCodeText(-1), QStringLiteral("未知天气"));
|
|
QA_EXPECT_EQ(WeatherSummaryFormatter::windDirectionText(0), QStringLiteral("北风"));
|
|
QA_EXPECT_EQ(WeatherSummaryFormatter::windDirectionText(90), QStringLiteral("东风"));
|
|
QA_EXPECT_EQ(WeatherSummaryFormatter::windDirectionText(225), QStringLiteral("西南风"));
|
|
}
|
|
|
|
QA_TEST(WeatherFormatter, FormatsCurrentWeatherWithSource)
|
|
{
|
|
WeatherReport report;
|
|
report.query.kind = WeatherQueryKind::Current;
|
|
report.location.cityName = QStringLiteral("西安");
|
|
report.location.countryName = QStringLiteral("中国");
|
|
report.location.source = WeatherLocationSource::SettingsDefault;
|
|
report.current.valid = true;
|
|
report.current.weatherCode = 0;
|
|
report.current.hasTemperature = true;
|
|
report.current.temperatureC = 28.5;
|
|
|
|
const QString message = WeatherSummaryFormatter().format(report);
|
|
QA_EXPECT(message.contains(QStringLiteral("使用设置页默认城市")));
|
|
QA_EXPECT(message.contains(QStringLiteral("西安,中国当前天气:晴")));
|
|
QA_EXPECT(message.contains(QStringLiteral("28.5℃")));
|
|
}
|
|
|
|
QA_TEST(WeatherFormatter, FormatsAmbiguousLocationAndMissingData)
|
|
{
|
|
WeatherReport report;
|
|
report.query.kind = WeatherQueryKind::Current;
|
|
report.location.cityName = QStringLiteral("Springfield");
|
|
report.location.countryName = QStringLiteral("United States");
|
|
report.hasLocationAmbiguity = true;
|
|
report.locationCandidates = {
|
|
{QStringLiteral("Springfield"), QStringLiteral("Illinois"), QStringLiteral("United States"), {}, 0.0, 0.0},
|
|
{QStringLiteral("Springfield"), QStringLiteral("Missouri"), QStringLiteral("United States"), {}, 0.0, 0.0},
|
|
};
|
|
|
|
const QString message = WeatherSummaryFormatter().format(report);
|
|
QA_EXPECT(message.contains(QStringLiteral("可能存在同名城市")));
|
|
QA_EXPECT(message.contains(QStringLiteral("Missouri")));
|
|
QA_EXPECT(message.contains(QStringLiteral("缺少当前天气")));
|
|
}
|