1 /*
   2  * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 /*
  27  * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos
  28  *
  29  * All rights reserved.
  30  *
  31  * Redistribution and use in source and binary forms, with or without
  32  * modification, are permitted provided that the following conditions are met:
  33  *
  34  *  * Redistributions of source code must retain the above copyright notice,
  35  *    this list of conditions and the following disclaimer.
  36  *
  37  *  * Redistributions in binary form must reproduce the above copyright notice,
  38  *    this list of conditions and the following disclaimer in the documentation
  39  *    and/or other materials provided with the distribution.
  40  *
  41  *  * Neither the name of JSR-310 nor the names of its contributors
  42  *    may be used to endorse or promote products derived from this software
  43  *    without specific prior written permission.
  44  *
  45  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  46  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  47  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  48  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  49  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  50  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  51  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  52  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  53  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  54  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  55  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  56  */
  57 package tck.java.time.temporal;
  58 
  59 import static org.testng.Assert.assertEquals;
  60 import static org.testng.Assert.assertTrue;
  61 
  62 import java.io.ByteArrayInputStream;
  63 import java.io.ByteArrayOutputStream;
  64 import java.io.ObjectInputStream;
  65 import java.io.ObjectOutputStream;
  66 import java.time.Duration;
  67 import java.time.LocalDate;
  68 import java.time.chrono.Chronology;
  69 import java.time.chrono.ChronoLocalDate;
  70 import java.time.chrono.IsoChronology;
  71 import java.time.temporal.ChronoUnit;
  72 import java.time.temporal.Temporal;
  73 import java.time.temporal.TemporalAccessor;
  74 import java.time.temporal.TemporalAdjuster;
  75 import java.time.temporal.TemporalAmount;
  76 import java.time.temporal.TemporalField;
  77 import java.time.temporal.TemporalUnit;
  78 import java.time.temporal.ValueRange;
  79 import java.util.ArrayList;
  80 import java.util.Collections;
  81 import java.util.List;
  82 
  83 import org.testng.Assert;
  84 import org.testng.annotations.DataProvider;
  85 import org.testng.annotations.Test;
  86 
  87 /**
  88  * Test assertions that must be true for the built-in ISO chronology.
  89  */
  90 @Test
  91 public class TestChronoLocalDate {
  92 
  93     //-----------------------------------------------------------------------
  94     // regular data factory for names and descriptions of ISO calendar
  95     //-----------------------------------------------------------------------
  96     @DataProvider(name = "calendars")
  97     Chronology[][] data_of_calendars() {
  98         return new Chronology[][]{
  99             {IsoChronology.INSTANCE},
 100         };
 101     }
 102 
 103     @Test(groups={"tck"}, dataProvider="calendars")
 104     public void test_badWithAdjusterChrono(Chronology chrono) {
 105         LocalDate refDate = LocalDate.of(1900, 1, 1);
 106         ChronoLocalDate date = chrono.date(refDate);
 107         for (Chronology[] clist : data_of_calendars()) {
 108             Chronology chrono2 = clist[0];
 109             ChronoLocalDate date2 = chrono2.date(refDate);
 110             TemporalAdjuster adjuster = new FixedAdjuster(date2);
 111             if (chrono != chrono2) {
 112                 try {
 113                     date.with(adjuster);
 114                     Assert.fail("WithAdjuster should have thrown a ClassCastException");
 115                 } catch (ClassCastException cce) {
 116                     // Expected exception; not an error
 117                 }
 118             } else {
 119                 // Same chronology,
 120                 ChronoLocalDate result = date.with(adjuster);
 121                 assertEquals(result, date2, "WithAdjuster failed to replace date");
 122             }
 123         }
 124     }
 125 
 126     @Test(groups={"tck"}, dataProvider="calendars")
 127     public void test_badPlusAdjusterChrono(Chronology chrono) {
 128         LocalDate refDate = LocalDate.of(1900, 1, 1);
 129         ChronoLocalDate date = chrono.date(refDate);
 130         for (Chronology[] clist : data_of_calendars()) {
 131             Chronology chrono2 = clist[0];
 132             ChronoLocalDate date2 = chrono2.date(refDate);
 133             TemporalAmount adjuster = new FixedAdjuster(date2);
 134             if (chrono != chrono2) {
 135                 try {
 136                     date.plus(adjuster);
 137                     Assert.fail("WithAdjuster should have thrown a ClassCastException");
 138                 } catch (ClassCastException cce) {
 139                     // Expected exception; not an error
 140                 }
 141             } else {
 142                 // Same chronology,
 143                 ChronoLocalDate result = date.plus(adjuster);
 144                 assertEquals(result, date2, "WithAdjuster failed to replace date");
 145             }
 146         }
 147     }
 148 
 149     @Test(groups={"tck"}, dataProvider="calendars")
 150     public void test_badMinusAdjusterChrono(Chronology chrono) {
 151         LocalDate refDate = LocalDate.of(1900, 1, 1);
 152         ChronoLocalDate date = chrono.date(refDate);
 153         for (Chronology[] clist : data_of_calendars()) {
 154             Chronology chrono2 = clist[0];
 155             ChronoLocalDate date2 = chrono2.date(refDate);
 156             TemporalAmount adjuster = new FixedAdjuster(date2);
 157             if (chrono != chrono2) {
 158                 try {
 159                     date.minus(adjuster);
 160                     Assert.fail("WithAdjuster should have thrown a ClassCastException");
 161                 } catch (ClassCastException cce) {
 162                     // Expected exception; not an error
 163                 }
 164             } else {
 165                 // Same chronology,
 166                 ChronoLocalDate result = date.minus(adjuster);
 167                 assertEquals(result, date2, "WithAdjuster failed to replace date");
 168             }
 169         }
 170     }
 171 
 172     @Test(groups={"tck"}, dataProvider="calendars")
 173     public void test_badPlusTemporalUnitChrono(Chronology chrono) {
 174         LocalDate refDate = LocalDate.of(1900, 1, 1);
 175         ChronoLocalDate date = chrono.date(refDate);
 176         for (Chronology[] clist : data_of_calendars()) {
 177             Chronology chrono2 = clist[0];
 178             ChronoLocalDate date2 = chrono2.date(refDate);
 179             TemporalUnit adjuster = new FixedTemporalUnit(date2);
 180             if (chrono != chrono2) {
 181                 try {
 182                     date.plus(1, adjuster);
 183                     Assert.fail("TemporalUnit.doPlus plus should have thrown a ClassCastException" + date.getClass()
 184                             + ", can not be cast to " + date2.getClass());
 185                 } catch (ClassCastException cce) {
 186                     // Expected exception; not an error
 187                 }
 188             } else {
 189                 // Same chronology,
 190                 ChronoLocalDate result = date.plus(1, adjuster);
 191                 assertEquals(result, date2, "WithAdjuster failed to replace date");
 192             }
 193         }
 194     }
 195 
 196     @Test(groups={"tck"}, dataProvider="calendars")
 197     public void test_badMinusTemporalUnitChrono(Chronology chrono) {
 198         LocalDate refDate = LocalDate.of(1900, 1, 1);
 199         ChronoLocalDate date = chrono.date(refDate);
 200         for (Chronology[] clist : data_of_calendars()) {
 201             Chronology chrono2 = clist[0];
 202             ChronoLocalDate date2 = chrono2.date(refDate);
 203             TemporalUnit adjuster = new FixedTemporalUnit(date2);
 204             if (chrono != chrono2) {
 205                 try {
 206                     date.minus(1, adjuster);
 207                     Assert.fail("TemporalUnit.doPlus minus should have thrown a ClassCastException" + date.getClass()
 208                             + ", can not be cast to " + date2.getClass());
 209                 } catch (ClassCastException cce) {
 210                     // Expected exception; not an error
 211                 }
 212             } else {
 213                 // Same chronology,
 214                 ChronoLocalDate result = date.minus(1, adjuster);
 215                 assertEquals(result, date2, "WithAdjuster failed to replace date");
 216             }
 217         }
 218     }
 219 
 220     @Test(groups={"tck"}, dataProvider="calendars")
 221     public void test_badTemporalFieldChrono(Chronology chrono) {
 222         LocalDate refDate = LocalDate.of(1900, 1, 1);
 223         ChronoLocalDate date = chrono.date(refDate);
 224         for (Chronology[] clist : data_of_calendars()) {
 225             Chronology chrono2 = clist[0];
 226             ChronoLocalDate date2 = chrono2.date(refDate);
 227             TemporalField adjuster = new FixedTemporalField(date2);
 228             if (chrono != chrono2) {
 229                 try {
 230                     date.with(adjuster, 1);
 231                     Assert.fail("TemporalField doWith() should have thrown a ClassCastException" + date.getClass()
 232                             + ", can not be cast to " + date2.getClass());
 233                 } catch (ClassCastException cce) {
 234                     // Expected exception; not an error
 235                 }
 236             } else {
 237                 // Same chronology,
 238                 ChronoLocalDate result = date.with(adjuster, 1);
 239                 assertEquals(result, date2, "TemporalField doWith() failed to replace date");
 240             }
 241         }
 242     }
 243 
 244     //-----------------------------------------------------------------------
 245     // isBefore, isAfter, isEqual, DATE_COMPARATOR
 246     //-----------------------------------------------------------------------
 247     @Test(groups={"tck"}, dataProvider="calendars")
 248     public void test_date_comparisons(Chronology chrono) {
 249         List<ChronoLocalDate> dates = new ArrayList<>();
 250 
 251         ChronoLocalDate date = chrono.date(LocalDate.of(1900, 1, 1));
 252 
 253         // Insert dates in order, no duplicates
 254         dates.add(date.minus(1000, ChronoUnit.YEARS));
 255         dates.add(date.minus(100, ChronoUnit.YEARS));
 256         dates.add(date.minus(10, ChronoUnit.YEARS));
 257         dates.add(date.minus(1, ChronoUnit.YEARS));
 258         dates.add(date.minus(1, ChronoUnit.MONTHS));
 259         dates.add(date.minus(1, ChronoUnit.WEEKS));
 260         dates.add(date.minus(1, ChronoUnit.DAYS));
 261         dates.add(date);
 262         dates.add(date.plus(1, ChronoUnit.DAYS));
 263         dates.add(date.plus(1, ChronoUnit.WEEKS));
 264         dates.add(date.plus(1, ChronoUnit.MONTHS));
 265         dates.add(date.plus(1, ChronoUnit.YEARS));
 266         dates.add(date.plus(10, ChronoUnit.YEARS));
 267         dates.add(date.plus(100, ChronoUnit.YEARS));
 268         dates.add(date.plus(1000, ChronoUnit.YEARS));
 269 
 270         // Check these dates against the corresponding dates for every calendar
 271         for (Chronology[] clist : data_of_calendars()) {
 272             List<ChronoLocalDate> otherDates = new ArrayList<>();
 273             Chronology chrono2 = clist[0];
 274             for (ChronoLocalDate d : dates) {
 275                 otherDates.add(chrono2.date(d));
 276             }
 277 
 278             // Now compare  the sequence of original dates with the sequence of converted dates
 279             for (int i = 0; i < dates.size(); i++) {
 280                 ChronoLocalDate a = dates.get(i);
 281                 for (int j = 0; j < otherDates.size(); j++) {
 282                     ChronoLocalDate b = otherDates.get(j);
 283                     int cmp = ChronoLocalDate.DATE_COMPARATOR.compare(a, b);
 284                     if (i < j) {
 285                         assertTrue(cmp < 0, a + " compare " + b);
 286                         assertEquals(a.isBefore(b), true, a + " isBefore " + b);
 287                         assertEquals(a.isAfter(b), false, a + " isAfter " + b);
 288                         assertEquals(a.isEqual(b), false, a + " isEqual " + b);
 289                     } else if (i > j) {
 290                         assertTrue(cmp > 0, a + " compare " + b);
 291                         assertEquals(a.isBefore(b), false, a + " isBefore " + b);
 292                         assertEquals(a.isAfter(b), true, a + " isAfter " + b);
 293                         assertEquals(a.isEqual(b), false, a + " isEqual " + b);
 294                     } else {
 295                         assertTrue(cmp == 0, a + " compare " + b);
 296                         assertEquals(a.isBefore(b), false, a + " isBefore " + b);
 297                         assertEquals(a.isAfter(b), false, a + " isAfter " + b);
 298                         assertEquals(a.isEqual(b), true, a + " isEqual " + b);
 299                     }
 300                 }
 301             }
 302         }
 303     }
 304 
 305     public void test_date_comparator_checkGenerics_ISO() {
 306         List<LocalDate> dates = new ArrayList<>();
 307         LocalDate date = LocalDate.of(1900, 1, 1);
 308 
 309         // Insert dates in order, no duplicates
 310         dates.add(date.minus(10, ChronoUnit.YEARS));
 311         dates.add(date.minus(1, ChronoUnit.YEARS));
 312         dates.add(date.minus(1, ChronoUnit.MONTHS));
 313         dates.add(date.minus(1, ChronoUnit.WEEKS));
 314         dates.add(date.minus(1, ChronoUnit.DAYS));
 315         dates.add(date);
 316         dates.add(date.plus(1, ChronoUnit.DAYS));
 317         dates.add(date.plus(1, ChronoUnit.WEEKS));
 318         dates.add(date.plus(1, ChronoUnit.MONTHS));
 319         dates.add(date.plus(1, ChronoUnit.YEARS));
 320         dates.add(date.plus(10, ChronoUnit.YEARS));
 321 
 322         List<LocalDate> copy = new ArrayList<>(dates);
 323         Collections.shuffle(copy);
 324         Collections.sort(copy, ChronoLocalDate.DATE_COMPARATOR);
 325         assertEquals(copy, dates);
 326         assertTrue(ChronoLocalDate.DATE_COMPARATOR.compare(copy.get(0), copy.get(1)) < 0);
 327     }
 328 
 329     public void test_date_comparator_checkGenerics_LocalDate() {
 330         List<LocalDate> dates = new ArrayList<>();
 331         LocalDate date = LocalDate.of(1900, 1, 1);
 332 
 333         // Insert dates in order, no duplicates
 334         dates.add(date.minus(10, ChronoUnit.YEARS));
 335         dates.add(date.minus(1, ChronoUnit.YEARS));
 336         dates.add(date.minus(1, ChronoUnit.MONTHS));
 337         dates.add(date.minus(1, ChronoUnit.WEEKS));
 338         dates.add(date.minus(1, ChronoUnit.DAYS));
 339         dates.add(date);
 340         dates.add(date.plus(1, ChronoUnit.DAYS));
 341         dates.add(date.plus(1, ChronoUnit.WEEKS));
 342         dates.add(date.plus(1, ChronoUnit.MONTHS));
 343         dates.add(date.plus(1, ChronoUnit.YEARS));
 344         dates.add(date.plus(10, ChronoUnit.YEARS));
 345 
 346         List<LocalDate> copy = new ArrayList<>(dates);
 347         Collections.shuffle(copy);
 348         Collections.sort(copy, ChronoLocalDate.DATE_COMPARATOR);
 349         assertEquals(copy, dates);
 350         assertTrue(ChronoLocalDate.DATE_COMPARATOR.compare(copy.get(0), copy.get(1)) < 0);
 351     }
 352 
 353     //-----------------------------------------------------------------------
 354     // Test Serialization of ISO via chrono API
 355     //-----------------------------------------------------------------------
 356     @Test( groups={"tck"}, dataProvider="calendars")
 357     public void test_ChronoSerialization(Chronology chrono) throws Exception {
 358         LocalDate ref = LocalDate.of(2000, 1, 5);
 359         ChronoLocalDate orginal = chrono.date(ref);
 360         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 361         ObjectOutputStream out = new ObjectOutputStream(baos);
 362         out.writeObject(orginal);
 363         out.close();
 364         ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
 365         ObjectInputStream in = new ObjectInputStream(bais);
 366         @SuppressWarnings("unchecked")
 367         ChronoLocalDate ser = (ChronoLocalDate) in.readObject();
 368         assertEquals(ser, orginal, "deserialized date is wrong");
 369     }
 370 
 371     /**
 372      * FixedAdjusted returns a fixed Temporal in all adjustments.
 373      * Construct an adjuster with the Temporal that should be returned from adjust.
 374      */
 375     static class FixedAdjuster implements TemporalAdjuster, TemporalAmount {
 376         private Temporal datetime;
 377 
 378         FixedAdjuster(Temporal datetime) {
 379             this.datetime = datetime;
 380         }
 381 
 382         @Override
 383         public Temporal adjustInto(Temporal ignore) {
 384             return datetime;
 385         }
 386 
 387         @Override
 388         public Temporal addTo(Temporal ignore) {
 389             return datetime;
 390         }
 391 
 392         @Override
 393         public Temporal subtractFrom(Temporal ignore) {
 394             return datetime;
 395         }
 396 
 397         @Override
 398         public long get(TemporalUnit unit) {
 399             throw new UnsupportedOperationException("Not supported yet.");
 400         }
 401 
 402         @Override
 403         public List<TemporalUnit> getUnits() {
 404             throw new UnsupportedOperationException("Not supported yet.");
 405         }
 406 
 407     }
 408 
 409     /**
 410      * FixedTemporalUnit returns a fixed Temporal in all adjustments.
 411      * Construct an FixedTemporalUnit with the Temporal that should be returned from addTo.
 412      */
 413     static class FixedTemporalUnit implements TemporalUnit {
 414         private Temporal temporal;
 415 
 416         FixedTemporalUnit(Temporal temporal) {
 417             this.temporal = temporal;
 418         }
 419 
 420         @Override
 421         public String getName() {
 422             return "FixedTemporalUnit";
 423         }
 424 
 425         @Override
 426         public Duration getDuration() {
 427             throw new UnsupportedOperationException("Not supported yet.");
 428         }
 429 
 430         @Override
 431         public boolean isDurationEstimated() {
 432             throw new UnsupportedOperationException("Not supported yet.");
 433         }
 434 
 435         @Override
 436         public boolean isSupportedBy(Temporal temporal) {
 437             throw new UnsupportedOperationException("Not supported yet.");
 438         }
 439 
 440         @SuppressWarnings("unchecked")
 441         @Override
 442         public <R extends Temporal> R addTo(R temporal, long amount) {
 443             return (R) this.temporal;
 444         }
 445 
 446         @Override
 447         public long between(Temporal temporal1, Temporal temporal2) {
 448             throw new UnsupportedOperationException("Not supported yet.");
 449         }
 450     }
 451 
 452     /**
 453      * FixedTemporalField returns a fixed Temporal in all adjustments.
 454      * Construct an FixedTemporalField with the Temporal that should be returned from adjustInto.
 455      */
 456     static class FixedTemporalField implements TemporalField {
 457         private Temporal temporal;
 458         FixedTemporalField(Temporal temporal) {
 459             this.temporal = temporal;
 460         }
 461 
 462         @Override
 463         public String getName() {
 464             return "FixedTemporalField";
 465         }
 466 
 467         @Override
 468         public TemporalUnit getBaseUnit() {
 469             throw new UnsupportedOperationException("Not supported yet.");
 470         }
 471 
 472         @Override
 473         public TemporalUnit getRangeUnit() {
 474             throw new UnsupportedOperationException("Not supported yet.");
 475         }
 476 
 477         @Override
 478         public ValueRange range() {
 479             throw new UnsupportedOperationException("Not supported yet.");
 480         }
 481 
 482         @Override
 483         public boolean isSupportedBy(TemporalAccessor temporal) {
 484             throw new UnsupportedOperationException("Not supported yet.");
 485         }
 486 
 487         @Override
 488         public ValueRange rangeRefinedBy(TemporalAccessor temporal) {
 489             throw new UnsupportedOperationException("Not supported yet.");
 490         }
 491 
 492         @Override
 493         public long getFrom(TemporalAccessor temporal) {
 494             throw new UnsupportedOperationException("Not supported yet.");
 495         }
 496 
 497         @SuppressWarnings("unchecked")
 498         @Override
 499         public <R extends Temporal> R adjustInto(R temporal, long newValue) {
 500             return (R) this.temporal;
 501         }
 502     }
 503 }