package org.codehaus.mojo.unix.maven.plugin; /* * The MIT License * * Copyright 2009 The Codehaus. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is furnished to do * so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ import fj.*; import fj.Function; import fj.pre.*; import fj.data.*; import static fj.data.HashMap.*; import static fj.data.Option.*; import static fj.data.Stream.*; import groovy.lang.*; import org.codehaus.mojo.unix.*; import org.codehaus.mojo.unix.FileAttributes; import static org.codehaus.mojo.unix.UnixFsObject.*; import org.codehaus.mojo.unix.deb.*; import org.codehaus.mojo.unix.sysvpkg.*; import org.codehaus.mojo.unix.sysvpkg.prototype.*; import org.codehaus.mojo.unix.rpm.*; import org.codehaus.mojo.unix.util.*; import static org.codehaus.mojo.unix.util.RelativePath.*; import static org.codehaus.mojo.unix.util.UnixUtil.*; import org.codehaus.mojo.unix.util.line.*; import org.codehaus.plexus.util.*; import org.joda.time.*; import java.io.*; import java.util.zip.*; /** * @author <a href="mailto:trygvis@codehaus.org">Trygve Laugstøl</a> * @version $Id$ */ public class ShittyUtil { public static final LocalDateTime START_OF_TIME = PrototypeFile.START_OF_TIME; public static final OutputStreamWriter out = new OutputStreamWriter( System.out ); public static final LineWriterWriter stream = new LineWriterWriter( out ); public static RelativePath r( String path ) { return relativePath( path ); } // ----------------------------------------------------------------------- // Misc Utils // ----------------------------------------------------------------------- public static File findArtifact( String groupId, String artifactId, String version, String type ) throws IOException { return findArtifact( groupId, artifactId, version, type, null ); } public static File findArtifact(String groupId, String artifactId, String version, String type, String classifier) throws IOException { File m2Repository = new File( System.getProperty( "user.home" ), ".m2/repository" ); // TODO: This can be improved if ( !m2Repository.isDirectory() ) { throw new IOException( "Unable to find local repository: " + m2Repository.getAbsolutePath() ); } String base = groupId.replace( '/', '.' ) + "/" + artifactId + "/" + version + "/" + artifactId + "-" + version; if ( classifier != null ) { base += "-" + classifier; } return new File( m2Repository, base + "." + type ); } // ----------------------------------------------------------------------- // Assertion tools // ----------------------------------------------------------------------- public static boolean assertFormat( String type, String tool, boolean available, Closure closure ) { boolean result; if ( available ) { stream.add( "*********************************************************************" ); stream.add( "* Running asserts for '" + type + "'" ); stream.add( "*********************************************************************" ); result = (Boolean) closure.call(); stream.add( "*********************************************************************" ); stream.add( "* Asserts completed for '" + type + "', success: " + result ); stream.add( "*********************************************************************" ); } else { stream.add( "*********************************************************************" ); stream.add( "* Skipping asserts for '" + type + "', " + tool + " is not available" ); stream.add( "*********************************************************************" ); result = true; } flush( stream ); return result; } public static <T> boolean assertRelaxed( T expected, T actual, Equal<T> equal ) { boolean ok = equal.eq( expected, actual ); if ( ok ) { return true; } stream.add( "*********************************************************************" ); stream.add( "* Failure *" ); stream.add( "*********************************************************************" ); stream.add( "Expected:" ); stream.add(); stream.add( expected.toString() ); stream.add(); stream.add( "Actual:" ); stream.add(); stream.add( actual.toString() ); flush( out ); return false; } public static boolean assertDebEntries( File deb, java.util.List<UnixFsObject> expectedFiles ) throws IOException { final HashMap<RelativePath, UnixFsObject> map = hashMap(); iterableStream( DpkgDebTool.contents( deb ) ).foreach( new Effect<UnixFsObject>() { public void e( UnixFsObject unixFsObject ) { map.set( unixFsObject.path, unixFsObject ); } } ); return assertEntries( iterableStream( expectedFiles ).map( unixFsObjectToP2 ), map, new UnixFsObjectChecker() ); } public static boolean assertSysvPkgEntries( File pkg, java.util.List<PkgchkUtil.FileInfo> expectedFiles ) throws IOException { final HashMap<RelativePath, PkgchkUtil.FileInfo> map = hashMap(); PkgchkUtil.getPackageInforForDevice( pkg ).foreach( new Effect<PkgchkUtil.FileInfo>() { public void e( PkgchkUtil.FileInfo fileInfo ) { map.set( relativePath( fileInfo.pathname ), fileInfo ); } } ); F<PkgchkUtil.FileInfo, P2<RelativePath, PkgchkUtil.FileInfo>> fileInfoToP2 = new F<PkgchkUtil.FileInfo, P2<RelativePath, PkgchkUtil.FileInfo>>() { public P2<RelativePath, PkgchkUtil.FileInfo> f( PkgchkUtil.FileInfo fileInfo ) { return P.p( relativePath( fileInfo.pathname ), fileInfo ); } }; F2<PkgchkUtil.FileInfo, PkgchkUtil.FileInfo, Boolean> fileInfoChecker = new F2<PkgchkUtil.FileInfo, PkgchkUtil.FileInfo, Boolean>() { public Boolean f( PkgchkUtil.FileInfo expected, PkgchkUtil.FileInfo actual ) { return expected.equalsIgnoreNull( actual ); } }; return assertEntries( iterableStream( expectedFiles ).map( fileInfoToP2 ), map, fileInfoChecker ); } public static boolean assertRpmEntries( File pkg, java.util.List<RpmUtil.FileInfo> expectedFiles ) throws IOException { final HashMap<RelativePath, RpmUtil.FileInfo> map = hashMap(); F<RpmUtil.FileInfo, P2<RelativePath, RpmUtil.FileInfo>> fileInfoToP2 = new F<RpmUtil.FileInfo, P2<RelativePath, RpmUtil.FileInfo>>() { public P2<RelativePath, RpmUtil.FileInfo> f( RpmUtil.FileInfo fileInfo ) { return P.p( relativePath( fileInfo.path ), fileInfo ); } }; iterableStream( RpmUtil.queryPackageForFileInfo( pkg ) ).foreach( new Effect<org.codehaus.mojo.unix.rpm.RpmUtil.FileInfo>() { public void e( org.codehaus.mojo.unix.rpm.RpmUtil.FileInfo fileInfo ) { map.set( relativePath( fileInfo.path ), fileInfo ); } } ); F2<RpmUtil.FileInfo, RpmUtil.FileInfo, Boolean> fileInfoChecker = new F2<RpmUtil.FileInfo, RpmUtil.FileInfo, Boolean>() { public Boolean f( RpmUtil.FileInfo expected, RpmUtil.FileInfo actual ) { return expected.equalsIgnoreNull( actual ); } }; return assertEntries( iterableStream( expectedFiles ).map( fileInfoToP2 ), map, fileInfoChecker ); } public static boolean assertZipEntries( File zip, java.util.List<UnixFsObject> expectedFiles ) throws IOException { HashMap<RelativePath, UnixFsObject> actualFiles = hashMap(); ZipInputStream zis = null; try { zis = new ZipInputStream( new FileInputStream( zip ) ); ZipEntry zipEntry = zis.getNextEntry(); while ( zipEntry != null ) { LocalDateTime time = new LocalDateTime( zipEntry.getTime() ); RelativePath path = relativePath( zipEntry.getName() ); UnixFsObject o; if ( zipEntry.isDirectory() ) { o = directory( path, time, FileAttributes.EMPTY ); } else { long size = zipEntry.getSize(); // For some reason ZipInputStream can't give me zipEntry objects with a reasonable getSize() if(size == -1) { size = 0; int s; while ( true ) { byte[] bytes = new byte[1024 * 128]; s = zis.read( bytes, 0, bytes.length ); if ( s == -1 ) { break; } size += s; } } o = regularFile( path, time, size, some( FileAttributes.EMPTY ) ); } actualFiles.set( path, o ); zipEntry = zis.getNextEntry(); } } finally { IOUtil.close( zis ); } return assertEntries( iterableStream( expectedFiles ).map( unixFsObjectToP2 ), actualFiles, new UnixFsObjectChecker() ); } public static <T extends LineProducer> boolean assertEntries( Stream<P2<RelativePath, T>> expectedFiles, HashMap<RelativePath, T> actualFiles, F2<T, T, Boolean> checker ) throws IOException { boolean success = true; for ( P2<RelativePath, T> p2 : expectedFiles ) { RelativePath expectedPath = p2._1(); Option<T> actualO = actualFiles.get( expectedPath ); if ( actualO.isSome() ) { T actual = actualO.some(); T expected = p2._2(); if ( !checker.f( expected, actual ) ) { stream.add( "Found invalid entry: " + expectedPath ); stream.add( "Expected" ); expected.streamTo( stream ); stream.add( "Actual" ); actual.streamTo( stream ); success = false; } else { // This output is a bit too noisy // stream.add( "Found entry: " + expected.pathname ); } actualFiles.delete( expectedPath ); } else { success = false; stream.add( "Missing entry: " + expectedPath ); } } if ( actualFiles.size() > 0 ) { success = false; stream.add( "Extra files in package:" ); for ( RelativePath path : actualFiles.keys() ) { stream.add( "Extra entry: " + path ); } } stream.flush(); return success; } private static class UnixFsObjectChecker implements F2<UnixFsObject, UnixFsObject, Boolean> { public Boolean f( UnixFsObject expected, UnixFsObject actual ) { return expected.path.equals( actual.path ) && ( expected.size == 0 || expected.size == actual.size ) && ( expected.lastModified == null || expected.lastModified.equals( START_OF_TIME ) || expected.lastModified.equals( actual.lastModified ) ); } } static F<UnixFsObject, P2<RelativePath, UnixFsObject>> unixFsObjectToP2 = new F<UnixFsObject, P2<RelativePath, UnixFsObject>>() { public P2<RelativePath, UnixFsObject> f( UnixFsObject unixFsObject ) { return P.p( unixFsObject.path, unixFsObject ); } }; public static final Equal<PkginfoFile> packageInfoEqual = Equal.equal( new F<PkginfoFile, F<PkginfoFile, Boolean>>() { public F<PkginfoFile, Boolean> f( final PkginfoFile tis ) { return new F<PkginfoFile, Boolean>() { public Boolean f( PkginfoFile that ) { Equal<Option<String>> optionStringEqual = Equal.equal( Function.curry( new F2<Option<String>, Option<String>, Boolean>() { public Boolean f( Option<String> a, Option<String> b ) { return a.isNone() || Equal.stringEqual.eq( a.some(), b.some() ); } }) ); Equal<List<String>> listStringEqual = Equal.listEqual( Equal.stringEqual ); return tis.arch.equals( that.arch ) && tis.category.equals( that.category ) && tis.name.equals( that.name ) && tis.pkg.equals( that.pkg ) && tis.version.equals( that.version ) && optionStringEqual.eq( tis.pstamp, that.pstamp ) && optionStringEqual.eq( tis.desc, that.desc ) && optionStringEqual.eq( tis.email, that.email ) && listStringEqual.eq( tis.classes, that.classes ); } }; } } ); public static final Equal<SpecFile> specFileEqual = Equal.equal( new F<SpecFile, F<SpecFile, Boolean>>() { public F<SpecFile, Boolean> f( final SpecFile tis ) { final Equal<String> eq = Equal.equal( new F<String, F<String, Boolean>>() { public F<String, Boolean> f( final String a ) { return new F<String, Boolean>() { public Boolean f( String b ) { return a == null || a.equals( b ); } }; } } ); return new F<SpecFile, Boolean>() { public Boolean f( SpecFile that ) { return eq.eq( tis.name, that.name ) && eq.eq( tis.version, that.version ) && eq.eq( tis.release, that.release ) && eq.eq( tis.summary, that.summary ) && eq.eq( tis.license, that.license ) && eq.eq( tis.group, that.group ) && eq.eq( tis.description, that.description ); } }; } } ); }