/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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.apache.mahout.cf.taste.impl.model.file; import org.apache.commons.lang.mutable.MutableBoolean; import org.apache.mahout.cf.taste.common.TasteException; import org.apache.mahout.cf.taste.impl.TasteTestCase; import org.apache.mahout.cf.taste.impl.common.LongPrimitiveIterator; import org.apache.mahout.cf.taste.impl.neighborhood.NearestNUserNeighborhood; import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender; import org.apache.mahout.cf.taste.impl.similarity.PearsonCorrelationSimilarity; import org.apache.mahout.cf.taste.model.DataModel; import org.apache.mahout.cf.taste.model.Preference; import org.apache.mahout.cf.taste.model.PreferenceArray; import org.apache.mahout.cf.taste.neighborhood.UserNeighborhood; import org.apache.mahout.cf.taste.recommender.Recommender; import org.apache.mahout.cf.taste.similarity.UserSimilarity; import org.junit.Before; import org.junit.Test; import java.io.File; import java.util.NoSuchElementException; /** <p>Tests {@link FileDataModel}.</p> */ public final class FileDataModelTest extends TasteTestCase { private static final String[] DATA = { "123,456,0.1", "123,789,0.6", "123,654,0.7", "234,123,0.5", "234,234,1.0", "234,999,0.9", "345,789,0.6", "345,654,0.7", "345,123,1.0", "345,234,0.5", "345,999,0.5", "456,456,0.1", "456,789,0.5", "456,654,0.0", "456,999,0.2",}; private DataModel model; private File testFile; @Override @Before public void setUp() throws Exception { super.setUp(); testFile = getTestTempFile("test.txt"); writeLines(testFile, DATA); model = new FileDataModel(testFile); } @Test public void testFile() throws Exception { UserSimilarity userSimilarity = new PearsonCorrelationSimilarity(model); UserNeighborhood neighborhood = new NearestNUserNeighborhood(3, userSimilarity, model); Recommender recommender = new GenericUserBasedRecommender(model, neighborhood, userSimilarity); assertEquals(1, recommender.recommend(123, 3).size()); assertEquals(0, recommender.recommend(234, 3).size()); assertEquals(1, recommender.recommend(345, 3).size()); // Make sure this doesn't throw an exception model.refresh(null); } @Test public void testTranspose() throws Exception { FileDataModel tModel = new FileDataModel(testFile, true, FileDataModel.DEFAULT_MIN_RELOAD_INTERVAL_MS); PreferenceArray userPrefs = tModel.getPreferencesFromUser(456); assertNotNull("user prefs are null and it shouldn't be", userPrefs); PreferenceArray pref = tModel.getPreferencesForItem(123); assertNotNull("pref is null and it shouldn't be", pref); assertEquals("pref Size: " + pref.length() + " is not: " + 3, 3, pref.length()); } @Test(expected = NoSuchElementException.class) public void testGetItems() throws Exception { LongPrimitiveIterator it = model.getItemIDs(); assertNotNull(it); assertTrue(it.hasNext()); assertEquals(123, it.nextLong()); assertTrue(it.hasNext()); assertEquals(234, it.nextLong()); assertTrue(it.hasNext()); assertEquals(456, it.nextLong()); assertTrue(it.hasNext()); assertEquals(654, it.nextLong()); assertTrue(it.hasNext()); assertEquals(789, it.nextLong()); assertTrue(it.hasNext()); assertEquals(999, it.nextLong()); assertFalse(it.hasNext()); it.next(); } @Test public void testPreferencesForItem() throws Exception { PreferenceArray prefs = model.getPreferencesForItem(456); assertNotNull(prefs); Preference pref1 = prefs.get(0); assertEquals(123, pref1.getUserID()); assertEquals(456, pref1.getItemID()); Preference pref2 = prefs.get(1); assertEquals(456, pref2.getUserID()); assertEquals(456, pref2.getItemID()); assertEquals(2, prefs.length()); } @Test public void testGetNumUsers() throws Exception { assertEquals(4, model.getNumUsers()); } @Test public void testNumUsersPreferring() throws Exception { assertEquals(2, model.getNumUsersWithPreferenceFor(456)); assertEquals(0, model.getNumUsersWithPreferenceFor(111)); assertEquals(0, model.getNumUsersWithPreferenceFor(111, 456)); assertEquals(2, model.getNumUsersWithPreferenceFor(123, 234)); } @Test public void testRefresh() throws Exception { final MutableBoolean initialized = new MutableBoolean(false); Runnable initializer = new Runnable() { @Override public void run() { try { model.getNumUsers(); initialized.setValue(true); } catch (TasteException te) { // oops } } }; new Thread(initializer).start(); Thread.sleep(1000L); // wait a second for thread to start and call getNumUsers() model.getNumUsers(); // should block assertTrue(initialized.booleanValue()); assertEquals(4, model.getNumUsers()); } @Test public void testExplicitRefreshAfterCompleteFileUpdate() throws Exception { File file = getTestTempFile("refresh"); writeLines(file, "123,456,3.0"); /* create a FileDataModel that always reloads when the underlying file has changed */ FileDataModel dataModel = new FileDataModel(file, false, 0L); assertEquals(3.0f, dataModel.getPreferenceValue(123L, 456L), EPSILON); /* change the underlying file, * we have to wait at least a second to see the change in the file's lastModified timestamp */ Thread.sleep(2000L); writeLines(file, "123,456,5.0"); dataModel.refresh(null); assertEquals(5.0f, dataModel.getPreferenceValue(123L, 456L), EPSILON); } @Test public void testToString() { assertFalse(model.toString().isEmpty()); } @Test(expected = IllegalArgumentException.class) public void testEmptyFile() throws Exception { File file = getTestTempFile("empty"); writeLines(file); //required to create file. new FileDataModel(file); } }