History | Log In     View a printable version of the current page.  
Issue Details (XML | Word | Printable)

Key: QUARTZ-549
Type: Bug Bug
Status: Closed Closed
Resolution: Fixed
Priority: Major Major
Assignee: Aaron Craven
Reporter: Pedro Ferreira
Votes: 1
Watchers: 3
Operations

If you were logged in you would be able to see more operations.
Quartz Scheduler

NthIncludedDayTriggerTest won't pass

Created: 28/Dec/06 06:49 PM   Updated: 12/Apr/08 11:10 AM
Component/s: Core
Affects Version/s: 1.6
Fix Version/s: 1.6.1

File Attachments: 1. File quartz-519-01.diff (3 kb)
2. File quartz-519-02.diff (2 kb)
3. File quartz-519-03.diff (6 kb)
4. File QUARTZ-519-04.diff (1 kb)
5. File QUARTZ-519-05.diff (1 kb)
6. File QUARTZ-549-06.diff (5 kb)
7. File quartz-549-07.diff (2 kb)

Environment:
Linux 2.6.x
Eclipse
JDK 1.5


 Description  « Hide
The unit test "testGetFireTimeAfter()" for "NthIncludedDayTriggerTest" fails. The last assertion seems to be the problem.

  // Test weekly
        NthIncludedDayTrigger weeklyTrigger = new NthIncludedDayTrigger();
        weeklyTrigger.setIntervalType(NthIncludedDayTrigger.INTERVAL_TYPE_WEEKLY);
        weeklyTrigger.setStartTime(startCalendar.getTime());
        weeklyTrigger.setN(3);
        weeklyTrigger.setFireAtTime("14:35:15");

        targetCalendar.set(2005, Calendar.JUNE, 7, 14, 35, 15);
        nextFireTime = weeklyTrigger.getFireTimeAfter(new Date(startCalendar.getTime().getTime() + 1000));
        assertEquals(targetCalendar.getTime(), nextFireTime);

If I turn:
weeklyTrigger.setN(3);
to:
weeklyTrigger.setN(2);

It will pass.

Is there any explanation for this?

Thanks.

 All   Comments   Change History      Sort Order:
Henri Yandell - [04/Jan/07 02:27 PM ]
It passes for me (once I remove the clover bits from osbuild.xml).

I'm running on OS X, timezone = PST, Java = 1.5.0_06, Ant 1.7.0, Locale is US_ENGLISH (as far as I know).

As a minor improvement, I imagine the test should be changed to say:

weeklyTrigger.setN( TriggerUtils.TUESDAY )

What's the locale/timezone for the machine you're running this on? Maybe that might provide a clue. I'm especially thinking in terms of the first day of the week being a different day than it is in US_ENGLISH - line 774 of NthIncludedDayTrigger says "//move to the first day of the week (SUNDAY)" and if your first day is MONDAY then that would make sense of the need to switch from 3 to 2 above.

Another option is that the javadoc talks about holidays and weekends being skipped - in your locale the holiday may clash with the unit test; however I think the first day of week reason above is most likely.

Pedro Ferreira - [04/Jan/07 05:15 PM ]
Ok, I'm using the pt_PT locale. As it seems, it doesn't start weeks on Sunday... http://www-950.ibm.com/software/globalization/icu/demo/locales/?d_=en&_=pt_PT says it starts at day 2 (what??)... Anyway, i believe the unit tests should assume a specific locale, no?

Thanks for all the info,

Pedro

Henri Yandell - [25/Jan/07 05:05 PM ]
I think it's more that NthIncludedDayTrigger's shouldn't be used in locales without Sunday as a start day. So the unit test is doing the right thing and showing a problem in the source.

James House - [17/Mar/07 12:18 AM ]
At the minimum we should document this limitation/issue in the javadoc of the trigger.

Henri Yandell - [21/Aug/07 07:50 AM ]
We should also fix the test so you can build in a different locale; even if the Trigger is useless in said locale.

