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