testing: renamed LinkedItems tests, added "no ID" variants
Numbering Client::Source::LinkedItems_xxx with xxx being a number is confusing, in particular when the same number stands for different test data. Now each set of linked items has an additional, unique name which is used for Client::Source::LinkedItems<Name>. Done in combination with adding more linked item tests and slightly reorganizing the logic for adding them: - a default set with VTIMEZONE is added in all cases - some SyncML servers override that default set - others, in particular peers accessed via their own backend, enable additional Client::Source tests on a case-by-case basis Exchange is only tested with its own default set (with "Standard Timezone" as TZID) and the all-day recurring set (as before). All other CalDAV servers are now also tested with the all-day set (previously exclusive to Exchange) and local floating time (= no TZID, new). Google CalDAV can't be tested with local time because it converts such events into the time zone of the current user. All-day events need special test data because Google adds a time to the UNTIL clause (http://code.google.com/p/google-caldav-issues/issues/detail?id=63). synccompare also needs to ignore that Google adds a redundant VTIMEZONE to the all-day test cases. Finally, Client::Source tests for updating a child event (with and without parent) without UID and RECURRENCE-ID inside the payload were added. These properties are removed via text operations. The expectation is that the source is able to add them back (if needed) based on the meta information that it has about the existing item. The file source is unable to do that. When using it in an HTTP server, the server will look to peers like a peer which doesn't support the semantic (which indeed it doesn't) and thus the client will add back the fields.
This commit is contained in:
parent
03d3c720ba
commit
741d55e8bf
|
@ -324,7 +324,10 @@ struct ClientTestConfig {
|
|||
* One example for main and subordinate items are a recurring
|
||||
* iCalendar 2.0 event and a detached recurrence.
|
||||
*/
|
||||
typedef std::vector<std::string> LinkedItems_t;
|
||||
typedef class LinkedItems : public std::vector<std::string> {
|
||||
public:
|
||||
std::string m_name; /**< used as Client::Source::LinkedItems<m_name> */
|
||||
} LinkedItems_t;
|
||||
|
||||
/**
|
||||
* The linked items may exist in different variations (outer vector).
|
||||
|
|
|
@ -118,6 +118,71 @@ static SyncMode OneWayFromLocalMode()
|
|||
return isServerMode() ? SYNC_ONE_WAY_FROM_SERVER : SYNC_ONE_WAY_FROM_CLIENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* remove a certain property from buffer, return removed line
|
||||
*/
|
||||
static string stripProperty(std::string &data, const std::string &prop)
|
||||
{
|
||||
std::string res;
|
||||
|
||||
size_t start = data.find(prop);
|
||||
if (start != data.npos) {
|
||||
size_t end = data.find('\n', start);
|
||||
if (end != data.npos) {
|
||||
size_t len = end + 1 - start;
|
||||
res = data.substr(start, len);
|
||||
data.erase(start, len);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* insert a property (must include line end) before the end of an item
|
||||
*/
|
||||
static void insertProperty(std::string &data,
|
||||
const std::string &prop,
|
||||
const std::string &endProp = "END:VEVENT")
|
||||
{
|
||||
size_t pos = data.find(endProp);
|
||||
data.insert(pos, prop);
|
||||
}
|
||||
|
||||
/**
|
||||
* remove parameter in all properties
|
||||
*/
|
||||
static void stripParameters(std::string &data,
|
||||
const std::string ¶m)
|
||||
{
|
||||
while (true) {
|
||||
size_t start = data.find(";" + param + "=");
|
||||
if (start == data.npos) {
|
||||
break;
|
||||
}
|
||||
size_t end = data.find_first_of(";:", start + 1);
|
||||
if (end == data.npos) {
|
||||
break;
|
||||
}
|
||||
data.erase(start, end - start);
|
||||
}
|
||||
}
|
||||
|
||||
static void stripComponent(std::string &data,
|
||||
const std::string &comp)
|
||||
{
|
||||
size_t start = data.find("BEGIN:" + comp);
|
||||
if (start != data.npos) {
|
||||
size_t end = data.find("END:" + comp);
|
||||
if (end != data.npos) {
|
||||
end = data.find('\n', end);
|
||||
if (end != data.npos) {
|
||||
data.erase(start, end + 1 - start);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Using this pointer automates the open()/beginSync()/endSync()/close()
|
||||
* life cycle: it automatically calls these functions when a new
|
||||
|
@ -287,12 +352,16 @@ void LocalTests::addTests() {
|
|||
|
||||
// create a sub-suite for each set of linked items
|
||||
for (int i = 0; i < (int)config.m_linkedItems.size(); i++) {
|
||||
CppUnit::TestSuite *linked = new CppUnit::TestSuite(getName() + StringPrintf("::LinkedItems_%d", i));
|
||||
const ClientTestConfig::LinkedItems_t &items = config.m_linkedItems[i];
|
||||
CppUnit::TestSuite *linked = new CppUnit::TestSuite(getName() + "::LinkedItems" + items.m_name);
|
||||
ADD_TEST_TO_SUITE(linked, LocalTests, testLinkedItemsParent);
|
||||
if (config.m_linkedItemsRelaxedSemantic) {
|
||||
ADD_TEST_TO_SUITE(linked, LocalTests, testLinkedItemsChild);
|
||||
}
|
||||
ADD_TEST_TO_SUITE(linked, LocalTests, testLinkedItemsParentChild);
|
||||
if (items[1].find("RECURRENCE-ID") != items[1].npos) {
|
||||
ADD_TEST_TO_SUITE(linked, LocalTests, testLinkedItemsInsertBothUpdateChildNoIDs);
|
||||
}
|
||||
if (config.m_linkedItemsRelaxedSemantic) {
|
||||
ADD_TEST_TO_SUITE(linked, LocalTests, testLinkedItemsChildParent);
|
||||
}
|
||||
|
@ -312,6 +381,9 @@ void LocalTests::addTests() {
|
|||
ADD_TEST_TO_SUITE(linked, LocalTests, testLinkedItemsParentUpdate);
|
||||
if (config.m_linkedItemsRelaxedSemantic) {
|
||||
ADD_TEST_TO_SUITE(linked, LocalTests, testLinkedItemsUpdateChild);
|
||||
if (items[1].find("RECURRENCE-ID") != items[1].npos) {
|
||||
ADD_TEST_TO_SUITE(linked, LocalTests, testLinkedItemsUpdateChildNoIDs);
|
||||
}
|
||||
}
|
||||
ADD_TEST_TO_SUITE(linked, LocalTests, testLinkedItemsInsertBothUpdateChild);
|
||||
ADD_TEST_TO_SUITE(linked, LocalTests, testLinkedItemsInsertBothUpdateParent);
|
||||
|
@ -1725,17 +1797,77 @@ void LocalTests::testLinkedItemsInsertBothUpdateParent() {
|
|||
}
|
||||
}
|
||||
|
||||
// - insert parent and child
|
||||
// - update child *without* UID and RECURRENCE-ID: source expected to re-insert them
|
||||
void LocalTests::testLinkedItemsInsertBothUpdateChildNoIDs() {
|
||||
ClientTestConfig::LinkedItems_t items = getParentChildData();
|
||||
|
||||
CPPUNIT_ASSERT_NO_THROW(deleteAll(createSourceA));
|
||||
std::string parent, child;
|
||||
std::string parentData, childData;
|
||||
TestingSyncSourcePtr copy;
|
||||
|
||||
// add parent and child, then update child
|
||||
CPPUNIT_ASSERT_NO_THROW(parent = insert(createSourceA, items[0], false, &parentData));
|
||||
CPPUNIT_ASSERT_NO_THROW(child = insert(createSourceA, items[1], false, &childData));
|
||||
|
||||
// remove UID and RECURRENCE-ID before updating
|
||||
std::string reducedChildData = items[1];
|
||||
std::string uid = stripProperty(reducedChildData, "UID");
|
||||
std::string rid = stripProperty(reducedChildData, "RECURRENCE-ID");
|
||||
CPPUNIT_ASSERT_NO_THROW(child = updateItem(createSourceA, config, child, reducedChildData, &childData));
|
||||
|
||||
// compare
|
||||
SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceA()));
|
||||
insertProperty(childData, uid, "END:VEVENT");
|
||||
insertProperty(childData, rid, "END:VEVENT");
|
||||
CPPUNIT_ASSERT_NO_THROW(compareDatabases(*copy, &parentData, &childData, NULL));
|
||||
}
|
||||
|
||||
// - insert child
|
||||
// - update child *without* UID and RECURRENCE-ID: source expected to re-insert them
|
||||
void LocalTests::testLinkedItemsUpdateChildNoIDs() {
|
||||
ClientTestConfig::LinkedItems_t items = getParentChildData();
|
||||
|
||||
CPPUNIT_ASSERT_NO_THROW(deleteAll(createSourceA));
|
||||
std::string child;
|
||||
std::string childData;
|
||||
TestingSyncSourcePtr copy;
|
||||
|
||||
// add child, then update child
|
||||
CPPUNIT_ASSERT_NO_THROW(child = insert(createSourceA, items[1], false, &childData));
|
||||
|
||||
// remove UID and RECURRENCE-ID before updating
|
||||
std::string reducedChildData = items[1];
|
||||
std::string uid = stripProperty(reducedChildData, "UID");
|
||||
std::string rid = stripProperty(reducedChildData, "RECURRENCE-ID");
|
||||
CPPUNIT_ASSERT_NO_THROW(child = updateItem(createSourceA, config, child, reducedChildData, &childData));
|
||||
|
||||
// compare
|
||||
SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceA()));
|
||||
insertProperty(childData, uid, "END:VEVENT");
|
||||
insertProperty(childData, rid, "END:VEVENT");
|
||||
CPPUNIT_ASSERT_NO_THROW(compareDatabases(*copy, &childData, NULL));
|
||||
}
|
||||
|
||||
ClientTestConfig::LinkedItems_t LocalTests::getParentChildData()
|
||||
{
|
||||
// extract _%d suffix and use it as index for our config
|
||||
// extract suffix and use it as index for our config
|
||||
std::string test = getCurrentTest();
|
||||
const std::string testname = "LinkedItems_";
|
||||
const std::string testname = "LinkedItems";
|
||||
size_t off = test.find(testname);
|
||||
CPPUNIT_ASSERT(off != test.npos);
|
||||
int i = atoi(test.c_str() + off + testname.size());
|
||||
CPPUNIT_ASSERT(i >= 0 && i < (int)config.m_linkedItems.size());
|
||||
CPPUNIT_ASSERT(config.m_linkedItems[i].size() >= 2);
|
||||
return config.m_linkedItems[i];
|
||||
off += testname.size();
|
||||
size_t end = test.find(':', off);
|
||||
CPPUNIT_ASSERT(end != test.npos);
|
||||
std::string name = test.substr(off, end - off);
|
||||
BOOST_FOREACH(const ClientTestConfig::LinkedItems_t &items, config.m_linkedItems) {
|
||||
if (items.m_name == name) {
|
||||
return items;
|
||||
}
|
||||
}
|
||||
CPPUNIT_ASSERT_MESSAGE("linked items test data not found", false);
|
||||
return ClientTestConfig::LinkedItems_t();
|
||||
}
|
||||
|
||||
SyncTests::SyncTests(const std::string &name, ClientTest &cl, std::vector<int> sourceIndices, bool isClientA) :
|
||||
|
@ -4909,6 +5041,7 @@ void ClientTest::getTestData(const char *type, Config &config)
|
|||
std::string server = currentServer();
|
||||
// default: time zones + UNTIL in UTC, with VALARM
|
||||
config.m_linkedItems.resize(1);
|
||||
config.m_linkedItems[0].m_name = "Default";
|
||||
config.m_linkedItems[0].resize(2);
|
||||
config.m_linkedItems[0][0] =
|
||||
"BEGIN:VCALENDAR\n"
|
||||
|
@ -4985,8 +5118,12 @@ void ClientTest::getTestData(const char *type, Config &config)
|
|||
"END:VEVENT\n"
|
||||
"END:VCALENDAR\n";
|
||||
|
||||
bool recurringAllDay = false;
|
||||
bool recurringNoTZ = false;
|
||||
|
||||
if (server == "funambol") {
|
||||
// converts UNTIL into floating time - broken?!
|
||||
config.m_linkedItems[0].m_name = "UntilFloatTime";
|
||||
config.m_linkedItems[0][0] =
|
||||
"BEGIN:VCALENDAR\n"
|
||||
"PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"
|
||||
|
@ -5024,8 +5161,9 @@ void ClientTest::getTestData(const char *type, Config &config)
|
|||
"LAST-MODIFIED:20080407T193241Z\n"
|
||||
"END:VEVENT\n"
|
||||
"END:VCALENDAR\n";
|
||||
} else if (server == "mobical") {
|
||||
} else if (server == "mobical") {
|
||||
// UTC time
|
||||
config.m_linkedItems[0].m_name = "UTC";
|
||||
config.m_linkedItems[0][0] =
|
||||
"BEGIN:VCALENDAR\n"
|
||||
"PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"
|
||||
|
@ -5064,8 +5202,9 @@ void ClientTest::getTestData(const char *type, Config &config)
|
|||
"DESCRIPTION:second instance modified\n"
|
||||
"END:VEVENT\n"
|
||||
"END:VCALENDAR\n";
|
||||
} else if (server == "memotoo") {
|
||||
} else if (server == "memotoo") {
|
||||
// local time, except for detached recurrence
|
||||
config.m_linkedItems[0].m_name = "LocalTime";
|
||||
config.m_linkedItems[0][0] =
|
||||
"BEGIN:VCALENDAR\n"
|
||||
"PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"
|
||||
|
@ -5105,6 +5244,7 @@ void ClientTest::getTestData(const char *type, Config &config)
|
|||
"END:VEVENT\n"
|
||||
"END:VCALENDAR\n";
|
||||
} else if (server == "exchange") {
|
||||
config.m_linkedItems[0].m_name = "StandardTZ";
|
||||
BOOST_FOREACH(std::string &item, config.m_linkedItems[0]) {
|
||||
// time zone name changes on server to "Standard Timezone",
|
||||
// with some information stripped
|
||||
|
@ -5117,10 +5257,44 @@ void ClientTest::getTestData(const char *type, Config &config)
|
|||
boost::replace_all(item, "X-LIC-LOCATION:Europe/Berlin\n", "");
|
||||
}
|
||||
|
||||
// also test recurring all-day events with exceptions
|
||||
recurringAllDay = true;
|
||||
} else {
|
||||
// in particular for Google Calendar: also try with
|
||||
// VALARM, because testing showed that the server works
|
||||
// differently with and without VALARM data included
|
||||
config.m_linkedItems.resize(2);
|
||||
config.m_linkedItems[1].m_name = "WithVALARM";
|
||||
config.m_linkedItems[1].resize(2);
|
||||
config.m_linkedItems[1][0] =
|
||||
const std::string valarm =
|
||||
"BEGIN:VALARM\n"
|
||||
"ACTION:DISPLAY\n"
|
||||
"DESCRIPTION:This is an event reminder\n"
|
||||
"TRIGGER;VALUE=DURATION;RELATED=START:-PT1H\n"
|
||||
"X-EVOLUTION-ALARM-UID:foo@bar\n"
|
||||
"END:VALARM\nEND:VEVENT";
|
||||
config.m_linkedItems[1][0] = config.m_linkedItems[0][0];
|
||||
boost::replace_first(config.m_linkedItems[1][0], "END:VEVENT", valarm);
|
||||
config.m_linkedItems[1][1] = config.m_linkedItems[0][1];
|
||||
boost::replace_first(config.m_linkedItems[1][1], "END:VEVENT", valarm);
|
||||
|
||||
// also enable other linked item variants
|
||||
recurringAllDay = true;
|
||||
recurringNoTZ = true;
|
||||
}
|
||||
|
||||
if (boost::starts_with(server, "google")) {
|
||||
// converts local time into time zone of the user,
|
||||
// which breaks the test
|
||||
recurringNoTZ = false;
|
||||
}
|
||||
|
||||
if (recurringAllDay) {
|
||||
// also test recurring all-day events with exceptions
|
||||
size_t index = config.m_linkedItems.size();
|
||||
config.m_linkedItems.resize(index + 1);
|
||||
config.m_linkedItems[index].m_name = "AllDay";
|
||||
config.m_linkedItems[index].resize(2);
|
||||
config.m_linkedItems[index][0] =
|
||||
"BEGIN:VCALENDAR\n"
|
||||
"PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"
|
||||
"VERSION:2.0\n"
|
||||
|
@ -5139,7 +5313,18 @@ void ClientTest::getTestData(const char *type, Config &config)
|
|||
"LAST-MODIFIED:20080407T193241Z\n"
|
||||
"END:VEVENT\n"
|
||||
"END:VCALENDAR\n";
|
||||
config.m_linkedItems[1][1] =
|
||||
|
||||
// workaround for http://code.google.com/p/google-caldav-issues/issues/detail?id=63
|
||||
// Google CalDAV inserts a time into the UNTIL clause, do the same in the
|
||||
// reference data.
|
||||
if (boost::starts_with(server, "google")) {
|
||||
config.m_linkedItems[index].m_name = "AllDayGoogle";
|
||||
boost::replace_first(config.m_linkedItems[index][0],
|
||||
"UNTIL=20080420",
|
||||
"UNTIL=20080420T070000Z");
|
||||
}
|
||||
|
||||
config.m_linkedItems[index][1] =
|
||||
"BEGIN:VCALENDAR\n"
|
||||
"PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"
|
||||
"VERSION:2.0\n"
|
||||
|
@ -5158,23 +5343,20 @@ void ClientTest::getTestData(const char *type, Config &config)
|
|||
"DESCRIPTION:second instance modified\n"
|
||||
"END:VEVENT\n"
|
||||
"END:VCALENDAR\n";
|
||||
} else {
|
||||
// in particular for Google Calendar: also try with
|
||||
// VALARM, because testing showed that the server works
|
||||
// differently with and without VALARM data included
|
||||
config.m_linkedItems.resize(2);
|
||||
config.m_linkedItems[1].resize(2);
|
||||
const std::string valarm =
|
||||
"BEGIN:VALARM\n"
|
||||
"ACTION:DISPLAY\n"
|
||||
"DESCRIPTION:This is an event reminder\n"
|
||||
"TRIGGER;VALUE=DURATION;RELATED=START:-PT1H\n"
|
||||
"X-EVOLUTION-ALARM-UID:foo@bar\n"
|
||||
"END:VALARM\nEND:VEVENT";
|
||||
config.m_linkedItems[1][0] = config.m_linkedItems[0][0];
|
||||
boost::replace_first(config.m_linkedItems[1][0], "END:VEVENT", valarm);
|
||||
config.m_linkedItems[1][1] = config.m_linkedItems[0][1];
|
||||
boost::replace_first(config.m_linkedItems[1][1], "END:VEVENT", valarm);
|
||||
}
|
||||
|
||||
if (recurringNoTZ) {
|
||||
// also test recurring event with no timezone
|
||||
size_t index = config.m_linkedItems.size();
|
||||
config.m_linkedItems.resize(index + 1);
|
||||
config.m_linkedItems[index].m_name = "NoTZ";
|
||||
config.m_linkedItems[index].resize(2);
|
||||
config.m_linkedItems[index][0] = config.m_linkedItems[0][0];
|
||||
config.m_linkedItems[index][1] = config.m_linkedItems[0][1];
|
||||
stripComponent(config.m_linkedItems[index][0], "VTIMEZONE");
|
||||
stripParameters(config.m_linkedItems[index][0], "TZID");
|
||||
stripComponent(config.m_linkedItems[index][1], "VTIMEZONE");
|
||||
stripParameters(config.m_linkedItems[index][1], "TZID");
|
||||
}
|
||||
|
||||
config.m_templateItem = config.m_insertItem;
|
||||
|
|
|
@ -589,6 +589,8 @@ public:
|
|||
virtual void testLinkedItemsUpdateChild();
|
||||
virtual void testLinkedItemsInsertBothUpdateChild();
|
||||
virtual void testLinkedItemsInsertBothUpdateParent();
|
||||
virtual void testLinkedItemsInsertBothUpdateChildNoIDs();
|
||||
virtual void testLinkedItemsUpdateChildNoIDs();
|
||||
|
||||
/** retrieve right set of items for running test */
|
||||
ClientTestConfig::LinkedItems_t getParentChildData();
|
||||
|
|
|
@ -387,8 +387,8 @@ def step2(resultdir, result, servers, indents, srcdir, shellprefix, backenddir):
|
|||
casename = m.group(3)
|
||||
# special case grouping of some tests: include group inside casename instead of
|
||||
# format, example:
|
||||
# <path>/Client_Source_apple_caldav_LinkedItems_1_testLinkedItemsParent
|
||||
m = re.match(r'(.*)_(LinkedItems_\d+)', format)
|
||||
# <path>/Client_Source_apple_caldav_LinkedItemsDefault_testLinkedItemsParent
|
||||
m = re.match(r'(.*)_(LinkedItems\w+)', format)
|
||||
if m:
|
||||
format = m.group(1)
|
||||
casename = m.group(2) + '::' + casename
|
||||
|
@ -411,9 +411,6 @@ def step2(resultdir, result, servers, indents, srcdir, shellprefix, backenddir):
|
|||
indents.append(indent)
|
||||
# must avoid :: in XML
|
||||
tag = casename.replace('::', '__')
|
||||
# special case LinkedItems_1::testLinkedItems...: shorten it
|
||||
# if tag.startswith('LinkedItems_'):
|
||||
# tag = tag.split('_', 1)[1]
|
||||
result.write(indent+'<'+tag+'>')
|
||||
match=format+'::'+casename
|
||||
matchOk=match+": okay \*\*\*"
|
||||
|
|
|
@ -923,6 +923,15 @@ evolutiontest = SyncEvolutionTest("evolution", compile,
|
|||
"", options.shell,
|
||||
"Client::Source SyncEvolution",
|
||||
[],
|
||||
"CLIENT_TEST_SKIP="
|
||||
"Client::Source::file_event::LinkedItemsDefault::testLinkedItemsInsertBothUpdateChildNoIDs,"
|
||||
"Client::Source::file_event::LinkedItemsDefault::testLinkedItemsUpdateChildNoIDs,"
|
||||
"Client::Source::file_event::LinkedItemsWithVALARM::testLinkedItemsInsertBothUpdateChildNoIDs,"
|
||||
"Client::Source::file_event::LinkedItemsWithVALARM::testLinkedItemsUpdateChildNoIDs,"
|
||||
"Client::Source::file_event::LinkedItemsAllDay::testLinkedItemsInsertBothUpdateChildNoIDs,"
|
||||
"Client::Source::file_event::LinkedItemsAllDay::testLinkedItemsUpdateChildNoIDs,"
|
||||
"Client::Source::file_event::LinkedItemsNoTZ::testLinkedItemsInsertBothUpdateChildNoIDs,"
|
||||
"Client::Source::file_event::LinkedItemsNoTZ::testLinkedItemsUpdateChildNoIDs",
|
||||
testPrefix=options.testprefix)
|
||||
context.add(evolutiontest)
|
||||
|
||||
|
@ -950,8 +959,10 @@ test = SyncEvolutionTest("googlecalendar", compile,
|
|||
"CLIENT_TEST_MODE=server " # for Client::Sync
|
||||
"CLIENT_TEST_FAILURES="
|
||||
# http://code.google.com/p/google-caldav-issues/issues/detail?id=61 "cannot remove detached recurrence"
|
||||
"Client::Source::google_caldav::LinkedItems_0::testLinkedItemsRemoveNormal,"
|
||||
"Client::Source::google_caldav::LinkedItems_1::testLinkedItemsRemoveNormal,"
|
||||
"Client::Source::google_caldav::LinkedItemsDefault::testLinkedItemsRemoveNormal,"
|
||||
"Client::Source::google_caldav::LinkedItemsNoTZ::testLinkedItemsRemoveNormal,"
|
||||
"Client::Source::google_caldav::LinkedItemsWithVALARM::testLinkedItemsRemoveNormal,"
|
||||
"Client::Source::google_caldav::LinkedItemsAllDayGoogle::testLinkedItemsRemoveNormal,"
|
||||
,
|
||||
testPrefix=options.testprefix)
|
||||
context.add(test)
|
||||
|
|
|
@ -297,6 +297,21 @@ sub NormalizeItem {
|
|||
while (s/^(\w+)([^:\n]*);$strip=\d+/$1$2/mg) {}
|
||||
}
|
||||
|
||||
# strip redundant VTIMEZONE definitions (happen to be
|
||||
# added by Google CalDAV server when storing an all-day event
|
||||
# which doesn't need any time zone definition)
|
||||
# http://code.google.com/p/google-caldav-issues/issues/detail?id=63
|
||||
while (m/(BEGIN:VTIMEZONE.*?TZID:([^\n]*)\n.*?END:VTIMEZONE\n)/gs) {
|
||||
my $def = $1;
|
||||
my $tzid = $2;
|
||||
# used as parameter?
|
||||
if (! m/;TZID=$tzid/) {
|
||||
# no, remove definition
|
||||
$def =~ s/([.*?!+{}])/\\$1/g;
|
||||
s!$def!!s;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$full_timezones) {
|
||||
# Strip trailing digits from TZID. They are appended by
|
||||
# Evolution and SyncEvolution to distinguish VTIMEZONE
|
||||
|
|
Loading…
Reference in New Issue