/*
* Copyright (C) 2015 RoboVM AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.robovm.objc;
import static org.junit.Assert.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.net.URL;
import java.net.URLEncoder;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.robovm.apple.foundation.NSString;
import org.robovm.apple.foundation.NSStringEncoding;
import org.robovm.apple.foundation.NSURL;
import org.robovm.rt.bro.Bro;
/**
* Tests our native <code>NSURLProtocol</code> which handles <code>jar:</code>
* URLs. This is registered on app startup by the native vm libs.
*/
public class JARURLProtocolTest {
@Before
public void setUp() {
Assume.assumeTrue(Bro.IS_DARWIN);
}
@Test
public void testReadSimpleEntry() throws Exception {
URL jarFile = createJar("resource.txt", "Hello world!!!");
URL url = rel(jarFile, "resource.txt", true);
NSURL nsUrl = new NSURL(url);
String expected = toString(url);
String actual = NSString.readURL(nsUrl, NSStringEncoding.UTF8);
assertEquals(expected, actual);
}
@Test
public void testReadEntryWithSpacesAndPlus() throws Exception {
// NOTE: jar: entries that contain spaces in JAR URLs are not URL
// encoded. Such URLs are illegal and will make NSURL fail. We need to
// initialize the NSURL with a URL that has been properly encoded which
// is why we pass encodePath=true to the NSURL but false to toString().
URL jarFile = createJar("resource with spaces and+.txt", "Hello world!!!");
NSURL nsUrl = new NSURL(rel(jarFile, "resource with spaces and+.txt", true));
String expected = toString(rel(jarFile, "resource with spaces and+.txt", false));
String actual = NSString.readURL(nsUrl, NSStringEncoding.UTF8);
assertEquals(expected, actual);
}
@Test
public void testReadLargeEntry() throws Exception {
// 8 MB of very compressable data
URL jarFile = createJar("resource.txt", new RepeatingCharSequence("01234567", 1024 * 1024));
URL url = rel(jarFile, "resource.txt", true);
NSURL nsUrl = new NSURL(url);
String expected = toString(url);
String actual = NSString.readURL(nsUrl, NSStringEncoding.UTF8);
assertEquals(expected, actual);
}
private static URL createJar(CharSequence... entries) throws IOException {
File jarFile = File.createTempFile(JARURLProtocolTest.class.getSimpleName() + " and spaces", ".jar");
int i = 0;
try (ZipOutputStream out = new ZipOutputStream(new FileOutputStream(jarFile))) {
CharSequence name = entries[i++];
CharSequence contents = entries[i++];
ZipEntry newEntry = new ZipEntry(name.toString());
out.putNextEntry(newEntry);
for (int j = 0; j < contents.length(); j++) {
out.write((byte) (contents.charAt(j) & 0x7f));
}
out.closeEntry();
}
return new URL("jar:" + jarFile.toURI() + "!/");
}
private static URL rel(URL url, String path, boolean encodePath) throws Exception {
if (encodePath) {
path = URLEncoder.encode(path, "utf8");
path = path.replace("+", "%20");
}
return new URL(url.getProtocol(), url.getHost(), url.getFile()
+ path);
}
private static String toString(URL url) throws IOException {
StringWriter output = new StringWriter();
try (Reader input = new InputStreamReader(url.openStream(), "utf8")) {
int n = 0;
char[] buffer = new char[8192];
while ((n = input.read(buffer)) != -1) {
output.write(buffer, 0, n);
}
}
return output.toString();
}
static class RepeatingCharSequence implements CharSequence {
final String s;
final int multiple;
public RepeatingCharSequence(String s, int multiple) {
this.s = s;
this.multiple = multiple;
}
@Override
public int length() {
return s.length() * multiple;
}
@Override
public char charAt(int index) {
return s.charAt(index % s.length());
}
@Override
public CharSequence subSequence(int start, int end) {
return null;
}
}
}