Henri Yandell - [19/Sep/07 11:35 PM ]
Scratch the 3->TriggerUtils.TUESDAY btw. The 3 means the 3rd included day, if the calendar provided says to ignore a holiday, it's possible that the 3rd day might be a wednesday etc.

Henri Yandell - [19/Sep/07 11:38 PM ]
Maybe Calendar.getFirstDayOfWeek can be used to solve this. First step, need to setup a test to replicate Pedro's problem.

Martin Ellis - [24/Oct/07 03:19 PM ]
Assuming the first day of the week is Sunday or Monday, the value of N for a weekly trigger is:

calendarDay - firstDayOfWeek + 1

where calendarDay is a Calendar.xxxDAY constant, and firstDayOfWeek is found by
Calendar.getInstance().getFirstDayOfWeek().

Attaching quartz-519-01.diff which uses this method in the tests to determine the value of N that should be used for the expected target dates.

(more to follow)

Martin Ellis - [24/Oct/07 03:24 PM ]
Another approach is to set the default locale which is used when running the tests.
If all the NthIncludedDayTrigger tests run in the same locale, then this sort of problem shouldn't occur.

The only problem might be that setting the locale might not work in an environment with certain security manager settings (but do people run tests like that?)

Attaching another diff (an alternative to the first) that sets the locale in the test setup, and restores it in the tear down.

Again, this passes for me on a machine with getFirstDayOfWeek = 2.

Martin Ellis - [24/Oct/07 03:29 PM ]
Last one for now...

This one removes the obligation on the client code to deal with the firstDayOfWeek of the current locale, by providing a setDayOfWeek(int) method, in addition to setN(int).

Hence, for a weekly trigger, the client can call setDayOfWeek(java.util.Calendar.xxxDAY), and not worry about which day the current locale regards as being the first day in the week.

It only works for weekly triggers - as demonstrated by the tests.
Again, this is an alternative to the previous two diffs.

Hope one of these is of some help...

Aaron Craven - [27/Mar/08 10:52 PM ]
Whew! You guys have certainly found quite a complicated one!

It definitely appears that the problem is here (starting at line 776)

        //move to the first day of the week (SUNDAY)
        currCal.add(java.util.Calendar.DAY_OF_MONTH,
                (afterCal.get(java.util.Calendar.DAY_OF_WEEK) - 1) * -1);

This code is assuming that the first day of the week has a value of 0, which evidently is not true. When writing this (and indeed, before I saw this entry) I had no idea there were places in the world that didn't start their weeks on the same day...

Anyhow, the point of the code above is to start checking dates on the first day of the week prior to the "afterDate". I think if we modify it to be sensitive to the first day of the week, we should be okay. That code might be a bit awkward to write though. Basically we need to figure out how many days to back up, but without relying on the values of the Calendar.DAY_OF_WEEK constants. Calendar.SUNDAY has the value 1, but if Monday is the first day of the week, we actually need to subtract 6 days (as though Calendar.SUNDAY == 7).

Now, I just hope that there aren't timezones that do something strange like start months on day 2 or have December as the first month of the year...

Aaron Craven - [27/Mar/08 10:53 PM ]
Oops... This line above:

This code is assuming that the first day of the week has a value of 0, which evidently is not true.

should read

This code is assuming that the first day of the week has a value of 1, which evidently is not always true.

Aaron Craven - [27/Mar/08 11:48 PM ]
Okay, last comment tonight, I promise. I'm attaching a fourth patch. I think it corrects the problem, but I'm not sure how to set up a test, so I'll need someone to help with that. The solution I used is hardly elegant, but I think it will do the trick. I'd appreciate it if this was fairly heavily tested, though...

Martin Ellis - [29/Mar/08 06:20 PM ]
The fourth patch doesn't work here - i.e. the tests still fail.

