/* * Copyright 2009-2016 Tilmann Zaeschke. All rights reserved. * * This file is part of ZooDB. * * ZooDB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * ZooDB is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ZooDB. If not, see <http://www.gnu.org/licenses/>. * * See the README and COPYING files for further information. */ package org.zoodb.test.api; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.fail; import java.util.Map; import java.util.WeakHashMap; import javax.jdo.JDOHelper; import javax.jdo.PersistenceManager; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.zoodb.test.testutil.TestTools; /** * Test harness for TransientField. * * @author Tilmann Zaeschke */ public final class TransientFieldTest { private static final String DB_NAME = "TestDb"; /** * Run before each test. */ @Before public void before() { //Nothing to do } /** * Run after each test. */ @After public void after() { //Nothing to do TestTools.closePM(); } /** * Initialise test suite, called only once before test suite is run. */ @BeforeClass public static void beforeClass() { TestTools.createDb(DB_NAME); TestTools.defineSchema(DB_NAME, TestTransient.class); } /** * Tear down test suite, called only once before test suite is run. */ @AfterClass public static void afterClass() { TestTools.removeDb(DB_NAME); } /** * Test initialisation of transient variables. */ @Test public void testInitialization() { System.out.println("Test 1"); PersistenceManager pm = TestTools.openPM(); pm.currentTransaction().begin(); TestTransient tt1 = new TestTransient(); //get defaults assertEquals(true, tt1.getTb1()); assertEquals(null, tt1.getTb2()); assertEquals(Boolean.TRUE, tt1.getTb3()); try { tt1.getTb2F(); fail(); } catch (NullPointerException e) { //OK } assertEquals(null, tt1.getTo1()); assertNotSame(null, tt1.getTo2()); //set tt1.setTb1(true); tt1.setTb2(Boolean.TRUE); tt1.setTb3(null); //overwrite tt1.setTb1F(Boolean.FALSE); tt1.setTb2F(false); tt1.setTb3F(true); tt1.setTo1("cc"); tt1.setTo2(null); //get assertEquals(false, tt1.getTb1()); assertEquals(Boolean.FALSE, tt1.getTb2()); assertEquals(Boolean.TRUE, tt1.getTb3()); assertEquals("cc", tt1.getTo1()); assertEquals(null, tt1.getTo2()); TestTools.closePM(pm); } /** * Test setting of transient variables for multiple objects. */ @Test public void testUniquity() { System.out.println("Test 2"); PersistenceManager pm = TestTools.openPM(); pm.currentTransaction().begin(); TestTransient tt1 = new TestTransient(); TestTransient tt2 = new TestTransient(); tt1.setTb1(true); tt1.setTb2(Boolean.TRUE); tt1.setTo1("Hello1"); tt1.setTo2("Hello1"); tt2.setTb1(false); tt2.setTb2(Boolean.FALSE); tt2.setTo1("Hello2"); tt2.setTo2(null); assertEquals(true, tt1.getTb1()); assertEquals(Boolean.TRUE, tt1.getTb2()); assertEquals("Hello1", tt1.getTo1()); assertEquals("Hello1", tt1.getTo2()); assertEquals(false, tt2.getTb1()); assertEquals(Boolean.FALSE, tt2.getTb2()); assertEquals("Hello2", tt2.getTo1()); assertEquals(null, tt2.getTo2()); TestTools.closePM(pm); } /** * Test re-associating of transient variables after reloading of parents. */ @Test public void testBecomePersistent() { System.out.println("Test 3"); PersistenceManager pm = TestTools.openPM(); pm.currentTransaction().begin(); TestTransient tt1 = new TestTransient(); TestTransient tt2 = new TestTransient(); TestTransient tt3 = new TestTransient(); tt1.setTb1(true); tt1.setTb2(Boolean.TRUE); tt1.setTo1("Hello1"); tt1.setTo2(tt2); tt2.setTb1(false); tt2.setTb2(Boolean.FALSE); tt2.setTo1(tt3); tt2.setTo2(null); pm.makePersistent(tt1); //Object TT1ID = pm.getObjectId(tt1); pm.makePersistent(tt2); assertEquals(true, tt1.getTb1()); assertEquals(Boolean.TRUE, tt1.getTb2()); assertEquals("Hello1", tt1.getTo1()); assertEquals(tt2, tt1.getTo2()); assertEquals(false, tt2.getTb1()); assertEquals(Boolean.FALSE, tt2.getTb2()); assertEquals(tt3, tt2.getTo1()); assertEquals(null, tt2.getTo2()); pm.currentTransaction().commit(); pm.currentTransaction().begin(); assertEquals(true, tt1.getTb1()); assertEquals(Boolean.TRUE, tt1.getTb2()); assertEquals("Hello1", tt1.getTo1()); assertEquals(tt2, tt1.getTo2()); assertEquals(false, tt2.getTb1()); assertEquals(Boolean.FALSE, tt2.getTb2()); assertEquals(tt3, tt2.getTo1()); assertEquals(null, tt2.getTo2()); pm.currentTransaction().rollback(); pm.currentTransaction().begin(); assertEquals(true, tt1.getTb1()); assertEquals(Boolean.TRUE, tt1.getTb2()); assertEquals("Hello1", tt1.getTo1()); assertEquals(tt2, tt1.getTo2()); assertEquals(false, tt2.getTb1()); assertEquals(Boolean.FALSE, tt2.getTb2()); assertEquals(tt3, tt2.getTo1()); assertEquals(null, tt2.getTo2()); TestTools.closePM(); } /** * Test re-associating of transient variables after reloading of parents. */ @Test public void testReload() { System.out.println("Test 4"); PersistenceManager pm = TestTools.openPM(); pm.currentTransaction().begin(); TestTransient tt1 = new TestTransient(); TestTransient tt2 = new TestTransient(); TestTransient tt3 = new TestTransient(); tt3.setTo2("TT3"); // pm.addNamedRoot("TTreload1", tt1); // pm.addNamedRoot("TTreload2", tt2); pm.makePersistent(tt1); Object TTreload1ID = pm.getObjectId(tt1); pm.makePersistent(tt2); Object TTreload2ID = pm.getObjectId(tt2); tt1.setTb1(true); tt1.setTb2(Boolean.TRUE); tt1.setTo1("Hello"); tt1.setTo2(tt2); assertNotNull(tt1.getTo2()); tt2.setTo1(null); tt2.setTo2(tt3); assertEquals(false, JDOHelper.isPersistent(tt3)); pm.currentTransaction().commit(); pm.currentTransaction().begin(); Object[] oa = new Object[]{tt1, tt2}; tt1 = null; tt2 = null; tt3 = null; pm.evictAll(oa); oa = null; pm.currentTransaction().commit(); TestTools.closePM(pm); System.gc(); //Start next transaction *********************** pm = TestTools.openPM(); pm.currentTransaction().begin(); TestTransient tt1b = (TestTransient) pm.getObjectById(TTreload1ID); TestTransient tt2b = (TestTransient) pm.getObjectById(TTreload2ID); assertEquals(true, tt1b.getTb1()); assertEquals(null, tt1b.getTb2()); assertEquals(null, tt1b.getTo1()); assertEquals(null, tt2b.getTo1()); assertEquals("fdfd", tt1b.getTo2()); assertEquals("fdfd", tt2b.getTo2()); pm.currentTransaction().rollback(); TestTools.closePM(); } /** * Test unregistering TransientFields. */ @Test public void testDeRegister() { System.out.println("Test 5"); PersistenceManager pm = null; try { pm = TestTools.openPM(); pm.currentTransaction().begin(); TestTransient tt1 = new TestTransient(); TestTransient tt2 = new TestTransient(); //transient TestTransient tt3 = new TestTransient(); tt1.setTb1(true); tt1.setTb2(Boolean.TRUE); tt1.setTo1("Hello1"); tt1.setTo2(tt2); tt2.setTb1(false); tt2.setTb2(Boolean.FALSE); tt2.setTo1(tt3); tt2.setTo2(null); tt3.setTb1(false); tt3.setTb2(Boolean.FALSE); tt3.setTo1("Hello3"); tt3.setTo2(null); pm.makePersistent(tt1); pm.makePersistent(tt2); pm.currentTransaction().commit(); pm.currentTransaction().begin(); tt1.deregister(); //tt2 is kept! tt3.deregister(); //should be reset assertEquals(true, tt1.getTb1()); assertEquals(null, tt1.getTb2()); assertEquals(Boolean.TRUE, tt1.getTb3()); assertEquals(null, tt1.getTo1()); assertEquals("fdfd", tt1.getTo2()); //should be the same assertEquals(false, tt2.getTb1()); assertEquals(Boolean.FALSE, tt2.getTb2()); assertEquals(Boolean.TRUE, tt2.getTb3()); assertEquals(tt3, tt2.getTo1()); assertEquals(null, tt2.getTo2()); //should be reset assertEquals(true, tt3.getTb1()); assertEquals(null, tt3.getTb2()); assertEquals(Boolean.TRUE, tt3.getTb3()); assertEquals(null, tt3.getTo1()); assertEquals("fdfd", tt3.getTo2()); } finally { if (pm != null) { TestTools.closePM(); } } } /** * Test re-associating of transient variables after reloading of parents. */ @Test public void testOutsideStore() { System.out.println("Test 6"); //Test before Store TestTransient tt1 = new TestTransient(); TestTransient tt2 = new TestTransient(); TestTransient tt3 = new TestTransient(); tt3.setTo2("TT6"); tt1.setTb1(true); tt1.setTb2(Boolean.TRUE); tt1.setTo1("Hello"); tt1.setTo2(tt2); assertNotNull(tt1.getTo2()); tt2.setTo1(null); tt2.setTo2(tt3); //Use in Store PersistenceManager pm = TestTools.openPM(); pm.currentTransaction().begin(); pm.makePersistent(tt1); // Object TT6ouID = pm.getObjectId(tt1); pm.makePersistent(tt2); pm.currentTransaction().commit(); pm.currentTransaction().begin(); // Object[] oa = new Object[]{tt1, tt2}; // tt1 = null; // tt2 = null; // tt3 = null; // store.evictAll(oa); // oa = null; pm.currentTransaction().commit(); TestTools.closePM(); System.gc(); //Test after Store assertEquals(true, tt1.getTb1()); assertEquals(Boolean.TRUE, tt1.getTb2()); assertEquals("Hello", tt1.getTo1()); System.out.println("TODO this used to work, but is not allowed anymore!"); //TODO this used to work, but is not allowed anymore! // assertNotNull(tt1.getTo2()); // assertEquals(null, tt2.getTo1()); // assertNotNull(tt2.getTo2()); //returns tt3 // assertEquals("TT6", ((TestTransient)tt2.getTo2()).getTo2()); // // // //Cleanup // tt1 = null; // tt2 = null; // tt3 = null; // System.gc(); // // // //Start next transaction *********************** // pm = TestTools.openPM(); // pm.currentTransaction().begin(); // TestTransient tt1b = (TestTransient) pm.getObjectById(TT6ouID); // assertEquals(true, tt1b.getTb1()); // assertEquals(null, tt1b.getTb2()); // assertEquals(null, tt1b.getTo1()); // assertEquals("fdfd", tt1b.getTo2()); // // pm.currentTransaction().rollback(); // TestTools.closePM(pm); } /** * Test garbage collection of owners and transient values. * The owners should always be collectible. * The values should be collectible if the owners are. * @throws InterruptedException */ @Test public void testGC() throws InterruptedException { System.out.println("Test 8"); PersistenceManager pm = TestTools.openPM(); pm.currentTransaction().begin(); Map<Object, Object> wKeys = new WeakHashMap<Object, Object>(); Map<Object, Object> wValues = new WeakHashMap<Object, Object>(); //Transient final int MAX_I = 10; for (int i = 0; i < MAX_I; i++ ) { TestTransient tt1 = new TestTransient(); TestTransient tt2 = new TestTransient(); Object o3 = new Object(); tt1.setTo1(tt2); tt1.setTo2(o3); wKeys.put(tt1, null); wValues.put(tt2, null); wValues.put(o3, null); // System.out.println("X " + TestTransient.getTfTo1().size(null) + " / " + // TestTransient.getTfTo2().size(null)); } //Wait for gc int w = 0; do { if (w++ >= 100) { System.out.println(TestTransient.getTfTo1().size(null)); System.out.println(TestTransient.getTfTo2().size(null)); fail("Failed(" + w + "): " + wKeys.size() + "/" + wValues.size() + "\n" + wKeys + " === " + wValues); } Thread.sleep(100); System.gc(); } while (wKeys.size() + wValues.size() >= MAX_I); //TODO remove? wKeys.clear(); wValues.clear(); System.out.println("*** CLEARED ***"); //Persistent for (int i = 0; i < MAX_I; i++ ) { TestTransient tt = new TestTransient(); pm.makePersistent(tt); TestTransient tt1 = new TestTransient(); pm.makePersistent(tt1); TestTransient tt2 = new TestTransient(); Object o3 = new Object(); tt.setTo1(tt1); tt.setTo1(tt2); tt.setTo2(o3); wKeys.put(tt, null); wValues.put(tt1, null); wValues.put(tt2, null); wValues.put(o3, null); } pm.currentTransaction().commit(); pm.currentTransaction().begin(); //Wait for gc, which should not delete anything for (int i = 0; i < 20; i++) { if (wKeys.size() + wValues.size() < MAX_I) { fail("Failed: " + wKeys.size() + "/" + wValues.size() + "\n"); //+ wKeys + " === " + wValues); } Thread.sleep(100); System.gc(); } TestTools.closePM(pm); //Wait for gc w = 0; do { if (w++ >= 100) { fail("Failed: " + wKeys.size() + "/" + wValues.size() + "\n"); //+ wKeys + " === " + wValues); } Thread.sleep(100); System.gc(); } while (wKeys.size() + wValues.size() >= MAX_I); } /** * Test multiple Stores. * @throws InterruptedException */ @Test public void testMultipleSessions() throws InterruptedException { //TODO disabled, because we do not support multiple sessions // fail("Fix multi-PM with file lock"); // System.out.println("Test 8"); // PersistenceManager pm1 = TestTools.openPM(); // pm1.currentTransaction().begin(); // // Object oidO1 = null; // Object oidV2 = null; // // //Transient // TestTransient tto1 = new TestTransient(); // TestTransient tto2 = new TestTransient(); // TestTransient ttv1 = new TestTransient(); // TestTransient ttv2 = new TestTransient(); // tto1.setTo1(ttv1); // tto2.setTo1(ttv2); // tto1.setTo2("tto1"); // tto2.setTo2("tto2"); // ttv1.setTo2("ttv1"); // ttv2.setTo2("ttv2"); // pm1.makePersistent(tto1); // pm1.makePersistent(ttv2); // oidO1 = pm1.getObjectId(tto1); // oidV2 = pm1.getObjectId(ttv2); // pm1.currentTransaction().commit(); // //store1.leave(); TODO JDO? // // PersistenceManager pm2 = TestTools.openPM(); // pm2.currentTransaction().begin(); // TestTransient tto1_2 = (TestTransient) pm2.getObjectById(oidO1); // TestTransient ttv2_2 = (TestTransient) pm2.getObjectById(oidV2); // assertEquals(null, tto1_2.getTo1()); // assertEquals("fdfd", tto1_2.getTo2()); // assertEquals("fdfd", ttv2_2.getTo2()); // tto1_2.setTo1(null); // ttv2_2.setTo1(null); // tto1_2.setTo2("tto1_2"); // ttv2_2.setTo2("ttv2_2"); // TestTools.closePM(pm2); // // // store1.join(); //TODO JDO? // assertEquals("tto1", tto1.getTo2()); // assertEquals("tto2", tto2.getTo2()); // assertEquals("ttv1", ttv1.getTo2()); // assertEquals("ttv2", ttv2.getTo2()); // assertEquals("ttv1", ((TestTransient)tto1.getTo1()).getTo2()); // assertEquals("ttv2", ((TestTransient)tto2.getTo1()).getTo2()); // TestTools.closePM(pm1); } }