1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a 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
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package libcore.java.util.jar;
18
19import java.io.ByteArrayOutputStream;
20import java.io.File;
21import java.io.FileOutputStream;
22import java.io.IOException;
23import java.io.InputStream;
24import java.util.jar.Attributes;
25import java.util.jar.JarEntry;
26import java.util.jar.JarFile;
27import java.util.jar.JarOutputStream;
28import java.util.jar.Manifest;
29import junit.framework.TestCase;
30import libcore.io.Streams;
31import static tests.support.Support_Exec.execAndGetOutput;
32import tests.support.resource.Support_Resources;
33
34
35public class DalvikExecTest extends TestCase {
36
37    String execDalvik1(String classpath, String mainClass, String arg1)
38            throws IOException, InterruptedException {
39        ProcessBuilder builder = new ProcessBuilder();
40
41        File dalvikvm = new File("/system/bin/dalvikvm");
42        if (dalvikvm.exists()) {
43            builder.command().add(dalvikvm.getPath());
44        } else {
45            builder.command().add("dalvikvm"); // for host mode, assume dalvikvm is on the path
46        }
47
48        builder.command().add("-Duser.language=en");
49        builder.command().add("-Duser.region=US");
50        builder.command().add("-Xbootclasspath:" + System.getProperty("java.boot.class.path"));
51        builder.command().add("-classpath");
52        builder.command().add(classpath);
53        builder.command().add(mainClass);
54
55        if (arg1 != null) {
56            builder.command().add(arg1);
57        }
58
59        // Create a writable dalvik-cache under ANDROID_DATA.
60        // The default dalvik-cache is only writable by the system user (and root).
61        String tmp = System.getProperty("java.io.tmpdir");
62        builder.environment().put("ANDROID_DATA", tmp);
63        new File(tmp, "dalvik-cache").mkdir();
64
65        return execAndGetOutput(builder);
66    }
67
68    String execDalvik (String classpath, String mainClass)
69            throws IOException, InterruptedException {
70        return execDalvik1(classpath, mainClass, null);
71    }
72
73    // Execute an existing JAR on dalvikvm using -classpath option.",
74    public void test_execExistingJar () throws IOException, InterruptedException {
75        String res;
76        File jarFile;
77        if (System.getProperty("java.vendor").contains("Android")) {
78            //
79            // Test against Android:
80            //
81            File tempDir = Support_Resources.createTempFolder();
82            jarFile = Support_Resources.copyFile(
83                    tempDir, null, "cts_dalvikExecTest.jar" );
84            res = execDalvik(jarFile.getAbsolutePath(), "dalvikExecTest.HelloWorld");
85            assertEquals("Hello Android World!", "Hello Android World!\n", res);
86
87            res = execDalvik(jarFile.getAbsolutePath(), "dalvikExecTest.ResourceDumper");
88            assertTrue("Android Resource Dumper started",
89                    res.contains("Android Resource Dumper started"));
90            assertTrue("This Resource contains some text.",
91                    res.contains("This Resource contains some text."));
92        } else {
93            //
94            // Test against RI:
95            //
96            // Do nothing!
97        }
98    }
99
100    // Create a temp file, fill it with contents according to Dalvik JAR format, and execute it on dalvikvm using -classpath option.",
101    public void test_execCreatedJar () throws IOException, InterruptedException {
102        File jarFile = File.createTempFile("cts_dalvikExecTest_", ".jar");
103        jarFile.deleteOnExit();
104
105        // Create a JAR output stream on the temp file:
106        JarOutputStream jarOut = new JarOutputStream(new FileOutputStream(jarFile));
107
108        // Define the entry for the classes.dex:
109        jarOut.putNextEntry(new JarEntry("classes.dex"));
110
111        // Fill in the classes.dex contents, i.e. the Dalvik executable code:
112        // (See below for the detailed source code contents.)
113        Streams.copy(Support_Resources.getResourceStream("cts_dalvikExecTest_classes.dex"), jarOut);
114
115        // Now add a resource file:
116        //
117        jarOut.putNextEntry(new JarEntry("dalvikExecTest/myResource"));
118        jarOut.write("This Resource contains some text.".getBytes());
119
120        // Close the stream to the completed JAR file.
121        jarOut.close();
122
123        // The resulting JAR file contains the classes listed at the end of this text,
124        // like the 'cts_dalvikExecTest.jar' as part of the resources, too.
125
126        String res;
127
128        res = execDalvik(jarFile.getAbsolutePath(), "dalvikExecTest.HelloWorld");
129        assertEquals("Hello Android World!", "Hello Android World!\n", res);
130
131        res = execDalvik(jarFile.getAbsolutePath(), "dalvikExecTest.ResourceDumper");
132        assertTrue("Android Resource Dumper started",
133                res.contains("Android Resource Dumper started"));
134        assertTrue("This Resource contains some text.",
135                res.contains("This Resource contains some text."));
136    }
137
138
139    /**
140     * This test does quite the same as test_execCreatedJar, but includes a manifest.
141     * Note however that the Dalvik JAR format does not require this manifest.
142     * We just test whether the manifest is placed correctly within the JAR by
143     * dumping its contents read as a simple text resource.
144     * No! We can't do that so easily either, as there are other (parent) JARs
145     * with a manifest inside, taken with precedence.
146     * So we will reopen the JAR as a JarFile and check the manifest
147     *  with a top level end-to-end approach.
148     */
149    public void test_execCreatedJarWithManifest () throws IOException, InterruptedException {
150        File jarFile = File.createTempFile("cts_dalvikExecTest_", ".jar");
151        jarFile.deleteOnExit();
152
153        // Create the manifest:
154        Manifest manifest = new Manifest();
155        Attributes attrs = manifest.getMainAttributes();
156        attrs.put(Attributes.Name.MANIFEST_VERSION, "3.1415962");
157        attrs.put(Attributes.Name.MAIN_CLASS, "dalvikExecTest.HelloWorld");
158        attrs.put(Attributes.Name.CLASS_PATH, jarFile.getName());
159
160        // Create a JAR output stream on the temp file using the manifest:
161        JarOutputStream jarOut = new JarOutputStream(new FileOutputStream(jarFile), manifest);
162
163        // Define the entry for the classes.dex:
164        jarOut.putNextEntry(new JarEntry("classes.dex"));
165
166        // Fill in the classes.dex contents, i.e. the Dalvik executable code:
167        // (See below for the detailed source code contents.)
168        Streams.copy(Support_Resources.getResourceStream("cts_dalvikExecTest_classes.dex"), jarOut);
169
170        // Now add a resource file:
171        //
172        jarOut.putNextEntry(new JarEntry("dalvikExecTest/myResource"));
173        jarOut.write("This Resource contains some text.".getBytes());
174
175        // Close the stream to the completed JAR file.
176        jarOut.close();
177
178        // The resulting JAR file contains the classes listed at the end of this text,
179        // like the 'cts_dalvikExecTest.jar' as part of the resources, too.
180
181        String res;
182
183        res = execDalvik(jarFile.getAbsolutePath(), "dalvikExecTest.HelloWorld");
184        assertEquals("Hello Android World!", "Hello Android World!\n", res);
185
186        res = execDalvik(jarFile.getAbsolutePath(), "dalvikExecTest.ResourceDumper");
187        assertTrue("Android Resource Dumper started",
188                res.contains("Android Resource Dumper started"));
189        assertTrue("This Resource contains some text.",
190                res.contains("This Resource contains some text."));
191
192        // And now reread the manifest:
193        //
194        JarFile jarIn = new JarFile(jarFile);
195        manifest = jarIn.getManifest();
196        attrs = manifest.getMainAttributes();
197        assertEquals("MANIFEST_VERSION must match!", "3.1415962",
198                attrs.get(Attributes.Name.MANIFEST_VERSION));
199        assertEquals("MAIN_CLASS must match!", "dalvikExecTest.HelloWorld",
200                attrs.get(Attributes.Name.MAIN_CLASS));
201        assertEquals("CLASS_PATH must match!", jarFile.getName(),
202                attrs.get(Attributes.Name.CLASS_PATH));
203    }
204
205
206    /*
207     * The following two classes are added, here, only for completeness.
208     * They form the contents of the dalvikExecTest package contained
209     * in the 'cts_dalvikExecTest_classes.dex' resource file.
210     */
211    /**
212     * @hide
213     */
214    public static class HelloWorld {
215
216        public static void main(String[] args) {
217            System.out.println("Hello Android World!");
218        }
219
220    }
221
222    public static class ResourceDumper {
223
224        static ByteArrayOutputStream outputFrom (InputStream input) throws IOException {
225            ByteArrayOutputStream out = new ByteArrayOutputStream();
226            byte[] buffer = new byte[512];
227            int total = 0;
228            int count;
229            count = input.read(buffer);
230            while (count != -1) {
231                out.write(buffer, 0, count);
232                total = total + count;
233                count = input.read(buffer);
234            }
235            return out;
236        }
237
238        public static void main(String[] args) throws IOException {
239            System.out.print("Android Resource Dumper started ");
240            String fileName;
241            if (args.length >= 1) {
242                fileName = args[0];
243                System.out.format("for argument '%s'.\n", fileName);
244            } else {
245                System.out.print("standard ");
246                fileName = "myResource";
247                System.out.println("for standard 'myResource'.");
248            }
249            InputStream is = ResourceDumper.class.getResourceAsStream(fileName);
250            if (is != null) {
251                System.out.println("Resource obtained and being dumped:");
252                System.out.println(outputFrom(is).toString());
253            }
254        }
255
256    }
257
258}
259