What's the behaviour for weekly triggers supposed to be?
Is the value you pass to setN(int) supposed to be one of Calendar.xxxDAY?
Or is it supposed to be the number of days since the start of the week?

My second patch might give you an idea of how to set up a test for how it will behave in a different locale.


Also, could someone look at the hunk from AnnualCalendarTest from my patches, please?
That test also still fails.

Thanks.

Martin Ellis - [29/Mar/08 06:33 PM ]

  "Is the value you pass to setN(int) supposed to be one of Calendar.xxxDAY?
  Or is it supposed to be the number of days since the start of the week?"

Just in case it's not obvious, the third patch allows the user to pick which of
these two behaviours they want.

Aaron Craven - [29/Mar/08 10:59 PM ]
Err... neither, really...

NthIncludedDayTrigger has no real use without a Calendar. When interval type is INTERVAL_TYPE_WEEKLY a value N indicates that the trigger should fire:

-on the nth day following the first day of the week that is not excluded by the associated calendar.

So, an example (using next week as an example):
N = 3,
My calendar excludes 4/1/2008,
In my timezone, the first day of the week is Sunday (3/30/2008)

The trigger should fire on 4/2/2008. The 3rd day of the week is actually 4/1/2008, but my Calendar excluded that date. Thus the name NthINCLUDEDDayTrigger.

In the absence of a Calendar, the trigger would indeed default to the Nth day of the week. It SHOULD be the Nth day after the first day of the week, which means in my example above, if the first day of the week is Monday (3/31/2008), the trigger should fire on (4/3/2008). I'm not sure if the tests or the trigger are working correctly for a different timezone, but that is the expected behavior.

I'll try to take a closer look soon.

Martin Ellis - [30/Mar/08 11:02 AM ]
Ah, yes. I understand what you mean. It's been so long since I looked at the issue that I'd forgotten what the class was about.

I think "on the nth day following the first day of the week that is not excluded by the associated calendar" clarifies the desired behaviour: you _do_ want the behaviour to vary, according to which day the locale defines as being the first in the week.

In that case, I don't see how this can be fixed without changing the tests, since they use constant 'N' and then assertEquals using specific dates. The test could be fixed by:

* varying N according to the current locale to suit the expected date (patch 1); or
* varying the expected date according to the current locale to suit N; or
* setting the default locale to use in the tests (patch 2).

I'm not sure whether there are any bugs in the code itself - I'm struggling to understand it.
However, I'd be rather more confident that the tests are in error, based on your explanation above.

Aaron Craven - [30/Mar/08 06:19 PM ]
As far as I can tell, the code did have a bug, in that it was relying on the constant values to help "calculate" the first day of the week. This presupposed that the first day of the week had the value of 1, which it does if the first day of the week is Calendar.SUNDAY. But that is not always true. So my patch corrects this (though not exactly in an elegant fasion).

Now I need to look at the test. I believe you are correct that the test needs to vary according to the local timezone (or at a minimum, test in multiple timezones). I would prefer to vary the expected date, rather than vary N, as it semantically matches what we're about to test better. Varying N feels rather like rigging the test to give me the answer I want, rather than ensuring that the answer I get is correct.

More to come...

Henri, with your permission, I think I'm going to take over this entry... at least temporarily.

Aaron Craven - [30/Mar/08 06:59 PM ]
Okay, both the trigger and the test were wrong, with the test not expecting the correct behavior, and with the trigger not exhibiting the correct behavior. Please apply this patch AND patch QUARTZ-519-04.diff and test.

This works in my timezone (America/New_York), and presumeably, in any timezone where java.util.Calendar.getFirstDayOfWeek() returns java.util.Calendar.SUNDAY. I need to know if it works in locales where this is not true

Aaron Craven - [30/Mar/08 07:00 PM ]
Haha! And I just realized all of our patches are named incorrectly... they all point to QUARTZ-519, which is unrelated...

