package com.xenoage.zong; import com.xenoage.utils.iterators.It; import com.xenoage.utils.jse.io.JseInputStream; import com.xenoage.utils.jse.io.JseOutputStream; import com.xenoage.utils.jse.log.DesktopLogProcessing; import com.xenoage.utils.log.Log; import com.xenoage.zong.desktop.io.midi.out.MidiScoreDocFileOutput; import com.xenoage.zong.desktop.io.musicxml.in.MusicXmlScoreDocFileInput; import com.xenoage.zong.desktop.io.pdf.out.PdfScoreDocFileOutput; import com.xenoage.zong.desktop.utils.JseZongPlatformUtils; import com.xenoage.zong.documents.ScoreDoc; import com.xenoage.zong.io.ScoreDocFactory; import com.xenoage.zong.layout.Layout; import com.xenoage.zong.layout.frames.ScoreFrame; import com.xenoage.zong.musiclayout.stampings.NoteheadStamping; import com.xenoage.zong.musiclayout.stampings.StaffSymbolStamping; import com.xenoage.zong.musiclayout.stampings.Stamping; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import java.io.File; import java.text.DecimalFormat; import java.util.List; import static com.xenoage.utils.iterators.It.it; import static com.xenoage.utils.jse.io.JseFileUtils.listFilesDeep; import static com.xenoage.zong.musicxml.util.MusicXMLFilenameFilter.musicXMLFilenameFilter; import static java.util.Comparator.comparing; /** * This test tries to load and layout a huge range of MusicXML files. * <p> * If the files do not exist, nothing is tested. This class allows to test * a large number of files locally, which can not be uploaded to the * public repository because of copyright restrictions. * <p> * This test should be excluded from the normal test suite and * should only be started manually. * * @author Andreas Wenger */ public class MusicXmlMassTest { private static final String dir = "../../Zong-Test/"; private static final String tempDir = "../../Zong-Test/Temp/"; //configure the test: just load the MusicXML or do furter checks, like //layout checking or trying to save it in different formats and load it again? private static boolean checkLayout = true; private static boolean saveAsMxl = false; //not supported yet private static boolean saveAsPdf = true; private static boolean saveAsMid = true; private static boolean loadFromSavedMxl = saveAsMxl && true; //not supported yet @Before public void setUp() { //Logging JseZongPlatformUtils.init(getClass().getSimpleName()); Log.init(new DesktopLogProcessing(getClass().getSimpleName())); } /** * We check all .xml files in the {@link #dir} directory * (and its subdirectories recursively). * Files and folders containing "__" are ignored. */ @Test public void testSampleFiles() { int ok = 0; List<File> files = listFilesDeep(new File(dir), musicXMLFilenameFilter); //remove all files called "mei.xml", they include not MusicXML but MEI files.removeIf(f -> f.getName().equals("mei.xml")); //remove all files and directories with prefix "__" files.removeIf(f -> f.getAbsolutePath().contains("__")); //sort alphabetically by filepath files.sort(comparing(File::getAbsolutePath)); System.out.println("Processing " + files.size() + " files..."); It<File> filesIt = it(files); for (File file : filesIt) { if (testFile(file)) ok++; else Assert.fail(); if (filesIt.getIndex() % 10 == 0) System.out.println("Progress: " + (100 * filesIt.getIndex() / files.size()) + "%"); } System.out.println("Could load " + ok + " of " + files.size() + " files (" + new DecimalFormat("#.##").format(100f * ok / files.size()) + "%)"); if (ok < files.size()) Assert.fail(); } //* @Test public void testSingleFile() { File file = new File(dir + "MusicXML/hausmusik.ch/b/badarczewska-Thekla von (1834-1861)/gebet-einer-jungfrau/gebet-einer-jungfrau.xml"); if (!testFile(file)) Assert.fail(); } //*/ private boolean testFile(File file) { try { //Load the file ScoreDocFactory.setErrorLayoutEnabled(false); //TIDY ScoreDoc score = new MusicXmlScoreDocFileInput().read( new JseInputStream(file), file.getAbsolutePath()); ScoreDocFactory.setErrorLayoutEnabled(true); //TIDY //Check layout of loaded file if (checkLayout) { checkLayout(score, file.getName()); } //Save it as MusicXML File mxlSavedFile = getTempOutputPath(file, "-saved.mxl"); if (saveAsMxl) { //new MusicXMLScoreDocFileOutput().write(score, new FileOutputStream(mxlSavedFile), mxlSavedFile); } //Save it as PDF if (saveAsPdf) { File pdfFile = getTempOutputPath(file, ".pdf"); new PdfScoreDocFileOutput().write(score, 0, new JseOutputStream(pdfFile)); } //Save it as MIDI if (saveAsMid) { File midFile = getTempOutputPath(file, ".mid"); new MidiScoreDocFileOutput().write(score, 0, new JseOutputStream(midFile)); } //Load it from saved MusicXML if (loadFromSavedMxl) { //TODO } //Success System.out.print("OK: " + file.toString().substring(dir.length()) + " (" + score.getScore().getInfo().getTitle() + ")"); @SuppressWarnings("unchecked") List<String> errorMessages = (List<String>) score.getScore().getMetaData().get("mxlerrors"); if (errorMessages != null) System.out.print(" ! " + errorMessages.size() + " warning(s)"); System.out.println(); return true; } catch (Throwable ex) { ex.printStackTrace(); //fail("Failed to load file: " + file); System.out.println("fail: " + file.toString().substring(dir.length())); return false; } } /** * Creates a filepath for an output file in the {@value #tempDir} directory, * with the same relative path as the original file and the given extension (like ".pdf") added. * If the parent directory for that file does not exist yet, it is created. */ private static File getTempOutputPath(File originalFile, String ext) { File file = new File(tempDir + originalFile.getPath().substring(dir.length()) + ext); file.getParentFile().mkdirs(); return file; } /** * Checks that the layout contains at least one score frame with at least * one {@link NoteheadStamping}. Otherwise an {@link AssertionError} is thrown. */ private void checkLayout(ScoreDoc doc, String filename) { Layout layout = doc.getLayout(); //at least one score frame? if (layout.getScoreFrames().size() == 0) throw new AssertionError("No score frames in layout"); //at least one staff symbol stamping? if (false == filename.contains("[no notes]")) { boolean stampingFound = false; noteheadSearch: for (ScoreFrame scoreFrame : layout.getScoreFrames()) { for (Stamping stamping : scoreFrame.getScoreFrameLayout().getMusicalStampings()) { if (stamping instanceof NoteheadStamping || stamping instanceof StaffSymbolStamping) { stampingFound = true; break noteheadSearch; } } } if (false == stampingFound) throw new AssertionError("No staff symbol stamping found in layout"); } } }