File indexing completed on 2022-11-29 19:02:33

0001 /*
0002     SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include <KOpeningHours/OpeningHours>
0008 
0009 #include <QTest>
0010 
0011 using namespace KOpeningHours;
0012 
0013 class ParserTest : public QObject
0014 {
0015     Q_OBJECT
0016 private Q_SLOTS:
0017     void testSuccess_data()
0018     {
0019         QTest::addColumn<QByteArray>("input");
0020         QTest::addColumn<QByteArray>("expectedOutput");
0021         QTest::addColumn<QByteArray>("expectedSimplifiedOutput");
0022 
0023 #define T(x) QTest::newRow(x) << QByteArray(x) << QByteArray(x) << QByteArray(x)
0024 #define T2(x, y) QTest::newRow(x) << QByteArray(x) << QByteArray(y) << QByteArray(y)
0025 #define T3(x, y, z) QTest::newRow(x) << QByteArray(x) << QByteArray(y ? y : x) << QByteArray(z)
0026         T("24/7");
0027         T("24/7 \"comment\"");
0028         T("24/7 closed");
0029         T("24/7 unknown \"comment\"");
0030         T("unknown \"comment\"");
0031         T("off");
0032         T("Dec off");
0033         T("Dec 25 off");
0034         T("Dec 25-26 off");
0035         T("Dec 24-26,31 off");
0036         T2("Jan 1,6 off", "Jan 01,06 off");
0037         T("Dec 24,25,26");
0038         T2("Jan 03,Dec 04,24 off", "Jan 03,Dec 04,24 off");
0039         T2("Jan 03, Dec 04, 24 off", "Jan 03,Dec 04,24 off");
0040         T2("07:30-20:00; Jan 03,13,23,Dec 04,14,24 off", "07:30-20:00; Jan 03,13,23,Dec 04,14,24 off");
0041         T("Dec 08:00");
0042         T("Dec 08:00-14:00");
0043         T("easter off");
0044         T("easter +1 day off");
0045         T("easter -2 days off");
0046         T2("whitsun off", "easter +49 days off");
0047         T2("whitsun +1 day off", "easter +50 days off");
0048         T("2020");
0049         T("2020-2021");
0050         T("1970-2022/2");
0051         T("2020+");
0052         T("2010,2020,2030");
0053         T("2010-2015,2020-2025,2030");
0054         T("2020-2022 Dec");
0055         T("2020 Dec-2022 Dec");
0056         T("2020-2022 Dec 24-26");
0057         T("2020 Dec 24-26");
0058         T("2021 10:00-20:00");
0059         T("PH off || open"); // https://openingh.openstreetmap.de/evaluation_tool/ says this means always open... bug in opening.js?
0060         T("PH off || unknown \"foo\"");
0061         T("2020 Jan-Apr");
0062         T("1980-2030/4");
0063         T("\"comment\"");
0064         T("PH off || 2020 open");
0065         T("Mo[1-2,4]");
0066         T2("We[-1] + 2 days", "We[-1] +2 days");
0067         T("10:00-16:00/15");
0068         T2("10:00-16:00/90", "10:00-16:00/01:30");
0069         T2("10:00-16:00/1:30", "10:00-16:00/01:30");
0070         T("10:00-10:00");
0071         T("PH off || open || unknown");
0072         T("10:00-12:00+");
0073         T("Jun 15-Aug 15 Mo-Fr 10:00-12:30");
0074         T2("Dec 01 +Su", "Dec 01 Su[1]");
0075         T2("Dec 01 -Su 08:00-12:00", "Dec 01 Su[-1] 08:00-12:00");
0076         T("Aug Mo[1]-Aug Sa[-1] closed");
0077         T("2020/2");
0078         T("\"Außerhalb der Semesterferien\": Mo-Fr 08:00-22:00; Sa,Su 10:00-20:00; \"Innerhalb der Semesterferien\": Mo-Fr 08:00-18:00; Sa,Su 10:00-16:00");
0079         T("PH Mo-Th 14:00-23:00");
0080         T2("Mo-Th PH 14:00-23:00", "PH Mo-Th 14:00-23:00");
0081         T("PH Fr,SH Fr 11:30-02:00");
0082         T2("PH Fr, SH Fr 11:30-02:00", "PH Fr,SH Fr 11:30-02:00");
0083         T2("PH Th,Fr, SH Tu,Fr 11:30-02:00", "PH Th,Fr,SH Tu,Fr 11:30-02:00");
0084         T3("Mo-Th 17:00-23:00, Mo-Th SH 14:00-23:00, Fr 14:00-02:00, Sa 11:30-22:00, Su 11:30-22:00, PH Mo-Th 11:30-23:00, PH Fr, SH Fr 11:30-02:00, PH Sa-Su 11:30-22:00",
0085            "Mo-Th 17:00-23:00, SH Mo-Th 14:00-23:00, Fr 14:00-02:00, Sa 11:30-22:00, Su 11:30-22:00, PH Mo-Th 11:30-23:00, PH Fr,SH Fr 11:30-02:00, PH Sa-Su 11:30-22:00",
0086            "Mo-Th 17:00-23:00, SH Mo-Th 14:00-23:00, Fr 14:00-02:00, Sa,Su 11:30-22:00, PH Mo-Th 11:30-23:00, PH Fr,SH Fr 11:30-02:00, PH Sa-Su 11:30-22:00");
0087         T("Sa,Su,PH,Mo");
0088         T2("Tu-Fr 10:00-17:00; Th 10:00-20:00; Sa,Su,PH Mo 11:00-18:00", "Tu-Fr 10:00-17:00; Th 10:00-20:00; Sa,Su,PH,Mo 11:00-18:00");
0089         T2("Mo,We,Th PH off", "PH Mo,We,Th off");
0090         T2("PH closed \"\"", "PH closed");
0091         T("Mar 01-Mar Su[-1] 17:00-18:00; PH off");
0092         T("Mar 01-Mar Su[-2] Mo 17:00-18:00; PH off");
0093         T("Oct Su[-1]-Dec 31 08:00-18:00");
0094         T("Oct Su[-1]-Dec 31 Su 08:00-18:00");
0095         T("Mar Su[1]-Oct Su[1]: 11:00-20:00; PH 11:00-20:00");
0096         T2("Mo 20:00-26:00", "Mo 20:00-26:00"); // https://github.com/osm-fr/osmose-backend/issues/1344
0097 
0098         // https://bugs.kde.org/show_bug.cgi?id=445963
0099         T("Th[1-2] 09:30-11:45");
0100         T("Th[1,2] 09:30-11:45");
0101         T("Mo[1,3,-1]"); // order as desired, -1 means last
0102         T("Mo[1,3,2]"); // currently not normalized
0103 
0104         // from https://wiki.openstreetmap.org/wiki/Key:opening_hours#Simple_examples
0105         T("Mo-Fr 08:00-17:30");
0106         T("Mo-Fr 08:00-12:00,13:00-17:30");
0107         T("Mo-Fr 08:00-12:00,13:00-17:30; Sa 08:00-12:00");
0108         T("Mo-Fr 08:00-12:00,13:00-17:30; Sa 08:00-12:00; PH off");
0109         T("Mo-Fr 08:00-12:00,13:00-17:30; Sa 08:00-12:00; PH 09:00-12:00");
0110 
0111         // from https://wiki.openstreetmap.org/wiki/Key:opening_hours#Examples
0112         T3("Sa-Su 00:00-24:00", nullptr, "Sa,Su 00:00-24:00");
0113         T("Mo-Fr 08:30-20:00");
0114         T("Mo 10:00-12:00,12:30-15:00; Tu-Fr 08:00-12:00,12:30-15:00; Sa 08:00-12:00");
0115         T("Mo-Su 08:00-18:00; Apr 10-15 off; Jun 08:00-14:00; Aug off; Dec 25 off");
0116         T("Mo-Sa 10:00-20:00; Tu off");
0117         T("Mo-Sa 10:00-20:00; Tu 10:00-14:00");
0118         T("sunrise-sunset");
0119         T("Su 10:00+");
0120         T2("week 1-53/2 Fr 09:00-12:00; week 2-52/2 We 09:00-12:00", "week 01-53/2 Fr 09:00-12:00; week 02-52/2 We 09:00-12:00");
0121         T("Mo-Sa 08:00-13:00,14:00-17:00 || \"by appointment\"");
0122         T3("Su-Tu 11:00-01:00, We-Th 11:00-03:00, Fr 11:00-06:00, Sa 11:00-07:00", nullptr, "Su-Tu 11:00-01:00, We,Th 11:00-03:00, Fr 11:00-06:00, Sa 11:00-07:00");
0123         T("Mo-Su,PH 15:00-03:00; easter -2 days off");
0124 
0125         // from https://openingh.openstreetmap.de/evaluation_tool/
0126         T("Mo-Fr 10:00-20:00; PH off");
0127         T("Mo,Tu,Th,Fr 12:00-18:00; Sa,PH 12:00-17:00; Th[3],Th[-1] off");
0128         T("00:00-24:00; Tu-Su,PH 08:30-09:00 off; Tu-Su 14:00-14:30 off; Mo 08:00-13:00 off");
0129         T3("Fr-Sa 18:00-06:00; PH off", nullptr, "Fr,Sa 18:00-06:00; PH off");
0130         T("Mo 10:00-12:00,12:30-15:00");
0131         T("Mo 10:00-12:00,12:30-15:00; Tu-Fr 08:00-12:00,12:30-15:00; Sa 08:00-12:00");
0132         T("\"only after registration\"; PH off");
0133         T("22:00-23:00; PH off");
0134         T("08:00-11:00; PH off");
0135         T("open; Mo 15:00-16:00 off; PH off");
0136         T("Mo-Su 22:00-23:00; We,PH off");
0137         T("We-Fr 10:00-24:00 open \"it is open\" || \"please call\"; PH off");
0138         T("Mo-Fr 08:00-11:00 || Tu-Th,PH open \"Emergency only\"");
0139         T3("Tu-Th,We 22:00-23:00 open \"Hot meals\"; PH off", nullptr, "Tu-Th 22:00-23:00 open \"Hot meals\"; PH off"); // We redundant
0140         T("Mo 12:00-14:00 open \"female only\", Mo 14:00-16:00 open \"male only\"; PH off");
0141         T("Apr: 22:00-23:00; PH off");
0142         T("Jul-Jan: 22:00-23:00; PH off");
0143         T("Jan-Jul: 22:00-23:00; PH off");
0144         T2("Jul 23-Jan 3: \"needs reservation by phone\"; PH off", "Jul 23-Jan 03: \"needs reservation by phone\"; PH off");
0145         T2("Jul 23-Jan 3: 22:00-23:00 \"Please make a reservation by phone.\"; PH off", "Jul 23-Jan 03: 22:00-23:00 \"Please make a reservation by phone.\"; PH off");
0146         T2("Jul 23-Jan 3: 08:00-11:00 \"Please make a reservation by phone.\"; PH off", "Jul 23-Jan 03: 08:00-11:00 \"Please make a reservation by phone.\"; PH off");
0147         T2("Jan 23-Jul 3: 22:00-23:00 \"Please make a reservation by phone.\"; PH off", "Jan 23-Jul 03: 22:00-23:00 \"Please make a reservation by phone.\"; PH off");
0148         T("Mar Su[-1]-Dec Su[1] -2 days: 22:00-23:00; PH off");
0149         T("Sa[1],Sa[1] +1 day 10:00-12:00 open \"first weekend in the month\"; PH off");
0150         T("Sa[-1],Sa[-1] +1 day 10:00-12:00 open \"last weekend in the month\"; PH off");
0151         T3("Sa-Su 00:00-24:00; PH off", nullptr, "Sa,Su 00:00-24:00; PH off");
0152         T("Mo-Fr 00:00-24:00; PH off");
0153         T("sunrise-sunset open \"Beware of sunburn!\"; PH off");
0154         T("sunset-sunrise open \"Beware of vampires!\"; PH off");
0155         T("(sunrise-00:30)-(sunrise+00:30)");
0156         T("(sunset+01:00)-24:00 || closed \"No drink before sunset!\"; PH off");
0157         T("22:00+; PH off");
0158         T("Tu,PH 23:59-22:59");
0159         T("We-Mo,PH 23:59-22:59");
0160         T2("week 2-52/2 We 00:00-24:00; week 1-53/2 Sa 00:00-24:00; PH off", "week 02-52/2 We 00:00-24:00; week 01-53/2 Sa 00:00-24:00; PH off");
0161         T2("week 4-16 We 00:00-24:00; week 38-42 Sa 00:00-24:00; PH off", "week 04-16 We 00:00-24:00; week 38-42 Sa 00:00-24:00; PH off");
0162         T("2012 easter -2 days-2012 easter +2 days: open \"Around easter\"; PH off");
0163         T("24/7 closed \"always closed\"");
0164         T2("2013,2015,2050-2053,2055/2,2020-2029/3,2060+ Jan 1", "2013,2015,2050-2053,2055/2,2020-2029/3,2060+ Jan 01");
0165         T2("Jan 23-Feb 11,Feb 12 00:00-24:00; PH off", "Jan 23-Feb 11,Feb 12 00:00-24:00; PH off");
0166         T("Apr-Oct Su[2] 14:00-18:00; Aug Su[-1] -1 day 10:00-18:00; Aug Su[-1] 10:00-18:00; PH off");
0167         T("Mo-Fr 08:00-12:00, We 14:00-18:00; Su,PH off"); // open We morning too
0168         T("Mo-Fr 08:00-12:00; We 14:00-18:00; Su,PH off"); // closed We morning
0169         T2("April-September; Mo-Fr 09:00-13:00, 14:00-18:00, Sa 10:00-13:00", "Apr-Sep; Mo-Fr 09:00-13:00,14:00-18:00, Sa 10:00-13:00");
0170 
0171         T("We; PH off");
0172         T("PH");
0173         T("PH Mo-Fr");
0174         T("PH -1 day");
0175         T("SH");
0176         T("SH,PH");
0177         T("PH,SH");
0178         T("We[1-3]");
0179         T("We[3-5]");
0180         T("Sa");
0181         T("Sa[1]");
0182         T("Sa[1-3]");
0183         T("Su[-1,1]");
0184         T("Tu-Th");
0185         T("Fr-Mo");
0186         T("Mo-Su; We \"only after registration\"");
0187         T("Oct: We[1]");
0188 
0189         // from https://github.com/dfaure/DataNovaImportScripts/blob/master/saved_opening_hours
0190         T3("Mo-Tu,Th-Fr 09:30-12:00; 2020 Dec 28 off; 2020 Dec 22,2020 Dec 29 off; We 15:00-17:00; 2020 Dec 23,2020 Dec 30 off; 2020 Dec 24,2020 Dec 31 off; Sa 10:00-12:00; 2020 Dec 26,2021 Jan 02 off; PH off",
0191            "Mo-Tu,Th-Fr 09:30-12:00; 2020 Dec 28 off; 2020 Dec 22,29 off; We 15:00-17:00; 2020 Dec 23,30 off; 2020 Dec 24,31 off; Sa 10:00-12:00; 2020 Dec 26,2021 Jan 02 off; PH off",
0192            "Mo,Tu,Th,Fr 09:30-12:00; 2020 Dec 28 off; 2020 Dec 22,29 off; We 15:00-17:00; 2020 Dec 23,30 off; 2020 Dec 24,31 off; Sa 10:00-12:00; 2020 Dec 26,2021 Jan 02 off; PH off");
0193 
0194         // real-world tests from Osmose that we were handling wrongly
0195         T("Tu-Fr 11:30-14:30 open, 14:30-18:00 open \"pickup only\", 18:00-22:00 open");
0196         T("SH Tu,Th 10:00-19:00");
0197         T2("Apr-Sep 09:00-19:00; Mar-Oct. 09:00-18:00; Nov.-Feb. 09:00-17:00", "Apr-Sep 09:00-19:00; Mar-Oct 09:00-18:00; Nov-Feb 09:00-17:00");
0198         T2("week 23-37 12:30-15:00,20:00-23:00; week 01-22,38-53 off", "week 23-37 12:30-15:00,20:00-23:00; week 01-22,38-53 off");
0199         T("Mo-Sa 09:00-20:00; Su[-2,-1] 12:30-18:00");
0200         T("Su off, Mo-Fr 08:30-13:00; Mo-Th 08:30-13:30,16:00-20:00");
0201         T3("Mo-Tu 09:00-12:00,14:00-18:00, We closed, Th-Sa 09:00-12:00,14:00-18:00; Su 09:30-12:30,14:30-18:00", nullptr,
0202            "Mo,Tu 09:00-12:00,14:00-18:00, We closed, Th-Sa 09:00-12:00,14:00-18:00; Su 09:30-12:30,14:30-18:00");
0203         T2("SH Sep-Jun Mo 10:52-15:52", "SH, Sep-Jun Mo 10:52-15:52"); // likely incorrect, but at least no information loss
0204 
0205         // weekday autocorrect
0206         T2("Tu, Th 13:30-19:00; SH Tu, Th 10:00-19:00; Fr 13:30-18:00; SH Fr 10:00-18:00; We, Sa 10:00-18:00; SH We, Sa 10:00-18:00", "Tu,Th 13:30-19:00; SH Tu,Th 10:00-19:00; Fr 13:30-18:00; SH Fr 10:00-18:00; We,Sa 10:00-18:00; SH We,Sa 10:00-18:00");
0207         T2("Mar Su[-1] - Oct Su[-1] - 1 days: Th 09:00-18:00, Tu 15:00-18:00; Sa 09:00-12:00; Oct Su[-1] - Mar Su[-1] - 1 days: Tu 09:00-17:00, Th 09:00-17:00, Sa 09:00-12:00; Mo, We, Fr, Su, PH Off",
0208            "Mar Su[-1]-Oct Su[-1] -1 day: Th 09:00-18:00, Tu 15:00-18:00; Sa 09:00-12:00; Oct Su[-1]-Mar Su[-1] -1 day: Tu 09:00-17:00, Th 09:00-17:00, Sa 09:00-12:00; Mo,We,Fr,Su,PH off");
0209         T2("Sep 16-Jun 15: Tu-Fr, Sa, Su [1,3] 09:00-14:00; Jun 16-Sep 15: Sa, Su [1,3] 13:00-19:00; Mo off, Su [2,4] off",
0210            "Sep 16-Jun 15: Tu-Fr,Sa,Su[1,3] 09:00-14:00; Jun 16-Sep 15: Sa,Su[1,3] 13:00-19:00; Mo off, Su[2,4] off");
0211         T2("2020-2021 Mo, Tu-Fr, Sa [-1] 09:00-14:00", "2020-2021 Mo,Tu-Fr,Sa[-1] 09:00-14:00");
0212         T2("week 1-3 Mo[2], Tu-Fr, Sa [-1] 09:00-14:00", "week 01-03 Mo[2],Tu-Fr,Sa[-1] 09:00-14:00");
0213         T2("Mo Fr 09:30-12:30 13:30-18:30", "Mo,Fr 09:30-12:30,13:30-18:30");
0214         T2("Mo Fr 09:30-12:30, 13:30-18:30 off", "Mo,Fr 09:30-12:30,13:30-18:30 off");
0215         T2("Mo, We, Fr 06:30-21:30; Tu, Th 09:00-21:30; Sa 09:00-17:00; Su 09:00-14:00", "Mo,We,Fr 06:30-21:30; Tu,Th 09:00-21:30; Sa 09:00-17:00; Su 09:00-14:00");
0216         T2("Lunes a sábado, 9:30 AM-5:30 PM", "Mo-Sa 09:30-17:30"); // bug 445784
0217 
0218         // lists of specific days
0219         T("2021 Dec 26-28,30-31,2022 Jan 02-03 off");
0220         T2("2021 Dec 26-28,Dec 30-31,2022 Jan 02-03 off", "2021 Dec 26-28,30-31,2022 Jan 02-03 off");
0221         T2("2021 Dec 22, 26-28, 29, 31, 2022 Jan 02-03 off", "2021 Dec 22,26-28,29,31,2022 Jan 02-03 off");
0222 
0223         // technically wrong but often found content in OSM for which we have error recovery
0224         T2("So", "Su");
0225         T2("Ph", "PH");
0226         T2("9:00-12:00", "09:00-12:00");
0227         T2("Mo-Fr 09:00-18:30;Sa 09:00-17:00", "Mo-Fr 09:00-18:30; Sa 09:00-17:00");
0228         T2("08:00-12:00;", "08:00-12:00");
0229         T2("14:00-20:00,", "14:00-20:00");
0230         T2("Mo 14:00-21:00; Tu-Th 10:00-21:00; Fr 10:00-18:00;Su, PH off|| \"Samstag zweimal im Monat, Details siehe Webseite\"", "Mo 14:00-21:00; Tu-Th 10:00-21:00; Fr 10:00-18:00; Su,PH off || \"Samstag zweimal im Monat, Details siehe Webseite\"");
0231         T2("Mo-Fr 06:30-12:00, 13:00-18:00", "Mo-Fr 06:30-12:00,13:00-18:00"); // see autocorrect()
0232         T2("we-mo 11:30-14:00, 17:30-22:00; tu off", "We-Mo 11:30-14:00,17:30-22:00; Tu off");
0233         T2("01:00-23:00; ", "01:00-23:00");
0234         T2("02:00-22:00,\n", "02:00-22:00");
0235         T2("Friday 08:00-12:00", "Fr 08:00-12:00");
0236         T2("Sat", "Sa");
0237         T2("december", "Dec");
0238         T2("Dec 24,25,26, Jan 1,6 off", "Dec 24,25,26,Jan 01,06 off");
0239         T2("Dec 24,25,26 open, Jan 1,6 off", "Dec 24,25,26 open, Jan 01,06 off");
0240         T2("Dec 6,4", "Dec 06,04");
0241         T2("Dec 3,2,1", "Dec 03,02,01");
0242         T2("07:30-20:00; Jan 03, 13, 23, Feb 03, 13, 23, Mar 03, 13, 23, Apr 03, 13, 23, Jun 03, 13, 23, Jul 03, 13, 23, Aug 03, 13, 23, Sep 03, 13, 23, Oct 03, 13, 23, Nov 03, 13, 23, Dec 03, 13, 23 off",
0243            "07:30-20:00; Jan 03,13,23,Feb 03,13,23,Mar 03,13,23,Apr 03,13,23,Jun 03,13,23,Jul 03,13,23,Aug 03,13,23,Sep 03,13,23,Oct 03,13,23,Nov 03,13,23,Dec 03,13,23 off");
0244         T2("Apr, May, Oct, Nov, Dec: Mo-Su, 10:00-19:00; Jun-Sep: Mo-Su:10:00-20:00", "Apr,May,Oct,Nov,Dec: Mo-Su, 10:00-19:00; Jun-Sep: Mo-Su 10:00-20:00");
0245 
0246         // Tolerance for incorrect casing
0247         T2("mo-fr 10:00-20:00", "Mo-Fr 10:00-20:00");
0248         T2("jan-feb 10:00-20:00", "Jan-Feb 10:00-20:00");
0249         T2("jan-feb,aug 10:00-20:00", "Jan-Feb,Aug 10:00-20:00");
0250         T2("SUNRISE-SUNSET", "sunrise-sunset");
0251         T2("(SUNrISE-01:00)-(SUnsET+01:00)", "(sunrise-01:00)-(sunset+01:00)");
0252         T2("su,sh off", "Su,SH off");
0253         T2("mo-fr CLOSED", "Mo-Fr closed");
0254 
0255         // Time correction
0256         T2("9h00-12h00", "09:00-12:00");
0257         T2("9h-12h", "09:00-12:00");
0258         T2("5H", "05:00");
0259         T2("06:00am", "06:00");
0260         T2("06:30pm", "18:30");
0261         T2("07:00 am", "07:00");
0262         T2("07:00 pm", "19:00");
0263         T2("5:00AM", "05:00");
0264         T2("5:02 PM", "17:02");
0265         T2("10a", "10:00");
0266         T2("10p", "22:00");
0267         T2("12:00 am", "00:00");
0268         T2("12:00pm", "12:00");
0269         T2("1 a.m", "01:00");
0270         T2("3p.m", "15:00");
0271         T2("12:01a.m.", "00:01");
0272         T2("12:01p.m.", "12:01");
0273         T2("11:59a", "11:59");
0274         T2("11:59p", "23:59");
0275         T2("9h00-12h00,14:00-17:00", "09:00-12:00,14:00-17:00");
0276         T2("9:00 am - 12:00 am", "09:00-24:00");
0277         T2("9 am - 12 am", "09:00-24:00");
0278         T2("11:00 am - 11:00 pm", "11:00-23:00");
0279         T2("09 : 00 - 12 : 00 , 13 : 00 - 19 : 00", "09:00-12:00,13:00-19:00");
0280         T2("10.30am - 4.30pm", "10:30-16:30");
0281         T2("17時00分~23時30分", "17:00-23:30");
0282 
0283         // alternative range separators
0284         T2("Mo-Fri 10am to 7pm, Saturday 11am to 6pm, Sun 11am to 4pm", "Mo-Fr 10:00-19:00, Sa 11:00-18:00, Su 11:00-16:00");
0285         T2("Monday to Friday 8:00AM to 4:30PM", "Mo-Fr 08:00-16:30");
0286         T2("1pm-3pm and 7pm-11pm", "13:00-15:00,19:00-23:00");
0287         T2("8h00 à 12h00 et 13h30 à 18h00", "08:00-12:00,13:30-18:00");
0288         T2("Samedi et Dimanche 5h30 - 12h30 Lundi 13h45 - 15h15", "Sa,Su 05:30-12:30; Mo 13:45-15:15");
0289         T2("Mo-Th 11:00-20:00 Friday & Saturday 11:00-21:00 Sunday 12:00-19:00", "Mo-Th 11:00-20:00; Fr,Sa 11:00-21:00; Su 12:00-19:00");
0290         T2("11:30-14:00、16:30-22:00", "11:30-14:00,16:30-22:00");
0291         T2("Lunedì al Venerdì 08:00-13:00", "Mo-Fr 08:00-13:00");
0292         T2("Monday through Friday 09:00 - 17:00", "Mo-Fr 09:00-17:00");
0293 
0294         // (mis)use of colon as a small-range selector separator
0295         T2("Fr: 17:00-19:00", "Fr 17:00-19:00");
0296         T2("Fr: closed", "Fr closed");
0297         T2("Tu-Su:07:00-00:00", "Tu-Su 07:00-24:00");
0298         T2("Du lundi au vendredi : 9:00-18:00", "Mo-Fr 09:00-18:00");
0299 
0300         // Unicode symbols
0301         T3("Mo–Tu", "Mo-Tu", "Mo,Tu");
0302         T2("13:41", "13:41");
0303         T2("10:00〜19:00", "10:00-19:00");
0304         T2("10:00-17:00", "10:00-17:00");
0305         T2("11:00−23:00", "11:00-23:00");
0306         T2("11:00ー15:00", "11:00-15:00");
0307         T2("We 09:00-18:00\xC2\xA0; Sa 09:00-19:00", "We 09:00-18:00; Sa 09:00-19:00"); // weird space
0308         T2("LUNDI 08:30 – 17:00", "Mo 08:30-17:00");
0309         T3("月,木,金,土,日 11:00-19:00", "Mo,Th,Fr,Sa,Su 11:00-19:00", "Th-Mo 11:00-19:00");
0310         T2("月-土 09:00-18:00", "Mo-Sa 09:00-18:00");
0311         T2("水曜日~土曜日10:00~19:00", "We-Sa 10:00-19:00");
0312         T2("月~土  17:00~23:00", "Mo-Sa 17:00-23:00");
0313         T2("Mo-Fr 08:00-17:00 “Visa Applications\"", "Mo-Fr 08:00-17:00 \"Visa Applications\"");
0314         T2("„nach Vereinbarung“", "\"nach Vereinbarung\"");
0315         T("\"„Termine nach Vereinbarung“\"");
0316 
0317         // non-English
0318         T2("Lundi au Vendredi 8h - 17h en continu", "Mo-Fr 08:00-17:00");
0319         T2("Lun-Ven 08:00-13:00", "Mo-Fr 08:00-13:00");
0320         T2("Lu-Sa 08:00-13:00", "Mo-Sa 08:00-13:00");
0321         T2("Domingo de 9: 00 am. a 1:00 pm", "Su 09:00-13:00");
0322         T2("Gio, Sab e Dom: 9.00-12.30", "Th,Sa,Su 09:00-12:30");
0323         T2("Segunda a Sexta 08:00h a 16:00h", "Mo-Fr 08:00-16:00");
0324         T2("lunedi,mercoledi,venerdi,sabato 09:00-20:00", "Mo,We,Fr,Sa 09:00-20:00");
0325         T2("Mi 08:00-18:00", "We 08:00-18:00");
0326         T2("Du Mardi au Vendredi 11h00-13h30 Le samedi 10h-19h", "Tu-Fr 11:00-13:30; Sa 10:00-19:00");
0327         T2("Senin-Sabtu 09:00-16:00, Minggu 09:00-18:00", "Mo-Sa 09:00-16:00, Su 09:00-18:00");
0328         T2("Lundi-samedi 8H00-12H00 16H00-20H00 dimanche,jours fériés 9H00-12H00 17H00-20H00", "Mo-Sa 08:00-12:00,16:00-20:00; Su,PH 09:00-12:00,17:00-20:00");
0329         T2("De février à novembre", "Feb-Nov");
0330         T3("Me,Je,Ve 8h-12h30, 14h-19h; Sa 8h-12h30, 14h-18h", "We,Th,Fr 08:00-12:30,14:00-19:00; Sa 08:00-12:30,14:00-18:00", "We-Fr 08:00-12:30,14:00-19:00; Sa 08:00-12:30,14:00-18:00");
0331         T2("Mo-Fr: 10:00-18:30 Uhr Sa: 10:00-13:30 Uhr", "Mo-Fr 10:00-18:30; Sa 10:00-13:30");
0332         T2("Montag & Dienstag Ruhetag", "Mo,Tu closed");
0333         T2("Понедельник - Суббота 09:00 - 21:00", "Mo-Sa 09:00-21:00");
0334         T2("Пон-Пт 08:00-20:00, Суб 08:00-19:00", "Mo-Fr 08:00-20:00, Sa 08:00-19:00");
0335         T2("Ноябрь-Март", "Nov-Mar");
0336         T2("с 08:30 до 23:00", "08:30-23:00");
0337         T2("рассвет - сумерки", "dawn-dusk");
0338         T2("от рассвета до сумерек", "dawn-dusk");
0339         T2("восход - закат", "sunrise-sunset");
0340         T2("с восхода пo закат", "sunrise-sunset");
0341         T2("от восхода дo заката", "sunrise-sunset");
0342         T2("Среда открыто; Пятница закрыто; Суббота неизвестно", "We open; Fr closed; Sa unknown");
0343         T2("Вт Выходной", "Tu closed");
0344         T2("Mo Cerrado; Tu Abierto, Fr libre", "Mo closed; Tu open, Fr closed");
0345 
0346         // recovery from wrong rule separators
0347         T2("Fr,Sa 10:00-02:00,Su 10:00-20:00", "Fr,Sa 10:00-02:00, Su 10:00-20:00");
0348         T2("tu-sa 12:00-14:30,mo-sa 18:30-22:00", "Tu-Sa 12:00-14:30, Mo-Sa 18:30-22:00");
0349         T2("Mo 07:00-12:00,Tu 15:00-20:00,We 07:00-12:00,Fr 15:00-20:00", "Mo 07:00-12:00, Tu 15:00-20:00, We 07:00-12:00, Fr 15:00-20:00");
0350         T2("Mo-Fr 09:00-17:00 Sa 09:00-14:00", "Mo-Fr 09:00-17:00; Sa 09:00-14:00");
0351         T2("Friday 11AM–2:30AM Saturday 10AM–3:30AM Sunday 9AM–4:30AM", "Fr 11:00-02:30; Sa 10:00-03:30; Su 09:00-04:30");
0352         T2("Mo-Sa 12:00-15:00; 18:00-24:00", "Mo-Sa 12:00-15:00,18:00-24:00");
0353         T2("Mo-Sa 12:00-15:00; Mo-Sa 18:00-24:00", "Mo-Sa 12:00-15:00,18:00-24:00");
0354         T2("Mo 12:00-15:00; Mo 18:00-24:00", "Mo 12:00-15:00,18:00-24:00");
0355         T("Mo-Sa 12:00-15:00 off; 18:00-24:00");
0356         T("Mo-Sa 12:00-15:00; Mo-Sa 18:00-24:00 closed");
0357         T("Mo 12:00-15:00; Mo 18:00-24:00 \"comment\"");
0358         T("Sa 08:00-12:00; 11:30-13:00 off");
0359 
0360         // recovery from wrong time selector separators
0361         T3("Dimanche Fermé Lundi 08:00 – 12:30 14:00 – 19:00 Mardi 08:00 – 12:30 14:00 – 19:00 Mercredi 08:00 – 12:30 14:00 – 19:00 Jeudi 08:00 – 12:30 14:00 – 19:00 Vendredi 08:00 – 12:30 14:00 – 19:00 Samedi 08:00 – 12:30 14:30 – 18:00",
0362            "Su closed; Mo 08:00-12:30,14:00-19:00; Tu 08:00-12:30,14:00-19:00; We 08:00-12:30,14:00-19:00; Th 08:00-12:30,14:00-19:00; Fr 08:00-12:30,14:00-19:00; Sa 08:00-12:30,14:30-18:00",
0363            "Su closed; Mo-Fr 08:00-12:30,14:00-19:00; Sa 08:00-12:30,14:30-18:00");
0364 
0365         // recovery from slashes abused as rule or timespan separators
0366         T2("09:00-12:00/13:00-19:00", "09:00-12:00,13:00-19:00");
0367         T2("10:00 - 13:30 / 17:00 - 20:30", "10:00-13:30,17:00-20:30");
0368         T2("Mo-Fr 6:00-18:00 / Sa 6:00-13:00 / So 7:00-17:00", "Mo-Fr 06:00-18:00; Sa 06:00-13:00; Su 07:00-17:00");
0369 
0370         // Simplification of the output
0371         T3("Mo 08:00-13:00; Tu 08:00-13:00", nullptr, "Mo,Tu 08:00-13:00");
0372         T3("Mo-Th 08:00-13:00; Sa[1],Su[-1] 08:00-13:00", "Mo-Th 08:00-13:00; Sa[1],Su[-1] 08:00-13:00", "Mo-Th,Sa[1],Su[-1] 08:00-13:00");
0373         T("easter +1 day 08:00-13:00; Tu,Sa,Su 08:00-13:00"); // does not simplify
0374         T3("Mo-Sa 12:00-15:00, Mo-Sa 18:00-24:00", "Mo-Sa 12:00-15:00, Mo-Sa 18:00-24:00", "Mo-Sa 12:00-15:00,18:00-24:00");
0375         T3("Mo 12:00-15:00, Mo 18:00-24:00", "Mo 12:00-15:00, Mo 18:00-24:00", "Mo 12:00-15:00,18:00-24:00");
0376         T3("Mo-We,Fr,Su 08:00-13:00", "Mo-We,Fr,Su 08:00-13:00", "Su-We,Fr 08:00-13:00");
0377         T3("Mo,We,Th,Tu,Sa 08:00-13:00", "Mo,We,Th,Tu,Sa 08:00-13:00", "Mo-Th,Sa 08:00-13:00"); // reordering
0378         T3("Mo-Fr,Tu,We 08:00-13:00", "Mo-Fr,Tu,We 08:00-13:00", "Mo-Fr 08:00-13:00"); // Tu,We already included
0379         T3("Sa-Mo 10:00-23:00, Th 10:00-23:00", "Sa-Mo 10:00-23:00, Th 10:00-23:00", "Sa-Mo,Th 10:00-23:00"); // beginDay > endDay
0380         T3("Sa-Mo 10:00-23:00, Fr 10:00-23:00", "Sa-Mo 10:00-23:00, Fr 10:00-23:00", "Fr-Mo 10:00-23:00"); // beginDay > endDay
0381         T3("Su-Th 10:00-23:00, Fr-Sa 10:00-23:00", "Su-Th 10:00-23:00, Fr-Sa 10:00-23:00", "Mo-Su 10:00-23:00"); // beginDay > endDay
0382         T3("Feb 1-Feb 29 Mo-Su 10:30-20:30; Aug 1-Aug 31 Mo-Fr 10:30-12:00; PH closed",
0383            "Feb 01-29 Mo-Su 10:30-20:30; Aug 01-31 Mo-Fr 10:30-12:00; PH closed",
0384            "Feb Mo-Su 10:30-20:30; Aug Mo-Fr 10:30-12:00; PH closed");
0385 
0386         // complex or creative 24/7 use
0387         T("06:00-01:00 open \"Dining in\" || 24/7 \"Drive-through\"");
0388         T2("Friday and Saturday 24/7 Sunday-Thursday 4:00 am to 12:00 am", "Fr,Sa 00:00-24:00; Su-Th 04:00-24:00"); // bug 445962
0389         // 24/7 as standalone small range selector not supported yet
0390         //T2("Apr-Oct: 24/7; Nov-Mar: Mo-Su 06:00-22:00", "Apr-Oct: 00:00-24:00; Nov-Mar: Mo-Su 06:00-22:00");
0391 
0392         // 4 digit times
0393         T2("0600-1800", "06:00-18:00");
0394         T2("0700-2000", "07:00-20:00");
0395         T2("Tu-Th 8:30-17:30, Fr 8:30-1700", "Tu-Th 08:30-17:30, Fr 08:30-17:00");
0396 #undef T
0397 #undef T2
0398 #undef T3
0399     }
0400 
0401     void testSuccess()
0402     {
0403         QFETCH(QByteArray, input);
0404         QFETCH(QByteArray, expectedOutput);
0405         QFETCH(QByteArray, expectedSimplifiedOutput);
0406         OpeningHours oh(input);
0407         QVERIFY(oh.error() != OpeningHours::SyntaxError);
0408         QCOMPARE(oh.normalizedExpression(), expectedOutput);
0409         QCOMPARE(oh.simplifiedExpression(), expectedSimplifiedOutput);
0410         // verify that simplifiedExpression() doesn't alter `oh`
0411         QCOMPARE(oh.normalizedExpression(), expectedOutput);
0412 
0413         // verify the expressions we generate are parsed correctly as well
0414         OpeningHours oh2(oh.normalizedExpression());
0415         QVERIFY(oh2.error() != OpeningHours::SyntaxError);
0416         QCOMPARE(oh2.normalizedExpression(), oh.normalizedExpression());
0417 
0418         OpeningHours oh3(oh.simplifiedExpression());
0419         QVERIFY(oh3.error() != OpeningHours::SyntaxError);
0420         QCOMPARE(oh3.normalizedExpression(), oh.simplifiedExpression());
0421     }
0422 
0423     void testFail_data()
0424     {
0425         QTest::addColumn<QByteArray>("input");
0426         QTest::addColumn<OpeningHours::Error>("error");
0427 
0428 #define T(x) QTest::newRow(x) << QByteArray(x) << OpeningHours::SyntaxError
0429         T("23/7");
0430         T("24/7 geöffnet");
0431         T("2020-2000");
0432         T("Jan-Apr 1");
0433         T("Feb-2020 Apr 1");
0434         T("Apr 1-Nov");
0435         T("Su[0]");
0436         T("Mo[6]");
0437         T("Mo[-0]");
0438         T("Tu[-6]");
0439         T("Mo[0-5]");
0440         T("We[4-2]");
0441         T("49:00");
0442         T("12:61");
0443         T("60p");
0444 
0445         T("Dec 24-Jan 1,6");
0446         T("Mo, 1:100");
0447 
0448         // from https://wiki.openstreetmap.org/wiki/Key:opening_hours#Common_mistakes
0449         T("7/8-23");
0450         T("07;00-2;00pm");
0451         T("08.00-16.00, public room till 03.00 a.m");
0452         T("09:00-21:00 TEL/072(360)3200");
0453         T("Dining in: 6am to 11pm; Drive thru: 24/7");
0454         T("MWThF: 1200-1800; SaSu: 1200-1700");
0455         T("BAR: Su-Mo 18:00-02:00; Tu-Th 18:00-03:00; Fr-Sa 18:00-04:00; CLUB: Tu-Th 20:00-03:00; Fr-Sa 20:00-04:00");
0456 
0457         // from https://openingh.openstreetmap.de/evaluation_tool/
0458         T("00:00-24:00 week 6 Mo-Su Feb; PH off");
0459         T("monday, Tu, wE, TH 12:00 - 20:00 ; 14:00-16:00 Off ; closed public Holiday");
0460 
0461         // from Osmose, should fail rather than silently drop the last part
0462         T("Mo-Th, Su 17:00-01:00, Fr-Sa 1700:0300");
0463 
0464         // creative uses found in the wild
0465         T("May 01-Jun 15,Sept 01-30 Mo-Fr 10:00-18:00 Sa-Su 09:00-19:00");
0466         T("Jul 22-Aug 18 2019: Tu-Su 10:00-13:00");
0467 #undef T
0468     }
0469 
0470     void testFail()
0471     {
0472         QFETCH(QByteArray, input);
0473         QFETCH(OpeningHours::Error, error);
0474         OpeningHours oh(input);
0475         QCOMPARE(oh.error(), error);
0476     }
0477 
0478     void testValidation_data()
0479     {
0480         QTest::addColumn<QByteArray>("expression");
0481         QTest::addColumn<OpeningHours::Error>("error");
0482 
0483         QTest::newRow("location") << QByteArray("sunrise-sunset") << OpeningHours::MissingLocation;
0484 #ifdef KOPENINGHOURS_VALIDATOR_ONLY
0485         QTest::newRow("PH") << QByteArray("PH off") << OpeningHours::NoError;
0486 #else
0487         QTest::newRow("PH") << QByteArray("PH off") << OpeningHours::MissingRegion;
0488 #endif
0489         QTest::newRow("SH") << QByteArray("SH off") << OpeningHours::UnsupportedFeature;
0490         QTest::newRow("time interval") << QByteArray("10:00-16:00/90") << OpeningHours::IncompatibleMode;
0491         QTest::newRow("time interval 2") << QByteArray("10:00-16:00/1:30") << OpeningHours::IncompatibleMode;
0492         QTest::newRow("week wrap") << QByteArray("week 45-13") << OpeningHours::UnsupportedFeature;
0493         QTest::newRow("single timepoint") << QByteArray("10:00") << OpeningHours::IncompatibleMode;
0494         QTest::newRow("month timepoint") << QByteArray("Dec 08:00") << OpeningHours::IncompatibleMode;
0495         QTest::newRow("wide range selector comment") << QByteArray("\"Außerhalb der Semesterferien\": Mo-Fr 08:00-22:00; Sa-Su 10:00-20:00; \"Innerhalb der Semesterferien\": Mo-Fr 08:00-18:00; Sa-Su 10:00-16:00;") << OpeningHours::UnsupportedFeature;
0496 
0497         QTest::newRow("empty") << QByteArray("") << OpeningHours::Null;
0498         QTest::newRow("empty comment") << QByteArray("\"\"") << OpeningHours::Null;
0499     }
0500 
0501     void testValidation()
0502     {
0503         QFETCH(QByteArray, expression);
0504         QFETCH(OpeningHours::Error, error);
0505 
0506         OpeningHours oh(expression);
0507         QCOMPARE(oh.error(), error);
0508         (void)oh.normalizedExpression(); // don't crash
0509         (void)oh.simplifiedExpression(); // don't crash
0510     }
0511 };
0512 
0513 QTEST_GUILESS_MAIN(ParserTest)
0514 
0515 #include "parsertest.moc"