blob: 719635a19a305842ce311026697e06217106e69c [file] [log] [blame]
package com.intellij.openapi.components.impl;
import com.intellij.application.options.PathMacrosImpl;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.*;
import com.intellij.openapi.components.impl.stores.*;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.testFramework.LightPlatformLangTestCase;
import com.intellij.util.xmlb.XmlSerializerUtil;
import gnu.trove.THashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;
public class ApplicationStoreTest extends LightPlatformLangTestCase {
private File testAppConfig;
private MyComponentStore componentStore;
@Override
public void setUp() throws Exception {
super.setUp();
String testAppConfigPath = System.getProperty("test.app.config.path");
if (testAppConfigPath == null) {
testAppConfig = FileUtil.createTempDirectory("testAppSettings", null);
}
else {
testAppConfig = new File(FileUtil.expandUserHome(testAppConfigPath));
}
FileUtil.delete(testAppConfig);
componentStore = new MyComponentStore(testAppConfig.getAbsolutePath());
}
@Override
public void tearDown() throws Exception {
try {
Disposer.dispose(componentStore);
componentStore = null;
}
finally {
try {
super.tearDown();
}
finally {
FileUtil.delete(testAppConfig);
}
}
}
public void testStreamProviderSaveIfSeveralStoragesConfigured() throws Exception {
SeveralStoragesConfigured component = new SeveralStoragesConfigured();
MyStreamProvider streamProvider = new MyStreamProvider();
componentStore.getStateStorageManager().setStreamProvider(streamProvider);
componentStore.initComponent(component, false);
component.foo = "newValue";
StoreUtil.doSave(componentStore);
assertThat(streamProvider.data.get(RoamingType.PER_USER).get(StoragePathMacros.APP_CONFIG + "/proxy.settings.xml"), equalTo("<application>\n" +
" <component name=\"HttpConfigurable\">\n" +
" <option name=\"foo\" value=\"newValue\" />\n" +
" </component>\n" +
"</application>"));
}
public void testLoadFromStreamProvider() throws Exception {
SeveralStoragesConfigured component = new SeveralStoragesConfigured();
MyStreamProvider streamProvider = new MyStreamProvider();
THashMap<String, String> map = new THashMap<String, String>();
map.put(StoragePathMacros.APP_CONFIG + "/proxy.settings.xml", "<application>\n" +
" <component name=\"HttpConfigurable\">\n" +
" <option name=\"foo\" value=\"newValue\" />\n" +
" </component>\n" +
"</application>");
streamProvider.data.put(RoamingType.PER_USER, map);
componentStore.getStateStorageManager().setStreamProvider(streamProvider);
componentStore.initComponent(component, false);
assertThat(component.foo, equalTo("newValue"));
}
private static class MyStreamProvider extends StreamProvider {
public final Map<RoamingType, Map<String, String>> data = new THashMap<RoamingType, Map<String, String>>();
@Override
public void saveContent(@NotNull String fileSpec,
@NotNull byte[] content,
int size,
@NotNull RoamingType roamingType,
boolean async) {
getMap(roamingType).put(fileSpec, new String(content, 0, size, CharsetToolkit.UTF8_CHARSET));
}
private Map<String, String> getMap(@NotNull RoamingType roamingType) {
Map<String, String> map = data.get(roamingType);
if (map == null) {
map = new THashMap<String, String>();
data.put(roamingType, map);
}
return map;
}
@Nullable
@Override
public InputStream loadContent(@NotNull String fileSpec, @NotNull RoamingType roamingType) throws IOException {
String data = getMap(roamingType).get(fileSpec);
return data == null ? null : new ByteArrayInputStream(data.getBytes(CharsetToolkit.UTF8_CHARSET));
}
@Override
public void delete(@NotNull String fileSpec, @NotNull RoamingType roamingType) {
Map<String, String> map = data.get(roamingType);
if (map != null) {
map.remove(fileSpec);
}
}
}
class MyComponentStore extends ComponentStoreImpl implements Disposable {
private final StateStorageManager stateStorageManager;
MyComponentStore(@NotNull final String testAppConfigPath) {
TrackingPathMacroSubstitutor macroSubstitutor = new ApplicationPathMacroManager().createTrackingSubstitutor();
stateStorageManager = new StateStorageManagerImpl(macroSubstitutor, "application", this, ApplicationManager.getApplication().getPicoContainer()) {
@Override
protected StorageData createStorageData(String storageSpec) {
return new FileBasedStorage.FileStorageData("application");
}
@Nullable
@Override
protected String getOldStorageSpec(Object component, final String componentName, final StateStorageOperation operation) {
return null;
}
@Override
protected String getVersionsFilePath() {
return testAppConfigPath + "/options/appComponentVersions.xml";
}
@Override
protected TrackingPathMacroSubstitutor getMacroSubstitutor(@NotNull final String fileSpec) {
if (fileSpec.equals(StoragePathMacros.APP_CONFIG + "/" + PathMacrosImpl.EXT_FILE_NAME + ".xml")) {
return null;
}
return super.getMacroSubstitutor(fileSpec);
}
};
stateStorageManager.addMacro(StoragePathMacros.APP_CONFIG, testAppConfigPath);
}
@Override
public void load() throws IOException, StateStorageException {
}
@NotNull
@Override
public StateStorageManager getStateStorageManager() {
return stateStorageManager;
}
@Override
public void dispose() {
}
@Nullable
@Override
protected StateStorage getDefaultsStorage() {
return null;
}
}
static class SeveralStoragesConfiguredStorageChooser implements StateStorageChooser<SeveralStoragesConfigured> {
@Override
public Storage[] selectStorages(Storage[] storages, SeveralStoragesConfigured component, StateStorageOperation operation) {
if (operation == StateStorageOperation.WRITE) {
for (Storage storage : storages) {
if (storage.file().equals(StoragePathMacros.APP_CONFIG + "/proxy.settings.xml")) {
return new Storage[]{storage};
}
}
}
return storages;
}
}
@State(
name = "HttpConfigurable",
storages = {
// we use two storages due to backward compatibility, see http://crucible.labs.intellij.net/cru/CR-IC-5142
@Storage(file = StoragePathMacros.APP_CONFIG + "/other.xml"),
@Storage(file = StoragePathMacros.APP_CONFIG + "/proxy.settings.xml")
},
storageChooser = SeveralStoragesConfiguredStorageChooser.class
)
static class SeveralStoragesConfigured implements PersistentStateComponent<SeveralStoragesConfigured> {
public String foo = "defaultValue";
@Nullable
@Override
public SeveralStoragesConfigured getState() {
return this;
}
@Override
public void loadState(SeveralStoragesConfigured state) {
XmlSerializerUtil.copyBean(state, this);
}
}
}