PageRenderTime 38ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/projects/netbeans-7.3/o.n.core/test/qa-functional/src/org/netbeans/core/validation/ValidateLayerConsistencyTest.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 1037 lines | 791 code | 94 blank | 152 comment | 168 complexity | 8e3c2a700b503a58229df8d5ff5f9043 MD5 | raw file
  1. /*
  2. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  3. *
  4. * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
  5. *
  6. * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
  7. * Other names may be trademarks of their respective owners.
  8. *
  9. * The contents of this file are subject to the terms of either the GNU
  10. * General Public License Version 2 only ("GPL") or the Common
  11. * Development and Distribution License("CDDL") (collectively, the
  12. * "License"). You may not use this file except in compliance with the
  13. * License. You can obtain a copy of the License at
  14. * http://www.netbeans.org/cddl-gplv2.html
  15. * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
  16. * specific language governing permissions and limitations under the
  17. * License. When distributing the software, include this License Header
  18. * Notice in each file and include the License file at
  19. * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
  20. * particular file as subject to the "Classpath" exception as provided
  21. * by Oracle in the GPL Version 2 section of the License file that
  22. * accompanied this code. If applicable, add the following below the
  23. * License Header, with the fields enclosed by brackets [] replaced by
  24. * your own identifying information:
  25. * "Portions Copyrighted [year] [name of copyright owner]"
  26. *
  27. * Contributor(s):
  28. *
  29. * The Original Software is NetBeans. The Initial Developer of the Original
  30. * Software is Sun Microsystems, Inc. Portions Copyright 2006 Sun
  31. * Microsystems, Inc. All Rights Reserved.
  32. *
  33. * If you wish your version of this file to be governed by only the CDDL
  34. * or only the GPL Version 2, indicate your decision by adding
  35. * "[Contributor] elects to include this software in this distribution
  36. * under the [CDDL or GPL Version 2] license." If you do not indicate a
  37. * single choice of license, a recipient has the option to distribute
  38. * your version of this file under either the CDDL, the GPL Version 2 or
  39. * to extend the choice of license to its licensees as provided above.
  40. * However, if you add GPL Version 2 code and therefore, elected the GPL
  41. * Version 2 license, then the option applies only if the new code is
  42. * made subject to such option by the copyright holder.
  43. */
  44. package org.netbeans.core.validation;
  45. import java.io.ByteArrayOutputStream;
  46. import java.io.File;
  47. import java.io.FileNotFoundException;
  48. import java.io.IOException;
  49. import java.io.InputStream;
  50. import java.io.PrintStream;
  51. import java.net.URL;
  52. import java.net.URLConnection;
  53. import java.text.MessageFormat;
  54. import java.util.ArrayList;
  55. import java.util.Arrays;
  56. import java.util.Collection;
  57. import java.util.Enumeration;
  58. import java.util.HashMap;
  59. import java.util.HashSet;
  60. import java.util.List;
  61. import java.util.Map;
  62. import java.util.MissingResourceException;
  63. import java.util.Properties;
  64. import java.util.Set;
  65. import java.util.TreeMap;
  66. import java.util.TreeSet;
  67. import java.util.jar.Manifest;
  68. import java.util.logging.Handler;
  69. import java.util.logging.Level;
  70. import java.util.logging.LogRecord;
  71. import java.util.logging.Logger;
  72. import java.util.regex.Matcher;
  73. import java.util.regex.Pattern;
  74. import javax.swing.Action;
  75. import javax.xml.parsers.DocumentBuilder;
  76. import javax.xml.parsers.DocumentBuilderFactory;
  77. import javax.xml.parsers.ParserConfigurationException;
  78. import javax.xml.parsers.SAXParser;
  79. import javax.xml.parsers.SAXParserFactory;
  80. import junit.framework.Test;
  81. import junit.framework.TestSuite;
  82. import org.netbeans.core.startup.layers.LayerCacheManager;
  83. import org.netbeans.junit.NbModuleSuite;
  84. import org.netbeans.junit.NbTestCase;
  85. import org.netbeans.junit.Log;
  86. import org.netbeans.junit.RandomlyFails;
  87. import org.openide.cookies.InstanceCookie;
  88. import org.openide.filesystems.FileObject;
  89. import org.openide.filesystems.FileSystem;
  90. import org.openide.filesystems.FileUtil;
  91. import org.openide.filesystems.MultiFileSystem;
  92. import org.openide.filesystems.XMLFileSystem;
  93. import org.openide.loaders.DataFolder;
  94. import org.openide.loaders.DataObject;
  95. import org.openide.loaders.DataShadow;
  96. import org.openide.modules.Dependency;
  97. import org.openide.util.Enumerations;
  98. import org.openide.util.Lookup;
  99. import org.openide.util.Mutex;
  100. import org.openide.util.NbCollections;
  101. import org.openide.xml.EntityCatalog;
  102. import org.openide.xml.XMLUtil;
  103. import org.w3c.dom.Document;
  104. import org.w3c.dom.Element;
  105. import org.w3c.dom.NodeList;
  106. import org.xml.sax.InputSource;
  107. import org.xml.sax.SAXException;
  108. /** Checks consistency of System File System contents.
  109. */
  110. public class ValidateLayerConsistencyTest extends NbTestCase {
  111. static {
  112. System.setProperty("java.awt.headless", "true");
  113. System.setProperty("org.openide.util.lookup.level", "FINE");
  114. }
  115. private static final String SFS_LB = "SystemFileSystem.localizingBundle";
  116. private ClassLoader contextClassLoader;
  117. public ValidateLayerConsistencyTest(String name) {
  118. super (name);
  119. }
  120. @Override
  121. protected int timeOut() {
  122. // sometimes can deadlock and then we need to see the thread dump
  123. return 1000 * 60 * 10;
  124. }
  125. public @Override void setUp() throws Exception {
  126. clearWorkDir();
  127. Mutex.EVENT.readAccess(new Mutex.Action<Void>() {
  128. public @Override Void run() {
  129. contextClassLoader = Thread.currentThread().getContextClassLoader();
  130. Thread.currentThread().setContextClassLoader(Lookup.getDefault().lookup(ClassLoader.class));
  131. return null;
  132. }
  133. });
  134. }
  135. public @Override void tearDown() {
  136. Mutex.EVENT.readAccess(new Mutex.Action<Void>() {
  137. public @Override Void run() {
  138. Thread.currentThread().setContextClassLoader(contextClassLoader);
  139. return null;
  140. }
  141. });
  142. }
  143. protected @Override boolean runInEQ() {
  144. return true;
  145. }
  146. public static Test suite() {
  147. TestSuite suite = new TestSuite();
  148. suite.addTest(NbModuleSuite.createConfiguration(ValidateLayerConsistencyTest.class).
  149. clusters("(?!ergonomics).*").enableClasspathModules(false).enableModules(".*").gui(false).suite());
  150. suite.addTest(NbModuleSuite.createConfiguration(ValidateLayerConsistencyTest.class).
  151. clusters("platform|ide").enableClasspathModules(false).enableModules(".*").gui(false).suite());
  152. return suite;
  153. }
  154. private void assertNoErrors(String message, Collection<String> warnings) {
  155. if (warnings.isEmpty()) {
  156. return;
  157. }
  158. StringBuilder b = new StringBuilder(message);
  159. for (String warning : new TreeSet<String>(warnings)) {
  160. b.append('\n').append(warning);
  161. }
  162. fail(b.toString());
  163. }
  164. /* Causes mysterious failure in otherwise OK-looking UI/Runtime/org-netbeans-modules-db-explorer-nodes-RootNode.instance:
  165. @Override
  166. protected Level logLevel() {
  167. return Level.FINER;
  168. }
  169. */
  170. /** whether an attribute will be handled in testInstantiateAllInstances anyway */
  171. private static boolean isInstanceAttribute(String attributeName) {
  172. if (attributeName.equals("instanceCreate")) {
  173. return true;
  174. }
  175. if (attributeName.equals("component")) {
  176. return true; // probably being used by TopComponent.openAction
  177. }
  178. return false;
  179. }
  180. public void testAreAttributesFine () {
  181. List<String> errors = new ArrayList<String>();
  182. FileObject root = FileUtil.getConfigRoot();
  183. Enumeration<? extends FileObject> files = Enumerations.concat(Enumerations.singleton(root), root.getChildren(true));
  184. while (files.hasMoreElements()) {
  185. FileObject fo = files.nextElement();
  186. if (
  187. "Keymaps/NetBeans/D-BACK_QUOTE.shadow".equals(fo.getPath()) ||
  188. "Keymaps/NetBeans55/D-BACK_QUOTE.shadow".equals(fo.getPath()) ||
  189. "Keymaps/Emacs/D-BACK_QUOTE.shadow".equals(fo.getPath())
  190. ) {
  191. // #46753
  192. continue;
  193. }
  194. if (
  195. "Services/Browsers/FirefoxBrowser.settings".equals(fo.getPath()) ||
  196. "Services/Browsers/MozillaBrowser.settings".equals(fo.getPath()) ||
  197. "Services/Browsers/NetscapeBrowser.settings".equals(fo.getPath())
  198. ) {
  199. // #161784
  200. continue;
  201. }
  202. Enumeration<String> attrs = fo.getAttributes();
  203. while (attrs.hasMoreElements()) {
  204. String name = attrs.nextElement();
  205. if (isInstanceAttribute(name)) {
  206. continue;
  207. }
  208. if (name.indexOf('\\') != -1) {
  209. errors.add("File: " + fo.getPath() + " attribute name must not contain backslashes: " + name);
  210. }
  211. Object attr = fo.getAttribute(name);
  212. if (attr == null) {
  213. CharSequence warning = Log.enable("", Level.WARNING);
  214. if (
  215. fo.getAttribute("class:" + name) != null &&
  216. fo.getAttribute(name) == null &&
  217. warning.length() == 0
  218. ) {
  219. // ok, factory method returned null
  220. continue;
  221. }
  222. errors.add("File: " + fo.getPath() + " attribute name: " + name);
  223. }
  224. if (attr instanceof URL) {
  225. URL u = (URL) attr;
  226. int read = -1;
  227. try {
  228. read = u.openStream().read(new byte[4096]);
  229. } catch (IOException ex) {
  230. errors.add(fo.getPath() + ": " + ex.getMessage());
  231. }
  232. if (read <= 0) {
  233. errors.add("URL resource does not exist: " + fo.getPath() + " attr: " + name + " value: " + attr);
  234. }
  235. }
  236. }
  237. }
  238. assertNoErrors("Some attributes in files are unreadable", errors);
  239. }
  240. public void testValidShadows () {
  241. // might be better to move into editor/options tests as it is valid only if there are options
  242. List<String> errors = new ArrayList<String>();
  243. FileObject root = FileUtil.getConfigRoot();
  244. Enumeration<? extends FileObject> en = root.getChildren(true);
  245. int cnt = 0;
  246. while (en.hasMoreElements()) {
  247. FileObject fo = en.nextElement();
  248. cnt++;
  249. // XXX #16761 Removing attr in MFO causes storing special-null value even in unneeded cases.
  250. // When the issue is fixed remove this hack.
  251. if("Windows2/Modes/debugger".equals(fo.getPath()) // NOI18N
  252. || "Windows2/Modes/explorer".equals(fo.getPath())) { // NOI18N
  253. continue;
  254. }
  255. if (
  256. "Keymaps/NetBeans/D-BACK_QUOTE.shadow".equals(fo.getPath()) ||
  257. "Keymaps/NetBeans55/D-BACK_QUOTE.shadow".equals(fo.getPath()) ||
  258. "Keymaps/Emacs/D-BACK_QUOTE.shadow".equals(fo.getPath())
  259. ) {
  260. // #46753
  261. continue;
  262. }
  263. try {
  264. DataObject obj = DataObject.find (fo);
  265. DataShadow ds = obj.getLookup().lookup(DataShadow.class);
  266. if (ds != null) {
  267. Object o = ds.getOriginal();
  268. if (o == null) {
  269. errors.add("File " + fo + " has no original.");
  270. }
  271. }
  272. else if ("shadow".equals(fo.getExt())) {
  273. errors.add("File " + fo + " is not a valid DataShadow.");
  274. }
  275. } catch (Exception ex) {
  276. ex.printStackTrace();
  277. errors.add ("File " + fo + " threw " + ex);
  278. }
  279. }
  280. assertNoErrors("Some shadow files in NetBeans profile are broken", errors);
  281. if (ValidateLayerConsistencyTest.class.getClassLoader() == ClassLoader.getSystemClassLoader()) {
  282. // do not check the count as this probably means we are running
  283. // plain Unit test and not inside the IDE mode
  284. return;
  285. }
  286. if (cnt == 0) {
  287. fail("No file objects on system file system!");
  288. }
  289. }
  290. @RandomlyFails
  291. public void testContentCanBeRead () {
  292. List<String> errors = new ArrayList<String>();
  293. byte[] buffer = new byte[4096];
  294. Enumeration<? extends FileObject> files = FileUtil.getConfigRoot().getChildren(true);
  295. while (files.hasMoreElements()) {
  296. FileObject fo = files.nextElement();
  297. if (!fo.isData ()) {
  298. continue;
  299. }
  300. long size = fo.getSize();
  301. try {
  302. long read = 0;
  303. InputStream is = fo.getInputStream();
  304. try {
  305. for (;;) {
  306. int len = is.read (buffer);
  307. if (len == -1) {
  308. break;
  309. }
  310. read += len;
  311. }
  312. } finally {
  313. is.close ();
  314. }
  315. if (size != -1) {
  316. assertEquals ("The amount of data in stream is the same as the length", size, read);
  317. }
  318. } catch (IOException ex) {
  319. ex.printStackTrace();
  320. errors.add ("File " + fo + " cannot be read: " + ex);
  321. }
  322. }
  323. assertNoErrors("Some files are unreadable", errors);
  324. }
  325. public void testInstantiateAllInstances () {
  326. List<String> errors = new ArrayList<String>();
  327. Enumeration<? extends FileObject> files = FileUtil.getConfigRoot().getChildren(true);
  328. while (files.hasMoreElements()) {
  329. FileObject fo = files.nextElement();
  330. if (skipFile(fo)) {
  331. continue;
  332. }
  333. try {
  334. DataObject obj = DataObject.find (fo);
  335. InstanceCookie ic = obj.getLookup().lookup(InstanceCookie.class);
  336. if (ic != null) {
  337. Object o = ic.instanceCreate ();
  338. if (fo.getPath().matches("Services/.+[.]instance")) {
  339. String instanceOf = (String) fo.getAttribute("instanceOf");
  340. if (instanceOf == null) {
  341. errors.add("File " + fo.getPath() + " should declare instanceOf");
  342. } else if (o != null) {
  343. for (String piece : instanceOf.split(", ?")) {
  344. if (!Class.forName(piece, true, Lookup.getDefault().lookup(ClassLoader.class)).isInstance(o)) {
  345. errors.add("File " + fo.getPath() + " claims to be a " + piece + " but is not (instance of " + o.getClass() + ")");
  346. }
  347. }
  348. }
  349. } else if (fo.getPath().matches("Services/.+[.]settings")) {
  350. if (!fo.asText().contains("<instanceof")) {
  351. errors.add("File " + fo.getPath() + " should declare <instanceof class=\"...\"/>");
  352. }
  353. // XXX test assignability here too, perhaps (but only used in legacy code)
  354. }
  355. }
  356. } catch (Exception ex) {
  357. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  358. PrintStream ps = new PrintStream(baos);
  359. ex.printStackTrace(ps);
  360. ps.flush();
  361. errors.add(
  362. "File " + fo.getPath() +
  363. "\nRead from: " + Arrays.toString((Object[])fo.getAttribute("layers")) +
  364. "\nthrew: " + baos);
  365. }
  366. }
  367. assertNoErrors("Some instances cannot be created", errors);
  368. }
  369. public void testActionInstancesOnlyInActionsFolder() {
  370. List<String> errors = new ArrayList<String>();
  371. Enumeration<? extends FileObject> files = FileUtil.getConfigRoot().getChildren(true);
  372. FILE: while (files.hasMoreElements()) {
  373. FileObject fo = files.nextElement();
  374. if (skipFile(fo)) {
  375. continue;
  376. }
  377. try {
  378. DataObject obj = DataObject.find (fo);
  379. InstanceCookie ic = obj.getLookup().lookup(InstanceCookie.class);
  380. if (ic == null) {
  381. continue;
  382. }
  383. Object o;
  384. try {
  385. o = ic.instanceCreate();
  386. } catch (ClassNotFoundException ok) {
  387. // wrong instances are catched by another test
  388. continue;
  389. }
  390. if (!(o instanceof Action)) {
  391. continue;
  392. }
  393. if (fo.hasExt("xml")) {
  394. continue;
  395. }
  396. if (fo.getPath().startsWith("Actions/")) {
  397. continue;
  398. }
  399. if (fo.getPath().startsWith("Editors/")) {
  400. // editor is a bit different world
  401. continue;
  402. }
  403. if (fo.getPath().startsWith("Databases/Explorer/")) {
  404. // db explorer actions shall not influence start
  405. // => let them be for now.
  406. continue;
  407. }
  408. if (fo.getPath().startsWith("WelcomePage/")) {
  409. // welcome screen actions are not intended for end user
  410. continue;
  411. }
  412. if (fo.getPath().startsWith("Projects/org-netbeans-modules-mobility-project/Actions/")) {
  413. // I am not sure what mobility is doing, but
  414. // I guess I do not need to care
  415. continue;
  416. }
  417. if (fo.getPath().startsWith("NativeProjects/Actions/")) {
  418. // XXX should perhaps be replaced
  419. continue;
  420. }
  421. if (fo.getPath().startsWith("contextmenu/uml/")) {
  422. // UML is not the most important thing to fix
  423. continue;
  424. }
  425. if (fo.getPath().equals("Menu/Help/org-netbeans-modules-j2ee-blueprints-ShowBluePrintsAction.instance")) {
  426. // action included in some binary blob
  427. continue;
  428. }
  429. if (Boolean.TRUE.equals(fo.getAttribute("misplaced.action.allowed"))) {
  430. // it seems necessary some actions to stay outside
  431. // of the Actions folder
  432. continue;
  433. }
  434. if (fo.hasExt("shadow")) {
  435. o = fo.getAttribute("originalFile");
  436. if (o instanceof String) {
  437. String origF = o.toString().replaceFirst("\\/*", "");
  438. if (origF.startsWith("Actions/")) {
  439. continue;
  440. }
  441. if (origF.startsWith("Editors/")) {
  442. continue;
  443. }
  444. }
  445. }
  446. errors.add("File " + fo.getPath() + " represents an action which is not in Actions/ subfolder. Provided by " + Arrays.toString((Object[])fo.getAttribute("layers")));
  447. } catch (Exception ex) {
  448. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  449. PrintStream ps = new PrintStream(baos);
  450. ex.printStackTrace(ps);
  451. ps.flush();
  452. errors.add ("File " + fo.getPath() + " threw: " + baos);
  453. }
  454. }
  455. assertNoErrors(errors.size() + " actions is not registered properly", errors);
  456. }
  457. public void testLayerOverrides() throws Exception {
  458. ClassLoader l = Lookup.getDefault().lookup(ClassLoader.class);
  459. assertNotNull ("In the IDE mode, there always should be a classloader", l);
  460. class ContentAndAttrs {
  461. final byte[] contents;
  462. final Map<String,Object> attrs;
  463. private final URL layerURL;
  464. ContentAndAttrs(byte[] contents, Map<String,Object> attrs, URL layerURL) {
  465. this.contents = contents;
  466. this.attrs = attrs;
  467. this.layerURL = layerURL;
  468. }
  469. public @Override String toString() {
  470. return "ContentAndAttrs[contents=" + Arrays.toString(contents) + ",attrs=" + attrs + ";from=" + layerURL + "]";
  471. }
  472. public @Override int hashCode() {
  473. return Arrays.hashCode(contents) ^ attrs.hashCode();
  474. }
  475. public @Override boolean equals(Object o) {
  476. if (!(o instanceof ContentAndAttrs)) {
  477. return false;
  478. }
  479. ContentAndAttrs caa = (ContentAndAttrs) o;
  480. return Arrays.equals(contents, caa.contents) && attrs.equals(caa.attrs);
  481. }
  482. }
  483. Map</* path */String,Map</* owner */String,ContentAndAttrs>> files = new TreeMap<String,Map<String,ContentAndAttrs>>();
  484. Map</* path */String,Map</* attr name */String,Map</* module name */String,/* attr value */Object>>> folderAttributes =
  485. new TreeMap<String,Map<String,Map<String,Object>>>();
  486. Map<String,Set<String>> directDeps = new HashMap<String,Set<String>>();
  487. StringBuffer sb = new StringBuffer();
  488. Map<String,URL> hiddenFiles = new HashMap<String, URL>();
  489. Set<String> allFiles = new HashSet<String>();
  490. final String suffix = "_hidden";
  491. Enumeration<URL> en = l.getResources("META-INF/MANIFEST.MF");
  492. while (en.hasMoreElements ()) {
  493. URL u = en.nextElement();
  494. InputStream is = u.openStream();
  495. Manifest mf;
  496. try {
  497. mf = new Manifest(is);
  498. } finally {
  499. is.close();
  500. }
  501. String module = mf.getMainAttributes ().getValue ("OpenIDE-Module");
  502. if (module == null) {
  503. continue;
  504. }
  505. String depsS = mf.getMainAttributes().getValue("OpenIDE-Module-Module-Dependencies");
  506. if (depsS != null) {
  507. Set<String> deps = new HashSet<String>();
  508. for (Dependency d : Dependency.create(Dependency.TYPE_MODULE, depsS)) {
  509. deps.add(d.getName().replaceFirst("/.+$", ""));
  510. }
  511. directDeps.put(module, deps);
  512. }
  513. for (boolean generated : new boolean[] {false, true}) {
  514. String layer;
  515. if (generated) {
  516. layer = "META-INF/generated-layer.xml";
  517. } else {
  518. layer = mf.getMainAttributes ().getValue ("OpenIDE-Module-Layer");
  519. if (layer == null) {
  520. continue;
  521. }
  522. }
  523. URL base = new URL(u, "../");
  524. URL layerURL = new URL(base, layer);
  525. URLConnection connect;
  526. try {
  527. connect = layerURL.openConnection();
  528. connect.connect();
  529. } catch (FileNotFoundException x) {
  530. if (generated) {
  531. continue;
  532. } else {
  533. throw x;
  534. }
  535. }
  536. connect.setDefaultUseCaches (false);
  537. FileSystem fs = new XMLFileSystem(layerURL);
  538. Enumeration<? extends FileObject> all = fs.getRoot().getChildren(true);
  539. while (all.hasMoreElements ()) {
  540. FileObject fo = all.nextElement ();
  541. String simplePath = fo.getPath();
  542. if (simplePath.endsWith(suffix)) {
  543. hiddenFiles.put(simplePath, layerURL);
  544. } else {
  545. allFiles.add(simplePath);
  546. }
  547. Number weight = (Number) fo.getAttribute("weight");
  548. // XXX if weight != null, test that it is actually overriding something or being overridden
  549. String weightedPath = weight == null ? simplePath : simplePath + "#" + weight;
  550. Map<String,Object> attributes = getAttributes(fo, base);
  551. if (fo.isFolder()) {
  552. for (Map.Entry<String,Object> attr : attributes.entrySet()) {
  553. Map<String,Map<String,Object>> m1 = folderAttributes.get(weightedPath);
  554. if (m1 == null) {
  555. m1 = new TreeMap<String,Map<String,Object>>();
  556. folderAttributes.put(weightedPath, m1);
  557. }
  558. Map<String,Object> m2 = m1.get(attr.getKey());
  559. if (m2 == null) {
  560. m2 = new TreeMap<String,Object>();
  561. m1.put(attr.getKey(), m2);
  562. }
  563. m2.put(module, attr.getValue());
  564. }
  565. continue;
  566. }
  567. Map<String,ContentAndAttrs> overrides = files.get(weightedPath);
  568. if (overrides == null) {
  569. overrides = new TreeMap<String,ContentAndAttrs>();
  570. files.put(weightedPath, overrides);
  571. }
  572. overrides.put(module, new ContentAndAttrs(fo.asBytes(), attributes, layerURL));
  573. }
  574. // make sure the filesystem closes the stream
  575. connect.getInputStream ().close ();
  576. }
  577. }
  578. assertFalse("At least one layer file is usually used", allFiles.isEmpty());
  579. for (Map.Entry<String,Map<String,ContentAndAttrs>> e : files.entrySet()) {
  580. Map<String,ContentAndAttrs> overrides = e.getValue();
  581. if (overrides.size() == 1) {
  582. continue;
  583. }
  584. Set<String> overriders = overrides.keySet();
  585. String file = e.getKey();
  586. if (new HashSet<ContentAndAttrs>(overrides.values()).size() == 1) {
  587. // All the same. Check whether these are parallel declarations (e.g. CND debugger vs. Java debugger), or vertical.
  588. for (String overrider : overriders) {
  589. Set<String> deps = new HashSet<String>(directDeps.get(overrider));
  590. deps.retainAll(overriders);
  591. if (!deps.isEmpty()) {
  592. sb.append(file).append(" is pointlessly overridden in ").append(overrider).
  593. append(" relative to ").append(deps.iterator().next()).append('\n');
  594. }
  595. }
  596. continue;
  597. }
  598. sb.append(file).append(" is provided by: ").append(overriders).append('\n');
  599. for (Map.Entry<String,ContentAndAttrs> entry : overrides.entrySet()) {
  600. ContentAndAttrs contentAttrs = entry.getValue();
  601. sb.append(" ").append(entry.getKey()).append(": content = '").append(new String(contentAttrs.contents)).
  602. append("', attributes = ").append(contentAttrs.attrs).append("\n");
  603. }
  604. }
  605. for (Map.Entry<String,Map<String,Map<String,Object>>> entry1 : folderAttributes.entrySet()) {
  606. for (Map.Entry<String,Map<String,Object>> entry2 : entry1.getValue().entrySet()) {
  607. if (new HashSet<Object>(entry2.getValue().values()).size() > 1) {
  608. sb.append("Some modules conflict on the definition of ").append(entry2.getKey()).append(" for ").
  609. append(entry1.getKey()).append(": ").append(entry2.getValue()).append("\n");
  610. }
  611. }
  612. }
  613. if (sb.length () > 0) {
  614. fail("Some modules override some files without using the weight attribute correctly\n" + sb);
  615. }
  616. for (Map.Entry<String, URL> e : hiddenFiles.entrySet()) {
  617. String p = e.getKey().substring(0, e.getKey().length() - suffix.length());
  618. if (allFiles.contains(p)) {
  619. continue;
  620. }
  621. sb.append("file ").append(e.getKey()).append(" from ").append(e.getValue()).append(" does not hide any other file\n");
  622. }
  623. if (sb.length () > 0) {
  624. fail ("There are some useless hidden files\n" + sb);
  625. }
  626. }
  627. /* Too many failures to solve right now.
  628. public void testLocalizingBundles() throws Exception {
  629. StringBuilder sb = new StringBuilder();
  630. for (URL u : NbCollections.iterable(Lookup.getDefault().lookup(ClassLoader.class).getResources("META-INF/MANIFEST.MF"))) {
  631. String layer;
  632. InputStream is = u.openStream();
  633. try {
  634. layer = new Manifest(is).getMainAttributes().getValue("OpenIDE-Module-Layer");
  635. if (layer == null) {
  636. continue;
  637. }
  638. } finally {
  639. is.close();
  640. }
  641. URL base = new URL(u, "../");
  642. URL layerURL = new URL(base, layer);
  643. URLConnection connect = layerURL.openConnection();
  644. connect.setDefaultUseCaches(false);
  645. for (FileObject fo : NbCollections.iterable(new XMLFileSystem(layerURL).getRoot().getChildren(true))) {
  646. Object v = getAttributes(fo, base).get(SFS_LB);
  647. if (v instanceof Exception) {
  648. sb.append(layerURL).append(": ").append(v).append("\n");
  649. }
  650. }
  651. }
  652. if (sb.length() > 0) {
  653. fail("Some localizing bundle declarations are wrong\n" + sb);
  654. }
  655. }
  656. */
  657. public void testNoWarningsFromLayerParsing() throws Exception {
  658. ClassLoader l = Lookup.getDefault().lookup(ClassLoader.class);
  659. assertNotNull ("In the IDE mode, there always should be a classloader", l);
  660. List<URL> urls = new ArrayList<URL>();
  661. Enumeration<URL> en = l.getResources("META-INF/MANIFEST.MF");
  662. while (en.hasMoreElements ()) {
  663. URL u = en.nextElement();
  664. InputStream is = u.openStream();
  665. Manifest mf;
  666. try {
  667. mf = new Manifest(is);
  668. } finally {
  669. is.close();
  670. }
  671. String module = mf.getMainAttributes ().getValue ("OpenIDE-Module");
  672. if (module == null) {
  673. continue;
  674. }
  675. String layer = mf.getMainAttributes ().getValue ("OpenIDE-Module-Layer");
  676. if (layer == null) {
  677. continue;
  678. }
  679. URL layerURL = new URL(u, "../" + layer);
  680. urls.add(layerURL);
  681. }
  682. File cacheDir;
  683. File workDir = getWorkDir();
  684. int i = 0;
  685. do {
  686. cacheDir = new File(workDir, "layercache"+i);
  687. i++;
  688. } while (!cacheDir.mkdir());
  689. System.setProperty("netbeans.user", cacheDir.getPath());
  690. LayerCacheManager bcm = LayerCacheManager.manager(true);
  691. Logger err = Logger.getLogger("org.netbeans.core.projects.cache");
  692. TestHandler h = new TestHandler();
  693. err.addHandler(h);
  694. ByteArrayOutputStream os = new ByteArrayOutputStream();
  695. bcm.store(bcm.createEmptyFileSystem(), urls, os);
  696. assertNoErrors("No errors or warnings during layer parsing", h.errors);
  697. }
  698. private static class TestHandler extends Handler {
  699. List<String> errors = new ArrayList<String>();
  700. TestHandler () {}
  701. public @Override void publish(LogRecord rec) {
  702. if (Level.WARNING.equals(rec.getLevel()) || Level.SEVERE.equals(rec.getLevel())) {
  703. errors.add(MessageFormat.format(rec.getMessage(), rec.getParameters()));
  704. }
  705. }
  706. List<String> errors() {
  707. return errors;
  708. }
  709. public @Override void flush() {}
  710. public @Override void close() throws SecurityException {}
  711. }
  712. public void testFolderOrdering() throws Exception {
  713. TestHandler h = new TestHandler();
  714. Logger.getLogger("org.openide.filesystems.Ordering").addHandler(h);
  715. Set<List<String>> editorMultiFolders = new HashSet<List<String>>();
  716. Pattern editorFolder = Pattern.compile("Editors/(application|text)/([^/]+)(/.+|$)");
  717. Enumeration<? extends FileObject> files = FileUtil.getConfigRoot().getChildren(true);
  718. while (files.hasMoreElements()) {
  719. FileObject fo = files.nextElement();
  720. if (fo.isFolder()) {
  721. loadChildren(fo);
  722. assertNull("OpenIDE-Folder-Order attr should not be used on " + fo, fo.getAttribute("OpenIDE-Folder-Order"));
  723. assertNull("OpenIDE-Folder-SortMode attr should not be used on " + fo, fo.getAttribute("OpenIDE-Folder-SortMode"));
  724. String path = fo.getPath();
  725. Matcher m = editorFolder.matcher(path);
  726. if (m.matches()) {
  727. List<String> multiPath = new ArrayList<String>(3);
  728. multiPath.add(path);
  729. if (m.group(2).endsWith("+xml")) {
  730. multiPath.add("Editors/" + m.group(1) + "/xml" + m.group(3));
  731. }
  732. multiPath.add("Editors" + m.group(3));
  733. editorMultiFolders.add(multiPath);
  734. }
  735. }
  736. }
  737. assertNoErrors("No warnings relating to folder ordering; " +
  738. "cf: http://deadlock.netbeans.org/hudson/job/nbms-and-javadoc/lastSuccessfulBuild/artifact/nbbuild/build/generated/layers.txt", h.errors());
  739. for (List<String> multiPath : editorMultiFolders) {
  740. List<FileSystem> layers = new ArrayList<FileSystem>(3);
  741. for (final String path : multiPath) {
  742. FileObject folder = FileUtil.getConfigFile(path);
  743. if (folder != null) {
  744. layers.add(new MultiFileSystem(folder.getFileSystem()) {
  745. protected @Override FileObject findResourceOn(FileSystem fs, String res) {
  746. FileObject f = fs.findResource(path + '/' + res);
  747. return Boolean.TRUE.equals(f.getAttribute("hidden")) ? null : f;
  748. }
  749. });
  750. }
  751. }
  752. loadChildren(new MultiFileSystem(layers.toArray(new FileSystem[layers.size()])).getRoot());
  753. assertNoErrors("No warnings relating to folder ordering in " + multiPath +
  754. "; cf: http://deadlock.netbeans.org/hudson/job/nbms-and-javadoc/lastSuccessfulBuild/artifact/nbbuild/build/generated/layers.txt",
  755. h.errors());
  756. }
  757. }
  758. private static void loadChildren(FileObject folder) {
  759. List<FileObject> kids = new ArrayList<FileObject>();
  760. for (DataObject kid : DataFolder.findFolder(folder).getChildren()) {
  761. kids.add(kid.getPrimaryFile());
  762. }
  763. FileUtil.getOrder(kids, true);
  764. }
  765. private static Map<String,Object> getAttributes(FileObject fo, URL base) {
  766. Map<String,Object> attrs = new TreeMap<String,Object>();
  767. Enumeration<String> en = fo.getAttributes();
  768. while (en.hasMoreElements()) {
  769. String attrName = en.nextElement();
  770. if (isInstanceAttribute(attrName)) {
  771. continue;
  772. }
  773. Object attr = fo.getAttribute(attrName);
  774. if (attrName.equals(SFS_LB)) {
  775. try {
  776. String bundleName = (String) attr;
  777. URL bundle = new URL(base, bundleName.replace('.', '/') + ".properties");
  778. Properties p = new Properties();
  779. InputStream is = bundle.openStream();
  780. try {
  781. p.load(is);
  782. } finally {
  783. is.close();
  784. }
  785. String path = fo.getPath();
  786. attr = p.get(path);
  787. if (attr == null) {
  788. attr = new MissingResourceException("No such bundle entry " + path + " in " + bundleName, bundleName, path);
  789. }
  790. } catch (Exception x) {
  791. attr = x;
  792. }
  793. }
  794. attrs.put(attrName, attr);
  795. }
  796. return attrs;
  797. }
  798. private static final String[] SKIPPED = {
  799. "Templates/GUIForms",
  800. "Palette/Borders/javax-swing-border-",
  801. "Palette/Layouts/javax-swing-BoxLayout",
  802. "Templates/Beans/",
  803. "PaletteUI/org-netbeans-modules-form-palette-CPComponent",
  804. "Templates/Ant/CustomTask.java",
  805. "Templates/Privileged/Main.shadow",
  806. "Templates/Privileged/JFrame.shadow",
  807. "Templates/Privileged/Class.shadow",
  808. "Templates/Classes",
  809. "Templates/JSP_Servlet",
  810. "EnvironmentProviders/ProfileTypes/Execution/nb-j2ee-deployment.instance",
  811. "Shortcuts/D-BACK_QUOTE.shadow",
  812. "Windows2/Components/", // cannot be loaded with a headless toolkit, so we have to skip these for now
  813. };
  814. private boolean skipFile(FileObject fo) {
  815. String s = fo.getPath();
  816. if (s.startsWith ("Templates/") && !s.startsWith ("Templates/Services")) {
  817. if (s.endsWith (".shadow") || s.endsWith (".java")) {
  818. return true;
  819. }
  820. }
  821. for (String skipped : SKIPPED) {
  822. if (s.startsWith(skipped)) {
  823. return true;
  824. }
  825. }
  826. String iof = (String) fo.getAttribute("instanceOf");
  827. if (iof != null) {
  828. for (String clz : iof.split("[, ]+")) {
  829. try {
  830. Class<?> c = Lookup.getDefault().lookup(ClassLoader.class).loadClass(clz);
  831. } catch (ClassNotFoundException x) {
  832. // E.g. Services/Hidden/org-netbeans-lib-jsch-antlibrary.instance in ide cluster
  833. // cannot be loaded (and would just be ignored) if running without java cluster
  834. System.err.println("Warning: skipping " + fo.getPath() + " due to inaccessible interface " + clz);
  835. return true;
  836. }
  837. }
  838. }
  839. return false;
  840. }
  841. public void testKeymapOverrides() throws Exception { // #170677
  842. List<String> warnings = new ArrayList<String>();
  843. FileObject[] keymaps = FileUtil.getConfigFile("Keymaps").getChildren();
  844. Map<String,Integer> definitionCountById = new HashMap<String,Integer>();
  845. assertTrue("Too many keymaps for too little bitfield", keymaps.length < 31);
  846. int keymapFlag = 1;
  847. for (FileObject keymap : keymaps) {
  848. for (FileObject shortcut : keymap.getChildren()) {
  849. DataObject d = DataObject.find(shortcut);
  850. if (d instanceof DataShadow) {
  851. String id = ((DataShadow) d).getOriginal().getPrimaryFile().getPath();
  852. Integer prior = definitionCountById.get(id);
  853. // a single keymap may provide alternative shortcuts for a given action. Count just once
  854. // per keymap.
  855. definitionCountById.put(id, prior == null ? keymapFlag : prior | keymapFlag);
  856. } else if (!d.getPrimaryFile().hasExt("shadow") && !d.getPrimaryFile().hasExt("removed")) {
  857. warnings.add("Anomalous file " + d);
  858. } // else #172453: BrokenDataShadow, OK
  859. }
  860. keymapFlag <<= 1;
  861. }
  862. int expected = (1 << keymaps.length) - 1;
  863. for (FileObject shortcut : FileUtil.getConfigFile("Shortcuts").getChildren()) {
  864. DataObject d = DataObject.find(shortcut);
  865. if (d instanceof DataShadow) {
  866. String id = ((DataShadow) d).getOriginal().getPrimaryFile().getPath();
  867. if (!org.openide.util.Utilities.isMac() && // Would fail on Mac due to applemenu module
  868. Integer.valueOf(expected).equals(definitionCountById.get(id)))
  869. {
  870. String layers = Arrays.toString((URL[]) d.getPrimaryFile().getAttribute("layers"));
  871. warnings.add(d.getPrimaryFile().getPath() + " " + layers + " useless since " + id + " is bound (somehow) in all keymaps");
  872. }
  873. } else if (!d.getPrimaryFile().hasExt("shadow")) {
  874. warnings.add("Anomalous file " + d);
  875. }
  876. }
  877. // XXX consider also checking for bindings in Shortcuts/ which are overridden in all keymaps or at least NetBeans
  878. // taking into consideration O- and D- virtual modifiers
  879. // (this is likely to be more common, e.g. mysterious Shortcuts/D-A.shadow in uml.drawingarea)
  880. // XXX check for shortcut conflict between Shortcuts and each keymap, e.g. Ctrl-R in Eclipse keymap
  881. assertNoErrors("Some shortcuts were overridden by keymaps", warnings);
  882. }
  883. /* XXX too many failures for now, some spurious; use regex, or look for unloc files/folders with loc siblings?
  884. public void testLocalizedFolderNames() throws Exception {
  885. List<String> warnings = new ArrayList<String>();
  886. for (String folder : new String[] {
  887. "Actions", // many legit failures!
  888. "OptionsDialog/Actions", // XXX #71280
  889. "Menu",
  890. "Toolbars",
  891. "org-netbeans-modules-java-hints/rules/hints",
  892. "Editors/FontsColors", // XXX exclude .../Defaults
  893. "Keymaps",
  894. "FormDesignerPalette", // XXX match any *Palette?
  895. "HTMLPalette",
  896. "XHTMLPalette",
  897. "JSPPalette",
  898. "SVGXMLPalette",
  899. "OptionsExport",
  900. // "Projects/.../Customizer",
  901. "QuickSearch",
  902. "Templates", // XXX exclude Privileged, Recent, Services
  903. }) {
  904. FileObject root = FileUtil.getConfigFile(folder);
  905. if (root == null) {
  906. continue;
  907. }
  908. for (FileObject d : NbCollections.iterable(root.getFolders(true))) {
  909. if (d.getAttribute("displayName") == null && d.getAttribute("SystemFileSystem.localizingBundle") == null) {
  910. warnings.add("No displayName for " + d.getPath());
  911. }
  912. }
  913. }
  914. assertNoErrors("Some folders need a localized display name", warnings);
  915. }
  916. */
  917. public void testTemplates() throws Exception { // #167205
  918. List<String> warnings = new ArrayList<String>();
  919. for (FileObject f : NbCollections.iterable(FileUtil.getConfigFile("Templates").getData(true))) {
  920. if (!Boolean.TRUE.equals(f.getAttribute("template"))) {
  921. continue; // will not appear in Template Manager
  922. }
  923. if (f.getSize() > 0) {
  924. continue; // Open in Editor will be enabled
  925. }
  926. if (f.getAttribute("instantiatingIterator") != null) { // TemplateWizard.CUSTOM_ITERATOR
  927. continue; // probably not designed to be edited as text
  928. }
  929. if (f.getAttribute("templateWizardIterator") != null) { // TemplateWizard.EA_ITERATOR
  930. continue; // same
  931. }
  932. String path = f.getPath();
  933. if (path.equals("Templates/Other/file") ||
  934. path.equals("Templates/Other/group.group")) {
  935. // If there're more files like this, consider adding an API
  936. // to mark them as intentionally non-editable
  937. continue; // intentionally empty and uneditable
  938. }
  939. warnings.add(path + " is empty but has no iterator and will therefore not be editable");
  940. }
  941. assertNoErrors("Problems in templates", warnings);
  942. }
  943. }