001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
003     * agreements. See the NOTICE file distributed with this work for additional information regarding
004     * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the
005     * "License"); you may not use this file except in compliance with the License. You may obtain a
006     * copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software distributed under the License
011     * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
012     * or implied. See the License for the specific language governing permissions and limitations under
013     * the License.
014     */
015    package org.apache.maven.lifecycle.internal;
016    
017    import junit.framework.TestCase;
018    import org.apache.maven.execution.MavenSession;
019    import org.apache.maven.lifecycle.LifecycleNotFoundException;
020    import org.apache.maven.lifecycle.LifecyclePhaseNotFoundException;
021    import org.apache.maven.lifecycle.internal.stub.ProjectDependencyGraphStub;
022    import org.apache.maven.plugin.InvalidPluginDescriptorException;
023    import org.apache.maven.plugin.MojoNotFoundException;
024    import org.apache.maven.plugin.PluginDescriptorParsingException;
025    import org.apache.maven.plugin.PluginNotFoundException;
026    import org.apache.maven.plugin.PluginResolutionException;
027    import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException;
028    import org.apache.maven.plugin.version.PluginVersionResolutionException;
029    
030    import java.io.ByteArrayOutputStream;
031    import java.io.PrintStream;
032    import java.util.ArrayList;
033    import java.util.Arrays;
034    import java.util.Iterator;
035    import java.util.List;
036    import java.util.concurrent.Callable;
037    import java.util.concurrent.CompletionService;
038    import java.util.concurrent.ExecutorCompletionService;
039    import java.util.concurrent.ExecutorService;
040    import java.util.concurrent.Executors;
041    import java.util.concurrent.Future;
042    
043    /**
044     * @author Kristian Rosenvold
045     */
046    public class ThreadOutputMuxerTest
047        extends TestCase
048    {
049    
050        final String paid = "Paid";
051    
052        final String in = "In";
053    
054        final String full = "Full";
055    
056        public void testSingleThreaded()
057            throws Exception
058        {
059            ProjectBuildList src = getProjectBuildList();
060            ProjectBuildList projectBuildList =
061                new ProjectBuildList( Arrays.asList( src.get( 0 ), src.get( 1 ), src.get( 2 ) ) );
062    
063            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
064            PrintStream systemOut = new PrintStream( byteArrayOutputStream );
065            ThreadOutputMuxer threadOutputMuxer = new ThreadOutputMuxer( projectBuildList, systemOut );
066    
067            threadOutputMuxer.associateThreadWithProjectSegment( projectBuildList.get( 0 ) );
068            System.out.print( paid );  // No, this does not print to system.out. It's part of the test
069            assertEquals( paid.length(), byteArrayOutputStream.size() );
070            threadOutputMuxer.associateThreadWithProjectSegment( projectBuildList.get( 1 ) );
071            System.out.print( in );  // No, this does not print to system.out. It's part of the test
072            assertEquals( paid.length(), byteArrayOutputStream.size() );
073            threadOutputMuxer.associateThreadWithProjectSegment( projectBuildList.get( 2 ) );
074            System.out.print( full ); // No, this does not print to system.out. It's part of the test
075            assertEquals( paid.length(), byteArrayOutputStream.size() );
076    
077            threadOutputMuxer.setThisModuleComplete( projectBuildList.get( 0 ) );
078            threadOutputMuxer.setThisModuleComplete( projectBuildList.get( 1 ) );
079            threadOutputMuxer.setThisModuleComplete( projectBuildList.get( 2 ) );
080            threadOutputMuxer.close();
081            assertEquals( ( paid + in + full ).length(), byteArrayOutputStream.size() );
082        }
083    
084        public void testMultiThreaded()
085            throws Exception
086        {
087            ProjectBuildList projectBuildList = getProjectBuildList();
088    
089            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
090            PrintStream systemOut = new PrintStream( byteArrayOutputStream );
091            final ThreadOutputMuxer threadOutputMuxer = new ThreadOutputMuxer( projectBuildList, systemOut );
092    
093            final List<String> stringList =
094                Arrays.asList( "Thinkin", "of", "a", "master", "plan", "Cuz", "ain’t", "nuthin", "but", "sweat", "inside",
095                               "my", "hand" );
096            Iterator<String> lyrics = stringList.iterator();
097            List<Outputter> outputters = new ArrayList<Outputter>();
098    
099            ExecutorService executor = Executors.newFixedThreadPool( 10 );
100            CompletionService<ProjectSegment> service = new ExecutorCompletionService<ProjectSegment>( executor );
101    
102            List<Future<ProjectSegment>> futures = new ArrayList<Future<ProjectSegment>>();
103            for ( ProjectSegment projectBuild : projectBuildList )
104            {
105                final Future<ProjectSegment> buildFuture =
106                    service.submit( new Outputter( threadOutputMuxer, projectBuild, lyrics.next() ) );
107                futures.add( buildFuture );
108            }
109    
110            for ( Future<ProjectSegment> future : futures )
111            {
112                future.get();
113            }
114            int expectedLength = 0;
115            for ( int i = 0; i < projectBuildList.size(); i++ )
116            {
117                expectedLength += stringList.get( i ).length();
118            }
119    
120            threadOutputMuxer.close();
121            final byte[] bytes = byteArrayOutputStream.toByteArray();
122            String result = new String( bytes );
123            assertEquals( result, expectedLength, bytes.length );
124    
125    
126        }
127    
128        class Outputter
129            implements Callable<ProjectSegment>
130        {
131            private final ThreadOutputMuxer threadOutputMuxer;
132    
133            private final ProjectSegment item;
134    
135            private final String response;
136    
137            Outputter( ThreadOutputMuxer threadOutputMuxer, ProjectSegment item, String response )
138            {
139                this.threadOutputMuxer = threadOutputMuxer;
140                this.item = item;
141                this.response = response;
142            }
143    
144            public ProjectSegment call()
145                throws Exception
146            {
147                threadOutputMuxer.associateThreadWithProjectSegment( item );
148                System.out.print( response );
149                threadOutputMuxer.setThisModuleComplete( item );
150                return item;
151            }
152        }
153    
154    
155        private ProjectBuildList getProjectBuildList()
156            throws InvalidPluginDescriptorException, PluginVersionResolutionException, PluginDescriptorParsingException,
157            NoPluginFoundForPrefixException, MojoNotFoundException, PluginNotFoundException, PluginResolutionException,
158            LifecyclePhaseNotFoundException, LifecycleNotFoundException
159        {
160            final MavenSession session = ProjectDependencyGraphStub.getMavenSession();
161            return ProjectDependencyGraphStub.getProjectBuildList( session );
162        }
163    }