// Copyright 2014 The Bazel Authors. All rights reserved.
//
// 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 com.google.devtools.build.lib.vfs;

import com.google.devtools.build.lib.testutil.TestUtils;
import com.google.devtools.build.lib.vfs.util.FileSystems;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

import java.io.File;
import java.io.IOException;

/**
 * (Slow) tests of FileSystem under concurrency.
 *
 * These tests are nondeterministic but provide good coverage nonetheless.
 */
@RunWith(JUnit4.class)
public class FileSystemConcurrencyTest {

  Path workingDir;

  @Before
  public final void initializeFileSystem() throws Exception  {
    FileSystem testFS = FileSystems.getNativeFileSystem();

    // Resolve symbolic links in the temp dir:
    workingDir = testFS.getPath(new File(TestUtils.tmpDir()).getCanonicalPath());
  }

  @Test
  public void testConcurrentSymlinkModifications() throws Exception {
    final Path xFile = workingDir.getRelative("file");
    FileSystemUtils.createEmptyFile(xFile);

    final Path xLinkToFile = workingDir.getRelative("link");

    // "Boxed" for pass-by-reference.
    final boolean[] run = { true };
    final IOException[] exception = { null };
    Thread createThread = new Thread() {
      @Override
      public void run() {
        while (run[0]) {
          if (!xLinkToFile.exists()) {
            try {
              xLinkToFile.createSymbolicLink(xFile);
            } catch (IOException e) {
              exception[0] = e;
              return;
            }
          }
        }
      }
    };
    Thread deleteThread = new Thread() {
      @Override
      public void run() {
        while (run[0]) {
          if (xLinkToFile.exists(Symlinks.NOFOLLOW)) {
            try {
              xLinkToFile.delete();
            } catch (IOException e) {
              exception[0] = e;
              return;
            }
          }
        }
      }
    };
    createThread.start();
    deleteThread.start();
    Thread.sleep(1000);
    run[0] = false;
    createThread.join(0);
    deleteThread.join(0);

    if (exception[0] != null) {
      throw exception[0];
    }
  }

}

@@ -648,24 +648,82 @@
 d.lib.vfs;%0A%0A
+import com.google.devtools.build.lib.testutil.TestThread;%0A
 import com.g
@@ -826,16 +826,86 @@
 ystems;%0A
+import java.io.File;%0Aimport java.util.concurrent.atomic.AtomicBoolean;
 %0Aimport 
@@ -1016,58 +1016,8 @@
 4;%0A%0A
-import java.io.File;%0Aimport java.io.IOException;%0A%0A
 /**%0A
@@ -1211,17 +1211,16 @@
 yTest %7B%0A
-%0A
   Path w
@@ -1576,30 +1576,24 @@
 ption %7B%0A    
-final 
 Path xFile =
@@ -1674,22 +1674,16 @@
 );%0A%0A    
-final 
 Path xLi
@@ -1734,128 +1734,61 @@
     
-// %22Boxed%22 for pass-by-reference.%0A    final b
+AtomicB
 oolean
-%5B%5D
  run = 
-%7B true %7D;%0A    final IOException%5B%5D exception = %7B null %7D
+new AtomicBoolean(true)
 ;%0A    
+Test
 Thre
@@ -1808,74 +1808,64 @@
 ad =
+%0A       
  new 
+Test
 Thread(
-) %7B
 %0A      
-@Override%0A      public void run() %7B%0A
+      () -%3E %7B%0A   
         
 whil
@@ -1852,32 +1852,35 @@
 -%3E %7B%0A           
+   
 while (run%5B0%5D) %7B
@@ -1865,39 +1865,48 @@
       while (run
-%5B0%5D) %7B%0A
+.get()) %7B%0A      
           if (!x
@@ -1930,36 +1930,24 @@
 ()) %7B%0A      
-      try %7B%0A
             
@@ -1938,34 +1938,32 @@
                 
-  
 xLinkToFile.crea
@@ -2001,143 +2001,54 @@
     
-%7D catch (IOException e) %7B%0A              exception%5B0%5D = e;%0A              return;%0A            %7D%0A          %7D%0A        %7D%0A      %7D%0A    %7D
+    %7D%0A              %7D%0A            %7D)
 ;%0A    
+Test
 Thre
@@ -2068,66 +2068,59 @@
 ad =
+%0A       
  new 
+Test
 Thread(
-) %7B
 %0A      
-@Override%0A      public void run() %7B%0A
+      () -%3E %7B%0A      
     
@@ -2133,19 +2133,22 @@
 ile (run
-%5B0%5D
+.get()
 ) %7B%0A    
@@ -2149,24 +2149,30 @@
 %7B%0A          
+ 
+     
 if (xLinkToF
@@ -2212,22 +2212,8 @@
     
-        try %7B%0A
     
@@ -2260,137 +2260,44 @@
     
-%7D catch (IOException e) %7B%0A              exception%5B0%5D = e;%0A              return;%0A            %7D%0A          %7D%0A        %7D%0A      %7D%0A    %7D
+    %7D%0A              %7D%0A            %7D)
 ;%0A  
@@ -2381,19 +2381,19 @@
  run
-%5B0%5D = 
+.set(
 false
+)
 ;%0A  
@@ -2415,103 +2415,134 @@
 join
-(0);%0A    deleteThread.join(0);%0A%0A    if (exception%5B0%5D != null) %7B%0A      throw exception%5B0%5D;%0A    %7D
+AndAssertState(TestUtils.WAIT_TIMEOUT_MILLISECONDS);%0A    deleteThread.joinAndAssertState(TestUtils.WAIT_TIMEOUT_MILLISECONDS);
 %0A  %7D

Use better atomic constructs in test.