From: <jbo...@li...> - 2005-09-29 22:36:14
|
Author: adamw Date: 2005-09-29 18:36:02 -0400 (Thu, 29 Sep 2005) New Revision: 1239 Added: trunk/forge/portal-extensions/shotoku/shotoku-test/src/java/org/jboss/shotoku/test/AddDeleteTest.java trunk/forge/portal-extensions/shotoku/shotoku-test/src/java/org/jboss/shotoku/test/PropertiesTest.java trunk/forge/portal-extensions/shotoku/shotoku-test/src/java/org/jboss/shotoku/test/ShotokuTest.java Modified: trunk/forge/portal-extensions/shotoku/shotoku-base/src/java/org/jboss/shotoku/AbstractResource.java trunk/forge/portal-extensions/shotoku/shotoku-base/src/java/org/jboss/shotoku/Directory.java trunk/forge/portal-extensions/shotoku/shotoku-base/src/java/org/jboss/shotoku/Node.java trunk/forge/portal-extensions/shotoku/shotoku-svn-service/src/java/org/jboss/shotoku/svn/service/SvnRepository.java trunk/forge/portal-extensions/shotoku/shotoku-svn/src/java/org/jboss/shotoku/svn/AbstractSvnResource.java trunk/forge/portal-extensions/shotoku/shotoku-svn/src/java/org/jboss/shotoku/svn/SvnContentManager.java trunk/forge/portal-extensions/shotoku/shotoku-svn/src/java/org/jboss/shotoku/svn/SvnDirectory.java trunk/forge/portal-extensions/shotoku/shotoku-svn/src/java/org/jboss/shotoku/svn/SvnHeadNode.java trunk/forge/portal-extensions/shotoku/shotoku-svn/src/java/org/jboss/shotoku/svn/SvnNewNode.java trunk/forge/portal-extensions/shotoku/shotoku-test/src/java/org/jboss/shotoku/test/ModifyContentTest.java Log: Tests and bug fixes Modified: trunk/forge/portal-extensions/shotoku/shotoku-base/src/java/org/jboss/shotoku/AbstractResource.java =================================================================== --- trunk/forge/portal-extensions/shotoku/shotoku-base/src/java/org/jboss/shotoku/AbstractResource.java 2005-09-29 08:17:04 UTC (rev 1238) +++ trunk/forge/portal-extensions/shotoku/shotoku-base/src/java/org/jboss/shotoku/AbstractResource.java 2005-09-29 22:36:02 UTC (rev 1239) @@ -15,7 +15,8 @@ */ public String getProperty(String propertyName) throws RepositoryException; /** - * Sets the value of a given property. + * Sets the value of a given property. Only after saving this change will + * be persisted. * @param propertyName Name of the property to set. * @param propertyValue Value of the property to set. */ @@ -39,8 +40,9 @@ */ public String getLogMessage() throws RepositoryException; /** - * Deletes this node or directory. This node should not be used after - * performing this operation. + * Deletes this node or directory (immediately, no <code>save()</code> + * is needed). This node should not be used after performing this + * operation. * @throws RepositoryException */ public void delete() throws RepositoryException; Modified: trunk/forge/portal-extensions/shotoku/shotoku-base/src/java/org/jboss/shotoku/Directory.java =================================================================== --- trunk/forge/portal-extensions/shotoku/shotoku-base/src/java/org/jboss/shotoku/Directory.java 2005-09-29 08:17:04 UTC (rev 1238) +++ trunk/forge/portal-extensions/shotoku/shotoku-base/src/java/org/jboss/shotoku/Directory.java 2005-09-29 22:36:02 UTC (rev 1239) @@ -48,7 +48,7 @@ /** * Creates and returns a new node in this directory. Only after saving, this - * node will be visible by other functions. + * node will be visible by other functions and persisted. * * @param name * Name of the new node. @@ -61,8 +61,9 @@ /** * Creates a new directory in this directory. Only after saving this - * directory, it will be visible by other functions. Also, new nodes/ - * directories in it will be possible to create only after saving. + * directory, it will be visible by other functions and persisted. + * Also, new nodes/ directories in it will be possible to create only + * after saving. * * @param name * Name of the directory to create. Modified: trunk/forge/portal-extensions/shotoku/shotoku-base/src/java/org/jboss/shotoku/Node.java =================================================================== --- trunk/forge/portal-extensions/shotoku/shotoku-base/src/java/org/jboss/shotoku/Node.java 2005-09-29 08:17:04 UTC (rev 1238) +++ trunk/forge/portal-extensions/shotoku/shotoku-base/src/java/org/jboss/shotoku/Node.java 2005-09-29 22:36:02 UTC (rev 1239) @@ -19,7 +19,8 @@ public String getContent() throws RepositoryException; /** - * Sets the content of this node. + * Sets the content of this node. Only after saving this change will be + * persisted. * * @param content * New content of this node. Modified: trunk/forge/portal-extensions/shotoku/shotoku-svn/src/java/org/jboss/shotoku/svn/AbstractSvnResource.java =================================================================== --- trunk/forge/portal-extensions/shotoku/shotoku-svn/src/java/org/jboss/shotoku/svn/AbstractSvnResource.java 2005-09-29 08:17:04 UTC (rev 1238) +++ trunk/forge/portal-extensions/shotoku/shotoku-svn/src/java/org/jboss/shotoku/svn/AbstractSvnResource.java 2005-09-29 22:36:02 UTC (rev 1239) @@ -82,6 +82,7 @@ service.getWriteLock(id, fullPath); if (!file.delete()) { + service.putWriteLock(id, fullPath); throw new RepositoryException("Unable to delete: " + fullPath); } Modified: trunk/forge/portal-extensions/shotoku/shotoku-svn/src/java/org/jboss/shotoku/svn/SvnContentManager.java =================================================================== --- trunk/forge/portal-extensions/shotoku/shotoku-svn/src/java/org/jboss/shotoku/svn/SvnContentManager.java 2005-09-29 08:17:04 UTC (rev 1238) +++ trunk/forge/portal-extensions/shotoku/shotoku-svn/src/java/org/jboss/shotoku/svn/SvnContentManager.java 2005-09-29 22:36:02 UTC (rev 1239) @@ -44,6 +44,7 @@ * @return A prefixed path to the given resource. */ private String getPrefixedPath(String path) { + if ("".equals(path)) return prefix; return prefix + '/' + path; } @@ -54,9 +55,19 @@ * @return A <code>java.io.File</code> object for the given path. */ File getFileForPath(String path) { + return getFileForPrefixedPath(getPrefixedPath(path)); + } + + /** + * Gets a <code>java.io.File</code> object that corresponds to a resource + * that, in the repository, can be found under the given path. + * @param path Path to the resource in the repository, already prefixed. + * @return A <code>java.io.File</code> object for the given path. + */ + File getFileForPrefixedPath(String path) { try { return new File( - service.getFileSystemPath(id, getPrefixedPath(path))); + service.getFileSystemPath(id, path)); } catch (SvnOperationFailed e) { throw new RepositoryException(e); } Modified: trunk/forge/portal-extensions/shotoku/shotoku-svn/src/java/org/jboss/shotoku/svn/SvnDirectory.java =================================================================== --- trunk/forge/portal-extensions/shotoku/shotoku-svn/src/java/org/jboss/shotoku/svn/SvnDirectory.java 2005-09-29 08:17:04 UTC (rev 1238) +++ trunk/forge/portal-extensions/shotoku/shotoku-svn/src/java/org/jboss/shotoku/svn/SvnDirectory.java 2005-09-29 22:36:02 UTC (rev 1239) @@ -77,18 +77,18 @@ public Node getNode(String name) throws RepositoryException, ResourceDoesNotExist { String reqFullPath = fullPath + "/" + name; - File reqNode = svnCm.getFileForPath(reqFullPath); + File reqNode = svnCm.getFileForPrefixedPath(reqFullPath); if (!reqNode.isFile()) - throw new ResourceDoesNotExist(file.getAbsolutePath()); + throw new ResourceDoesNotExist(reqNode.getAbsolutePath()); return new SvnHeadNode(id, reqFullPath, reqNode, name); } public Directory getDirectory(String name) throws RepositoryException, ResourceDoesNotExist { String reqFullPath = fullPath + "/" + name; - File reqDir = svnCm.getFileForPath(reqFullPath); + File reqDir = svnCm.getFileForPrefixedPath(reqFullPath); if (!reqDir.isDirectory()) - throw new ResourceDoesNotExist(file.getAbsolutePath()); + throw new ResourceDoesNotExist(reqDir.getAbsolutePath()); return new SvnDirectory(id, reqFullPath, reqDir, name, svnCm); } Modified: trunk/forge/portal-extensions/shotoku/shotoku-svn/src/java/org/jboss/shotoku/svn/SvnHeadNode.java =================================================================== --- trunk/forge/portal-extensions/shotoku/shotoku-svn/src/java/org/jboss/shotoku/svn/SvnHeadNode.java 2005-09-29 08:17:04 UTC (rev 1238) +++ trunk/forge/portal-extensions/shotoku/shotoku-svn/src/java/org/jboss/shotoku/svn/SvnHeadNode.java 2005-09-29 22:36:02 UTC (rev 1239) @@ -1,15 +1,15 @@ package org.jboss.shotoku.svn; -import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; -import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; +import java.nio.ByteBuffer; import java.nio.channels.FileChannel; +import java.nio.charset.Charset; import org.jboss.shotoku.Directory; import org.jboss.shotoku.History; @@ -17,7 +17,6 @@ public class SvnHeadNode extends SvnNode { private String newContent; - private String oldContent; public SvnHeadNode(String id, String fullPath, File file, String name) { super(id, fullPath, file, name); @@ -27,27 +26,18 @@ public String getContent() { if (newContent != null) return newContent; - if (oldContent == null) { - try { - BufferedReader bf = new BufferedReader(new FileReader(file)); - - StringBuffer sf = new StringBuffer(); - while (true) { - String line = bf.readLine(); - if (line == null) - break; - sf.append(line); - sf.append('\n'); - } - - oldContent = sf.toString(); - bf.close(); - } catch (IOException e) { - throw new RepositoryException(e); - } + try { + FileChannel fc = new FileInputStream(file).getChannel(); + ByteBuffer buff = ByteBuffer.allocate((int) file.length()); + fc.read(buff); + buff.flip(); + + return Charset.forName( + System.getProperty("file.encoding")).decode( + buff).toString(); + } catch (IOException e) { + throw new RepositoryException(e); } - - return oldContent; } public void setContent(String content) { @@ -86,13 +76,8 @@ return newContent != null || super.checkForChanges(); } - public void save(String logMessage) { - // Checking if there is anything to save. - if (!checkForChanges()) return; - + protected void saveWithLock(String logMessage) { try { - service.getWriteLock(id, fullPath); - // Saving modified properties. save(); @@ -107,10 +92,9 @@ throw new RepositoryException(e); } - pw.println(newContent); + pw.print(newContent); pw.close(); - - oldContent = newContent; + newContent = null; } @@ -120,6 +104,15 @@ } } + public void save(String logMessage) { + // Checking if there is anything to save. + if (!checkForChanges()) return; + + service.getWriteLock(id, fullPath); + + saveWithLock(logMessage); + } + public void copyToFile(String filename) throws RepositoryException { try { // Create channel on the source Modified: trunk/forge/portal-extensions/shotoku/shotoku-svn/src/java/org/jboss/shotoku/svn/SvnNewNode.java =================================================================== --- trunk/forge/portal-extensions/shotoku/shotoku-svn/src/java/org/jboss/shotoku/svn/SvnNewNode.java 2005-09-29 08:17:04 UTC (rev 1238) +++ trunk/forge/portal-extensions/shotoku/shotoku-svn/src/java/org/jboss/shotoku/svn/SvnNewNode.java 2005-09-29 22:36:02 UTC (rev 1239) @@ -93,28 +93,38 @@ @Override public void save(String logMessage) throws RepositoryException { - if (!saved) { + if (saved) + super.save(); + else { + service.getWriteLock(id, fullPath); + try { - file.createNewFile(); - service.add(id, fullPath); + if (!file.createNewFile()) { + service.putWriteLock(id, fullPath); + throw new RepositoryException( + "Could not create file: " + + file.getAbsolutePath()); + } } catch (IOException e) { + service.putWriteLock(id, fullPath); throw new RepositoryException(e); + } + + try { + service.add(id, fullPath); } catch (SvnOperationFailed e) { throw new RepositoryException(e); } - } - super.save(logMessage); + saveWithLock(logMessage); - saved = true; + saved = true; + } } @Override public void setContent(String content) { - if (saved) - super.setContent(content); - else - this.content = content; + super.setContent(content); + this.content = content; } - } Modified: trunk/forge/portal-extensions/shotoku/shotoku-svn-service/src/java/org/jboss/shotoku/svn/service/SvnRepository.java =================================================================== --- trunk/forge/portal-extensions/shotoku/shotoku-svn-service/src/java/org/jboss/shotoku/svn/service/SvnRepository.java 2005-09-29 08:17:04 UTC (rev 1238) +++ trunk/forge/portal-extensions/shotoku/shotoku-svn-service/src/java/org/jboss/shotoku/svn/service/SvnRepository.java 2005-09-29 22:36:02 UTC (rev 1239) @@ -18,6 +18,7 @@ import org.tmatesoft.svn.core.wc.SVNClientManager; import org.tmatesoft.svn.core.wc.SVNPropertyData; import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc.SVNStatusType; import org.tmatesoft.svn.core.wc.SVNUpdateClient; import org.tmatesoft.svn.core.wc.SVNWCUtil; @@ -115,10 +116,7 @@ log.info("Performing delayed op: " + op.getClass().getName()); op.performOperation(ourClientManager); - } catch (SVNException e) { - // TODO Delete - e.printStackTrace(); - + } catch (SVNException e) { tryCleanup(); log.warn("Performing delayed op: " + op.getClass().getName() + " failed.", e); } @@ -130,6 +128,10 @@ } public void getWriteLock(String path) { + // TODO + if (path.contains("//")) + throw new RuntimeException("Two / in: " + path); + Semaphore s; synchronized (semaphores) { @@ -144,6 +146,7 @@ s.acquire(); } catch (InterruptedException e) { // We never interrupt the threads. + throw new RuntimeException(e); } } @@ -165,14 +168,27 @@ public String getProperty(String path, String name) throws SvnOperationFailed { try { + File file = new File(getFileSystemPath(path)); + + System.out.println(ourClientManager.getStatusClient().doStatus(file, false). + getContentsStatus()); + + // Checking if this item is under version control + // already. If not - returning null, as it cannot have + // any saved properties. + if (SVNStatusType.STATUS_UNVERSIONED == + ourClientManager.getStatusClient().doStatus(file, false). + getContentsStatus()) + return null; + SVNPropertyData data = ourClientManager.getWCClient().doGetProperty( - new File(getFileSystemPath(path)), name, - SVNRevision.WORKING, SVNRevision.WORKING, + file, name, SVNRevision.WORKING, SVNRevision.WORKING, false); return data == null ? null : data.getValue(); } catch (SVNException e) { + e.printStackTrace(); throw new SvnOperationFailed(e); } } Added: trunk/forge/portal-extensions/shotoku/shotoku-test/src/java/org/jboss/shotoku/test/AddDeleteTest.java =================================================================== --- trunk/forge/portal-extensions/shotoku/shotoku-test/src/java/org/jboss/shotoku/test/AddDeleteTest.java 2005-09-29 08:17:04 UTC (rev 1238) +++ trunk/forge/portal-extensions/shotoku/shotoku-test/src/java/org/jboss/shotoku/test/AddDeleteTest.java 2005-09-29 22:36:02 UTC (rev 1239) @@ -0,0 +1,110 @@ +package org.jboss.shotoku.test; + +import org.jboss.shotoku.Node; +import org.jboss.shotoku.exceptions.ResourceDoesNotExist; + +public class AddDeleteTest extends ShotokuTest { + private final static String TEST_FILE = "add-delete-test-1"; + + private void checkTestFileNotExists() { + try { + cm.getNode(TEST_FILE); + + // An exception should be thrown. + fail("A node which shouldn't exist, does."); + } catch (ResourceDoesNotExist e) { + // Getting here means everything is ok. + } + } + + public void testDeleteWithoutSave() { + Node n = cm.getRootDirectory().newNode(TEST_FILE); + + checkTestFileNotExists(); + + n.delete(); + + checkTestFileNotExists(); + } + + public void testDeleteWithSaveImmediate() { + Node n = cm.getRootDirectory().newNode(TEST_FILE); + + checkTestFileNotExists(); + + n.save(TEST_FILE); + + // The file should exist now. + try { + cm.getNode(TEST_FILE); + } catch (ResourceDoesNotExist e) { + fail(e.getMessage()); + } + + n.delete(); + + checkTestFileNotExists(); + } + + public void testDeleteWithSaveDelayed() { + Node n = cm.getRootDirectory().newNode(TEST_FILE); + + checkTestFileNotExists(); + + n.save(TEST_FILE); + + // The file should exist now. + try { + cm.getNode(TEST_FILE); + } catch (ResourceDoesNotExist e) { + fail(e.getMessage()); + } + + // Waiting for a commit for 20 seconds ... + try { + Thread.sleep(1000 * 20); + } catch (InterruptedException e) { + fail(e.getMessage()); + } + + // The file should still exist. + try { + cm.getNode(TEST_FILE); + } catch (ResourceDoesNotExist e) { + fail(e.getMessage()); + } + + n.delete(); + + checkTestFileNotExists(); + } + + public void testAddAfterDelete() { + // Adding and deleting + Node n = cm.getRootDirectory().newNode(TEST_FILE); + n.save(TEST_FILE); + n.delete(); + + // Only adding. + n = cm.getRootDirectory().newNode(TEST_FILE); + n.save(TEST_FILE); + + // Waiting for a commit for 20 seconds ... + try { + Thread.sleep(1000 * 20); + } catch (InterruptedException e) { + fail(e.getMessage()); + } + + // The node should exist. + // The file should still exist. + try { + cm.getNode(TEST_FILE); + } catch (ResourceDoesNotExist e) { + fail(e.getMessage()); + } + + // Finally - deleting the node. + n.delete(); + } +} Modified: trunk/forge/portal-extensions/shotoku/shotoku-test/src/java/org/jboss/shotoku/test/ModifyContentTest.java =================================================================== --- trunk/forge/portal-extensions/shotoku/shotoku-test/src/java/org/jboss/shotoku/test/ModifyContentTest.java 2005-09-29 08:17:04 UTC (rev 1238) +++ trunk/forge/portal-extensions/shotoku/shotoku-test/src/java/org/jboss/shotoku/test/ModifyContentTest.java 2005-09-29 22:36:02 UTC (rev 1239) @@ -1,24 +1,63 @@ package org.jboss.shotoku.test; -import org.jboss.shotoku.ContentManager; +import org.jboss.shotoku.Node; -import junit.framework.TestCase; - -public class ModifyContentTest extends TestCase { - private ContentManager cm; +public class ModifyContentTest extends ShotokuTest { + private final static String TEST_FILE = "modify-content-test-1"; + private final static String TEST_CONTENT = "content 1"; + private final static String TEST_CONTENT_2 = "content 2"; + private final static String TEST_CONTENT_3 = "content 3"; - public ModifyContentTest() { - cm = ContentManager.getContentManager("shotoku-test"); - } - @Override protected void setUp() throws Exception { + Node n = cm.getRootDirectory().newNode(TEST_FILE); + n.setContent("initial"); + n.save(TEST_FILE); + } + public void testOneContentChange() { + // Getting the test node. + Node n = cm.getNode(TEST_FILE); + + // Setting content, but not saving. + n.setContent(TEST_CONTENT); + + // Getting the same node again. Its content shouldn't be + // modified. + Node n2 = cm.getNode(TEST_FILE); + assertFalse(TEST_CONTENT.equals(n2.getContent())); + + // Saving the first node. + n.save(TEST_FILE); + + // Now both contents should be the same. + assertTrue(TEST_CONTENT.equals(n2.getContent())); + + // Getting a third node, checking its content, it should + // be modified. + assertTrue(TEST_CONTENT.equals(cm.getRootDirectory().getNode( + TEST_FILE).getContent())); } - + + public void testTwoContentChange() { + Node n = cm.getNode(TEST_FILE); + Node n2 = cm.getNode(TEST_FILE); + + // Setting and saving content for the first time. + n.setContent(TEST_CONTENT_2); + n.save(TEST_FILE); + + // Setting and saving content for the second time. + n2.setContent(TEST_CONTENT_3); + n2.save(TEST_FILE); + + // Checking if the change is there. + assertTrue(TEST_CONTENT_3.equals(cm.getRootDirectory().getNode( + TEST_FILE).getContent())); + } + @Override protected void tearDown() throws Exception { - + cm.getNode(TEST_FILE).delete(); } - } Added: trunk/forge/portal-extensions/shotoku/shotoku-test/src/java/org/jboss/shotoku/test/PropertiesTest.java =================================================================== --- trunk/forge/portal-extensions/shotoku/shotoku-test/src/java/org/jboss/shotoku/test/PropertiesTest.java 2005-09-29 08:17:04 UTC (rev 1238) +++ trunk/forge/portal-extensions/shotoku/shotoku-test/src/java/org/jboss/shotoku/test/PropertiesTest.java 2005-09-29 22:36:02 UTC (rev 1239) @@ -0,0 +1,71 @@ +package org.jboss.shotoku.test; + +import org.jboss.shotoku.Node; + +public class PropertiesTest extends ShotokuTest { + private final static String TEST_FILE = "properties-test-1"; + private final static String PROP_NAME = "prop1"; + private final static String PROP_VAL_1 = "val1"; + private final static String PROP_VAL_2 = "val2"; + + @Override + protected void setUp() throws Exception { + Node n = cm.getRootDirectory().newNode(TEST_FILE); + System.out.println("Creating ... "); + n.save(TEST_FILE); + System.out.println("Done"); + } + + public void testOnePropertyChange() { + // Getting the test node. + Node n = cm.getNode(TEST_FILE); + + // The file is new, this property shouldn't have a value. + assertNull(n.getProperty(PROP_NAME)); + + n.setProperty(PROP_NAME, PROP_VAL_1); + + // Still, the property shouldn't be set yet on a new node, + // but should be set on the old one. + assertNull(cm.getNode(TEST_FILE).getProperty(PROP_NAME)); + assertTrue(PROP_VAL_1.equals(n.getProperty(PROP_NAME))); + + // Saving - the change should be persisted. + n.save(TEST_FILE); + + // Now it should be visible everywhere. + assertTrue(PROP_VAL_1.equals(n.getProperty(PROP_NAME))); + assertTrue(PROP_VAL_1.equals(cm.getNode(TEST_FILE).getProperty( + PROP_NAME))); + } + + public void testTwoPropertyChange() { + // Getting the test nodes. + Node n = cm.getNode(TEST_FILE); + Node n2 = cm.getNode(TEST_FILE); + + // The file is new, this property shouldn't have a value. + assertNull(n.getProperty(PROP_NAME)); + + // Setting first value. + n.setProperty(PROP_NAME, PROP_VAL_1); + n.save(TEST_FILE); + + // Setting the second value. + n2.setProperty(PROP_NAME, PROP_VAL_2); + n2.save(TEST_FILE); + + // Now everywhere the new value should be visible. + assertTrue(PROP_VAL_2.equals(n.getProperty(PROP_NAME))); + assertTrue(PROP_VAL_2.equals(n2.getProperty(PROP_NAME))); + assertTrue(PROP_VAL_2.equals(cm.getNode(TEST_FILE).getProperty( + PROP_NAME))); + } + + @Override + protected void tearDown() throws Exception { + System.out.println("Deleting ... "); + cm.getNode(TEST_FILE).delete(); + System.out.println("Done"); + } +} Added: trunk/forge/portal-extensions/shotoku/shotoku-test/src/java/org/jboss/shotoku/test/ShotokuTest.java =================================================================== --- trunk/forge/portal-extensions/shotoku/shotoku-test/src/java/org/jboss/shotoku/test/ShotokuTest.java 2005-09-29 08:17:04 UTC (rev 1238) +++ trunk/forge/portal-extensions/shotoku/shotoku-test/src/java/org/jboss/shotoku/test/ShotokuTest.java 2005-09-29 22:36:02 UTC (rev 1239) @@ -0,0 +1,13 @@ +package org.jboss.shotoku.test; + +import org.jboss.shotoku.ContentManager; + +import junit.framework.TestCase; + +public class ShotokuTest extends TestCase { + protected ContentManager cm; + + public ShotokuTest() { + cm = ContentManager.getContentManager("shotoku-test"); + } +} |