1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
3    * agreements. See the NOTICE file distributed with this work for additional information regarding
4    * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the
5    * "License"); you may not use this file except in compliance with the License. You may obtain a
6    * copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software distributed under the License
11   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12   * or implied. See the License for the specific language governing permissions and limitations under
13   * the License.
14   */
15  package org.apache.maven.lifecycle.internal;
16  
17  import junit.framework.TestCase;
18  import org.apache.maven.execution.MavenSession;
19  import org.apache.maven.lifecycle.LifecycleNotFoundException;
20  import org.apache.maven.lifecycle.LifecyclePhaseNotFoundException;
21  import org.apache.maven.lifecycle.internal.stub.ProjectDependencyGraphStub;
22  import org.apache.maven.plugin.InvalidPluginDescriptorException;
23  import org.apache.maven.plugin.MojoNotFoundException;
24  import org.apache.maven.plugin.PluginDescriptorParsingException;
25  import org.apache.maven.plugin.PluginNotFoundException;
26  import org.apache.maven.plugin.PluginResolutionException;
27  import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException;
28  import org.apache.maven.plugin.version.PluginVersionResolutionException;
29  
30  import java.io.ByteArrayOutputStream;
31  import java.io.PrintStream;
32  import java.util.ArrayList;
33  import java.util.Arrays;
34  import java.util.Iterator;
35  import java.util.List;
36  import java.util.concurrent.Callable;
37  import java.util.concurrent.CompletionService;
38  import java.util.concurrent.ExecutorCompletionService;
39  import java.util.concurrent.ExecutorService;
40  import java.util.concurrent.Executors;
41  import java.util.concurrent.Future;
42  
43  /**
44   * @author Kristian Rosenvold
45   */
46  public class ThreadOutputMuxerTest
47      extends TestCase
48  {
49  
50      final String paid = "Paid";
51  
52      final String in = "In";
53  
54      final String full = "Full";
55  
56      public void testSingleThreaded()
57          throws Exception
58      {
59          ProjectBuildList src = getProjectBuildList();
60          ProjectBuildList projectBuildList =
61              new ProjectBuildList( Arrays.asList( src.get( 0 ), src.get( 1 ), src.get( 2 ) ) );
62  
63          ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
64          PrintStream systemOut = new PrintStream( byteArrayOutputStream );
65          ThreadOutputMuxer threadOutputMuxer = new ThreadOutputMuxer( projectBuildList, systemOut );
66  
67          threadOutputMuxer.associateThreadWithProjectSegment( projectBuildList.get( 0 ) );
68          System.out.print( paid );  // No, this does not print to system.out. It's part of the test
69          assertEquals( paid.length(), byteArrayOutputStream.size() );
70          threadOutputMuxer.associateThreadWithProjectSegment( projectBuildList.get( 1 ) );
71          System.out.print( in );  // No, this does not print to system.out. It's part of the test
72          assertEquals( paid.length(), byteArrayOutputStream.size() );
73          threadOutputMuxer.associateThreadWithProjectSegment( projectBuildList.get( 2 ) );
74          System.out.print( full ); // No, this does not print to system.out. It's part of the test
75          assertEquals( paid.length(), byteArrayOutputStream.size() );
76  
77          threadOutputMuxer.setThisModuleComplete( projectBuildList.get( 0 ) );
78          threadOutputMuxer.setThisModuleComplete( projectBuildList.get( 1 ) );
79          threadOutputMuxer.setThisModuleComplete( projectBuildList.get( 2 ) );
80          threadOutputMuxer.close();
81          assertEquals( ( paid + in + full ).length(), byteArrayOutputStream.size() );
82      }
83  
84      public void testMultiThreaded()
85          throws Exception
86      {
87          ProjectBuildList projectBuildList = getProjectBuildList();
88  
89          ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
90          PrintStream systemOut = new PrintStream( byteArrayOutputStream );
91          final ThreadOutputMuxer threadOutputMuxer = new ThreadOutputMuxer( projectBuildList, systemOut );
92  
93          final List<String> stringList =
94              Arrays.asList( "Thinkin", "of", "a", "master", "plan", "Cuz", "ain?t", "nuthin", "but", "sweat", "inside",
95                             "my", "hand" );
96          Iterator<String> lyrics = stringList.iterator();
97          List<Outputter> outputters = new ArrayList<Outputter>();
98  
99          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 }