Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 534 fix date utils test #535

Merged
merged 31 commits into from
Feb 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
c2753c2
Change test offsets to something that will fail in all systems
ggalmazor Jan 30, 2020
cbe9268
Fix the test by refreshing the base value we use to define expectations
ggalmazor Jan 30, 2020
fe64bb2
Use the same assertions for consistency
ggalmazor Jan 30, 2020
6592037
Migrate test to java.time
ggalmazor Jan 30, 2020
0a48fa4
Migrate assertions to something that's easier to reason about
ggalmazor Jan 30, 2020
0d3538e
Move the parseTime test to its own test class
ggalmazor Jan 30, 2020
2252a57
Add default zone to the list and remove unnecessary before/after methods
ggalmazor Jan 30, 2020
72b6797
Migrate DateUtils.parseDateTime tests to use OffsetDateTime.parse
ggalmazor Jan 30, 2020
8e22fa0
Extract the DateUtils.parseDateTime to their own test class
ggalmazor Jan 30, 2020
3d934b7
Add a case without input time zone for completeness
ggalmazor Jan 30, 2020
c3bc3b2
Remove ignored test
ggalmazor Jan 30, 2020
218d603
These members are only used in a test. Move to local
ggalmazor Jan 30, 2020
9ae9636
Simplify by reusing the same test date time
ggalmazor Jan 30, 2020
f06f589
Simplify. XML date strings can be parsed by LocalDate
ggalmazor Jan 30, 2020
7f49465
Remove indirection for easier understanding of the test
ggalmazor Jan 30, 2020
9558475
Simplify by removing repetition
ggalmazor Jan 30, 2020
294f5bf
Extract sanity check to its own test class
ggalmazor Jan 30, 2020
cfbe477
Now the test method's name is more precise and there is no indirection
ggalmazor Jan 30, 2020
5a4ead2
Improve by using human readable dates and java.time parse.
ggalmazor Jan 30, 2020
3df902f
Migrate formatting test to use Locales and values that depend on the JVM
ggalmazor Jan 30, 2020
16f998f
Simplify by leaving only the moving parts
ggalmazor Jan 30, 2020
e8e9dc1
Wrap block execution to ensure it runs in a certain locale safely
ggalmazor Jan 30, 2020
86a2c08
Simplify by requiring less state and making the assertion more explicit
ggalmazor Jan 30, 2020
863da8d
Refactor: move creation of variable to where it's used
ggalmazor Jan 30, 2020
f5e0a0f
Migrate assertions to assertThat for conformity and natural reading
ggalmazor Jan 30, 2020
79c2014
Extract static part from the assertions block
ggalmazor Jan 30, 2020
5f2c10d
Extract test about DateUtils.format localization to its own class
ggalmazor Jan 30, 2020
aae6935
Simplify by replacing the wrapper class with a stream of Locale
ggalmazor Jan 30, 2020
e8343b4
Rename for conformity
ggalmazor Jan 30, 2020
8001c81
Extract method to helper class and reuse
ggalmazor Jan 30, 2020
362a664
Migrate test suite to use SystemHelper withLocale and withTimeZone
ggalmazor Jan 30, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,63 +1,54 @@
package org.javarosa.core.model.data.test;

import static org.javarosa.test.utils.SystemHelper.withTimeZone;
import static org.junit.Assert.assertEquals;

import java.util.Date;
import java.util.TimeZone;
import org.javarosa.core.model.data.TimeData;
import org.javarosa.core.model.utils.DateUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.Date;
import java.util.TimeZone;

import static org.junit.Assert.assertEquals;

