diff --git a/diylc/diylc-core/src/main/java/org/diylc/presenter/DatasheetService.java b/diylc/diylc-core/src/main/java/org/diylc/presenter/DatasheetService.java index 24bc5ad0..abff8506 100644 --- a/diylc/diylc-core/src/main/java/org/diylc/presenter/DatasheetService.java +++ b/diylc/diylc-core/src/main/java/org/diylc/presenter/DatasheetService.java @@ -120,4 +120,10 @@ public class DatasheetService { return null; } + + // Add method to clear cache for testing + public void clearCache() { + datasheetCache.clear(); + lookupCache.clear(); + } } diff --git a/diylc/diylc-core/src/test/java/org/diylc/presenter/ComponentProcessorTest.java b/diylc/diylc-core/src/test/java/org/diylc/presenter/ComponentProcessorTest.java new file mode 100644 index 00000000..27c2b2c4 --- /dev/null +++ b/diylc/diylc-core/src/test/java/org/diylc/presenter/ComponentProcessorTest.java @@ -0,0 +1,219 @@ +package org.diylc.presenter; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import java.awt.Point; +import java.awt.geom.Point2D; +import java.util.Arrays; +import java.util.List; + +import org.diylc.common.ComponentType; +import org.diylc.common.IComponentTransformer; +import org.diylc.common.PropertyWrapper; +import org.diylc.components.AbstractComponent; +import org.diylc.core.CreationMethod; +import org.diylc.core.IDIYComponent; +import org.diylc.core.VisibilityPolicy; +import org.diylc.core.annotations.BomPolicy; +import org.diylc.core.annotations.ComponentDescriptor; +import org.diylc.core.annotations.EditableProperty; +import org.diylc.core.annotations.KeywordPolicy; +import org.junit.Before; +import org.junit.Test; + +public class ComponentProcessorTest { + + private ComponentProcessor processor; + + @Before + public void setUp() { + processor = ComponentProcessor.getInstance(); + } + + @ComponentDescriptor( + name = "Test Component", + category = "Test", + author = "Test Author", + description = "Test Description", + instanceNamePrefix = "TEST", + creationMethod = CreationMethod.POINT_BY_POINT, + zOrder = IDIYComponent.COMPONENT, + flexibleZOrder = false, + bomPolicy = BomPolicy.NEVER_SHOW, + autoEdit = false, + enableCache = true, + keywordPolicy = KeywordPolicy.SHOW_VALUE, + keywordTag = "", + enableDatasheet = false, + datasheetCreationStepCount = 0 + ) + public static class TestComponent extends AbstractComponent { + private String testProperty = "test"; + private int numericProperty = 42; + + @EditableProperty + public String getTestProperty() { + return testProperty; + } + + public void setTestProperty(String value) { + this.testProperty = value; + } + + @EditableProperty(name = "Number") + public int getNumericProperty() { + return numericProperty; + } + + public void setNumericProperty(int value) { + this.numericProperty = value; + } + + // Implement required methods + @Override + public void drawIcon(java.awt.Graphics2D g2d, int width, int height) {} + + @Override + public void draw(java.awt.Graphics2D g2d, org.diylc.core.ComponentState componentState, + boolean outlineMode, org.diylc.core.Project project, + org.diylc.core.IDrawingObserver drawingObserver) {} + + @Override + public Point2D getControlPoint(int index) { + return new Point(0, 0); + } + + @Override + public boolean isControlPointSticky(int index) { + return true; + } + + @Override + public int getControlPointCount() { + return 1; + } + + @Override + public void setControlPoint(Point2D point, int index) {} + + @Override + public VisibilityPolicy getControlPointVisibilityPolicy(int index) { + return VisibilityPolicy.WHEN_SELECTED; + } + + @Override + public Object getValue() { + return null; + } + + @Override + public void setValue(Object value) {} + } + + @Test + public void testExtractComponentType() { + ComponentType type = processor.extractComponentTypeFrom(TestComponent.class); + + assertNotNull("Component type should not be null", type); + assertEquals("Test Component", type.getName()); + assertEquals("Test", type.getCategory()); + assertEquals("Test Author", type.getAuthor()); + assertEquals("Test Description", type.getDescription()); + assertEquals("TEST", type.getNamePrefix()); + assertEquals(TestComponent.class, type.getInstanceClass()); + } + + @Test + public void testExtractProperties() { + List properties = processor.extractProperties(TestComponent.class); + + assertNotNull("Properties list should not be null", properties); + assertEquals("Should have 3 properties", 3, properties.size()); + + // Find and verify testProperty + PropertyWrapper testProp = findPropertyByName(properties, "TestProperty"); + assertNotNull("TestProperty should exist", testProp); + assertEquals(String.class, testProp.getType()); + + // Find and verify numericProperty + PropertyWrapper numProp = findPropertyByName(properties, "Number"); + assertNotNull("Number property should exist", numProp); + assertEquals(int.class, numProp.getType()); + + // Find and verify name property from AbstractComponent + PropertyWrapper nameProp = findPropertyByName(properties, "Name"); + assertNotNull("Name property should exist", nameProp); + assertEquals(String.class, nameProp.getType()); + } + + @Test + public void testGetMutualSelectionProperties() throws Exception { + TestComponent comp1 = new TestComponent(); + TestComponent comp2 = new TestComponent(); + + // Set same values + comp1.setTestProperty("same"); + comp2.setTestProperty("same"); + comp1.setNumericProperty(42); + comp2.setNumericProperty(42); + comp1.setName("same name"); + comp2.setName("same name"); + + List mutualProps = processor.getMutualSelectionProperties( + Arrays.asList(comp1, comp2)); + + assertNotNull("Mutual properties should not be null", mutualProps); + assertEquals("Should have 3 mutual properties", 3, mutualProps.size()); + assertTrue("All properties should be unique", + mutualProps.stream().allMatch(PropertyWrapper::isUnique)); + } + + @Test + public void testGetMutualSelectionPropertiesWithDifferentValues() throws Exception { + TestComponent comp1 = new TestComponent(); + TestComponent comp2 = new TestComponent(); + + // Set different values + comp1.setTestProperty("value1"); + comp2.setTestProperty("value2"); + comp1.setNumericProperty(42); + comp2.setNumericProperty(43); + comp1.setName("name1"); + comp2.setName("name2"); + + List mutualProps = processor.getMutualSelectionProperties( + Arrays.asList(comp1, comp2)); + + assertNotNull("Mutual properties should not be null", mutualProps); + assertEquals("Should have 3 mutual properties", 3, mutualProps.size()); + + // Each property should not be unique since they have different values + for (PropertyWrapper prop : mutualProps) { + assertFalse("Property '" + prop.getName() + "' should not be unique", + prop.isUnique()); + } + } + + @Test + public void testComponentPointsTouch() { + TestComponent comp1 = new TestComponent(); + TestComponent comp2 = new TestComponent(); + + assertTrue("Components with same point should touch", + ComponentProcessor.componentPointsTouch(comp1, comp2)); + } + + @Test + public void testHasStickyPoint() { + TestComponent component = new TestComponent(); + assertTrue("Should have sticky point", ComponentProcessor.hasStickyPoint(component)); + } + + private PropertyWrapper findPropertyByName(List properties, String name) { + return properties.stream() + .filter(p -> p.getName().equals(name)) + .findFirst() + .orElse(null); + } +} diff --git a/diylc/diylc-core/src/test/java/org/diylc/presenter/DatasheetServiceTest.java b/diylc/diylc-core/src/test/java/org/diylc/presenter/DatasheetServiceTest.java new file mode 100644 index 00000000..08fdb8ee --- /dev/null +++ b/diylc/diylc-core/src/test/java/org/diylc/presenter/DatasheetServiceTest.java @@ -0,0 +1,266 @@ +package org.diylc.presenter; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class DatasheetServiceTest { + + private DatasheetService service; + + @Mock + private ClassLoader mockClassLoader; + + @Before + public void setUp() { + service = DatasheetService.getInstance(); + service.clearCache(); // Clear cache before each test + Thread.currentThread().setContextClassLoader(mockClassLoader); + } + + @Test + public void testLoadDatasheet() throws Exception { + // Create a mock datasheet content + String datasheetContent = + "1N4001,50V,1A,1.1V\n" + + "1N4002,100V,1A,1.1V\n" + + "1N4003,200V,1A,1.1V"; + + // Create a mock URL that returns our test content + URL mockUrl = mock(URL.class); + when(mockUrl.openStream()).thenReturn( + new ByteArrayInputStream(datasheetContent.getBytes())); + + // Setup the mock ClassLoader to return our mock URL + when(mockClassLoader.getResource("TestComponent.datasheet")) + .thenReturn(mockUrl); + + // Test loading the datasheet + List result = service.loadDatasheet(TestComponent.class); + + // Verify results + assertNotNull("Datasheet should not be null", result); + assertEquals("Should have 3 entries", 3, result.size()); + + // Verify first entry + String[] firstEntry = result.get(0); + assertEquals("1N4001", firstEntry[0]); + assertEquals("50V", firstEntry[1]); + assertEquals("1A", firstEntry[2]); + assertEquals("1.1V", firstEntry[3]); + + // Verify second entry + String[] secondEntry = result.get(1); + assertEquals("1N4002", secondEntry[0]); + assertEquals("100V", secondEntry[1]); + + // Verify third entry + String[] thirdEntry = result.get(2); + assertEquals("1N4003", thirdEntry[0]); + assertEquals("200V", thirdEntry[1]); + } + + @Test + public void testLoadDatasheetFileNotFound() { + // Setup the mock ClassLoader to return null (file not found) + when(mockClassLoader.getResource("TestComponent.datasheet")) + .thenReturn(null); + + List result = service.loadDatasheet(TestComponent.class); + + assertNull("Result should be null when file is not found", result); + } + + @Test + public void testLookupExactMatch() throws Exception { + // Create a mock datasheet content + String datasheetContent = + "TEST,KEY,10.0\n" + + "TEST,KEY,20.0\n" + + "TEST,OTHER,15.0\n" + + "OTHER,KEY,10.0"; + + URL mockUrl = mock(URL.class); + when(mockUrl.openStream()).thenReturn( + new ByteArrayInputStream(datasheetContent.getBytes())); + when(mockClassLoader.getResource("DatasheetServiceTest.datasheet")) + .thenReturn(mockUrl); + + // Create test data + String[] result = service.lookup( + DatasheetServiceTest.class, // Using test class itself as component class + 0.1, // 0.1% tolerance + "TEST|KEY", // Key to look up + 10.0 // Value to match + ); + + assertNotNull("Should find matching entry", result); + assertEquals("TEST", result[0]); + assertEquals("KEY", result[1]); + assertEquals("10.0", result[2]); + } + + @Test + public void testLookupWithinTolerance() throws Exception { + // Create a mock datasheet content + String datasheetContent = + "TEST,KEY,10.0\n" + + "TEST,KEY,20.0\n" + + "TEST,OTHER,15.0\n" + + "OTHER,KEY,10.0"; + + URL mockUrl = mock(URL.class); + when(mockUrl.openStream()).thenReturn( + new ByteArrayInputStream(datasheetContent.getBytes())); + when(mockClassLoader.getResource("DatasheetServiceTest.datasheet")) + .thenReturn(mockUrl); + + // Should match 10.0 with 5% tolerance when looking for 10.2 + String[] result = service.lookup( + DatasheetServiceTest.class, + 5.0, // 5% tolerance + "TEST|KEY", + 10.2 // Should match 10.0 within 5% tolerance + ); + + assertNotNull("Should find matching entry within tolerance", result); + assertEquals("10.0", result[2]); + } + + @Test + public void testLookupNoMatch() throws Exception { + // Create a mock datasheet content + String datasheetContent = + "TEST,KEY,10.0\n" + + "TEST,KEY,20.0\n" + + "TEST,OTHER,15.0\n" + + "OTHER,KEY,10.0"; + + URL mockUrl = mock(URL.class); + when(mockUrl.openStream()).thenReturn( + new ByteArrayInputStream(datasheetContent.getBytes())); + when(mockClassLoader.getResource("DatasheetServiceTest.datasheet")) + .thenReturn(mockUrl); + + // Should not match when value is outside tolerance + String[] result = service.lookup( + DatasheetServiceTest.class, + 1.0, // 1% tolerance + "TEST|KEY", + 15.0 // No value close enough to match + ); + + assertNull("Should not find matching entry", result); + } + + @Test + public void testLookupNonexistentKey() throws Exception { + // Create a mock datasheet content + String datasheetContent = + "TEST,KEY,10.0\n" + + "TEST,KEY,20.0\n" + + "TEST,OTHER,15.0\n" + + "OTHER,KEY,10.0"; + + URL mockUrl = mock(URL.class); + when(mockUrl.openStream()).thenReturn( + new ByteArrayInputStream(datasheetContent.getBytes())); + when(mockClassLoader.getResource("DatasheetServiceTest.datasheet")) + .thenReturn(mockUrl); + + String[] result = service.lookup( + DatasheetServiceTest.class, + 0.1, + "NONEXISTENT|KEY", // Key that doesn't exist in datasheet + 10.0 + ); + + assertNull("Should not find matching entry for nonexistent key", result); + } + + @Test + public void testLookupWithTolerance() throws Exception { + // Create a mock datasheet content with resistor values + String datasheetContent = + "0805,1%,100,0.125W\n" + + "0805,1%,220,0.125W\n" + + "0805,1%,470,0.125W"; + + URL mockUrl = mock(URL.class); + when(mockUrl.openStream()).thenReturn( + new ByteArrayInputStream(datasheetContent.getBytes())); + when(mockClassLoader.getResource("TestComponent.datasheet")) + .thenReturn(mockUrl); + + // Test lookup with 5% tolerance (should match 220 when looking for 225) + String[] result = service.lookup(TestComponent.class, 5.0, "0805|1%", 225.0); + + assertNotNull("Should find matching entry within tolerance", result); + assertEquals("0805", result[0]); + assertEquals("1%", result[1]); + assertEquals("220", result[2]); + } + + @Test + public void testLookupOutsideTolerance() throws Exception { + // Create a mock datasheet content + String datasheetContent = + "0805,1%,100,0.125W\n" + + "0805,1%,220,0.125W\n" + + "0805,1%,470,0.125W"; + + URL mockUrl = mock(URL.class); + when(mockUrl.openStream()).thenReturn( + new ByteArrayInputStream(datasheetContent.getBytes())); + when(mockClassLoader.getResource("TestComponent.datasheet")) + .thenReturn(mockUrl); + + // Test lookup with value outside tolerance + String[] result = service.lookup(TestComponent.class, 1.0, "0805|1%", 250.0); + + assertNull("Should not find matching entry outside tolerance", result); + } + + @Test + public void testLookupMultipleValues() throws Exception { + // Create a mock datasheet content with multiple value matches + String datasheetContent = + "TO220,NPN,50,5,0.7\n" + // Vce, Ic, Vbe + "TO220,NPN,60,8,0.7\n" + + "TO220,NPN,100,10,0.7"; + + URL mockUrl = mock(URL.class); + when(mockUrl.openStream()).thenReturn( + new ByteArrayInputStream(datasheetContent.getBytes())); + when(mockClassLoader.getResource("TestComponent.datasheet")) + .thenReturn(mockUrl); + + // Test lookup matching multiple values within tolerance + String[] result = service.lookup(TestComponent.class, 5.0, "TO220|NPN", + 98.0, 9.8, 0.71); + + assertNotNull("Should find matching entry for multiple values", result); + assertEquals("TO220", result[0]); + assertEquals("NPN", result[1]); + assertEquals("100", result[2]); + assertEquals("10", result[3]); + assertEquals("0.7", result[4]); + } + + // Test component class + private static class TestComponent { + // Empty class just for testing + } +} \ No newline at end of file diff --git a/diylc/diylc-core/src/test/resources/org/diylc/presenter/DatasheetServiceTest.datasheet b/diylc/diylc-core/src/test/resources/org/diylc/presenter/DatasheetServiceTest.datasheet new file mode 100644 index 00000000..5249c552 --- /dev/null +++ b/diylc/diylc-core/src/test/resources/org/diylc/presenter/DatasheetServiceTest.datasheet @@ -0,0 +1,4 @@ +TEST,KEY,10.0 mm +TEST,KEY,20.0 mm +TEST,OTHER,15.0 mm +OTHER,KEY,10.0 mm \ No newline at end of file