Aaron Craven - [30/Mar/08 07:01 PM ]
Okay, I lied. I didn't wait on Henri to give permission. You can yell at me later, if you'd like, Henri :-)

Aaron Craven - [30/Mar/08 07:03 PM ]
I'm raising the priority on this to Major, as it will make the trigger work incorrectly in some timezones. It's not a blocker, but I think it's a definite must before the next release.

Henri Yandell - [31/Mar/08 01:14 AM ]
It's all yours. I always upload my latest status on issues, so never worry about diving in and fixing things instead :)

Martin Ellis - [31/Mar/08 02:13 AM ]
OK, I'll try and test this tonight... perhaps late afternoon in your locale ;o)

I'll also file the AnnualCalendarTest problem separately, if it's not already been done.

Martin Ellis - [31/Mar/08 02:42 PM ]
With patches 4 and 5:
testGetFireTimeAfter() passes, but
testTimeZone() fails.

Presumably testTimeZone() was also failing before.
It's the same kind of problem.

Perhaps it's worth surrounding these tests in something like:

        Locale saved = Locale.getDefault();
        Locale[] testLocales = new Locale[] { Locale.US, Locale.UK };
        for (int i = 0; i < testLocales.length; i++) {
            Locale.setDefault(testLocales[i]);

            // test body
        }
        Locale.setDefault(saved);

to avoid regressions?

Aaron Craven - [31/Mar/08 07:19 PM ]
Ugggg... I don't know enough about how TimeZones work to figure this one out. As far as I know, the Trigger is behaving correctly now, but I can't figure out what this test is doing. It's working exclusively with GMT and EST, which both indicate that the first day of the week is Sunday, so I don't see how this test is failing... But as I said, I don't understand the timezone stuff.

Going to request James or one of the other developers take a look.

Martin Ellis - [01/Apr/08 12:46 AM ]
The first day of week is determined by the locale - not the timezone. So for example, here the first day of week is Monday for GMT.

If you specify both timezone and locale to Calendar.getInstance() then everyone will get the same result, regardless of which locale is the default. If you don't specify, then the default locale is used, and we get different results (unless you set the default locale yourself).

If you try surrounding the test in the previous snippet, and tracing through it in the debugger, you'll see what I mean.

Aaron Craven - [02/Apr/08 11:01 PM ]
Thank you for your explanation Martin, it helped me immensely (though I must say the timezone stuff still throws me for a loop).

I'm attaching QUARTZ-549-06.diff as a replacement for QUARTZ-549-05.diff. I think this one will pass, though I'm not sure if the timezone test is actually doing anything useful anymore. I'll get some of the other developers to review it after I know it works in timezones where the first day of the week is not Sunday.

To recap, I'm asking you to test based on the current SVN contents + QUARTZ-519-04.diff + QUARTZ-549-06.diff. 04 patches the trigger, 06 patches the tests.

And I *reaallly* hope this one's right :-)

Aaron Craven - [06/Apr/08 03:29 PM ]
Were you able to test this Martin?

Martin Ellis - [06/Apr/08 05:32 PM ]
Aaron, Sorry for the delay.

We changed to daylight savings time on 31 March, and at work have been dealing with a number of bugs that came to light after that. So I just couldn't bear to look at this bug during the week!

Anyway, I've just tested it now and it seems fine. *Phew!*

Guess this can be closed now. Thanks.

Aaron Craven - [06/Apr/08 05:57 PM ]
Wonderful! I'm going to get at least one of the other developers to review my work before I commit it, due to the complexity here and my own lack of knowledge regarding Quartz's timezone support. After that, I'll commit and resolve the issue.

Thank you very much for all your help and time with this one, Martin. It's very much appreciated.

Aaron Craven - [10/Apr/08 08:34 PM ]
Correction to Trigger. This brings us up to patch 6 for the tests and patch 7 for the trigger.

Aaron Craven - [12/Apr/08 11:10 AM ]
Committed changes (finally). See notes for more information