/**
This test is intended to show the limitation of {@link TimeData}.

Using this data type in countries which change their time (summer/winter - DST) will cause that forms saved during
wintertime and then edited during summertime (and vice versa) will be treated as saved in a neighbor's timezone.
It's because we have just time and time offset like: 10:00:00.000+02:00 but we don't know when the form has been
saved so we parse it using the current date.

Example:
If we saved 10:00:00.000+02:00 during summertime (in Poland) and we are editing the form during winter time our
timezone is +01:00 not +02:00. As mentioned above javarosa doesn't know that the form has been saved in the same location
but different timezone because of DST so it treats the value like saved in the neighbor timezone
(in Kiev or London for example).

Related issues:
https://github.com/opendatakit/javarosa/pull/478
https://github.com/opendatakit/collect/issues/170
* This test is intended to show the limitation of {@link TimeData}.
* <p>
* Using this data type in countries which change their time (summer/winter - DST) will cause that forms saved during
* wintertime and then edited during summertime (and vice versa) will be treated as saved in a neighbor's timezone.
* It's because we have just time and time offset like: 10:00:00.000+02:00 but we don't know when the form has been
* saved so we parse it using the current date.
* <p>
* Example:
* If we saved 10:00:00.000+02:00 during summertime (in Poland) and we are editing the form during winter time our
* timezone is +01:00 not +02:00. As mentioned above javarosa doesn't know that the form has been saved in the same location
* but different timezone because of DST so it treats the value like saved in the neighbor timezone
* (in Kiev or London for example).
* <p>
* Related issues:
* https://github.com/opendatakit/javarosa/pull/478
* https://github.com/opendatakit/collect/issues/170
*/
public class TimeDataLimitationsTest {

private TimeZone backupTimeZone;

@Before
public void setUp() {
backupTimeZone = TimeZone.getDefault();
}

@After
public void tearDown() {
TimeZone.setDefault(backupTimeZone);
}
public static final TimeZone WARSAW = TimeZone.getTimeZone("Europe/Warsaw");
public static final TimeZone KIEV = TimeZone.getTimeZone("Europe/Kiev");

@Test
public void editingFormsSavedInDifferentTimezoneTest() {
StringWrapper savedTime = StringWrapper.empty();
// A user is in Warsaw (GMT+2) saved a form with the time question
TimeZone.setDefault(TimeZone.getTimeZone("Europe/Warsaw"));

boolean isSummerTime = TimeZone.getDefault().inDaylightTime(new Date());
String savedTime = isSummerTime ? "10:00:00.000+02:00" : "10:00:00.000+01:00";

// A user opens saved form in Warsaw as well - the hour should be the same
TimeData timeData = new TimeData(DateUtils.parseTime(savedTime));
assertEquals("10:00", timeData.getDisplayText());

withTimeZone(WARSAW, () -> {
boolean isSummerTime = TimeZone.getDefault().inDaylightTime(new Date());
savedTime.set(isSummerTime ? "10:00:00.000+02:00" : "10:00:00.000+01:00");

// A user opens saved form in Warsaw as well - the hour should be the same
TimeData timeData = new TimeData(DateUtils.parseTime(savedTime.get()));
assertEquals("10:00", timeData.getDisplayText());
});
// A user travels to Kiev (GMT+3) and opens the saved form again - the hour should be edited +1h
TimeZone.setDefault(TimeZone.getTimeZone("Europe/Kiev"));
timeData = new TimeData(DateUtils.parseTime(savedTime));
assertEquals("11:00", timeData.getDisplayText());
withTimeZone(KIEV, () -> {
TimeData timeData = new TimeData(DateUtils.parseTime(savedTime.get()));
assertEquals("11:00", timeData.getDisplayText());
});

}

@Test
Expand All @@ -67,17 +58,37 @@ public void editingFormsSavedInTheSameLocationButAfterDSTChangeTest() {
dateFields.month = 8;
dateFields.day = 1;

// A user is in Warsaw (during summer time - GMT+2) and saved a form with the time question
TimeZone.setDefault(TimeZone.getTimeZone("Europe/Warsaw"));
String savedTime = "10:00:00.000+02:00";
withTimeZone(WARSAW, () -> {
String savedTime = "10:00:00.000+02:00";

// A user opens saved form in Warsaw and during summertime as well - the hour should be the same
TimeData timeData = new TimeData(DateUtils.parseTimeWithFixedDate(savedTime, dateFields));
assertEquals("10:00", timeData.getDisplayText());

// A user opens saved form in Warsaw as well but during wintertime - the hour is edited -1h (the mentioned limitation)
dateFields.month = 12;
timeData = new TimeData(DateUtils.parseTimeWithFixedDate(savedTime, dateFields));
assertEquals("09:00", timeData.getDisplayText());
});
}

static class StringWrapper {
private String value;

StringWrapper(String value) {
this.value = value;
}

static StringWrapper empty() {
return new StringWrapper("");
}

// A user opens saved form in Warsaw and during summertime as well - the hour should be the same
TimeData timeData = new TimeData(DateUtils.parseTimeWithFixedDate(savedTime, dateFields));
assertEquals("10:00", timeData.getDisplayText());
public String get() {
return value;
}

// A user opens saved form in Warsaw as well but during wintertime - the hour is edited -1h (the mentioned limitation)
dateFields.month = 12;
timeData = new TimeData(DateUtils.parseTimeWithFixedDate(savedTime, dateFields));
assertEquals("09:00", timeData.getDisplayText());
public void set(String value) {
this.value = value;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright (C) 2009 JavaRosa
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/

package org.javarosa.core.model.utils.test;

import static java.time.DayOfWeek.SUNDAY;
import static java.time.Month.JANUARY;
import static java.time.format.TextStyle.SHORT;
import static org.hamcrest.Matchers.is;
import static org.javarosa.test.utils.SystemHelper.withLocale;
import static org.junit.Assert.assertThat;

import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.util.Date;
import java.util.Locale;
import java.util.stream.Stream;
import org.javarosa.core.model.utils.DateUtils;
import org.junit.Test;

public class DateUtilsFormatLocalizationTests {
@Test
public void format_is_localized() {
// Use a Sunday in January for our test
LocalDateTime localDateTime = LocalDateTime.parse("2018-01-07T10:20:30.400");
Date date = Date.from(localDateTime.toInstant(OffsetDateTime.now().getOffset()));

Stream.of(
Locale.ENGLISH,
Locale.forLanguageTag("es-ES"),
Locale.FRENCH
).forEach(locale -> withLocale(locale, l -> {
assertThat(DateUtils.format(date, "%b"), is(JANUARY.getDisplayName(SHORT, l)));
assertThat(DateUtils.format(date, "%a"), is(SUNDAY.getDisplayName(SHORT, l)));
}));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright (C) 2009 JavaRosa
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/

package org.javarosa.core.model.utils.test;

import static java.util.TimeZone.getTimeZone;
import static org.hamcrest.Matchers.is;
import static org.javarosa.core.model.utils.DateUtils.FORMAT_ISO8601;
import static org.javarosa.core.model.utils.DateUtils.formatDateTime;
import static org.javarosa.core.model.utils.DateUtils.parseDateTime;
import static org.javarosa.test.utils.SystemHelper.withTimeZone;
import static org.junit.Assert.assertThat;

import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.TimeZone;
import java.util.stream.Stream;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

@RunWith(Parameterized.class)
public class DateUtilsFormatSanityCheckTests {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could probably be renamed. DateUtilsFormatTests would be better, I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one was tricky to name. "Format" tells half of the story. These tests format a date and then parse it to verify we get the same value back.

@Parameterized.Parameter(value = 0)
public long inputTimestamp;

@Parameterized.Parameters(name = "Input timestamp: {0}")
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][]{
{1300139579000L},
{0}
});
}

@Test
public void sanity_check_iso_format_and_parse_back() {
Date input = new Date(inputTimestamp);
Stream.of(
TimeZone.getDefault(),
getTimeZone("UTC"),
getTimeZone("GMT+12"),
getTimeZone("GMT-13"),
getTimeZone("GMT+0230")
).forEach(timeZone -> withTimeZone(timeZone, () ->
assertThat(parseDateTime(formatDateTime(input, FORMAT_ISO8601)), is(input))));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright (C) 2009 JavaRosa
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/

package org.javarosa.core.model.utils.test;

import static org.hamcrest.Matchers.is;
import static org.javarosa.core.model.utils.DateUtils.getXMLStringValue;
import static org.junit.Assert.assertThat;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.util.Date;
import org.junit.Test;

public class DateUtilsGetXmlStringValueTest {
/**
* This test ensures that the Strings returned
* by the getXMLStringValue function are in
* the proper XML compliant format, which can be
* parsed by LocalDate.parse()
*/
@Test
public void xml_string_is_well_formatted() {
LocalDateTime nowDateTime = LocalDateTime.now();
Date nowDate = Date.from(nowDateTime.toInstant(OffsetDateTime.now().getOffset()));
String nowXmlFormatterDate = getXMLStringValue(nowDate);
assertThat(LocalDate.parse(nowXmlFormatterDate), is(nowDateTime.toLocalDate()));
}

}
Loading